Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix validation error due to native img tag when lightbox and carousel is enabled #7158

Merged
merged 23 commits into from
Jul 7, 2022

Conversation

thelovekesh
Copy link
Collaborator

@thelovekesh thelovekesh commented Jun 17, 2022

Summary

This PR aims to fix validation errors by using the native img tag.

Errors:

  • Lightbox attribute is not identified in <img> tag by amp validator yet.
  • layout and object-fir attributes are not identified in <img> tag by amp validator yet.

When adding lightbox and carousel with native img used:

Before:
image

After:
image

Fixes #7152

Checklist

  • My code is tested and passes existing tests.
  • My code follows the Engineering Guidelines (updates are often made to the guidelines, check it out periodically).

@thelovekesh thelovekesh self-assigned this Jun 17, 2022
@github-actions
Copy link
Contributor

github-actions bot commented Jun 17, 2022

Plugin builds for 3e558de are ready 🛎️!

Comment on lines 113 to 134
$img_elements = $this->dom->xpath->query(
'.//img[ @layout = "fill" or @object-fit = "cover" ]',
$this->dom->body
);

/*
* Remove `layout` and `object-fit` attributes from native img as they are not supported by AMP yet.
* Add style attribute which is used to set `object-fit` and `layout` attributes.
* @see <https://github.com/ampproject/amp-wp/issues/7152#issuecomment-1157933188>
* @todo Remove this once `layout` and `object-fit` attributes are supported by AMP spec for native img tag.
*/
$style_layout_fill = 'position:absolute; left:0; right:0; top:0; bottom: 0; width:100%; height:100%;';
$style_object_fit = 'object-fit:cover;';
foreach ( $img_elements as $img_element ) {
$remove_layout_attr = $img_element->removeAttribute( Attribute::LAYOUT );
$remove_object_fit_attr = $img_element->removeAttribute( Attribute::OBJECT_FIT );
$style_attr_content = sprintf( '%s %s', $remove_layout_attr ? $style_layout_fill : '', $remove_object_fit_attr ? $style_object_fit : '' );

if ( ' ' !== $style_attr_content ) {
$img_element->setAttribute( Attribute::STYLE, $style_attr_content );
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic may make more sense in the image sanitizer, specifically in the adjust_and_replace_node method:

private function adjust_and_replace_node( Element $node ) {
if ( $this->args['native_img_used'] || ( $node->parentNode instanceof Element && Tag::PICTURE === $node->parentNode->tagName ) ) {
$attributes = $this->maybe_add_lightbox_attributes( [], $node ); // @todo AMP doesn't support lightbox on <img> yet.
// Set decoding=async by default. See <https://core.trac.wordpress.org/ticket/53232>.
if ( ! $node->hasAttribute( Attribute::DECODING ) ) {
$attributes[ Attribute::DECODING ] = 'async';
}
// @todo This class should really only be added if we actually have to provide dimensions.
$attributes[ Attribute::CLASS_ ] = (string) $node->getAttribute( Attribute::CLASS_ );
if ( ! empty( $attributes[ Attribute::CLASS_ ] ) ) {
$attributes[ Attribute::CLASS_ ] .= ' ';
}
$attributes[ Attribute::CLASS_ ] .= 'amp-wp-enforced-sizes';
foreach ( $attributes as $name => $value ) {
$node->setAttribute( $name, $value );
}
return;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where you're currently adding the lightbox handling.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where you're currently adding the lightbox handling.

Handling it in the image sanitizer because need to check if an image has lightbox enabled to not.

/*
* Mark lightbox as px-verified attribute until it's supported by AMP spec.
* @see <https://github.com/ampproject/amp-wp/issues/7152#issuecomment-1157933188>
* @todo Remove this once lightbox is added in `lightboxable-elements` for native img tag in AMP spec.
*/
if ( isset( $attributes['data-amp-lightbox'] ) ) {
$node->setAttribute( Attribute::LIGHTBOX, 'true' );
$node_attr = $node->getAttributeNode( Attribute::LIGHTBOX );
if ( $node_attr instanceof DOMAttr ) {
ValidationExemption::mark_node_as_px_verified( $node_attr );
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, I think the logic here would all make more sense in the image sanitizer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, but the problem is that AMP_Gallery_Block_Sanitizer runs after AMP_Img_Sanitizer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you still planning to try to move this code to the img sanitizer by changing the order that the sanitizers run in? That is, if the gallery block sanitizer runs before the image sanitizer then the logic should be able to be consolidated in the img sanitizer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you still planning to try to move this code

I have tried it locally but changing the order of sanitizers causes markup error as the gallery block expects sanitized images.

One more idea that came up to my mind is to create a mini native IMG attributes sanitizer which can be called after gallery block. Doing so can help to sanitize more attributes if required in near future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like your idea of having a new mini native img sanitizer that runs after the gallery sanitizer to specifically handle the lightbox, layout, and object-fit attributes. It would be better yet to have it run in the img sanitizer, but consolidating them into one special sanitizer is better than having them spread out across two sanitizers as they are now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, should I create a mini native img attributes sanitizer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please create an AMP_Native_Img_Attributes_Sanitizer.

includes/sanitizers/class-amp-img-sanitizer.php Outdated Show resolved Hide resolved
includes/sanitizers/class-amp-img-sanitizer.php Outdated Show resolved Hide resolved
includes/sanitizers/class-amp-img-sanitizer.php Outdated Show resolved Hide resolved
thelovekesh and others added 3 commits June 18, 2022 01:17
Co-authored-by: Weston Ruter <westonruter@google.com>
Co-authored-by: Weston Ruter <westonruter@google.com>
…box-with-native-img

* 'develop' of github.com:ampproject/amp-wp:
  Bump moment from 2.29.3 to 2.29.4
  Bump eslint from 8.18.0 to 8.19.0
  Bump dependabot/fetch-metadata from 1.3.2 to 1.3.3
  Bump dependabot/fetch-metadata from 1.3.1 to 1.3.2
  Bump @babel/plugin-proposal-class-properties from 7.17.12 to 7.18.6
  Bump @babel/core from 7.18.5 to 7.18.6
  Bump eslint-plugin-react from 7.30.0 to 7.30.1
  Bump lint-staged from 13.0.2 to 13.0.3
  Bump postcss-preset-env from 7.7.1 to 7.7.2
  Bump shell-quote from 1.7.2 to 1.7.3
  Bump guzzlehttp/guzzle from 6.5.7 to 6.5.8
  Bump eslint-plugin-jsdoc from 39.3.2 to 39.3.3
  Bump eslint from 8.17.0 to 8.18.0
Comment on lines +63 to +89
// Images with object-fit attributes.
$img_elements = $this->dom->xpath->query(
'.//img[ @object-fit ]',
$this->dom->body
);
if ( $img_elements instanceof DOMNodeList ) {
foreach ( $img_elements as $img_element ) {
/** @var Element $img_element */
$value = $img_element->getAttribute( Attribute::OBJECT_FIT );
$img_element->removeAttribute( Attribute::OBJECT_FIT );
$img_element->addInlineStyle( sprintf( 'object-fit:%s', $value ) );
}
}

// Images with object-position attributes.
$img_elements = $this->dom->xpath->query(
'.//img[ @object-position ]',
$this->dom->body
);
if ( $img_elements instanceof DOMNodeList ) {
foreach ( $img_elements as $img_element ) {
/** @var Element $img_element */
$value = $img_element->getAttribute( Attribute::OBJECT_POSITION );
$img_element->removeAttribute( Attribute::OBJECT_POSITION );
$img_element->addInlineStyle( sprintf( 'object-position:%s', $value ) );
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out that amp-video also supports object-fit and object-position attributes, so it could be that there could end up being some video elements that have these attributes. In this case, the sanitizer could be made more generic instead of being specifically for images. But since we don't have any known cases of this happening for video, this could be done later. Or it could be done now if you want.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't think of any such use case. IMO we are amending changes to img tags only so can't we keep it as it is?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, it could be that a theme/plugin author is adding: <video object-fit=cover …> and is then relying on the fact that this is converged to <amp-video object-fit=cover>. But in moderate sandboxing, no conversion is being done, so this would then be a validation error and the attribute should be moved to style as we're doing for img.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. got it now. But I think a new PR should introduce it instead of adding it in the same. Also as mentioned But since we don't have any known cases of this happening for video, this could be done later. IMO adding it with some use case make more sense. Thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user reports this issue we can follow up with the change.

@westonruter westonruter merged commit 0bf2bbb into develop Jul 7, 2022
@westonruter westonruter deleted the fix/lightbox-with-native-img branch July 7, 2022 19:58
@westonruter westonruter modified the milestones: v2.3.1, v2.4 Jan 26, 2023
@westonruter westonruter added the Changelogged Whether the issue/PR has been added to release notes. label Feb 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelogged Whether the issue/PR has been added to release notes.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Invalid Attribute Error displayed in Editor
2 participants