diff --git a/includes/sanitizers/class-amp-audio-sanitizer.php b/includes/sanitizers/class-amp-audio-sanitizer.php
index a90040a7cce..e204c7e27cb 100644
--- a/includes/sanitizers/class-amp-audio-sanitizer.php
+++ b/includes/sanitizers/class-amp-audio-sanitizer.php
@@ -8,6 +8,10 @@ class AMP_Audio_Sanitizer extends AMP_Base_Sanitizer {
private static $script_slug = 'amp-audio';
private static $script_src = 'https://cdn.ampproject.org/v0/amp-audio-0.1.js';
+ protected $DEFAULT_ARGS = array(
+ 'require_https_src' => true,
+ );
+
public function get_scripts() {
if ( ! $this->did_convert_elements ) {
return array();
@@ -24,9 +28,15 @@ public function sanitize() {
}
for ( $i = $num_nodes - 1; $i >= 0; $i-- ) {
+ $orig_src = array();
+
$node = $nodes->item( $i );
$old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
+ if ( isset( $old_attributes['src'] ) ) {
+ $orig_src[] = $old_attributes['src'];
+ }
+
$new_attributes = $this->filter_attributes( $old_attributes );
$new_node = AMP_DOM_Utils::create_node( $this->dom, 'amp-audio', $new_attributes );
@@ -35,21 +45,36 @@ public function sanitize() {
foreach ( $node->childNodes as $child_node ) {
$new_child_node = $child_node->cloneNode( true );
$old_child_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $new_child_node );
+
+ if ( isset( $old_child_attributes['src'] ) ) {
+ $orig_src[] = $old_child_attributes['src'];
+ }
+
$new_child_attributes = $this->filter_attributes( $old_child_attributes );
// Only append source tags with a valid src attribute
if ( ! empty( $new_child_attributes['src'] ) && 'source' === $new_child_node->tagName ) {
+ AMP_DOM_Utils::add_attributes_to_node( $new_child_node, $new_child_attributes );
$new_node->appendChild( $new_child_node );
}
}
// If the node has at least one valid source, replace the old node with it.
+ // If the node has no valid sources, but at least one invalid (http) one, add a fallback element.
// Otherwise, just remove the node.
- //
- // TODO: Add a fallback handler.
- // See: https://github.com/ampproject/amphtml/issues/2261
if ( 0 === $new_node->childNodes->length && empty( $new_attributes['src'] ) ) {
- $node->parentNode->removeChild( $node );
+ if ( ! empty( $orig_src ) ) {
+ $fallback_node = $this->create_fallback_node(
+ sprintf(
+ wp_kses( __( 'Could not load audio.', 'amp' ), array( 'a' => array( 'href' => true ) ) ),
+ esc_url( array_shift( $orig_src ) )
+ )
+ );
+
+ $node->parentNode->replaceChild( $fallback_node, $node );
+ } else {
+ $node->parentNode->removeChild( $node );
+ }
} else {
$node->parentNode->replaceChild( $new_node, $node );
}
diff --git a/includes/sanitizers/class-amp-base-sanitizer.php b/includes/sanitizers/class-amp-base-sanitizer.php
index 3a4d78c079d..4ada9b64156 100644
--- a/includes/sanitizers/class-amp-base-sanitizer.php
+++ b/includes/sanitizers/class-amp-base-sanitizer.php
@@ -102,14 +102,31 @@ public function add_or_append_attribute( &$attributes, $key, $value, $separator
* @return string
*/
public function maybe_enforce_https_src( $src, $force_https = false ) {
+ $https_required = isset( $this->args['require_https_src'] ) && true === $this->args['require_https_src'];
$protocol = strtok( $src, ':' );
+
+ if ( $protocol === $src && ( $https_required || $force_https ) ) {
+ if ( 0 === strpos( $src, '//' ) ) {
+ // src has relative protocol, ie //example.com/asdf, so add https
+ $src = set_url_scheme( $src, 'https' );
+ } else if ( 0 === strpos( $src, '/' ) ) {
+ // src is URL relative to the site root
+ $src = home_url( $src );
+ } else {
+ // src is URL relative to current URI
+ global $wp;
+ $src = home_url( trailingslashit( $wp->request ) . $src );
+ }
+
+ $protocol = strtok( $src, ':' );
+ }
+
if ( 'https' !== $protocol ) {
// Check if https is required
- if ( isset( $this->args['require_https_src'] ) && true === $this->args['require_https_src'] ) {
+ if ( $https_required ) {
// Remove the src. Let the implementing class decide what do from here.
$src = '';
- } elseif ( ( ! isset( $this->args['require_https_src'] ) || false === $this->args['require_https_src'] )
- && true === $force_https ) {
+ } elseif ( ! $https_required && true === $force_https ) {
// Don't remove the src, but force https instead
$src = set_url_scheme( $src, 'https' );
}
@@ -117,4 +134,25 @@ public function maybe_enforce_https_src( $src, $force_https = false ) {
return $src;
}
+
+ protected function create_fallback_node( $content, $container_el = 'blockquote', $attributes = array() ) {
+ $defaults = array(
+ 'class' => 'amp-wp-fallback',
+ );
+ $attributes = wp_parse_args( $attributes, $defaults );
+
+ $fallback_node = AMP_DOM_Utils::create_node(
+ $this->dom,
+ $container_el,
+ $attributes
+ );
+
+ if ( $content ) {
+ $fallback_content = $this->dom->createDocumentFragment();
+ $fallback_content->appendXML( $content );
+ $fallback_node->appendChild( $fallback_content );
+ }
+
+ return $fallback_node;
+ }
}
diff --git a/includes/sanitizers/class-amp-iframe-sanitizer.php b/includes/sanitizers/class-amp-iframe-sanitizer.php
index 95de9680f65..0d8edcbc5bd 100644
--- a/includes/sanitizers/class-amp-iframe-sanitizer.php
+++ b/includes/sanitizers/class-amp-iframe-sanitizer.php
@@ -15,7 +15,8 @@ class AMP_Iframe_Sanitizer extends AMP_Base_Sanitizer {
private static $script_src = 'https://cdn.ampproject.org/v0/amp-iframe-0.1.js';
protected $DEFAULT_ARGS = array(
- 'add_placeholder' => false,
+ 'require_https_src' => true,
+ 'add_placeholder' => false,
);
public function get_scripts() {
@@ -37,16 +38,29 @@ public function sanitize() {
$node = $nodes->item( $i );
$old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
+ $orig_src = '';
+ if ( isset( $old_attributes['src'] ) ) {
+ $orig_src = $old_attributes['src'];
+ }
+
$new_attributes = $this->filter_attributes( $old_attributes );
- // If the src doesn't exist, remove the node.
- // This means that it never existed or was invalidated
- // while filtering attributes above.
- //
- // TODO: add a filter to allow for a fallback element in this instance.
- // See: https://github.com/ampproject/amphtml/issues/2261
+ // If the filtered src doesn't exist, but there's an invalid src, add a fallback element.
+ // Otherwise remove the node.
if ( empty( $new_attributes['src'] ) ) {
- $node->parentNode->removeChild( $node );
+ if ( ! empty( $orig_src ) ) {
+ $fallback_node = $this->create_fallback_node(
+ sprintf(
+ wp_kses( __( 'Could not load iframe.', 'amp' ), array( 'a' => array( 'href' => true ) ) ),
+ esc_url( $orig_src )
+ )
+ );
+
+ $node->parentNode->replaceChild( $fallback_node, $node );
+ } else {
+ $node->parentNode->removeChild( $node );
+ }
+
continue;
}
diff --git a/includes/sanitizers/class-amp-video-sanitizer.php b/includes/sanitizers/class-amp-video-sanitizer.php
index b17f9e5836e..cd4b33d9ed9 100644
--- a/includes/sanitizers/class-amp-video-sanitizer.php
+++ b/includes/sanitizers/class-amp-video-sanitizer.php
@@ -10,6 +10,10 @@ class AMP_Video_Sanitizer extends AMP_Base_Sanitizer {
public static $tag = 'video';
+ protected $DEFAULT_ARGS = array(
+ 'require_https_src' => true,
+ );
+
public function sanitize() {
$nodes = $this->dom->getElementsByTagName( self::$tag );
$num_nodes = $nodes->length;
@@ -18,9 +22,15 @@ public function sanitize() {
}
for ( $i = $num_nodes - 1; $i >= 0; $i-- ) {
+ $orig_src = array();
+
$node = $nodes->item( $i );
$old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node );
+ if ( isset( $old_attributes['src'] ) ) {
+ $orig_src[] = $old_attributes['src'];
+ }
+
$new_attributes = $this->filter_attributes( $old_attributes );
$new_attributes = $this->enforce_fixed_height( $new_attributes );
@@ -32,21 +42,36 @@ public function sanitize() {
foreach ( $node->childNodes as $child_node ) {
$new_child_node = $child_node->cloneNode( true );
$old_child_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $new_child_node );
+
+ if ( isset( $old_child_attributes['src'] ) ) {
+ $orig_src[] = $old_child_attributes['src'];
+ }
+
$new_child_attributes = $this->filter_attributes( $old_child_attributes );
// Only append source tags with a valid src attribute
if ( ! empty( $new_child_attributes['src'] ) && 'source' === $new_child_node->tagName ) {
+ AMP_DOM_Utils::add_attributes_to_node( $new_child_node, $new_child_attributes );
$new_node->appendChild( $new_child_node );
}
}
// If the node has at least one valid source, replace the old node with it.
+ // If the node has no valid sources, but at least one invalid (http) one, add a fallback element.
// Otherwise, just remove the node.
- //
- // TODO: Add a fallback handler.
- // See: https://github.com/ampproject/amphtml/issues/2261
if ( 0 === $new_node->childNodes->length && empty( $new_attributes['src'] ) ) {
- $node->parentNode->removeChild( $node );
+ if ( ! empty( $orig_src ) ) {
+ $fallback_node = $this->create_fallback_node(
+ sprintf(
+ wp_kses( __( 'Could not load video.', 'amp' ), array( 'a' => array( 'href' => true ) ) ),
+ esc_url( array_shift( $orig_src ) )
+ )
+ );
+
+ $node->parentNode->replaceChild( $fallback_node, $node );
+ } else {
+ $node->parentNode->removeChild( $node );
+ }
} else {
$node->parentNode->replaceChild( $new_node, $node );
}
diff --git a/tests/test-amp-audio-converter.php b/tests/test-amp-audio-converter.php
index 8552265660e..6fe8f964e21 100644
--- a/tests/test-amp-audio-converter.php
+++ b/tests/test-amp-audio-converter.php
@@ -70,10 +70,17 @@ public function get_data() {
'
Could not load audio.', + ), ); } @@ -90,7 +97,7 @@ public function test_converter( $source, $expected ) { public function test__https_required() { $source = ''; - $expected = ''; + $expected = '
Could not load audio.'; $dom = AMP_DOM_Utils::get_dom_from_content( $source ); $sanitizer = new AMP_Audio_Sanitizer( $dom, array( diff --git a/tests/test-amp-iframe-sanitizer.php b/tests/test-amp-iframe-sanitizer.php index ae2e535a47e..2f2cd94c104 100644 --- a/tests/test-amp-iframe-sanitizer.php +++ b/tests/test-amp-iframe-sanitizer.php @@ -15,7 +15,7 @@ public function get_data() { 'force_https' => array( '', - '
Could not load iframe.', ), 'iframe_without_dimensions' => array( @@ -116,7 +116,7 @@ public function test_converter( $source, $expected ) { public function test__https_required() { $source = ''; - $expected = ''; + $expected = '
Could not load iframe.'; $dom = AMP_DOM_Utils::get_dom_from_content( $source ); $sanitizer = new AMP_Iframe_Sanitizer( $dom, array( diff --git a/tests/test-amp-video-sanitizer.php b/tests/test-amp-video-sanitizer.php index 481d2d64248..cd31b84d1c9 100644 --- a/tests/test-amp-video-sanitizer.php +++ b/tests/test-amp-video-sanitizer.php @@ -71,10 +71,17 @@ public function get_data() { '
Could not load video.', + ), ); } @@ -91,7 +98,7 @@ public function test_converter( $source, $expected ) { public function test__https_required() { $source = ''; - $expected = ''; + $expected = '
Could not load video.'; $dom = AMP_DOM_Utils::get_dom_from_content( $source ); $sanitizer = new AMP_Video_Sanitizer( $dom, array(