diff --git a/src/AMP.php b/src/AMP.php index ea74d094..9836e62a 100644 --- a/src/AMP.php +++ b/src/AMP.php @@ -65,6 +65,7 @@ class AMP 'Lullabot\AMP\Pass\TwitterTransformPass', 'Lullabot\AMP\Pass\StandardScanPass', 'Lullabot\AMP\Pass\StandardFixPass', + 'Lullabot\AMP\Pass\AmpImgFixPass', 'Lullabot\AMP\Pass\StandardFixPassTwo', 'Lullabot\AMP\Pass\MinimumValidFixPass', 'Lullabot\AMP\Pass\StatisticsPass' diff --git a/src/Pass/AmpImgFixPass.php b/src/Pass/AmpImgFixPass.php new file mode 100644 index 00000000..2fa35b2f --- /dev/null +++ b/src/Pass/AmpImgFixPass.php @@ -0,0 +1,80 @@ +validation_result->errors as $error) { + if (in_array($error->code, [ValidationErrorCode::MANDATORY_ATTR_MISSING, ValidationErrorCode::IMPLIED_LAYOUT_INVALID, ValidationErrorCode::SPECIFIED_LAYOUT_INVALID]) && + !$error->resolved && + !empty($error->dom_tag) && + $error->dom_tag->tagName == 'amp-img' + ) { + $amp_img_el = new DOMQuery($error->dom_tag); + + if (in_array($error->code, [ValidationErrorCode::IMPLIED_LAYOUT_INVALID, ValidationErrorCode::SPECIFIED_LAYOUT_INVALID])) { + $amp_img_el->attr('layout', 'responsive'); + $error->addActionTaken(new ActionTakenLine('amp-img', ActionTakenType::AMP_IMG_FIX_RESPONSIVE)); + $error->resolved = true; + } + + $layout = ParsedTagSpec::parseLayout($amp_img_el->attr('layout')); + if ($error->code == ValidationErrorCode::MANDATORY_ATTR_MISSING && + ($layout !== AmpLayoutLayout::RESPONSIVE || !in_array($error->params[0], ['height', 'width'])) + ) { + continue; + } + + $success = $this->setAmpImgAttributes($amp_img_el); + if ($success) { + $error->addActionTaken(new ActionTakenLine('amp-img', ActionTakenType::AMP_IMG_FIX)); + $error->resolved = true; + } else { + $error->resolved = false; + } + } + } + } +} diff --git a/src/Pass/ImgTagTransformPass.php b/src/Pass/ImgTagTransformPass.php index e0166a94..6b9f1766 100644 --- a/src/Pass/ImgTagTransformPass.php +++ b/src/Pass/ImgTagTransformPass.php @@ -73,9 +73,18 @@ function pass() $context_string = $this->getContextString($dom_el); $new_dom_el = $this->cloneAndRenameDomElement($dom_el, 'amp-img'); $new_el = $el->prev(); - $el->remove(); // remove the old img tag - $this->setAmpImgAttributes($new_el); + $success = $this->setAmpImgAttributes($new_el); + // We were not able to get the image dimensions, abort conversion. + if(!$success) { + $this->addActionTaken(new ActionTakenLine('img', ActionTakenType::IMG_COULD_NOT_BE_CONVERTED, $lineno, $context_string)); + // Abort the conversion and remove the new img tag + $new_el->remove(); + continue; + } + + $el->remove(); // remove the old img tag + $this->setLayoutIfNoLayout($new_el, 'responsive'); $this->context->addLineAssociation($new_dom_el, $lineno); $this->addActionTaken(new ActionTakenLine('img', ActionTakenType::IMG_CONVERTED, $lineno, $context_string)); } @@ -163,9 +172,12 @@ protected function setAmpImgAttributes(DOMQuery $el) if ($dimensions !== false) { $el->attr('width', $dimensions['width']); $el->attr('height', $dimensions['height']); + return true; + } else { + return false; } } - $this->setLayoutIfNoLayout($el, 'responsive'); + return true; } } diff --git a/src/Pass/StandardFixPass.php b/src/Pass/StandardFixPass.php index 76716b29..8e67110f 100644 --- a/src/Pass/StandardFixPass.php +++ b/src/Pass/StandardFixPass.php @@ -42,9 +42,12 @@ class StandardFixPass extends BasePass ValidationErrorCode::DISALLOWED_ATTR, ValidationErrorCode::MISSING_URL, ValidationErrorCode::UNESCAPED_TEMPLATE_IN_ATTR_VALUE, - ValidationErrorCode::TEMPLATE_PARTIAL_IN_ATTR_VALUE - // @todo ValidationErrorCode::MUTUALLY_EXCLUSIVE_ATTRS? + ValidationErrorCode::TEMPLATE_PARTIAL_IN_ATTR_VALUE, + ValidationErrorCode::ATTR_DISALLOWED_BY_SPECIFIED_LAYOUT, + ValidationErrorCode::ATTR_DISALLOWED_BY_IMPLIED_LAYOUT, + ValidationErrorCode::SPECIFIED_LAYOUT_INVALID ]; + protected $remove_tags_for_codes = [ ValidationErrorCode::WRONG_PARENT_TAG, ValidationErrorCode::DISALLOWED_TAG, diff --git a/src/Utility/ActionTakenType.php b/src/Utility/ActionTakenType.php index 946746fb..9e138cdb 100644 --- a/src/Utility/ActionTakenType.php +++ b/src/Utility/ActionTakenType.php @@ -24,6 +24,7 @@ class ActionTakenType const PROPERTY_REMOVED = 'property value pair was removed from attribute due to validation issues.'; const PROPERTY_REMOVED_ATTRIBUTE_REMOVED = 'property value pair was removed from attribute due to validation issues. The resulting attribute was empty and was also removed.'; const IMG_CONVERTED = 'tag was converted to the amp-img tag.'; + const IMG_COULD_NOT_BE_CONVERTED = 'tag could NOT be converted to the amp-img tag as the image is not accessible.'; const INSTAGRAM_CONVERTED = 'instagram embed code was converted to the amp-instagram tag.'; const PINTEREST_CONVERTED = 'pinterest embed code was converted to the amp-pinterest tag.'; const VINE_CONVERTED = 'vine embed code was converted to the amp-vine tag.'; @@ -47,4 +48,6 @@ class ActionTakenType const TAG_REMOVED_FROM_HEAD_AFTER_REVALIDATE_FAILED = 'tag removed from head as it still does not validate. Could not fix tag validation problems.'; const ATTRIBUTE_REMOVED_MUTUALLY_EXCLUSIVE = 'attribute(s) removed as they were mutually exclusive.'; const ISSUE_RESOLVED = 'no further action required as this issue was resolved due to an earlier fix'; + const AMP_IMG_FIX = 'tried to fix problems with amp-img by adjusting one or more of: height, width, and setting layout to responsive'; + const AMP_IMG_FIX_RESPONSIVE = 'tried to fix problems with amp-img by setting layout to responsive'; } diff --git a/src/Validate/ParsedTagSpec.php b/src/Validate/ParsedTagSpec.php index c5393029..32981647 100644 --- a/src/Validate/ParsedTagSpec.php +++ b/src/Validate/ParsedTagSpec.php @@ -643,21 +643,21 @@ public function validateLayout(Context $context, array $attrs_by_key, SValidatio $input_layout = self::parseLayout($layout_attr); if (!empty($layout_attr) && $input_layout === AmpLayoutLayout::UNKNOWN) { $context->addError(ValidationErrorCode::INVALID_ATTR_VALUE, - ['layout', self::getTagSpecName($this->spec), $layout_attr], $this->spec->spec_url, $result); + ['layout', self::getTagSpecName($this->spec), $layout_attr], $this->spec->spec_url, $result, 'layout'); return; } $input_width = new CssLengthAndUnit($width_attr, true); if (!$input_width->is_valid) { $context->addError(ValidationErrorCode::INVALID_ATTR_VALUE, - ['width', self::getTagSpecName($this->spec), $width_attr], $this->spec->spec_url, $result); + ['width', self::getTagSpecName($this->spec), $width_attr], $this->spec->spec_url, $result, 'width'); return; } $input_height = new CssLengthAndUnit($height_attr, true); if (!$input_height->is_valid) { $context->addError(ValidationErrorCode::INVALID_ATTR_VALUE, - ['height', self::getTagSpecName($this->spec), $height_attr], $this->spec->spec_url, $result); + ['height', self::getTagSpecName($this->spec), $height_attr], $this->spec->spec_url, $result, 'height'); return; } @@ -667,7 +667,7 @@ public function validateLayout(Context $context, array $attrs_by_key, SValidatio if ($height->is_auto && $layout !== AmpLayoutLayout::FLEX_ITEM) { $context->addError(ValidationErrorCode::INVALID_ATTR_VALUE, - ['height', self::getTagSpecName($this->spec), $height_attr], $this->spec->spec_url, $result); + ['height', self::getTagSpecName($this->spec), $height_attr], $this->spec->spec_url, $result, 'height'); return; } @@ -675,7 +675,7 @@ public function validateLayout(Context $context, array $attrs_by_key, SValidatio $code = empty($layout_attr) ? ValidationErrorCode::IMPLIED_LAYOUT_INVALID : ValidationErrorCode::SPECIFIED_LAYOUT_INVALID; $context->addError($code, - [$layout, self::getTagSpecName($this->spec)], $this->spec->spec_url, $result); + [$layout, self::getTagSpecName($this->spec)], $this->spec->spec_url, $result, empty($layout_attr) ? '' : 'layout'); return; } @@ -702,7 +702,7 @@ public function validateLayout(Context $context, array $attrs_by_key, SValidatio return; } else if ($width->is_auto) { $context->addError(ValidationErrorCode::INVALID_ATTR_VALUE, - ['width', self::getTagSpecName($this->spec), 'auto'], $this->spec->spec_url, $result); + ['width', self::getTagSpecName($this->spec), 'auto'], $this->spec->spec_url, $result, 'width'); return; } } @@ -717,7 +717,7 @@ public function validateLayout(Context $context, array $attrs_by_key, SValidatio $code = empty($layout_attr) ? ValidationErrorCode::ATTR_DISALLOWED_BY_IMPLIED_LAYOUT : ValidationErrorCode::ATTR_DISALLOWED_BY_SPECIFIED_LAYOUT; $context->addError($code, - ['heights', self::getTagSpecName($this->spec), $layout], $this->spec->spec_url, $result); + ['heights', self::getTagSpecName($this->spec), $layout], $this->spec->spec_url, $result, 'heights'); return; } } diff --git a/tests/test-data/full-html/amp_layouts.html.out b/tests/test-data/full-html/amp_layouts.html.out index aa9b7b71..7de86436 100644 --- a/tests/test-data/full-html/amp_layouts.html.out +++ b/tests/test-data/full-html/amp_layouts.html.out @@ -65,7 +65,7 @@ - + @@ -82,10 +82,10 @@ TODO(johannes): Long run we should get rid of container or use it. https://github.com/ampproject/amphtml/issues/1109 --> - + - + @@ -116,10 +116,10 @@ - + - + @@ -128,10 +128,10 @@ - + - + @@ -318,16 +318,19 @@ FAIL on line 76 - The attribute 'height' in tag 'amp-img' is set to the invalid value 'auto'. [code: INVALID_ATTR_VALUE category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-img.html] + ACTION TAKEN: amp-img.height attribute was removed due to validation issues. - The mandatory attribute 'src' is missing in tag 'amp-img'. [code: MANDATORY_ATTR_MISSING category: AMP_TAG_PROBLEM see: https://www.ampproject.org/docs/reference/amp-img.html] on line 96 - The specified layout 'CONTAINER' is not supported by tag 'amp-img'. [code: SPECIFIED_LAYOUT_INVALID category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-img.html] + ACTION TAKEN: amp-img.layout attribute was removed due to validation issues. on line 99 - The attribute 'layout' in tag 'amp-img' is set to the invalid value 'unknown'. [code: INVALID_ATTR_VALUE category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-img.html] + ACTION TAKEN: amp-img.layout attribute was removed due to validation issues. on line 130 - The implied layout 'FIXED_HEIGHT' is not supported by tag 'amp-pixel'. @@ -336,18 +339,22 @@ FAIL on line 134 - The attribute 'width' in tag 'amp-pixel' is set to the invalid value 'X'. [code: INVALID_ATTR_VALUE category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-pixel.html] + ACTION TAKEN: amp-pixel.width attribute was removed due to validation issues. on line 138 - The attribute 'height' in tag 'amp-pixel' is set to the invalid value 'X'. [code: INVALID_ATTR_VALUE category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-pixel.html] + ACTION TAKEN: amp-pixel.height attribute was removed due to validation issues. on line 154 - The attribute 'heights' in tag 'amp-img' is disallowed by implied layout 'FIXED_HEIGHT'. [code: ATTR_DISALLOWED_BY_IMPLIED_LAYOUT category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-img.html] + ACTION TAKEN: amp-img.heights attribute was removed due to validation issues. COMPONENT NAMES WITH JS PATH ------------------------------ diff --git a/tests/test-data/full-html/mandatory_dimensions.html.out b/tests/test-data/full-html/mandatory_dimensions.html.out index e44bc6ad..ab515a83 100644 --- a/tests/test-data/full-html/mandatory_dimensions.html.out +++ b/tests/test-data/full-html/mandatory_dimensions.html.out @@ -113,17 +113,17 @@ - - + + - - + + @@ -320,14 +320,17 @@ FAIL on line 110 - The specified layout 'CONTAINER' is not supported by tag 'amp-img'. [code: SPECIFIED_LAYOUT_INVALID category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-img.html] + ACTION TAKEN: amp-img.layout attribute was removed due to validation issues. on line 111 - The specified layout 'CONTAINER' is not supported by tag 'amp-img'. [code: SPECIFIED_LAYOUT_INVALID category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-img.html] + ACTION TAKEN: amp-img.layout attribute was removed due to validation issues. on line 112 - The implied layout 'CONTAINER' is not supported by tag 'amp-img'. [code: IMPLIED_LAYOUT_INVALID category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-img.html] + ACTION TAKEN: amp-img tried to fix problems with amp-img by setting layout to responsive on line 115 - The mandatory attribute 'height' is missing in tag 'amp-img'. @@ -348,10 +351,12 @@ FAIL on line 119 - The attribute 'height' in tag 'amp-img' is set to the invalid value 'auto'. [code: INVALID_ATTR_VALUE category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-img.html] + ACTION TAKEN: amp-img.height attribute was removed due to validation issues. on line 120 - The attribute 'width' in tag 'amp-img' is set to the invalid value 'auto'. [code: INVALID_ATTR_VALUE category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-img.html] + ACTION TAKEN: amp-img.width attribute was removed due to validation issues. on line 123 - Inconsistent units for width and height in tag 'amp-img' - width is specified in 'px' whereas height is specified in 'rem'. diff --git a/tests/test-data/full-html/several_errors.html.out b/tests/test-data/full-html/several_errors.html.out index 0aa9363a..c2eb7209 100644 --- a/tests/test-data/full-html/several_errors.html.out +++ b/tests/test-data/full-html/several_errors.html.out @@ -29,7 +29,7 @@
Tables are allowed
- + @@ -104,6 +104,7 @@ FAIL on line 34 - The attribute 'width' in tag 'amp-ad' is set to the invalid value '100%'. [code: INVALID_ATTR_VALUE category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/amp-ad.html] + ACTION TAKEN: amp-ad.width attribute was removed due to validation issues. - The mandatory attribute 'type' is missing in tag 'amp-ad'. [code: MANDATORY_ATTR_MISSING category: AMP_TAG_PROBLEM see: https://www.ampproject.org/docs/reference/amp-ad.html] diff --git a/tests/test-data/full-html/validator-amp-soundcloud.html.out b/tests/test-data/full-html/validator-amp-soundcloud.html.out index 56d88988..b08b3abd 100644 --- a/tests/test-data/full-html/validator-amp-soundcloud.html.out +++ b/tests/test-data/full-html/validator-amp-soundcloud.html.out @@ -35,7 +35,7 @@ - + @@ -117,6 +117,7 @@ FAIL on line 46 - The specified layout 'RESPONSIVE' is not supported by tag 'amp-soundcloud'. [code: SPECIFIED_LAYOUT_INVALID category: AMP_LAYOUT_PROBLEM see: https://www.ampproject.org/docs/reference/extended/amp-soundcloud.html] + ACTION TAKEN: amp-soundcloud.layout attribute was removed due to validation issues. COMPONENT NAMES WITH JS PATH ------------------------------ diff --git a/tests/test-data/full-html/validator-amp-springboard-player.html.out b/tests/test-data/full-html/validator-amp-springboard-player.html.out index f44cf2be..ba904860 100644 --- a/tests/test-data/full-html/validator-amp-springboard-player.html.out +++ b/tests/test-data/full-html/validator-amp-springboard-player.html.out @@ -45,7 +45,7 @@ - + @@ -180,6 +180,7 @@ FAIL on line 80 - The specified layout 'FIXED_HEIGHT' is not supported by tag 'amp-springboard-player'. [code: SPECIFIED_LAYOUT_INVALID category: AMP_LAYOUT_PROBLEM see: https://github.com/ampproject/amphtml/blob/master/extensions/amp-springboard-player/amp-springboard-player.html] + ACTION TAKEN: amp-springboard-player.layout attribute was removed due to validation issues. - The mandatory attribute 'data-content-id' is missing in tag 'amp-springboard-player'. [code: MANDATORY_ATTR_MISSING category: AMP_TAG_PROBLEM see: https://github.com/ampproject/amphtml/blob/master/extensions/amp-springboard-player/amp-springboard-player.html] - The mandatory attribute 'data-domain' is missing in tag 'amp-springboard-player'.