profile
viewpoint
Weston Ruter westonruter @Google Portland, OR, USA https://weston.ruter.net/ Googler 👨🏻‍💻 @amphtml, PWA, & open web; @WordPress core team. Daddy² & husband. Go by 🏃🏻🚴🏻🚌🚈 Philologist & hispanohablante [ˈwɛsˌtn̩ ˈɹuːˌɾɚ]

ampproject/amp-wp 1556

Enable AMP on your WordPress site, the WordPress way.

GoogleChrome/web-vitals 990

Essential metrics for a healthy site.

google/site-kit-wp 859

Site Kit is a one-stop solution for WordPress users to use everything Google has to offer to make them successful on the web.

GoogleChromeLabs/pwa-wp 368

WordPress feature plugin to bring Progressive Web Apps (PWA) to Core

GoogleChromeLabs/wp-sitemaps 80

Proposal to integrate basic XML Sitemaps in WordPress Core

google/web-stories-wp 53

Web Stories WordPress plugin

luehrsenheinrich/wp-quicklink 46

The WordPress plugin for quicklink. ⚡️Faster subsequent page-loads by prefetching in-viewport links during idle time.

GoogleChromeLabs/wp-native-lazyload 43

WordPress plugin to lazy-load media using the native browser feature.

GoogleChromeLabs/wp-origination 33

Determine the origin of where things come from in WordPress whether slow code, inefficient queries, or bad markup.

GoogleChromeLabs/wordpressdev 32

WordPress development environment for core, plugins, and themes, based on Lando.

issue closedampproject/amp-wp

Request for Expert Mode. Or at least a more granular/controllable Tree-Shaking

Options – Not decisions

I have recently encountered the problem of being limited by the tree-shaking mechanisms of the amp-plugin.

It seems to like to remove some more complicated selectors, which I happen to need. I assume that the Tree-shaker might might not exactly get that I need those classes and definitions.

Nevertheless: What I personally would prefer, would be a slightly more granular developer edition of the plugin. Where we could actually control more precisely how AMP is enforced...

My primary use case for the plugin has been the suppression of custom JS/CSS by plugins. Nowadays – in light of being a good citizen – i took care of that myself, in order to get the system slightly better optimized anyway.

So basically I need it for things like WP Forms-Integration...

I could imagine that more savvy developers/designers than me might find the suggestion useful. It would be for people who know what they are doing (and me) …

The plugin is really great… I have used it already on multiple sites. We are using AMP by now almost anywhere we can.

Cheers Martin


Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance criteria

  • <!-- One or more bullet points for acceptance criteria. -->

Implementation brief

  • <!-- One or more bullet points for how to technically resolve the issue. For significant Implementation Design, it is ok use a Google document accessible by anyone. -->

QA testing instructions

  • <!-- One or more bullet points to describe how to test the implementation in QA. -->

Demo

  • <!-- A video or screenshots demoing the implementation. -->

Changelog entry

  • <!-- One sentence summarizing the PR, to be used in the changelog. -->

closed time in 13 hours

mainframeai

issue commentampproject/amp-wp

Request for Expert Mode. Or at least a more granular/controllable Tree-Shaking

I don't understand. How are you intending to add the classes dynamically? You can't use JS for that, but you can use the AMP toggleClass action, or you can use amp-bind. When doing so, the AMP plugin will automatically detect the class names being used and account for them when tree shaking.

I suggest creating a standalone example of what you are intending to do, outside of WordPress. For example, put the desired static markup onto a Glitch or JSBin or CodePen or whatever. Then when that is working as you expect, incorporate the code into WordPress. If the tree shaker is still removing CSS undesirably, then we can determine if there is a bug here in the plugin.

mainframeai

comment created time in 13 hours

issue commentampproject/amp-wp

Request for Expert Mode. Or at least a more granular/controllable Tree-Shaking

Your CSS is using a class selector .chide but there is no such class in the HTML.

I would love to prevent AMPlification on already AMP-centric components which were done via a custom Gutenberg-component. ¶ If I write e.g. a dedicated component with amp-video or amp-img component inside, I get amp errors in the backend, which I have to tell my clients to ignore...

I'm not sure I understand. If you're already using AMP-centric (AMP-compatible) components, then “AMPification” would be just passing through the markup without making changes. If you are getting errors, then apparently you're not writing valid AMP. What errors are being reported? Are they related to using “transformed” AMP as the input? Elements, attributes, and class names that start with i-amphtml- are not allowed in normal AMP documents. To avoid markup causing validation errors in the AMP plugin, make sure that it also avoids validation errors when being pasted into validator.amp.dev.

mainframeai

comment created time in 14 hours

issue commentampproject/amp-wp

Request for Expert Mode. Or at least a more granular/controllable Tree-Shaking

I have recently encountered the problem of being limited by the tree-shaking mechanisms of the amp-plugin. It seems to like to remove some more complicated selectors, which I happen to need. I assume that the Tree-shaker might might not exactly get that I need those classes and definitions.

Please share the selectors specifically that are getting tree-shaken undesirably, along with the corresponding markup. It is possible to exempt certain selectors from tree shaking via filter.

What I personally would prefer, would be a slightly more granular developer edition of the plugin. Where we could actually control more precisely how AMP is enforced...

More granular in what way?

mainframeai

comment created time in 15 hours

issue openedafragen/github-updater

Integrate more seamlessly with core plugin installation

When the GitHub Update plugin is installed, it adds its UI under the Settings menu. This was unexpected for me. I was expecting there to be a way to install a plugin from the plugins screen when clicking the Upload Plugin button:

image

It would be great if the GitHub Updater plugin installation form were included here somehow:

image

This would make it much more discoverable for how to install plugins from GitHub, as right now I feel the GitHub Updater UI is somewhat buried.

The same applies to the theme installation flow.

created time in 16 hours

issue commentafragen/github-updater

Updating plugins stored in Gists

Awesome.

I've tried installing https://gist.github.com/westonruter/b2554073461707a88057bb86b6b603d6 as follows:

image

Shouldn't the Remove Repository Host dropdown field be moved to the top as the first field, before the Plugin URI?

The plugin gets successfully installed:

image

The plugin was placed into a directory amp-gravity-forms-submission-shim based on the plugin file of amp-gravity-forms-submission-shim.php. That's perfect.

However, on this screen there is no indication of the name of the plugin that was installed. Should there not be the name of the plugin shown (or at least the slug) in addition to an Activate button?

westonruter

comment created time in 16 hours

PR opened ampproject/amp-wp

Reviewers
Remove duplicated HTML_GET_HEAD_OPENING_TAG_PATTERN in comment

Summary

Fixes typo with PHP code copied into comment.

Checklist

  • [ ] My pull request is addressing an open issue (please create one otherwise).
  • [ ] 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).
+1 -1

0 comment

1 changed file

pr created time in a day

create barnchampproject/amp-wp

branch : fix/duplicated-constant-in-comment

created branch time in a day

issue commentampproject/amp-wp

Implement support for Mobile Redirection

Additional requirements:

  1. Add a section the admin screen which has a checkbox enable mobile redirection. This section will only be displayed when the site is in Transitional or Reader mode.
  2. When a user is automatically redirected to the AMP version (or lands on the AMP version), there needs to be a way to navigate back to the non-AMP version. This can be implemented by default via a section output at wp_footer (or amp_post_template_footer) which has a centered link inside of it saying something like “Leave mobile version”; this would text would apply both to Reader mode and Transitional mode, whereas presently the Reader mode has “Exit Reader Mode”. This would be used instead the Exit Reader Mode link currently shown in the header in Reader mode, but which has problematic placement there (see #4573).
  3. The “Display link to exit reader mode?” checkbox in the Customizer can remain but be rebranded to “Display link to exit mobile version”. When checked, there could also be a text field allowing the user to customize what the link text contains. In classic AMP Reader mode, there is already an AMP panel that has this setting in it; non-classic Reader mode and Transitional mode, a new Customizer section for “AMP” settings would be needed and in this section this same checkbox control can be added.
  4. When the user manually navigates to the non-AMP version, auto-redirection needs to be turned off. This likely involves adding a query var (like ?noamp=1) when linking from AMP to non-AMP, and when loading the non-AMP page this query var must prevent redirection while also setting a flag in sessionStorage to prevent redirecting the from non-AMP to AMP at the next page navigation.
  5. Automatically add ?noamp=1 to any intra-site link URLs that AMP-to-AMP linking skips, either due to filter or the noamphtml link relation. In the same way, AMP-to-AMP linking should skip any URLs that have this ?noamp=1 query parameter present.
  6. If the user is redirected from the AMP version to the non-AMP version due to there being kept invalid markup, redirection back to the AMP version must not be done. This may require adding a new query var, which at present is only added when the user is logged in:

https://github.com/ampproject/amp-wp/blob/710f0f70b9a7679cb69232746cdd0f244bb4af5f/includes/class-amp-theme-support.php#L2116-L2119

amedina

comment created time in 2 days

issue commentampproject/amp-wp

Minimize validation error warnings when Developer Tools are turned off

The mechanism for disabling dev tools is being explored in #4644, with the code likely being split out from that prototype branch.

westonruter

comment created time in 2 days

push eventampproject/amp-wp

Weston Ruter

commit sha bb0a315febc1213f63dc94ba2873f057f2e3a6c8

Extend WordPress.tv embed handler to also handle VideoPress

view details

Pierre Gordon

commit sha f2fd17d835dd50cf2dc966ca93b99147a59d1a1d

Fix unit tests (#4564) * Fix unit tests * Add loading attribute depending on WP version (cherry picked from commit 4f9ac9cebf39e4355c33b658867d816f67840b81)

view details

Pierre Gordon

commit sha 66f7a939602a8a418c7416dfbfd9732a7470db3c

Add input file configuration parsing for spec tests (#4662) * Update spec files * Adapt spec test to extract configuration arguments from input files * Add STYLES configuration key to AmpRuntimeCss transformer * Nake use of styles provided via config if available * Remove runtime style tag if stylesheet is linked * Add more tests to assert runtime transformer behavior * Complete stubbed requests data * Remove unused import * Add git attributes file to mark certain file as being generated * Use substr() instead of a replacement for removing the leading comment * Remove redundant JSONOBJECT_AS_ARRAY constant * Move .gitattributes file into lib/optimizer folder * Revert "Move .gitattributes file into lib/optimizer folder"

view details

Pierre Gordon

commit sha 83d7646c5ca493d9feb6f8cf83626fe08a5474cd

Tweak optimizer for consistency (#4614) * Make configuration argument optional for transformation engine * Replace reference to Go filename * Replace copypasta in filesystem transport * Swap required order of configuration and remote erequest objects * Add `null` type information where applicable * Fix broken tests * Use reflection to detect dependencies in correct order * Add tests for dependency resolution * Remove Configurable interface from schema transformer * Fix broken test about link ordering after optimization

view details

Weston Ruter

commit sha 02a10f942f49a02140aa6aeff1be3b52e0f2bad7

Skip Hulu external-http tests that fail when endpoint is down

view details

Pierre Gordon

commit sha bd4714c577b93ab96350e55d54a23736c2df83d1

Fix gallery embed test

view details

Pierre Gordon

commit sha 46bcdfd1475a40a86be53f793785da24cafb9f38

Re-add new lines to tests

view details

Pierre Gordon

commit sha 3591f03ab0cb73a2656d07cb026528701ffbe304

Fix lint error

view details

Pierre Gordon

commit sha fc8825388401b3522dd3141bb3a8b5dbbf6d5adf

Fix loading attribute issue

view details

Weston Ruter

commit sha b223ef4cee022f08288be8bc16daf681b2cdedea

Merge pull request #4755 from ampproject/fix/videopress Extend WordPress.tv embed handler to also handle VideoPress

view details

push time in 3 days

delete branch ampproject/amp-wp

delete branch : fix/videopress

delete time in 3 days

PR merged ampproject/amp-wp

Extend WordPress.tv embed handler to also handle VideoPress cla: yes

Summary

Addresses issue #4754

WordPress.tv uses the VideoPress infrastructure. However, we were only directly supporting WordPress.tv but not embeds from VideoPress itself. This fixes that. Note that both WordPress.tv and VideoPress are blocks that core supports.

Checklist

  • [x] My pull request is addressing an open issue (please create one otherwise).
  • [x] My code is tested and passes existing tests.
  • [x] My code follows the Engineering Guidelines (updates are often made to the guidelines, check it out periodically).
+948 -203

15 comments

80 changed files

westonruter

pr closed time in 3 days

pull request commentampproject/amp-wp

Extend WordPress.tv embed handler to also handle VideoPress

Thank you very much for wrestling with this one.

westonruter

comment created time in 3 days

Pull request review commentampproject/amp-wp

Omit non-standard type attribute during <amp-iframe> normalization

 private function normalize_attributes( $attributes ) { 					// No need to copy. 					break; +				case 'type':+					/*+					 * Though type is non-standard attribute in <iframe> tag, some embeds (i.e Amazon Kindle Embed) uses+					 * it, therefore we need to omit this.+					 */+					break;+

Also, the YouTube embed code doesn't use this type attribute as far as I know. It's only the Jetpack embed handler for YouTube that used to use it (but now does no longer).

itowhid06

comment created time in 3 days

Pull request review commentampproject/amp-wp

Omit non-standard type attribute during <amp-iframe> normalization

 public function sanitize() { 	 *      @type int $frameborder <iframe> `frameborder` attribute - Filter to '0' or '1'; default to '0' 	 *      @type bool $allowfullscreen <iframe> `allowfullscreen` attribute - Convert 'false' to empty string '' 	 *      @type bool $allowtransparency <iframe> `allowtransparency` attribute - Convert 'false' to empty string ''+	 *      @type string $type <iframe> `type` attribute - Pass along if found

Is this a bool?

itowhid06

comment created time in 3 days

pull request commentampproject/amp-wp

Omit non-standard type attribute during <amp-iframe> normalization

@pierlon So you think it should be done in the sanitizer for all iframes and not in an embed handler for just Kindle?

Perhaps it should only be ignored when it has a value of text/html?

itowhid06

comment created time in 3 days

issue commentAutomattic/jetpack

Widgets: AMP Compatibility

I believe this needs to be reopened since #15722 was fixing one widget in particular.

jeherve

comment created time in 3 days

pull request commentampproject/amp-wp

Omit non-standard type attribute during <amp-iframe> normalization

Yeah, it depends on how common this bogus attribute is.

itowhid06

comment created time in 3 days

pull request commentampproject/amp-wp

Omit non-standard type attribute during <amp-iframe> normalization

I think the better approach would be to handle this at the embed level, rather than the sanitizer level, if this is primarily an issue with Kindle embeds. /cc @pierlon

Otherwise, that validation error should probably be raised so that authors know they have a non-standard attribute that should be removed. See also See also https://github.com/Automattic/jetpack/pull/15225.

itowhid06

comment created time in 3 days

issue commentGoogleChromeLabs/wp-origination

Avoid Gutenberg comments

Yeah, the HTML comments are just the transport mechanism. They are not the ideal user interface for viewing. Both of these problems could be solved by "HTML source maps". See thread: https://twitter.com/igrigorik/status/1249857435323396096

Mte90

comment created time in 3 days

issue commentampproject/amp-wp

Add CI test for performance

Yes, that makes sense to me. @amedina currently has access to the DNS for amp-wp.org, so he can add the desired CNAME.

westonruter

comment created time in 3 days

pull request commentampproject/amp-wp

Use responsive layout for blocks with alignwide class

I also squashed the commits together to cherry-pick onto the 1.5 branch: 1ec497a.

deepaklalwani97

comment created time in 3 days

pull request commentampproject/amp-wp

Extend WordPress.tv embed handler to also handle VideoPress

@pierlon This PR has exposed that builds in the 1.5 branch are failing and have been failing:

image

Could you investigate and fix?

westonruter

comment created time in 3 days

issue commentampproject/amp-wp

Tumblr embeds cause validation errors and fail to embed

@pierlon I'm assuming this should be handled as part of #4650.

westonruter

comment created time in 3 days

issue openedampproject/amp-wp

Tumblr embeds cause validation errors and fail to embed

Bug Description

When embedding a Tumblr post like https://teded.tumblr.com/post/184736320764/how-do-vaccines-work

I get a validation error for an invalid script:

image

And the post fails to embed:

image

Expected Behaviour

The AMP version should look the same as the non-AMP verison.

Steps to reproduce

Add a Tumblr block for https://teded.tumblr.com/post/184736320764/how-do-vaccines-work


Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance criteria

  • <!-- One or more bullet points for acceptance criteria. -->

Implementation brief

I think all that is needed is fixing the pattern for the data-href attribute:

- if ( preg_match( '#data-href="(?P<href>https://embed.tumblr.com/embed/post/\w+/\w+)"#', $cache, $matches ) ) {
+ if ( preg_match( '#data-href="(?P<href>https://embed.tumblr.com/embed/post/[^"]+?)"#', $cache, $matches ) ) {

QA testing instructions

  • <!-- One or more bullet points to describe how to test the implementation in QA. -->

Demo

  • <!-- A video or screenshots demoing the implementation. -->

Changelog entry

  • <!-- One sentence summarizing the PR, to be used in the changelog. -->

created time in 3 days

push eventampproject/amp-wp

Weston Ruter

commit sha 1ec497a7a960e51d0d991704d82159869747ea17

Use responsive layout for blocks with alignwide class (#4693)

view details

Weston Ruter

commit sha bb0a315febc1213f63dc94ba2873f057f2e3a6c8

Extend WordPress.tv embed handler to also handle VideoPress

view details

push time in 3 days

issue commentampproject/amp-wp

Scaffolding for AMP Setup Wizard

Unless all developer actions could be performed inside the docker container 🤔

Exactly. This actually is what I'm doing via Lando in the wordpressdev environment. Lando is based on Docker. The main thing that is not ideal is the slower performance of running things in the container. But it's worth it for the benefits of containerization for me. But I was curious if you the Docker setup had that in an alternative way.

jwold

comment created time in 3 days

push eventampproject/amp-wp

Weston Ruter

commit sha 1ec497a7a960e51d0d991704d82159869747ea17

Use responsive layout for blocks with alignwide class (#4693)

view details

push time in 3 days

push eventampproject/amp-wp

Deepak Lalwani

commit sha 6a1f8150ea145cc37224851753a775f93ca88dbf

Use responsive layout for blocks with alignwide class

view details

Deepak Lalwani

commit sha 9319d687871522329e2301b674713ca9cd83f61e

Added short description comment

view details

Deepak Lalwani

commit sha bdfa9380f14af02b495d6fe03465d70d0f6ea38e

Format code and update comment description

view details

Deepak Lalwani

commit sha 31d720d4beee3a149455a91305d99bb90bca3851

Use preg_split to split the class attribute

view details

Deepak Lalwani

commit sha 5d97c5fd5ca481b0e35a311c7a28b7b9e15e0177

Use align-wide theme support check to use responsive layout

view details

Weston Ruter

commit sha 710f0f70b9a7679cb69232746cdd0f244bb4af5f

Merge pull request #4693 from rtCamp/fix/4692-wide-width-alignment Use responsive layout for blocks with alignwide class

view details

push time in 3 days

PR merged ampproject/amp-wp

Use responsive layout for blocks with alignwide class cla: yes

Summary

  • Use the responsive layout for amp-iframe if the Gutenberg block has wide alignment.

Addresses issue #4692

Checklist

  • [x] My pull request is addressing an open issue (please create one otherwise).
  • [x] My code is tested and passes existing tests.
  • [x] My code follows the Engineering Guidelines (updates are often made to the guidelines, check it out periodically).
+35 -6

3 comments

3 changed files

deepaklalwani97

pr closed time in 3 days

pull request commentampproject/amp-wp

Use responsive layout for blocks with alignwide class

I noticed that gfycat embeds weren't getting sized responsively, but I believe they'll be accounted for in #4650.

deepaklalwani97

comment created time in 3 days

issue commentampproject/amp-wp

Scaffolding for AMP Setup Wizard

Composer and NPM should be part of the Docker container(s) to make this easier, no?

jwold

comment created time in 3 days

issue commentampproject/amp-wp

Scaffolding for AMP Setup Wizard

John walked me through getting Docker setup on my computer and I was able to verify that this is working in QA (screenshot)

Aside: @johnwatkins0 Could you outline the steps you went through to do this? We may want to add this to the plugin wiki for others to get set up as well.

jwold

comment created time in 3 days

push eventampproject/amp-wp

Weston Ruter

commit sha 197f3f5e5e75f69d99fd737caded9c009cb45ded

Bump v1.5.1-alpha

view details

Andrija Vucinic

commit sha 54ada670be01611b67b9d4917f394c2dab71588e

Bad usage of `tax_query` param deletes a lot more than it should (#4488) * Bad usage of `tax_query` param deletes a lot more than it should Wrong usage of `tax_query`. Please check: https://developer.wordpress.org/reference/classes/wp_query/#taxonomy-parameters. This ended up deleting all reusable blocks for our customers. A hotfix release would be smart. * Register taxonomy up-front so it can be used to query * Opt to remove remove_amp_story_templates entirely Co-authored-by: Weston Ruter <westonruter@google.com>

view details

Alain Schlesser

commit sha 2f2ff282dea87f28b348c02874807e8e625af577

Only use LIBXML_HTML_NODEFDTD is available (#4486) * Only use LIBXML_HTML_NODEFDTD is available * Actually remove constants Co-Authored-By: Pierre Gordon <16200219+pierlon@users.noreply.github.com> Co-authored-by: Pierre Gordon <16200219+pierlon@users.noreply.github.com>

view details

Alain Schlesser

commit sha 23a7dfaf86698a3d45b5a2cf6455ac424aa24850

Safeguard metadata configuration value against bad filters (#4487) * Improve validation errors for transformer configuration * Safeguard metadata configuration value against bad filters

view details

Weston Ruter

commit sha 268bcd0b65fcc601b4c896cc2c97dae8b50ba70a

Bump 1.5.1

view details

Weston Ruter

commit sha c95819f2da711db838fc67151ec8549f39c233cf

Bump 1.5.2-alpha

view details

Pierre Gordon

commit sha e382be8325fe6e4d668850d0912ad255eb65790d

Fix failing tests (#4507)

view details

Pierre Gordon

commit sha 03af649a313216018a5fb11e5d8c4698bd1a4aae

Only move meta tags to the head when required and add processing for meta[http-equiv] (#4505) * Do not process generic meta tags * Obtain non-body meta name attribute pattern from spec * Make use of BODY_ANCESTOR_META_TAG_SPEC_NAME constant * Test meta[schema] and meta[property] * Add recognition and repositioning of meta[http-equiv] elements * Add critical use_document_element=true arg for AMP_Tag_And_Attribute_Sanitizer * Add tests for the discrete spec'ed meta tags Co-authored-by: Weston Ruter <westonruter@google.com>

view details

Weston Ruter

commit sha b546914941ac9664ac0e3809409ca35e634f9113

Cast i-amphtml-intrinsic-sizer dimensions to integers (#4506)

view details

Alain Schlesser

commit sha 5935fdef5d4d71b5ccce824d13230f02440eec83

Raise default threshold for disabling CSS caching (#4513) * Raise default threshold from 50 to 5000 * Reset option for disabling CSS cache on update * Fix inverted logic in version check Co-Authored-By: Weston Ruter <westonruter@google.com> Co-authored-by: Weston Ruter <westonruter@google.com>

view details

Pierre Gordon

commit sha 567cbbc4b8adb90cdfd5a24b9fbb16f88d5bae8a

Update selected featured image ID on select (#4453) * Update selected featured image ID on select * Add sanity check for featured image ID being possibly undefined * Ensure the featured image is the first item in the collection

view details

Ryan Kienstra

commit sha b804586dc1318a7df3832d2f58bfc6f75b0f765b

Account for more YouTube URL formats (#4508) * Account for hyphens or underscores in YouTube URL The ID should be able to include a - or _, like: https://www.youtube.com/watch?v=xo68-iWaKv8 https://youtu.be/CMrv_D78oxY * Test the method instead of the full filtered content Testing the filtered content would require adding another 2 mocks to mock_http_request() This isn't ideal, though. * Align the param descriptions vertically Before, the param tag descriptions were not aligned. * Change the name of the dataProvider method * Commit Weston's suggestion, add some test cases Weston's suggestion for get_video_id_from_url() parses the URL, instead of only matching. Also, add some tests, drawing from: https://gist.github.com/rodrigoborgesdeoliveira/987683cfbfcc8d800192da1e73adc486 * Add another embed URL test case Now, there's a case for one ending in a query param, and for not. * Add a test for youtube-nocookie As Weston mentioned, support for this is needed. * Ensure that the first segment isn't account or playlist As Alain mentioned, is possible to have a playlist URL like https://www.youtube.com/playlist?list=PLCra4VPr-3frJzAd-lVYo3-34wu0Eax_u * Indicate unused URL_PATTERN constant is deprecated * Include youtube-nocookie.com domains for completeness in oEmbed filtering * Eliminate duplicate YouTube URL parsing in video_override method * Eliminate redundant URL parsing * Harden support for short URLs and use allowlist for root segments * Disrecard subdomains when checking for YouTube domain Co-authored-by: Weston Ruter <westonruter@google.com>

view details

Weston Ruter

commit sha c89c7521996de230f8c1e394cc9297afdc478e1d

Fix processing of element child sanitization loop when invalid elements are replaced with children (#4512) * Add failing test showing missing amp-form component script * Fix sanitization loop when invalid node is replaced with children * Add additional test case for gathering script from invalid parent

view details

Weston Ruter

commit sha a2104ce9f416e5264ad362e6f59a7bfdf2b9c053

Update hostname used for WordPress TV embeds to fix external HTTP requests (#4524)

view details

Alain Schlesser

commit sha a4f6d4c8347e2c44288cc2fc3d9c2708f15c57bd

Add CSS monitoring time series to Site Health debugging info (#4519)

view details

Weston Ruter

commit sha 6d37a283cfdd6114873466280a60d6a22f76ed8f

Fix securing multi-line mustache templates (#4521)

view details

Pierre Gordon

commit sha abe6acb0fc8290de52c4c9bffd8532d961801a06

Cache response status and headers when fetching external stylesheets (#4509) * Cache response status code and headers when fetching external stylesheets * Remove whitespace * Separate throw annotations Co-Authored-By: Alain Schlesser <alain.schlesser@gmail.com> * Rename CachedData to CacheResponse * Rename exception not being thrown * Apply suggestions from Alain Co-Authored-By: Alain Schlesser <alain.schlesser@gmail.com> * Add FailedRemoteRequest interface to mark exception classes related to remote requests * Use specific exception name in docblock Co-Authored-By: Weston Ruter <westonruter@google.com> * Use specific exception name in docblock Co-Authored-By: Weston Ruter <westonruter@google.com> * Check if cached response is an instance of CachedResponse::class * Remove unused imports Co-authored-by: Alain Schlesser <alain.schlesser@gmail.com> Co-authored-by: Weston Ruter <westonruter@google.com>

view details

Weston Ruter

commit sha 3102c5dea2435e09ae71908a50b0656e34365e33

Bump version to 1.5.1-RC1

view details

Weston Ruter

commit sha 3e8f139a6123b3ff10705a4e8c8cf7a734022c11

Bump 1.5.2

view details

Weston Ruter

commit sha 601d24adc457af39f96a1aff82a951a6bc9c7f12

Bump 1.5.3-alpha

view details

push time in 3 days

pull request commentampproject/amp-wp

Extend WordPress.tv embed handler to also handle VideoPress

Or rather, rebase this exclusively to the 1.5 branch perhaps.

westonruter

comment created time in 3 days

pull request commentampproject/amp-wp

Extend WordPress.tv embed handler to also handle VideoPress

We can defer to your solution instead.

westonruter

comment created time in 3 days

PR opened ampproject/amp-wp

Reviewers
Extend WordPress.tv embed handler to also handle VideoPress

Summary

Addresses issue #4754

WordPress.tv uses the VideoPress infrastructure. However, we were only directly supporting WordPress.tv but not embeds from VideoPress itself. This fixes that. Note that both WordPress.tv and VideoPress are blocks that core supports.

Checklist

  • [ ] My pull request is addressing an open issue (please create one otherwise).
  • [ ] 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).
+41 -21

0 comment

2 changed files

pr created time in 3 days

create barnchampproject/amp-wp

branch : fix/videopress

created branch time in 3 days

issue openedampproject/amp-wp

Embedding VideoPress video causes validation error

Bug Description

When embedding a VideoPress video such as https://videopress.com/v/kUJmAcSf in a VideoPress block, the result is a validation error:

image

The fix for this was partially implemented in #3542 because WordPress.tv also uses VideoPress architecture. So it just needs to be extended.

Expected Behaviour

No validation error should be raised when embedding videos from VideoPress.

Steps to reproduce

<!-- Please provide detailed steps on how to reproduce the bug. Provide a URL where the issue can be seen on the frontend when possible, otherwise go to “View source” in the browser and copy all to paste in a Gist and share it. -->

  1. Enable Standard mode.
  2. Embed a VideoPress block with the URL https://videopress.com/v/kUJmAcSf
  3. Save draft
  4. See validation error

Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance criteria

  • <!-- One or more bullet points for acceptance criteria. -->

Implementation brief

  • <!-- One or more bullet points for how to technically resolve the issue. For significant Implementation Design, it is ok use a Google document accessible by anyone. -->

QA testing instructions

  • <!-- One or more bullet points to describe how to test the implementation in QA. -->

Demo

  • <!-- A video or screenshots demoing the implementation. -->

Changelog entry

  • <!-- One sentence summarizing the PR, to be used in the changelog. -->

created time in 3 days

pull request commentAutomattic/jetpack

AMP Sharing Support - Include all services

I've looked in to this. The reason is that the Lovecraft theme has a rule that the last item in the post content div should have no bottom margin.

.post-content *:last-child { margin-bottom: 0; }

@MattGeri I bet this is actually a bug in Lovecraft, then. I bet that @andersnoren actually intended this CSS instead:

.post-content > *:last-child { margin-bottom: 0; }

So as to remove the bottom margin on the last element in the post content. This makes more sense than to remove all bottom margin from all last-child elements in the entire tree.

That being the case, I'd say there's nothing that Jetpack needs to do here.

MattGeri

comment created time in 4 days

issue commentGoogleChromeLabs/wp-origination

Avoid Gutenberg comments

I'm not sure I understand. The point of the HTML comments is to annotate markup on the page with the source of where it came from.

Mte90

comment created time in 4 days

issue closedGoogleChromeLabs/wp-origination

Avoid Gutenberg comments

I saw the project and seems interesting, I was just wondering if instead of comments that need to be parsed is not better to use javascript variables.
As today:

<!-- origination
{
    "callback": "rel_canonical",
    "duration": 0.0026450157165527344,
    "index": 242,
    "name": "wp_head",
    "priority": 10,
    "source": {
        "file": "/app/public/core-dev/src/wp-includes/link-template.php",
        "name": "wp-includes",
        "type": "core"
    }
}
-->
<script>
origination.push({
    "callback": "rel_canonical",
    "duration": 0.0026450157165527344,
    "index": 242,
    "name": "wp_head",
    "priority": 10,
    "source": {
        "file": "/app/public/core-dev/src/wp-includes/link-template.php",
        "name": "wp-includes",
        "type": "core"
    }
});
</script>

With the origination var initialized in the head as example. In this way is not required to parse the whole page and js can be executed quickly. AS it is a different url with a custom parameter to have js with a var doesn't create performance issues.

closed time in 4 days

Mte90

issue commentGoogleChromeLabs/wp-origination

Avoid Gutenberg comments

Good question, but no, that won't help. The point of the comments is to be able to surround markup in the page with where it is coming from. So for example, if there is a script in the page, there needs to be a way to walk up the DOM tree to find the “open” Gutenberg-style HTML comments to identify the source for where that script came from.

Also, the data structure needing to be generated is a tree, not a list, as hooks often trigger other hooks, so there will be nested sources for given markup.

Mte90

comment created time in 4 days

Pull request review commentgoogle/web-stories-wp

Try using AMP Optimizer if available

 protected function replace_body_end_tag( $content ) { 		return str_replace( '</body>', $output . '</body>', $content ); 	} +	/**+	 * Get the configuration object to use.+	 *+	 * @return Configuration Optimizer configuration to use.+	 */+	private function get_optimizer_configuration() {+		$transformers = [+			AmpSchemaOrgMetadata::class,+			AmpRuntimeCss::class,+			TransformedIdentifier::class,+			ReorderHead::class,+		];++		$configuration = [ Configuration::KEY_TRANSFORMERS => $transformers ];+		$config        = new Configuration( $configuration );+		$config->registerConfigurationClass(+			AmpSchemaOrgMetadata::class,+			AmpSchemaOrgMetadataConfiguration::class+		);++		return $config;+	}++	/**+	 * Optimizes the resulting markup using AMP Optimizer if available.+	 *+	 * @param string $content Story markup.+	 *+	 * @return string Filtered content.+	 */+	protected function optimize_markup( $content ) {+		if ( ! class_exists( '\AmpProject\Optimizer\TransformationEngine' ) ) {+			return $content;+		}++		$enable_optimizer = true;++		/** This filter is documented in amp-wp/includes/class-amp-theme-support.php */+		$enable_optimizer = apply_filters( 'amp_enable_optimizer', $enable_optimizer );++		if ( ! $enable_optimizer ) {+			return $content;+		}++		// Needed for \AmpProject\Optimizer\Transformer\AmpRuntimeCss.+		$content = str_replace( '</head>', '<style amp-runtime></style></head>', $content );

Given that stories can't do SSR, should the AmpRuntimeCss be done?

swissspidy

comment created time in 4 days

push eventampproject/amp-wp

John Watkins

commit sha 58eb8c3c5581699b249e8b019a60201f86b9e00c

AMP onboarding scaffolding

view details

John Watkins

commit sha d6792cb75d29a02bf839af20a3683c21b4a0504e

Rename 'onboarding' to 'setup'

view details

John Watkins

commit sha 790cfe162c468a97c2c2bed2e3e9f4a097a61791

Eliminate need for most manual polyfills

view details

John Watkins

commit sha 258f0544084ea8c55c13b6ac8f3f7ad13dbfbc94

Add api-fetch inline scripts

view details

John Watkins

commit sha b5bcfda481083f753041896266ec90c5cceff711

Merge branch 'develop' into feature/4718-onboarding-scaffolding

view details

John Watkins

commit sha f3616a4f3b5cc958fd3dc311648ce55ac0eb7a8f

Remove unneeded require

view details

John Watkins

commit sha 74ec8eb2a90e89bbcdbeec294f69c0565c318ef1

Remove no longer used dataProvider

view details

John Watkins

commit sha 5a8cc1f2552a779670a216df13bd88416ef46efc

Remove debug code

view details

John Watkins

commit sha d58f88ffdf4668a628d30e6933d68f9bf00e6c31

phpstan fixes

view details

John Watkins

commit sha e2f7384ddde52aadc6b91420b4620530991ad124

E2E - reset site

view details

John Watkins

commit sha 87d88d7760d486c9efd307b93033d6655ff4808a

Fix failing PHPUnit test

view details

John Watkins

commit sha ad5d25d470d36e69a8af7b06a709b92dd44385ce

Allow feature with URL flag

view details

John Watkins

commit sha d0f8270a34ed2a21d0f0bdb07e0bd6f7f5f0c47e

Add new action to hook in submenu

view details

John Watkins

commit sha 8e6ea2a4c3e4c2a3d9ac3d0dc8a196f3ebc7f0bc

PHPCS fix

view details

John Watkins

commit sha b25e6871654dda08d0b8e5b0e9e711780d570d3a

Setup wizard refactor

view details

John Watkins

commit sha 02756e1999c5ce0e58d5f7944b1b423f27877f8c

Unit test fix

view details

John Watkins

commit sha d278a65a121c8630f98e69c61d3ad2d3f02add0e

Comment fix

view details

John Watkins

commit sha 32d0ff064ed57e43ed422ef626bb6cfe6090dbc4

Update bin/local-env/install-wordpress.sh: incorrect variable name Co-authored-by: Alain Schlesser <alain.schlesser@gmail.com>

view details

John Watkins

commit sha 11c57ead5556d2ee7fd9be6eb0dae0facd782b82

Update to WPCS-friendly return type Co-authored-by: Alain Schlesser <alain.schlesser@gmail.com>

view details

John Watkins

commit sha 3412e20d80c9e6606dec5321461b92767e09c721

Make class final Co-authored-by: Alain Schlesser <alain.schlesser@gmail.com>

view details

push time in 4 days

delete branch ampproject/amp-wp

delete branch : feature/4718-onboarding-scaffolding

delete time in 4 days

PR merged ampproject/amp-wp

Reviewers
Scaffolding for new onboarding wizard cla: yes

Summary

"Setup Wizard" submenu page

Sets up a new Submenu page called "Setup Wizard." This name can easily be changed. The markup on the page consists of an empty div serving as the root for the future onboarding wizard.

JS directory

Creates a JS directory called /setup. Inside the directory is an entry file that renders test text ("Setup Wizard") into the app root for testing purposes.

Refactor polyfills/new polyfills

~Updates the existing polyfills webpack config and removes some files from the js/polyfills directory. What those files were doing can be accomplished via Webpack only. For reference, I referred to the output setting in the Gutenberg plugin webpack config.~

~I moved some of the WP core packages from devDependencies to dependencies to allow Webpack to determine which packages need to be polyfilled.~

~The core packages compiled by the Webpack config will be loaded only if the WP version is below a target version, currently set to WP 5.4. In that version and later, the copies provided by core will be loaded instead.~

Edit note: The above is mostly no longer included. I did keep in the change to how @wordpress/dom-ready, @wordpress/i18n, and @wordpress/url were polyfilled.

Tests

Adds unit tests to cover the change and a small e2e test to be expanded on in future tasks.

Addresses issue #4718

Checklist

  • [x] My pull request is addressing an open issue (please create one otherwise).
  • [x] My code is tested and passes existing tests.
  • [x] My code follows the Engineering Guidelines (updates are often made to the guidelines, check it out periodically).
+1243 -1464

8 comments

17 changed files

johnwatkins0

pr closed time in 4 days

Pull request review commentampproject/amp-wp

Add ability to suppress plugins from AMP responses

 public static function get_validated_environment() { 	 *     Staleness of the validation results. An empty array if the results are fresh. 	 * 	 *     @type string $theme   The theme that was active but is no longer. Absent if theme is the same.-	 *     @type array  $plugins Plugins that used to be active but are no longer, or which are active now but weren't. Absent if the plugins were the same.+	 *     @type array  $plugins Plugins that used to be active but are no longer, or which are active now but weren't. Also includes plugins that have version updates. Absent if the plugins were the same. 	 *     @type array  $options Options that are now different. Absent if the options were the same.

Actually, you identified some code that needed to be resurrected to address #3474. Resurrection implemented in 4e1c478.

westonruter

comment created time in 4 days

push eventampproject/amp-wp

Weston Ruter

commit sha 4e1c478c4b304a129b5054d12d058289b7cc0450

Include options in validated environment and staleness check See #3474

view details

Weston Ruter

commit sha 2c3f2ca36f52b792a658543ac0dcf4ecfe925398

Fix PHPStan analysis by unsetting WP_Block_Type script and style instead of setting to null

view details

push time in 4 days

Pull request review commentampproject/amp-wp

Add ability to suppress plugins from AMP responses

 private function list_template_conditional_options( $options, $parent = null ) { 		<?php 	} +	/**+	 * Get plugin errors by sources.+	 *+	 * @return array Plugin errors by source.+	 */+	private static function get_plugin_errors_by_sources() {+		$errors_by_sources = AMP_Validated_URL_Post_Type::get_recent_validation_errors_by_source(); // @todo Exclude reviewed errors?+		unset( $errors_by_sources['plugin']['gutenberg'] ); // Omit Gutenberg to prevent unintentional attribution for shortcodes.+		unset( $errors_by_sources['plugin']['amp'] ); // Omit AMP because disabling in AMP responses would be bad!+		if ( isset( $errors_by_sources['plugin'] ) ) {+			return $errors_by_sources['plugin'];+		}+		return [];+	}++	/**+	 * Get suppressible plugin sources.+	 *+	 * @return string[] Plugin sources which are suppressible.+	 */+	private static function get_suppressible_plugin_sources() {+		return array_unique(+			array_intersect(+				array_merge(+					array_keys( self::get_plugin_errors_by_sources() ),+					array_keys( AMP_Options_Manager::get_option( Option::SUPPRESSED_PLUGINS ) )+				),+				array_map(+					function ( $plugin_file ) {+						return strtok( $plugin_file, '/' );+					},+					get_option( 'active_plugins', [] )+				)+			)+		);+	}++	/**+	 * Render suppressed plugins.+	 *+	 * @since 1.6+	 */+	public function render_suppressed_plugins() {+		$suppressed_plugins = AMP_Options_Manager::get_option( Option::SUPPRESSED_PLUGINS );++		require_once ABSPATH . 'wp-admin/includes/plugin.php';+		$plugins = get_plugins();+		unset( $plugins['amp/amp.php'] );+		foreach ( array_keys( $plugins ) as $plugin_file ) {+			if ( ! is_plugin_active( $plugin_file ) ) {+				unset( $plugins[ $plugin_file ] );+			}+		}+		uasort(+			$plugins,+			static function ( $a, $b ) {+				return strcmp( $a['Name'], $b['Name'] );+			}+		);++		?>+		<fieldset>+			<h4 class="title hidden"><?php esc_html_e( 'Suppressed Plugins', 'amp' ); ?></h4>+			<p>+				<?php esc_html_e( 'When a plugin emits invalid markup that causes an AMP validation error, one option is to review the invalid markup and allow it to be removed. Another option is to suppress the plugin from doing anything when rendering AMP pages. What follows is the list of active plugins with any causing validation errors being highlighted. If a plugin is emitting invalid markup that is causing validation errors and this plugin is not necessary on the AMP version of the page, it can be suppressed.', 'amp' ); ?>+			</p>++			<style>+			.amp-suppressed-plugins .suppressed-plugin:checked + label {+				text-decoration: line-through;+			}+			.amp-suppressed-plugins .plugin > details {+				margin-left: 30px;+			}+			.amp-suppressed-plugins .plugin > details > ul {+				margin-left: 30px;+				margin-top: 0.5em;+				margin-bottom: 1em;+				list-style-type: disc;+			}+			.amp-suppressed-plugins summary {+				user-select: none;+				cursor: pointer;+			}+			</style>++			<?php+			$element_name = AMP_Options_Manager::OPTION_NAME . '[' . Option::SUPPRESSED_PLUGINS . ']';++			$errors_by_sources = self::get_plugin_errors_by_sources()+			?>+			<ul>+				<?php foreach ( $plugins as $plugin_file => $plugin ) : ?>+					<?php+					$plugin_slug = strtok( $plugin_file, '/' );++					$is_suppressed = array_key_exists( $plugin_slug, $suppressed_plugins );+					if ( ! $is_suppressed && ! isset( $errors_by_sources[ $plugin_slug ] ) ) {+						continue;+					}+					?>+					<li class="plugin">+						<input+							type="checkbox"+							class="suppressed-plugin"+							id="<?php echo esc_attr( "$element_name-$plugin_file" ); ?>"+							name="<?php echo esc_attr( $element_name . '[]' ); ?>"+							value="<?php echo esc_attr( $plugin_slug ); ?>"+							<?php checked( $is_suppressed ); ?>+						>+						<label for="<?php echo esc_attr( "$element_name-$plugin_file" ); ?>">+							<?php+							if ( ! $is_suppressed ) {+								echo '<strong>';+							}+							echo esc_html( $plugin['Name'] );+							if ( ! $is_suppressed ) {+								echo '</strong>';+							}+							?>+						</label>+						<?php if ( $is_suppressed && version_compare( $suppressed_plugins[ $plugin_slug ][ Option::SUPPRESSED_PLUGINS_LAST_VERSION ], $plugins[ $plugin_file ]['Version'], '!=' ) ) : ?>+							<small>+								<?php if ( $suppressed_plugins[ $plugin_slug ][ Option::SUPPRESSED_PLUGINS_LAST_VERSION ] && $plugins[ $plugin_file ]['Version'] ) : ?>

This is here in the rare situation where a plugin was updated to add a version which previously lacked it, or it was updated to remove the version where it previously had one.

westonruter

comment created time in 4 days

Pull request review commentampproject/amp-wp

Add ability to suppress plugins from AMP responses

 private function list_template_conditional_options( $options, $parent = null ) { 		<?php 	} +	/**+	 * Get plugin errors by sources.+	 *+	 * @return array Plugin errors by source.+	 */+	private static function get_plugin_errors_by_sources() {+		$errors_by_sources = AMP_Validated_URL_Post_Type::get_recent_validation_errors_by_source(); // @todo Exclude reviewed errors?+		unset( $errors_by_sources['plugin']['gutenberg'] ); // Omit Gutenberg to prevent unintentional attribution for shortcodes.+		unset( $errors_by_sources['plugin']['amp'] ); // Omit AMP because disabling in AMP responses would be bad!+		if ( isset( $errors_by_sources['plugin'] ) ) {+			return $errors_by_sources['plugin'];+		}+		return [];+	}++	/**+	 * Get suppressible plugin sources.+	 *+	 * @return string[] Plugin sources which are suppressible.+	 */+	private static function get_suppressible_plugin_sources() {+		return array_unique(+			array_intersect(+				array_merge(+					array_keys( self::get_plugin_errors_by_sources() ),+					array_keys( AMP_Options_Manager::get_option( Option::SUPPRESSED_PLUGINS ) )+				),+				array_map(+					function ( $plugin_file ) {+						return strtok( $plugin_file, '/' );+					},+					get_option( 'active_plugins', [] )+				)+			)+		);

Yes, good. Done in d24b29c6bff37e676196dbe8dd89bad95615e4b9.

westonruter

comment created time in 4 days

push eventampproject/amp-wp

Weston Ruter

commit sha d24b29c6bff37e676196dbe8dd89bad95615e4b9

Clarify logic in get_suppressible_plugin_sources

view details

Weston Ruter

commit sha 4fcebbcf3aa797fb9b0822ff40a507e5303bb4f1

Fix syntax error after ccbf771b

view details

push time in 4 days

push eventampproject/amp-wp

Weston Ruter

commit sha ccbf771bdd481e6f22189a998866913667a555b8

Remove redundant check for whether validation error has WP_Term Co-authored-by: Pierre Gordon <16200219+pierlon@users.noreply.github.com>

view details

push time in 4 days

Pull request review commentampproject/amp-wp

Add ability to suppress plugins from AMP responses

 private function list_template_conditional_options( $options, $parent = null ) { 		<?php 	} +	/**+	 * Get plugin errors by sources.+	 *+	 * @return array Plugin errors by source.+	 */+	private static function get_plugin_errors_by_sources() {+		$errors_by_sources = AMP_Validated_URL_Post_Type::get_recent_validation_errors_by_source(); // @todo Exclude reviewed errors?+		unset( $errors_by_sources['plugin']['gutenberg'] ); // Omit Gutenberg to prevent unintentional attribution for shortcodes.+		unset( $errors_by_sources['plugin']['amp'] ); // Omit AMP because disabling in AMP responses would be bad!+		if ( isset( $errors_by_sources['plugin'] ) ) {+			return $errors_by_sources['plugin'];+		}+		return [];+	}++	/**+	 * Get suppressible plugin sources.+	 *+	 * @return string[] Plugin sources which are suppressible.+	 */+	private static function get_suppressible_plugin_sources() {+		return array_unique(+			array_intersect(+				array_merge(+					array_keys( self::get_plugin_errors_by_sources() ),+					array_keys( AMP_Options_Manager::get_option( Option::SUPPRESSED_PLUGINS ) )+				),+				array_map(+					function ( $plugin_file ) {+						return strtok( $plugin_file, '/' );+					},+					get_option( 'active_plugins', [] )+				)+			)+		);+	}++	/**+	 * Render suppressed plugins.+	 *+	 * @since 1.6+	 */+	public function render_suppressed_plugins() {+		$suppressed_plugins = AMP_Options_Manager::get_option( Option::SUPPRESSED_PLUGINS );++		require_once ABSPATH . 'wp-admin/includes/plugin.php';+		$plugins = get_plugins();+		unset( $plugins['amp/amp.php'] );+		foreach ( array_keys( $plugins ) as $plugin_file ) {+			if ( ! is_plugin_active( $plugin_file ) ) {+				unset( $plugins[ $plugin_file ] );+			}+		}+		uasort(+			$plugins,+			static function ( $a, $b ) {+				return strcmp( $a['Name'], $b['Name'] );+			}+		);++		?>+		<fieldset>+			<h4 class="title hidden"><?php esc_html_e( 'Suppressed Plugins', 'amp' ); ?></h4>+			<p>+				<?php esc_html_e( 'When a plugin emits invalid markup that causes an AMP validation error, one option is to review the invalid markup and allow it to be removed. Another option is to suppress the plugin from doing anything when rendering AMP pages. What follows is the list of active plugins with any causing validation errors being highlighted. If a plugin is emitting invalid markup that is causing validation errors and this plugin is not necessary on the AMP version of the page, it can be suppressed.', 'amp' ); ?>+			</p>++			<style>+			.amp-suppressed-plugins .suppressed-plugin:checked + label {+				text-decoration: line-through;+			}+			.amp-suppressed-plugins .plugin > details {+				margin-left: 30px;+			}+			.amp-suppressed-plugins .plugin > details > ul {+				margin-left: 30px;+				margin-top: 0.5em;+				margin-bottom: 1em;+				list-style-type: disc;+			}+			.amp-suppressed-plugins summary {+				user-select: none;+				cursor: pointer;+			}+			</style>++			<?php+			$element_name = AMP_Options_Manager::OPTION_NAME . '[' . Option::SUPPRESSED_PLUGINS . ']';++			$errors_by_sources = self::get_plugin_errors_by_sources()+			?>+			<ul>+				<?php foreach ( $plugins as $plugin_file => $plugin ) : ?>+					<?php+					$plugin_slug = strtok( $plugin_file, '/' );

Yeah, it's a good point. The reality is that AMP_Validation_Manager::get_source() is not able to detect such nested plugins anyway. The source slug extracted from a plugin will always be just the directory name if the plugin is not a single file:

https://github.com/ampproject/amp-wp/blob/9d662d07da228c6cc8dedfab5abc724feaf39177/includes/validation/class-amp-validation-manager.php#L1636-L1639

So that is why the the directory alone is being considered (or else just the file name if the plugin is not in a directory).

westonruter

comment created time in 4 days

push eventampproject/amp-wp

Weston Ruter

commit sha 85de42e3752547bfc16d4094934906fe2d9f587b

Make closures static Co-authored-by: Pierre Gordon <16200219+pierlon@users.noreply.github.com>

view details

push time in 4 days

push eventampproject/amp-wp

Renovate Bot

commit sha 94a4691daad5870b0d200e569327bef8f37bff13

Update dependency terser-webpack-plugin to v2.3.6

view details

Weston Ruter

commit sha 919f1b9ef001f4f64534755506df44e8b91a15e2

Make collection the default Schema.org type for archive queries

view details

Weston Ruter

commit sha 16b1374227c0b24c51e96194b7af7ab7ef4dc5de

Test that author archive results in collection type

view details

Pierre Gordon

commit sha e1994de4d30dd0d87b74349bf302ed480afa36da

Use amp-embedly-card for Tiktok embeds

view details

Pierre Gordon

commit sha 8ab4907e9e2516443a5be58817b13a7dedcc81f0

Disable share icons

view details

Alain Schlesser

commit sha 41595d65e2cbdf20715eea554fa3a6acb3ab0cb7

Bump PHP Stan level to 4

view details

Alain Schlesser

commit sha 6c272288dc515c33721a26458703acbed908890d

Fix bug in Reorderhead with checking against empty nodelist

view details

Alain Schlesser

commit sha 14084492b0a71fd6b24121140dbbba8ed0868aa6

Fix bug with comparing floats in ServerSideRendering

view details

Alain Schlesser

commit sha 63e99a63ca0624d5cff8f6e8d8d1ac85b89e337b

Remove unneeded check for null as DOMElement is enforced

view details

Alain Schlesser

commit sha 83018f2168f2fa48ac21724f11ac01ea94d85855

Fix bug with status code comparison

view details

Pierre Gordon

commit sha adb2b7d57b76f9588e655c97c2b53af698fd7166

Prevent rendering `amp_status` custom field when editing post

view details

Weston Ruter

commit sha 0470c0825db9c2ddd2d5ffb5b27ff83ecfda82ba

Merge pull request #4682 from ampproject/fix/4633-tiktok-embed Use amp-embedly-card for Tiktok embeds

view details

Pierre Gordon

commit sha cc6cc7186542a42d602b384b0a34ee552a567cc4

Pin amp-experiment to v0.1 (#4690)

view details

Weston Ruter

commit sha 928fd900b7ea2519ffc59d32f4b80b40d876b9ce

Update allowed tags/attributes from spec in amphtml to 2005050322002

view details

Pierre Gordon

commit sha d2379f277a14fdc94798e618920923eebf8d4b6d

Revert "Prevent rendering `amp_status` custom field when editing post" This reverts commit adb2b7d5

view details

Pierre Gordon

commit sha 2da4b55fe65cc1620ed5253f9e7b3b495145f00a

Suppress Site Health ICU test if site or home URL is not an IDN (#4698) * Suppress ICU test if site or home URL is not an IDN * Check if any domain labels start with `xn--` to determine if a domain is an IDN * Return true if IDN domain found * Update src/Admin/SiteHealth.php Make `is_intl_extension_needed()` private Co-authored-by: Weston Ruter <westonruter@google.com> Co-authored-by: Weston Ruter <westonruter@google.com>

view details

Renovate Bot

commit sha 8f346c21dec0c393794383e8ec1e76413907e412

Update dependency @wordpress/e2e-test-utils to v4.7.0

view details

Weston Ruter

commit sha d70c20db43ed4c731d94f056adb1a46b592a64ae

Use CollectionPage rather than Collection Co-authored-by: Alain Schlesser <alain.schlesser@gmail.com>

view details

WhiteSource Renovate

commit sha f8910e7206bda6ce2d64df4bacf86c24e08cedfb

Update dependency wp-coding-standards/wpcs to v2.3.0 (#4721)

view details

WhiteSource Renovate

commit sha 7e0669ca4aa38c506d5ffe149378bb8925d78106

Update dependency uuid to v7.0.3 (#4490)

view details

push time in 4 days

Pull request review commentampproject/amp-wp

Scaffolding for new onboarding wizard

+<?php+/**+ * AMP setup wizard page.+ *+ * @package AMP+ * @since @todo NEW_ONBOARDING_RELEASE_VERSION+ */++/**+ * AMP setup wizard submenu page class.+ *+ * @since @todo NEW_ONBOARDING_RELEASE_VERSION+ */+final class AMP_Setup_Wizard_Submenu_Page {+	/**+	 * Handle for JS file.+	 *+	 * @var string+	 */+	const JS_HANDLE = 'amp-setup';++	/**+	 * HTML ID for the app root element.+	 *+	 * @var string+	 */+	const APP_ROOT_ID = 'amp-setup';++	/**+	 * Sets up hooks.+	 */+	public function init() {+		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );+	}++	/**+	 * Renders the setup screen markup.+	 */+	public function render() {+		?>+			<div id="<?php echo esc_attr( static::APP_ROOT_ID ); ?>"></div>+		<?php+	}++	/**+	 * Provides the setup screen handle.+	 *+	 * @return string+	 */+	public function screen_handle() {+		return sprintf( 'amp_page_%s', AMP_Setup_Wizard_Submenu::SCREEN_ID );+	}++	/**+	 * Enqueues setup assets.+	 *+	 * @param string $hook_suffix The current admin page.+	 */+	public function enqueue_assets( $hook_suffix ) {+		if ( $this->screen_handle() !== $hook_suffix ) {+			return;+		}++		wp_enqueue_script(+			self::JS_HANDLE,+			amp_get_asset_url( 'js/' . self::JS_HANDLE . '.js' ),+			[],+			AMP__VERSION,

Actually I'm not seeing an amp-setup.asset.php being generated like for the other scripts. Why?

johnwatkins0

comment created time in 4 days

Pull request review commentampproject/amp-wp

Scaffolding for new onboarding wizard

 function amp_bootstrap_admin() { 	$site_health = new SiteHealth(); 	$site_health->init(); }++/**+ * Whether to activate the new onboarding feature.+ *+ * @return bool+ */+function amp_should_use_new_onboarding() {+	global $wp_version;++	if ( version_compare( $wp_version, '5.0', '<' ) ) {+		return false;+	}++	// @todo Remove this check when the onboarding feature is released.+	if ( '1' === filter_input( INPUT_GET, 'amp-new-onboarding', FILTER_SANITIZE_NUMBER_INT ) ) {

Granted this is just a feature flag, so no big deal.

johnwatkins0

comment created time in 4 days

Pull request review commentampproject/amp-wp

Scaffolding for new onboarding wizard

+<?php+/**+ * AMP setup wizard page.+ *+ * @package AMP+ * @since @todo NEW_ONBOARDING_RELEASE_VERSION+ */++/**+ * AMP setup wizard submenu page class.+ *+ * @since @todo NEW_ONBOARDING_RELEASE_VERSION+ */+final class AMP_Setup_Wizard_Submenu_Page {+	/**+	 * Handle for JS file.+	 *+	 * @var string+	 */+	const JS_HANDLE = 'amp-setup';++	/**+	 * HTML ID for the app root element.+	 *+	 * @var string+	 */+	const APP_ROOT_ID = 'amp-setup';++	/**+	 * Sets up hooks.+	 */+	public function init() {+		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );+	}++	/**+	 * Renders the setup screen markup.+	 */+	public function render() {+		?>+			<div id="<?php echo esc_attr( static::APP_ROOT_ID ); ?>"></div>+		<?php+	}++	/**+	 * Provides the setup screen handle.+	 *+	 * @return string+	 */+	public function screen_handle() {+		return sprintf( 'amp_page_%s', AMP_Setup_Wizard_Submenu::SCREEN_ID );+	}++	/**+	 * Enqueues setup assets.+	 *+	 * @param string $hook_suffix The current admin page.+	 */+	public function enqueue_assets( $hook_suffix ) {+		if ( $this->screen_handle() !== $hook_suffix ) {+			return;+		}++		wp_enqueue_script(+			self::JS_HANDLE,+			amp_get_asset_url( 'js/' . self::JS_HANDLE . '.js' ),+			[],+			AMP__VERSION,

Shouldn't this be using the version as exported into amp-setup.assets.php?

johnwatkins0

comment created time in 4 days

Pull request review commentampproject/amp-wp

Scaffolding for new onboarding wizard

+<?php+/**+ * AMP setup wizard page.+ *+ * @package AMP+ * @since @todo NEW_ONBOARDING_RELEASE_VERSION+ */++/**+ * AMP setup wizard submenu page class.+ *+ * @since @todo NEW_ONBOARDING_RELEASE_VERSION+ */+final class AMP_Setup_Wizard_Submenu_Page {+	/**+	 * Handle for JS file.+	 *+	 * @var string+	 */+	const JS_HANDLE = 'amp-setup';++	/**+	 * HTML ID for the app root element.+	 *+	 * @var string+	 */+	const APP_ROOT_ID = 'amp-setup';++	/**+	 * Sets up hooks.+	 */+	public function init() {+		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );+	}++	/**+	 * Renders the setup screen markup.+	 */+	public function render() {+		?>+			<div id="<?php echo esc_attr( static::APP_ROOT_ID ); ?>"></div>+		<?php+	}++	/**+	 * Provides the setup screen handle.+	 *+	 * @return string+	 */+	public function screen_handle() {+		return sprintf( 'amp_page_%s', AMP_Setup_Wizard_Submenu::SCREEN_ID );+	}++	/**+	 * Enqueues setup assets.+	 *+	 * @param string $hook_suffix The current admin page.+	 */+	public function enqueue_assets( $hook_suffix ) {+		if ( $this->screen_handle() !== $hook_suffix ) {+			return;+		}++		wp_enqueue_script(+			self::JS_HANDLE,+			amp_get_asset_url( 'js/' . self::JS_HANDLE . '.js' ),+			[],

Shouldn't this be using the dependencies as exported into amp-setup.assets.php?

johnwatkins0

comment created time in 4 days

Pull request review commentampproject/amp-wp

Scaffolding for new onboarding wizard

 function amp_bootstrap_admin() { 	$site_health = new SiteHealth(); 	$site_health->init(); }++/**+ * Whether to activate the new onboarding feature.+ *+ * @return bool+ */+function amp_should_use_new_onboarding() {+	global $wp_version;++	if ( version_compare( $wp_version, '5.0', '<' ) ) {

Minor thing, but instead of getting the global variable you could rather do:

	if ( version_compare( get_bloginfo( 'version' ), '5.0', '<' ) ) {
johnwatkins0

comment created time in 4 days

Pull request review commentampproject/amp-wp

Scaffolding for new onboarding wizard

+<?php+/**+ * AMP setup wizard page.+ *+ * @package AMP+ * @since @todo NEW_ONBOARDING_RELEASE_VERSION+ */++/**+ * AMP setup wizard submenu page class.+ *+ * @since @todo NEW_ONBOARDING_RELEASE_VERSION+ */+final class AMP_Setup_Wizard_Submenu_Page {+	/**+	 * Handle for JS file.+	 *+	 * @var string+	 */+	const JS_HANDLE = 'amp-setup';++	/**+	 * HTML ID for the app root element.+	 *+	 * @var string+	 */+	const APP_ROOT_ID = 'amp-setup';++	/**+	 * Sets up hooks.+	 */+	public function init() {+		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );+	}++	/**+	 * Renders the setup screen markup.+	 */+	public function render() {+		?>+			<div id="<?php echo esc_attr( static::APP_ROOT_ID ); ?>"></div>+		<?php+	}++	/**+	 * Provides the setup screen handle.+	 *+	 * @return string+	 */+	public function screen_handle() {+		return sprintf( 'amp_page_%s', AMP_Setup_Wizard_Submenu::SCREEN_ID );+	}++	/**+	 * Enqueues setup assets.+	 *+	 * @param string $hook_suffix The current admin page.+	 */+	public function enqueue_assets( $hook_suffix ) {+		if ( $this->screen_handle() !== $hook_suffix ) {+			return;+		}++		wp_enqueue_script(+			self::JS_HANDLE,+			amp_get_asset_url( 'js/' . self::JS_HANDLE . '.js' ),+			[],+			AMP__VERSION,+			true+		);++		wp_localize_script(+			self::JS_HANDLE,+			'ampSetup',+			[+				'APP_ROOT_ID' => self::APP_ROOT_ID,+			]+		);

I recommend using wp_add_inline_script() and manually export data via wp_json_encode(). The reason for this is that wp_localize_script() casts all array values to strings, which can be problematic when exporting integers and booleans. This isn't an issue here, but it's something to keep in mind.

johnwatkins0

comment created time in 4 days

Pull request review commentampproject/amp-wp

Scaffolding for new onboarding wizard

 function amp_bootstrap_admin() { 	$site_health = new SiteHealth(); 	$site_health->init(); }++/**+ * Whether to activate the new onboarding feature.+ *+ * @return bool+ */+function amp_should_use_new_onboarding() {+	global $wp_version;++	if ( version_compare( $wp_version, '5.0', '<' ) ) {+		return false;+	}++	// @todo Remove this check when the onboarding feature is released.+	if ( '1' === filter_input( INPUT_GET, 'amp-new-onboarding', FILTER_SANITIZE_NUMBER_INT ) ) {

Note that I generally don't recommend using filter_input() because (1) core doesn't use it, and (2) as far as I know the input values can't be mutated during unit testing.

johnwatkins0

comment created time in 4 days

issue commentampproject/amp-wp

Minimize validation error warnings when Developer Tools are turned off

On a related note, I think the “Enable AMP” checkbox should be only presented to administrator users. Instead of non-admin authors seeing AMP validation errors and just going straight to disabling AMP for the post, they should instead be directed to contact the administrator to get advice on the issue and then the administrator can decide whether to disable AMP for the post.

Furthermore, the “Enable AMP” checkbox should be gated behind whether developer tools are enabled, not just whether the user is an administrator. For more on this, see https://github.com/ampproject/amp-wp/issues/1864#issuecomment-631850402.

westonruter

comment created time in 4 days

issue commentampproject/amp-wp

Remove "Your site does not allow AMP to be disabled" warning

Ah, ok.

Would this also be applied for plugins?

Yes, as I don't think we'd be able to tell the difference between whether a theme or plugin is trying to prevent disabling.

In short, we'd be eliminating the immutable aspect of the supportable templates.

However, at the same time, the notices and the toggle here should only be exposed to users who have developer tools enabled. In other words, the notice could actually remain in place, to some degree but whether the AMP Enabled toggle (or the notice) are displayed as a whole should be gated behind whether a user has “developer tools” enabled, as noted in https://github.com/ampproject/amp-wp/issues/2673#issuecomment-542303464.

So this will mean that themes/plugins will continue to specify the default for whether a given template is enabled, and normal users won't be able to make any changes to that… but then users who have access to developer tools (who also have access to the admin screen, for example) can then make the decision for templates should be enabled for AMP. This may conflict with what the theme desires to be enabled/disabled for AMP, but in the coming world of Bento AMP the previous reason for immutable is going away: AMP components will be usable on non-AMP pages, so themes will be able to freely use them in their templates regardless of whether the page is being post-processed.

So long story short: the notices/warnings can actually remain, but we will suppress them and the AMP enabled toggle, if the user does not have developer tools enabled.

swissspidy

comment created time in 4 days

issue openedampproject/amp-wp

Normalize script contents that contain nonces

Feature description

A common source of validation errors is a script that is enqueued along with a call to wp_localize_script() which contains a nonce. For example:

add_action( 'wp_enqueue_scripts', function() {
	wp_enqueue_script( 'foo', 'https://example.com/foo.js' );
	wp_localize_script( 'foo', '_foo', [
		'nonce' => wp_create_nonce( 'foo' ),
	] );
} );

This results in two validation errors:

image

The validation error for the inline script, however, will continually keep appearing as new even after it has been reviewed repeatedly. This is because the nonce will change on a daily basis. For this reason, it may be a good idea to default to normalize the text value of such validation errors to normalize them, for example to replace the nonce string with __NONCE__:

add_filter( 'amp_validation_error', function ( $error ) {
	if (
		isset( $error['code'], $error['node_name'], $error['text'] )
		&&
		'DISALLOWED_TAG' === $error['code']
		&&
		'script' === $error['node_name']
	) {
		$error['text'] = preg_replace( '/"[0-9a-f]{10}"/', '__HEXSTR10__', $error['text'] );
	}
	return $error;
} );

Note that we're already doing this for the ver URL parameters for enqueued scripts and styles:

https://github.com/ampproject/amp-wp/blob/9886b99469155b192e0a9c7769f84d8ea2f254b9/includes/sanitizers/class-amp-base-sanitizer.php#L600-L605

However, it is much easier to correctly normalize a ver parameter than to normalize a string that looks like a nonce. But the worst case is that we replace a non-nonce string with a placeholder, which isn't the end of the world.


Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance criteria

  • <!-- One or more bullet points for acceptance criteria. -->

Implementation brief

  • <!-- One or more bullet points for how to technically resolve the issue. For significant Implementation Design, it is ok use a Google document accessible by anyone. -->

QA testing instructions

  • <!-- One or more bullet points to describe how to test the implementation in QA. -->

Demo

  • <!-- A video or screenshots demoing the implementation. -->

Changelog entry

  • <!-- One sentence summarizing the PR, to be used in the changelog. -->

created time in 4 days

push eventampproject/amp-wp

Weston Ruter

commit sha 919f1b9ef001f4f64534755506df44e8b91a15e2

Make collection the default Schema.org type for archive queries

view details

Weston Ruter

commit sha 16b1374227c0b24c51e96194b7af7ab7ef4dc5de

Test that author archive results in collection type

view details

Weston Ruter

commit sha d70c20db43ed4c731d94f056adb1a46b592a64ae

Use CollectionPage rather than Collection Co-authored-by: Alain Schlesser <alain.schlesser@gmail.com>

view details

Weston Ruter

commit sha 77430165b611134b6c0c40a5cc6d46f98e3bb97e

Merge branch 'develop' of github.com:ampproject/amp-wp into add/archive-collection-schema-type * 'develop' of github.com:ampproject/amp-wp: Raise PHPStan level to 4 in lib/common (#4686) Update dependency uuid to v8 Update dependency webpack to v4.43.0 Update dependency eslint-plugin-jest to v23.11.0 Update dependency eslint-plugin-react to v7.20.0 (#4691) Update dependency postcss to v7.0.30 (#4649) Update dependency moment to v2.25.3 (#4639) Update dependency babel-jest to v26 (#4652) Update dependency uuid to v7.0.3 (#4490) Update dependency wp-coding-standards/wpcs to v2.3.0 (#4721) Suppress Site Health ICU test if site or home URL is not an IDN (#4698) Pin amp-experiment to v0.1 (#4690) Strip multiple BOM characters (#4683) Strip leading BOM and whitespace and trailing HTML comment before parsing validation response JSON (#4679) Disable share icons Use amp-embedly-card for Tiktok embeds Update dependency xwp/wp-dev-lib to v1.6.4 Update dependency eslint to v7 Update dependency terser-webpack-plugin to v2.3.6

view details

Weston Ruter

commit sha 9d662d07da228c6cc8dedfab5abc724feaf39177

Merge pull request #4680 from ampproject/add/archive-collection-schema-type Make collection the default Schema.org type for archive queries

view details

push time in 4 days

delete branch ampproject/amp-wp

delete branch : add/archive-collection-schema-type

delete time in 4 days

PR merged ampproject/amp-wp

Reviewers
Make collection the default Schema.org type for archive queries cla: yes

Summary

Addresses issue #4666

Checklist

  • [x] My pull request is addressing an open issue (please create one otherwise).
  • [x] 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).
+16 -9

3 comments

2 changed files

westonruter

pr closed time in 4 days

push eventampproject/amp-wp

Weston Ruter

commit sha f5658bb258247940486b29a5d4b2778784ac6f05

Make collection the default Schema.org type for archive queries (#4680)

view details

push time in 4 days

issue openedampproject/amp-wp

Store revision history for validation error statuses

Feature description

When someone changes the status of a validation error, such as to keep some invalid markup, there is currently no history of this change being made. Therefore, some author who has access to developer tools could decide to keep some invalid markup that actually occurs on every page (see #4751), and the result would be turning off AMP for the entire site. (See #2673 for restricting access to dev tools by user role.) There should be revision history for changes to the validation error statuses so that there is an audit trail of such changes being made to the site.


Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance criteria

  • <!-- One or more bullet points for acceptance criteria. -->

Implementation brief

  • The user ID that changed the status of the error as well as the datetime at which the change was made could be stored in term_meta for the amp_validation_error taxonomy term. Alternatively, the revision history could be stored as post revisions of the amp_validated_url, but this this wouldn't be as useful to see who changed the status of an error that occurs across multiple URLs.

QA testing instructions

  • <!-- One or more bullet points to describe how to test the implementation in QA. -->

Demo

  • <!-- A video or screenshots demoing the implementation. -->

Changelog entry

  • <!-- One sentence summarizing the PR, to be used in the changelog. -->

created time in 4 days

issue openedampproject/amp-wp

Indicate validation errors which occur on other URLs and warn when keeping them

Feature description

When an invalid script is being enqueued unconditionally on every template, every URL that is validated will have a validation error for that invalid markup being listed. When a user looks at the list of validation errors for a given URL, there is currently no indication of which errors are (potentially) unique to that URL and which are known to also occur on other URLs as well. There should be a link to see the list of URLs by opening the validation error single view in a new window. When they change the status of validation errors that occur on other URLs to keep the invalid markup, there should be more explicit warnings that doing so will disable AMP potentially across the entire site.


Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance criteria

  • <!-- One or more bullet points for acceptance criteria. -->

Implementation brief

  • <!-- One or more bullet points for how to technically resolve the issue. For significant Implementation Design, it is ok use a Google document accessible by anyone. -->

QA testing instructions

  • <!-- One or more bullet points to describe how to test the implementation in QA. -->

Demo

  • <!-- A video or screenshots demoing the implementation. -->

Changelog entry

  • <!-- One sentence summarizing the PR, to be used in the changelog. -->

created time in 4 days

push eventampproject/amp-wp

Pierre Gordon

commit sha adb2b7d57b76f9588e655c97c2b53af698fd7166

Prevent rendering `amp_status` custom field when editing post

view details

Pierre Gordon

commit sha d2379f277a14fdc94798e618920923eebf8d4b6d

Revert "Prevent rendering `amp_status` custom field when editing post" This reverts commit adb2b7d5

view details

Pierre Gordon

commit sha 33886dfa3edd9eb3ed22809ce41a06173c566b36

Update AMP status for a post via its REST API field

view details

Pierre Gordon

commit sha c81895875d614682768e96ebdb744c7e3d525d2c

Set 403 status for insufficient permission error Co-authored-by: Weston Ruter <westonruter@google.com>

view details

Pierre Gordon

commit sha 02625fb09eea381c65a4d1611cdbff77fef71bb7

Register `amp_enabled` REST field for `post` post type only

view details

Pierre Gordon

commit sha 49992b009c6da80bc13fc40d6410231d3e3eedbb

Return an error if the post type is invalid or the AMP status failed to be updated

view details

Pierre Gordon

commit sha f48bbdaeda2d265dee39b30e3aad1b1a67ef47ad

Register REST field for `page` post type

view details

Pierre Gordon

commit sha 91a5eb8d6014c8e85cf3047ed44dc4ff82bdb782

Get eligible post types from `AMP_Post_Type_Support::get_post_types_for_rest_api()`

view details

Weston Ruter

commit sha a46bdb32b1c691f4c71fe6e3b0675cb316e61e47

Merge branch 'develop' of github.com:ampproject/amp-wp into fix/4659-enable-amp-toggle * 'develop' of github.com:ampproject/amp-wp: (46 commits) Update dependency eslint-plugin-jest to v23.13.1 Update dependency autoprefixer to v9.8.0 Update dependency eslint-plugin-jest to v23.13.0 Fix phpdoc for sanitizer_classes and embed_handler_classes Remove array_column() polyfill since plugin requires PHP 5.6+ Update dependency @wordpress/scripts to v9.1.0 Remove redundant subtraction Raise level to 3 Adapt type-hint for AMP_Image_Dimension_Extractor::normalize_url() Use empty string for pruning textContent Adapt typehint for AMP_Content->sanitizer_classes Adapt typehint for AMP_Tag_And_Attribute_Sanitizer->allowed_tags Adapt typehints for methods returning a rule spec check result Adapt typehint for AMP_Style_Sanitizer->current_sources Adapt return typehint for AMP_YouTube_Embed_Handler::get_video_id_from_url() Adapt return value for failed AMP_Twitter_Embed_Handler::get_tweet_id() Use unset instead of null assignment for AMP_Playlist_Embed_Handler->removed_shortcode_callback Adapt xpath queries in AMP_Facebook_Embed_Handler::sanitize_raw_embeds() Adapt return typehint for AMP_DailyMotion_Embed_Handler::get_video_id_from_url() Adapt typehint of AMP_CLI_Validation_Command->force_crawl_urls ...

view details

Weston Ruter

commit sha 1744a3044511d9274d785d28d8e01d5a83140c8b

Update method names, comments, and add versions

view details

Weston Ruter

commit sha 24206a2b38a8262417c906f67436b5474d628a26

Give explicit null return value for update_amp_enabled_rest_field

view details

Weston Ruter

commit sha 90df45d3447b1e52d739b2b9379526a81aa81051

Fix logic for determining which post types should get REST API fields

view details

Weston Ruter

commit sha 0e9d4ad2120d4a357626405fb792678fb1822f7e

Ensure boolean return value for isAMPEnabled

view details

Weston Ruter

commit sha 1b04ac25f3bfcb3168a735667739d04553d1f124

Merge pull request #4689 from ampproject/fix/4659-enable-amp-toggle Expose `amp_status` as an attribute in the REST API and always protect the postmeta counterpart

view details

push time in 4 days

delete branch ampproject/amp-wp

delete branch : fix/4659-enable-amp-toggle

delete time in 4 days

PR merged ampproject/amp-wp

Reviewers
Expose `amp_status` as an attribute in the REST API and always protect the postmeta counterpart cla: yes

Summary

<!-- Please reference the issue this PR addresses. If one doesn't exist, please create one and put in its description what you would have otherwise put here in the PR description. Do not use "Fixes" or "Closes" as the issue needs to remain open for QA/UAT purposes until the release is published. -->

This PR adds a new REST API attribute to AMP supported post types called amp_enabled, which is a boolean representation of the amp_status post meta. It's counterpart, the amp_status post meta, is now permanently protected and cannot be modified via an API request nor updated via the Custom Fields meta box, for example.

The AMP status can now only be modified via the REST API, given that the user has the right permissions to do so.

Addresses issue #4659.

Checklist

  • [x] My pull request is addressing an open issue (please create one otherwise).
  • [x] My code is tested and passes existing tests.
  • [x] My code follows the Engineering Guidelines (updates are often made to the guidelines, check it out periodically).
+296 -109

4 comments

10 changed files

pierlon

pr closed time in 4 days

pull request commentampproject/amp-wp

Expose `amp_status` as an attribute in the REST API and always protect the postmeta counterpart

Going a head and merging this. @schlessera can follow up with anything that needs to be amended.

@pierlon Let's skip the 1.5 branch merge for now. This can be fixed with the workaround plugin for anyone that has the issue.

pierlon

comment created time in 4 days

Pull request review commentampproject/amp-wp

Expose `amp_status` as an attribute in the REST API and always protect the postmeta counterpart

 public function preview_post_link( $link ) {  		return $link; 	}++	/**+	 * Hide the `amp_status` custom field when edition a post.+	 *+	 * @param bool   $protected Whether the key is considered protected.+	 * @param string $meta_key Metadata key.+	 * @param string $meta_type Type of object metadata is for.+	 * @return bool+	 */+	public function hide_amp_status_meta( $protected, $meta_key, $meta_type ) {+		global $pagenow;++		if ( is_admin() && 'post.php' === $pagenow && self::STATUS_POST_META_KEY === $meta_key && 'post' === $meta_type ) {

Maybe instead of is_admin() then the check should be for ! REST_REQUEST, since the point is to only expose it in the REST API.

pierlon

comment created time in 4 days

issue closedampproject/amphtml

Allow @document (and @-moz-document) CSS at-rules?

A few WordPress themes I've been evaluating for AMP compatibility are making use of the experimental @document CSS at-rule (more specifically, @-moz-document).

Specifically, there are 69 instances I've found across ~4,000 themes.

Every instance I've seen is used as a browser sniffing technique, for example:

@-moz-document url-prefix() {
    /* Firefox-specific rules here! */
}

Nevertheless, the MDN docs notes that this at-rule has been disabled now in stylesheets coming from webpages:

<blockquote><p><strong>Note</strong>: There is a -moz-prefixed version of this property — <code>@-moz-document</code>. This has been limited to use only in user and UA sheets in Firefox 59 in Nightly and Beta — an experiment designed to mitigate potential CSS injection attacks (<a class="external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1035091" rel="noopener" title="FIXED: limit @-moz-document to user and UA sheets (Makes it useless for exfiltration in CSS-injection attacks)">bug 1035091</a>).</p></blockquote>

This being the case, perhaps this answers the question and support should not be added. Since AMP pages are never user or UA sheets, the rule would never be used by any browser yet.

closed time in 4 days

westonruter

issue commentafragen/github-updater

Updating plugins stored in Gists

I tried installing a Gist plugin but leaving the slug blank:

It would be nice if it tried to automatically detect the plugin's slug from the plugin file in the Gist. But otherwise, installing with the Gist ID as the directory is not bad. The warning is unexpected, however.

westonruter

comment created time in 5 days

issue commentafragen/github-updater

Updating plugins stored in Gists

I tried installing a Gist plugin but leaving the slug blank:

image

I got an unexpected warning:

image

Nevertheless, the plugin was installed into the directory name eaf04a2d7de0c3c778d10f3a5a67da6d4fe9aa4b-n0AI35.

I deleted the plugin and tried again, this time with a slug provided:

image

This time no warning:

image

It was installed into the amp-gravity-forms-submission-shim directory as expected.

I'm seeing the plugin listed without the warning now:

image

And if I manually change the local copy's version to an older version (0.1.3) and then check for updates, I see it:

image

The update succeeds:

image

image

westonruter

comment created time in 5 days

issue openedampproject/amp-wp

Script translations lack source attribition

Bug Description

Given a plugjn that enqueues a script and adds script translations:

add_action(
	'wp_enqueue_scripts',
	function () {
		wp_enqueue_script( 'foo', plugin_dir_url( __FILE__ ) . 'foo.js', [], false, true );
		wp_set_script_translations( 'foo', 'foo' );
	}
);

This results in i18n.js being eheuqued from core, but the plugin is properly attributed. The foo.js file is also properly attributed to the plugin. The translations script however is not properly attributed:

image

The plugin is currently accounting for inline scripts added via wp_localize_script() and wp_add_inline_script() but it is not for wp_set_script_translations().

Expected Behaviour

Scripts output via wp_set_script_translations() should be attributed to the plugin/theme that added it.

Steps to reproduce

  1. Create a plugin with the above PHP code.
  2. Validate a page.

Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance criteria

  • <!-- One or more bullet points for acceptance criteria. -->

Implementation brief

  • <!-- One or more bullet points for how to technically resolve the issue. For significant Implementation Design, it is ok use a Google document accessible by anyone. -->

QA testing instructions

  • <!-- One or more bullet points to describe how to test the implementation in QA. -->

Demo

  • <!-- A video or screenshots demoing the implementation. -->

Changelog entry

  • <!-- One sentence summarizing the PR, to be used in the changelog. -->

created time in 5 days

issue commentampproject/amp-wp

Suppress validation errors that may occur from fatal errors being displayed

To reproduce the issue, I had to enable Xdebug and turn on the html_errors PHP setting.

pierlon

comment created time in 5 days

delete branch ampproject/amp-wp

delete branch : fix/4580-suppress-printed-errors

delete time in 5 days

push eventampproject/amp-wp

Pierre Gordon

commit sha e86de11756247ca34bc5c5a4e4e9f6ae51252f8d

Suppress display of fatal errors during validate request

view details

Pierre Gordon

commit sha 0614c80de60edf4aff4859250ce063fee0874af4

Typecast `display_errors` config before comparing value

view details

Weston Ruter

commit sha 5510c09563f422cc0691118f8cb2a64d7919eadf

Eliminate needless variable

view details

Weston Ruter

commit sha 5fff885b784b22d469a4be538554351a35f4d37e

Merge pull request #4746 from ampproject/fix/4580-suppress-printed-errors Suppress display of fatal errors during validate request

view details

push time in 5 days

PR merged ampproject/amp-wp

Reviewers
Suppress display of fatal errors during validate request cla: yes

Summary

This PR suppresses the display of any fatal errors that may arise during validation. When these errors are displayed, they may be flagged as validation errors and can can give a false count of the actual state of validation errors for the page.

Addresses issue #4747.

Demo

With this gist plugin activated:

  • Additional validation errors should be reported when fatal errors are displayed:

  • Only relevant errors are reported when the display of fatal errors are suppressed:

Checklist

  • [x] My pull request is addressing an open issue (please create one otherwise).
  • [ ] My code is tested and passes existing tests.
  • [x] My code follows the Engineering Guidelines (updates are often made to the guidelines, check it out periodically).
+6 -0

0 comment

1 changed file

pierlon

pr closed time in 5 days

push eventampproject/amp-wp

Weston Ruter

commit sha 5510c09563f422cc0691118f8cb2a64d7919eadf

Eliminate needless variable

view details

push time in 5 days

Pull request review commentampproject/amp-wp

Suppress display of fatal errors during validate request

 public static function init_validate_request() { 		if ( true === $should_validate_response ) { 			self::add_validation_error_sourcing(); 			self::$is_validate_request = true;++			$display_errors = (string) ini_get( 'display_errors' );++			if ( '1' === $display_errors ) {

Minor improvement:

			if ( '1' === (string) ini_get( 'display_errors' ) ) {
pierlon

comment created time in 5 days

push eventGoogleChromeLabs/wp-origination

Viktor Szépe

commit sha 4c87fed2d5ba8c192ffb1f4fb614e24e1cdb0af1

Missing return statement

view details

Weston Ruter

commit sha 61fd855e16350f5dd64e33c9efaf64a8527b7e8a

Merge pull request #2 from szepeviktor/patch-1 Missing return statement

view details

push time in 5 days

PR merged GoogleChromeLabs/wp-origination

Missing return statement

@westonruter Would you use static analysis to spot these errors?

+1 -1

1 comment

1 changed file

szepeviktor

pr closed time in 5 days

pull request commentGoogleChromeLabs/wp-origination

Missing return statement

Nice catch. Ugly typo! Yes, PHPStan would be good to add here, but I'm not going to be picking up Origination again until Q3 at the earliest.

szepeviktor

comment created time in 5 days

Pull request review commentgoogle/web-stories-wp

Try using AMP Optimizer if available

 protected function replace_body_end_tag( $content ) { 		return str_replace( '</body>', $output . '</body>', $content ); 	} +	/**+	 * Get the configuration object to use.+	 *+	 * @return Configuration Optimizer configuration to use.+	 */+	private function get_optimizer_configuration() {+		$transformers = [+			AmpSchemaOrgMetadata::class,+			AmpRuntimeCss::class,+			TransformedIdentifier::class,+			ReorderHead::class,+		];++		$configuration = [ Configuration::KEY_TRANSFORMERS => $transformers ];+		$config        = new Configuration( $configuration );+		$config->registerConfigurationClass(+			AmpSchemaOrgMetadata::class,+			AmpSchemaOrgMetadataConfiguration::class+		);++		return $config;+	}++	/**+	 * Optimizes the resulting markup using AMP Optimizer if available.+	 *+	 * @param string $content Story markup.+	 *+	 * @return string Filtered content.+	 */+	protected function optimize_markup( $content ) {+		if ( ! class_exists( '\AmpProject\Optimizer\TransformationEngine' ) ) {+			return $content;+		}++		$enable_optimizer = true;++		/** This filter is documented in amp-wp/includes/class-amp-theme-support.php */+		$enable_optimizer = apply_filters( 'amp_enable_optimizer', $enable_optimizer );++		if ( ! $enable_optimizer ) {+			return $content;+		}++		// Needed for \AmpProject\Optimizer\Transformer\AmpRuntimeCss.+		$content = str_replace( '</head>', '<style amp-runtime></style></head>', $content );

The TransformedIdentifier transformer is what is is responsible for flagging a document as being transformed which would then allow style[amp-runtime], which you are including. So that seems OK. But injecting the element in this way seems not ideal.

Flagging a document as being transformed is normally bound up with a document also going through SSR. Hence the AmpRuntimeCss, ServerSideRendering, and TransformedIdentifier form a pair: https://github.com/ampproject/amp-wp/blob/8d5f29f1f22e6623789dca250901cff0a559e590/includes/class-amp-theme-support.php#L2182-L2194

The ServerSideRendering transformer adds the style[amp-runtime] element. However, why is this not the responsibility of the AmpRuntimeCss transformer?

@schlessera Should this logic be moved from ServerSideRendering to AmpRuntimeCss?

// Emit the amp-runtime marker to indicate that we're applying server side rendering in the document.
$ampRuntimeMarker = $document->createElement(Tag::STYLE);
$ampRuntimeMarker->setAttribute(Attribute::AMP_RUNTIME, '');
$document->head->insertBefore($ampRuntimeMarker, $document->head->hasChildNodes() ? $document->head->firstChild : null);
swissspidy

comment created time in 5 days

Pull request review commentgoogle/web-stories-wp

Try using AMP Optimizer if available

 protected function replace_body_end_tag( $content ) { 		return str_replace( '</body>', $output . '</body>', $content ); 	} +	/**+	 * Get the configuration object to use.+	 *+	 * @return Configuration Optimizer configuration to use.+	 */+	private function get_optimizer_configuration() {+		$transformers = [+			AmpSchemaOrgMetadata::class,+			AmpRuntimeCss::class,+			TransformedIdentifier::class,+			ReorderHead::class,+		];++		$configuration = [ Configuration::KEY_TRANSFORMERS => $transformers ];+		$config        = new Configuration( $configuration );+		$config->registerConfigurationClass(+			AmpSchemaOrgMetadata::class,+			AmpSchemaOrgMetadataConfiguration::class+		);++		return $config;+	}++	/**+	 * Optimizes the resulting markup using AMP Optimizer if available.+	 *+	 * @param string $content Story markup.+	 *+	 * @return string Filtered content.+	 */+	protected function optimize_markup( $content ) {+		if ( ! class_exists( '\AmpProject\Optimizer\TransformationEngine' ) ) {+			return $content;+		}++		$enable_optimizer = true;++		/** This filter is documented in amp-wp/includes/class-amp-theme-support.php */+		$enable_optimizer = apply_filters( 'amp_enable_optimizer', $enable_optimizer );++		if ( ! $enable_optimizer ) {+			return $content;+		}++		// Needed for \AmpProject\Optimizer\Transformer\AmpRuntimeCss.+		$content = str_replace( '</head>', '<style amp-runtime></style></head>', $content );++		$errors         = new ErrorCollection();+		$optimizer      = new TransformationEngine( $this->get_optimizer_configuration() );+		$optimized_html = $optimizer->optimizeHtml( $content, $errors );++		if ( count( $errors ) === 0 ) {+			return $optimized_html;+		}++		$error_messages = array_map(+			static function( Error $error ) {+				return ' - ' . $error->getCode() . ': ' . $error->getMessage();+			},+			iterator_to_array( $errors )+		);++		return str_replace(+			'</html>',+			"\n" . '<!---' . "\n" . 'AMP optimization could not be completed due to the following:' . "\n" . implode( "\n", $error_messages ) . "\n" . '-->' . "\n" . '</html>',+			$content+		);

Wouldn't it be safer to append this to $content rather than try to inject it? In theory, there could be <!--</html>--> in the page.

swissspidy

comment created time in 5 days

issue commentampproject/amp-wp

Fatal errors are not exposed when they occur during validation requests

Given that #4747 has been opened, I'm moving this issue to QA Passed.

westonruter

comment created time in 5 days

Pull request review commentampproject/amp-wp

Suppress display of fatal errors during validate request

 public static function init_validate_request() { 		if ( true === $should_validate_response ) { 			self::add_validation_error_sourcing(); 			self::$is_validate_request = true;++			$display_errors = ini_get( 'display_errors' );++			if ( ! empty( $display_errors ) || 'stderr' !== $display_errors ) {

Should we just check for the value of '1'?

			$display_errors = (string) ini_get( 'display_errors' );

			if ( '1' === $display_errors ) {
pierlon

comment created time in 5 days

Pull request review commentampproject/amp-wp

Suppress display of fatal errors during validate request

 public static function init_validate_request() { 		if ( true === $should_validate_response ) { 			self::add_validation_error_sourcing(); 			self::$is_validate_request = true;++			$display_errors = ini_get( 'display_errors' );++			if ( ! empty( $display_errors ) || 'stderr' !== $display_errors ) {+				// Suppress the display of fatal errors that may arise during validation so that they will not be counted+				// as actual validation errors.+				ini_set( 'display_errors', false );

WordPress core is using 0 here:

				ini_set( 'display_errors', 0 );
pierlon

comment created time in 5 days

push eventampproject/amp-wp

Weston Ruter

commit sha 928fd900b7ea2519ffc59d32f4b80b40d876b9ce

Update allowed tags/attributes from spec in amphtml to 2005050322002

view details

Weston Ruter

commit sha c26c0ca5a79ff257917ba1032d28192e4cb91ecf

Merge branch 'develop' of github.com:ampproject/amp-wp into update/amphtml-2005050322002 * 'develop' of github.com:ampproject/amp-wp: (47 commits) Adapt docblocks Display PHPStan version in Travis Update dependency eslint-plugin-jest to v23.13.1 Adapt in libraries as well Always download latest version of PHPStan Update dependency autoprefixer to v9.8.0 Update dependency eslint-plugin-jest to v23.13.0 Fix phpdoc for sanitizer_classes and embed_handler_classes Remove array_column() polyfill since plugin requires PHP 5.6+ Update dependency @wordpress/scripts to v9.1.0 Remove redundant subtraction Raise level to 3 Adapt type-hint for AMP_Image_Dimension_Extractor::normalize_url() Use empty string for pruning textContent Adapt typehint for AMP_Content->sanitizer_classes Adapt typehint for AMP_Tag_And_Attribute_Sanitizer->allowed_tags Adapt typehints for methods returning a rule spec check result Adapt typehint for AMP_Style_Sanitizer->current_sources Adapt return typehint for AMP_YouTube_Embed_Handler::get_video_id_from_url() Adapt return value for failed AMP_Twitter_Embed_Handler::get_tweet_id() ...

view details

Weston Ruter

commit sha fad7c8f34deddb2bc22261417a11be1d2a74e958

Update tests to reflect removal of style attribute constraint

view details

Weston Ruter

commit sha d2801893b95d7530290247d8b763f048ffcbb1ae

Add tests for tweetid binding, Google Fonts v2, and enterkeyhint

view details

Weston Ruter

commit sha 8d5f29f1f22e6623789dca250901cff0a559e590

Merge pull request #4701 from ampproject/update/amphtml-2005050322002 Update allowed tags/attributes from spec in amphtml to 2005050322002

view details

push time in 5 days

delete branch ampproject/amp-wp

delete branch : update/amphtml-2005050322002

delete time in 5 days

PR merged ampproject/amp-wp

Reviewers
Update allowed tags/attributes from spec in amphtml to 2005050322002 cla: yes

Previously #4548.

  • [x] Run ./bin/amphtml-update.sh (lando ssh -c 'bash ./bin/amphtml-update.sh vendor/amphtml').
  • [x] Examine diff for changelog.
  • [x] Examine upstream diff to ensure nothing was missed.
  • [x] ~Update spec generator as needed based on spec format changes.~
  • [x] ~Modify validating sanitizer based on changes to spec, if needed.~
  • [x] Add tests for key changes.

Changelog

  • Allow amp-state and template in amp-story-grid-layer.
  • Add show-tooltip attribute to a elements (only relevant to stories apparently).
  • Add support for [data-tweetid] AMP-bind attribute on amp-twitter.
  • Require src on amp-ad-custom elements.
  • Add amp-story-page-attachment[href] spec.
  • Remove blacklisted_value_regex from style attributes. This is not because !important is now allowed, but because the validator is now doing CSS parsing. This means font_url_spec and image_url_spec are removed from the CSS validator spec; a new doc_css_bytes property is added which apparently refers to gathering the byte length of style attributes to add to style[amp-custom], which isn't relevant for the plugin since all inline styles are transformed to style rules.
  • Add enterkeyhint attribute to input (and other input-like AMP components).
  • Recognize Google Fonts v2 stylesheet URLs and relax FontAwesome stylesheet URL allowlist.

Details

(
    PREV_VERSION=2004142326360;
    THIS_VERSION=2005050322002; 
    git checkout $THIS_VERSION;
    git diff $PREV_VERSION...$THIS_VERSION -w -- $( git ls-files | grep '.protoascii' );
    git checkout - > /dev/null
)

<details> <summary>2004142326360...2005050322002</summary>

diff --git a/extensions/amp-ad-custom/validator-amp-ad-custom.protoascii b/extensions/amp-ad-custom/validator-amp-ad-custom.protoascii
index a6c8580be..c0afd0715 100644
--- a/extensions/amp-ad-custom/validator-amp-ad-custom.protoascii
+++ b/extensions/amp-ad-custom/validator-amp-ad-custom.protoascii
@@ -33,6 +33,7 @@ tags: {  # <amp-ad-custom>
   disallowed_ancestor: "AMP-APP-BANNER"
   attrs: {
     name: "src"
+    mandatory: true
     value_url: {
       protocol: "https"
       allow_relative: false
diff --git a/extensions/amp-list/validator-amp-list.protoascii b/extensions/amp-list/validator-amp-list.protoascii
index 9f490851a..49714710a 100644
--- a/extensions/amp-list/validator-amp-list.protoascii
+++ b/extensions/amp-list/validator-amp-list.protoascii
@@ -117,7 +117,6 @@ tags: {  # <amp-list> with mandatory src and/or [src] attr
   }
   attr_lists: "extended-amp-global"
   amp_layout: {
-    supported_layouts: CONTAINER
     supported_layouts: FILL
     supported_layouts: FIXED
     supported_layouts: FIXED_HEIGHT
@@ -214,6 +213,7 @@ tags: {  # <amp-list>
     value: "always"
     value: "no"
     value: "refresh"
+    value: "refresh-evaluate" # Experimental.
   }
   attrs: {
     name: "diffable"
@@ -242,7 +242,7 @@ tags: {  # <amp-list>
   }
   attr_lists: "extended-amp-global"
   amp_layout: {
-    supported_layouts: CONTAINER
+    supported_layouts: CONTAINER # Experimental, email-only.
     supported_layouts: FILL
     supported_layouts: FIXED
     supported_layouts: FIXED_HEIGHT
@@ -308,7 +308,6 @@ tags: {  # <amp-list>
   }
   attr_lists: "extended-amp-global"
   amp_layout: {
-    supported_layouts: CONTAINER
     supported_layouts: FILL
     supported_layouts: FIXED
     supported_layouts: FIXED_HEIGHT
diff --git a/extensions/amp-story/validator-amp-story.protoascii b/extensions/amp-story/validator-amp-story.protoascii
index 9b1c87daa..28f50ae94 100644
--- a/extensions/amp-story/validator-amp-story.protoascii
+++ b/extensions/amp-story/validator-amp-story.protoascii
@@ -621,7 +621,7 @@ descendant_tag_list: {
 }
 descendant_tag_list: {
   name: "amp-story-grid-layer-allowed-descendants"
-  tag: "A"
+  tag: "A" # `show-tooltip` attribute is whitelisted in validator-main.protoascii.
   tag: "ABBR"
   tag: "ADDRESS"
   tag: "AMP-ANALYTICS"
@@ -637,6 +637,7 @@ descendant_tag_list: {
   tag: "AMP-LIST"
   tag: "AMP-LIVE-LIST"
   tag: "AMP-PIXEL"
+  tag: "AMP-STATE"
   tag: "AMP-TIMEAGO"
   tag: "AMP-TWITTER"
   tag: "AMP-VIDEO"
@@ -736,6 +737,7 @@ descendant_tag_list: {
   tag: "TABLE"
   tag: "TBODY"
   tag: "TD"
+  tag: "TEMPLATE"
   tag: "TEXT"
   tag: "TEXTPATH"
   tag: "TFOOT"
@@ -755,9 +757,38 @@ descendant_tag_list: {
   tag: "VKERN"
   tag: "WBR"
 }
-tags: {  # <amp-story-page-attachment>
+tags: {  # <amp-story-page-attachment> with href
+  html_format: AMP
+  tag_name: "AMP-STORY-PAGE-ATTACHMENT"
+  spec_name: "amp-story-page-attachment[href]"
+  mandatory_ancestor: "AMP-STORY-PAGE"
+  mandatory_last_child: true
+  attrs: {
+    name: "layout"
+    mandatory: true
+    value: "nodisplay"
+  }
+  attrs: {
+    name: "theme"
+    value: "dark"
+    value: "light"
+  }
+  attrs: {
+    name: "href"
+    mandatory: true
+    value_url: {
+      protocol: "http"
+      protocol: "https"
+    }
+  }
+  child_tags: {
+    mandatory_num_child_tags: 0
+  }
+}
+tags: {  # <amp-story-page-attachment> with no href
   html_format: AMP
   tag_name: "AMP-STORY-PAGE-ATTACHMENT"
+  spec_name: "amp-story-page-attachment"
   mandatory_ancestor: "AMP-STORY-PAGE"
   descendant_tag_list: "amp-story-page-attachment-allowed-descendants"
   mandatory_last_child: true
diff --git a/extensions/amp-twitter/validator-amp-twitter.protoascii b/extensions/amp-twitter/validator-amp-twitter.protoascii
index 8922ff7ae..6a2c3fc48 100644
--- a/extensions/amp-twitter/validator-amp-twitter.protoascii
+++ b/extensions/amp-twitter/validator-amp-twitter.protoascii
@@ -104,6 +104,10 @@ tags: {  # <amp-twitter>
     name: "data-tweetid"
     mandatory_oneof: "['data-momentid', 'data-timeline-source-type', 'data-tweetid']"
   }
+  # <amp-bind>
+  attrs: {
+    name: "[data-tweetid]"
+  }
   attr_lists: "extended-amp-global"
   amp_layout: {
     supported_layouts: FILL
diff --git a/validator/validator-css.protoascii b/validator/validator-css.protoascii
index e4777e882..8de8067ba 100644
--- a/validator/validator-css.protoascii
+++ b/validator/validator-css.protoascii
@@ -14,16 +14,455 @@
 # limitations under the license.
 #
 
-# Defines how much css is allowed in the entire document. This is the sum of
-# `<style amp-custom>` and `style=` attribute inline CSS. If a CssLengthSpec
-# is not defined for a particular html_format, then no limit is enforced for
-# inline styles but a limit may be enforced on particular style tags. Those
-# would be defined in that tag's CdataSpec.
-css_length_spec: {
+declaration_list {
+  name: "BASIC_DECLARATIONS"
+  declaration: { name: "align-content" }
+  declaration: { name: "align-items" }
+  declaration: { name: "align-self" }
+  declaration: { name: "all" }
+  declaration: { name: "animation" }
+  declaration: { name: "animation-delay" }
+  declaration: { name: "animation-direction" }
+  declaration: { name: "animation-duration" }
+  declaration: { name: "animation-fill-mode" }
+  declaration: { name: "animation-iteration-count" }
+  declaration: { name: "animation-name" }
+  declaration: { name: "animation-play-state" }
+  declaration: { name: "animation-timing-function" }
+  declaration: { name: "backface-visibility" }
+  declaration: { name: "background" }
+  declaration: { name: "background-attachment" }
+  declaration: { name: "background-blend-mode" }
+  declaration: { name: "background-clip" }
+  declaration: { name: "background-color" }
+  declaration: { name: "background-image" }
+  declaration: { name: "background-origin" }
+  declaration: { name: "background-position" }
+  declaration: { name: "background-repeat" }
+  declaration: { name: "background-size" }
+  declaration: { name: "border" }
+  declaration: { name: "border-bottom" }
+  declaration: { name: "border-bottom-color" }
+  declaration: { name: "border-bottom-left-radius" }
+  declaration: { name: "border-bottom-right-radius" }
+  declaration: { name: "border-bottom-style" }
+  declaration: { name: "border-bottom-width" }
+  declaration: { name: "border-collapse" }
+  declaration: { name: "border-color" }
+  declaration: { name: "border-image" }
+  declaration: { name: "border-image-outset" }
+  declaration: { name: "border-image-repeat" }
+  declaration: { name: "border-image-slice" }
+  declaration: { name: "border-image-source" }
+  declaration: { name: "border-image-width" }
+  declaration: { name: "border-left" }
+  declaration: { name: "border-left-color" }
+  declaration: { name: "border-left-style" }
+  declaration: { name: "border-left-width" }
+  declaration: { name: "border-radius" }
+  declaration: { name: "border-right" }
+  declaration: { name: "border-right-color" }
+  declaration: { name: "border-right-style" }
+  declaration: { name: "border-right-width" }
+  declaration: { name: "border-spacing" }
+  declaration: { name: "border-style" }
+  declaration: { name: "border-top" }
+  declaration: { name: "border-top-color" }
+  declaration: { name: "border-top-left-radius" }
+  declaration: { name: "border-top-right-radius" }
+  declaration: { name: "border-top-style" }
+  declaration: { name: "border-top-width" }
+  declaration: { name: "border-width" }
+  declaration: { name: "bottom" }
+  declaration: { name: "box-decoration-break" }
+  declaration: { name: "box-shadow" }
+  declaration: { name: "box-sizing" }
+  declaration: { name: "break-after" }
+  declaration: { name: "break-before" }
+  declaration: { name: "break-inside" }
+  declaration: { name: "caption-side" }
+  declaration: { name: "caret-color" }
+  declaration: { name: "clear" }
+  declaration: { name: "clip" }
+  declaration: { name: "color" }
+  declaration: { name: "column-count" }
+  declaration: { name: "column-fill" }
+  declaration: { name: "column-gap" }
+  declaration: { name: "column-rule" }
+  declaration: { name: "column-rule-color" }
+  declaration: { name: "column-rule-style" }
+  declaration: { name: "column-rule-width" }
+  declaration: { name: "column-span" }
+  declaration: { name: "column-width" }
+  declaration: { name: "columns" }
+  declaration: { name: "content" }
+  declaration: { name: "counter-increment" }
+  declaration: { name: "counter-reset" }
+  declaration: { name: "cursor" }
+  declaration: { name: "direction" }
+  declaration: { name: "display" }
+  declaration: { name: "empty-cells" }
+  declaration: { name: "filter" }
+  declaration: { name: "flex" }
+  declaration: { name: "flex-basis" }
+  declaration: { name: "flex-direction" }
+  declaration: { name: "flex-flow" }
+  declaration: { name: "flex-grow" }
+  declaration: { name: "flex-shrink" }
+  declaration: { name: "flex-wrap" }
+  declaration: { name: "float" }
+  declaration: { name: "font" }
+  declaration: { name: "font-family" }
+  declaration: { name: "font-feature-settings" }
+  declaration: { name: "font-kerning" }
+  declaration: { name: "font-language-override" }
+  declaration: { name: "font-size" }
+  declaration: { name: "font-size-adjust" }
+  declaration: { name: "font-stretch" }
+  declaration: { name: "font-style" }
+  declaration: { name: "font-synthesis" }
+  declaration: { name: "font-variant" }
+  declaration: { name: "font-variant-alternates" }
+  declaration: { name: "font-variant-caps" }
+  declaration: { name: "font-variant-east-asian" }
+  declaration: { name: "font-variant-ligatures" }
+  declaration: { name: "font-variant-numeric" }
+  declaration: { name: "font-variant-position" }
+  declaration: { name: "font-weight" }
+  declaration: { name: "grid" }
+  declaration: { name: "grid-area" }
+  declaration: { name: "grid-auto-columns" }
+  declaration: { name: "grid-auto-flow" }
+  declaration: { name: "grid-auto-rows" }
+  declaration: { name: "grid-column" }
+  declaration: { name: "grid-column-end" }
+  declaration: { name: "grid-column-gap" }
+  declaration: { name: "grid-column-start" }
+  declaration: { name: "grid-gap" }
+  declaration: { name: "grid-row" }
+  declaration: { name: "grid-row-end" }
+  declaration: { name: "grid-row-gap" }
+  declaration: { name: "grid-row-start" }
+  declaration: { name: "grid-template" }
+  declaration: { name: "grid-template-areas" }
+  declaration: { name: "grid-template-columns" }
+  declaration: { name: "grid-template-rows" }
+  declaration: { name: "hanging-punctuation" }
+  declaration: { name: "height" }
+  declaration: { name: "hyphens" }
+  declaration: { name: "image-rendering" }
+  declaration: { name: "isolation" }
+  declaration: { name: "justify-content" }
+  declaration: { name: "left" }
+  declaration: { name: "letter-spacing" }
+  declaration: { name: "line-break" }
+  declaration: { name: "line-height" }
+  declaration: { name: "list-style" }
+  declaration: { name: "list-style-image" }
+  declaration: { name: "list-style-position" }
+  declaration: { name: "list-style-type" }
+  declaration: { name: "margin" }
+  declaration: { name: "margin-bottom" }
+  declaration: { name: "margin-left" }
+  declaration: { name: "margin-right" }
+  declaration: { name: "margin-top" }
+  declaration: { name: "max-height" }
+  declaration: { name: "max-width" }
+  declaration: { name: "min-height" }
+  declaration: { name: "min-width" }
+  declaration: { name: "mix-blend-mode" }
+  declaration: { name: "object-fit" }
+  declaration: { name: "object-position" }
+  declaration: { name: "opacity" }
+  declaration: { name: "order" }
+  declaration: { name: "orphans" }
+  declaration: { name: "outline" }
+  declaration: { name: "outline-color" }
+  declaration: { name: "outline-offset" }
+  declaration: { name: "outline-style" }
+  declaration: { name: "outline-width" }
+  declaration: { name: "overflow" }
+  declaration: { name: "overflow-wrap" }
+  declaration: { name: "overflow-x" }
+  declaration: { name: "overflow-y" }
+  declaration: { name: "padding" }
+  declaration: { name: "padding-bottom" }
+  declaration: { name: "padding-left" }
+  declaration: { name: "padding-right" }
+  declaration: { name: "padding-top" }
+  declaration: { name: "page-break-after" }
+  declaration: { name: "page-break-before" }
+  declaration: { name: "page-break-inside" }
+  declaration: { name: "perspective" }
+  declaration: { name: "perspective-origin" }
+  declaration: { name: "pointer-events" }
+  # CSS property `position` with values `fixed` and `sticky are not allowed.
+  # From https://www.w3schools.com/cssref/pr_class_position.asp
+  declaration: {
+    name: "position"
+    value_casei: "absolute"
+    value_casei: "inherit"
+    value_casei: "initial"
+    value_casei: "relative"
+    value_casei: "static"
+  }
+  declaration: { name: "quotes" }
+  declaration: { name: "resize" }
+  declaration: { name: "right" }
+  declaration: { name: "tab-size" }
+  declaration: { name: "table-layout" }
+  declaration: { name: "text-align" }
+  declaration: { name: "text-align-last" }
+  declaration: { name: "text-combine-upright" }
+  declaration: { name: "text-decoration" }
+  declaration: { name: "text-decoration-color" }
+  declaration: { name: "text-decoration-line" }
+  declaration: { name: "text-decoration-style" }
+  declaration: { name: "text-fill-color" }
+  declaration: { name: "text-indent" }
+  declaration: { name: "text-justify" }
+  declaration: { name: "text-orientation" }
+  declaration: { name: "text-overflow" }
+  declaration: { name: "text-shadow" }
+  declaration: { name: "text-stroke" }
+  declaration: { name: "text-stroke-color" }
+  declaration: { name: "text-stroke-width" }
+  declaration: { name: "text-transform" }
+  declaration: { name: "text-underline-position" }
+  declaration: { name: "top" }
+  declaration: { name: "transform" }
+  declaration: { name: "transform-origin" }
+  declaration: { name: "transform-style" }
+  declaration: { name: "transition" }
+  declaration: { name: "transition-delay" }
+  declaration: { name: "transition-duration" }
+  declaration: { name: "transition-property" }
+  declaration: { name: "transition-timing-function" }
+  declaration: { name: "unicode-bidi" }
+  declaration: { name: "user-select" }
+  declaration: { name: "vertical-align" }
+  declaration: { name: "visibility" }
+  declaration: { name: "white-space" }
+  declaration: { name: "widows" }
+  declaration: { name: "width" }
+  declaration: { name: "word-break" }
+  declaration: { name: "word-spacing" }
+  declaration: { name: "word-wrap" }
+  declaration: { name: "writing-mode" }
+  declaration: { name: "z-index" }
+}
+
+declaration_list {
+  name: "SVG_BASIC_DECLARATIONS"
+  # SVG specific properties:
+  # https://www.w3.org/TR/SVG11/styling.html#SVGStylingProperties
+  declaration: { name: "alignment-baseline" }
+  declaration: { name: "baseline-shift" }
+  declaration: { name: "clip-path" }
+  declaration: { name: "clip-rule" }
+  declaration: { name: "color-interpolation" }
+  declaration: { name: "color-interpolation-filters" }
+  declaration: { name: "color-profile" } # SVG
+  declaration: { name: "color-rendering" } # SVG
+  declaration: { name: "dominant-baseline" }
+  declaration: { name: "enable-background" }
+  declaration: { name: "fill" }
+  declaration: { name: "fill-opacity" }
+  declaration: { name: "fill-rule" }
+  declaration: { name: "flood-color" }
+  declaration: { name: "flood-opacity" }
+  declaration: { name: "glyph-orientation-horizontal" }
+  declaration: { name: "glyph-orientation-vertical" }
+  declaration: { name: "kerning" }
+  declaration: { name: "lighting-color" }
+  declaration: { name: "marker" }
+  declaration: { name: "marker-end" }
+  declaration: { name: "marker-mid" }
+  declaration: { name: "marker-start" }
+  declaration: { name: "mask" }
+  declaration: { name: "shape-rendering" }
+  declaration: { name: "stop-color" }
+  declaration: { name: "stop-opacity" }
+  declaration: { name: "stroke" }
+  declaration: { name: "stroke-dasharray" }
+  declaration: { name: "stroke-dashoffset" }
+  declaration: { name: "stroke-linecap" }
+  declaration: { name: "stroke-linejoin" }
+  declaration: { name: "stroke-miterlimit" }
+  declaration: { name: "stroke-opacity" }
+  declaration: { name: "stroke-width" }
+  declaration: { name: "text-anchor" }
+  declaration: { name: "text-rendering" }
+}
+
+declaration_list {
+  name: "AMP_ONLY_DECLARATIONS"
+  declaration: { name: "clip-path" }
+}
+
+# DocCSS rules for AMP for non-transformed documents.
+css {
   html_format: AMP
+  disabled_by: "transformed"
+  spec_url:
+  "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#stylesheets"
+
   max_bytes: 75000
   max_bytes_per_inline_style: 1000
-  spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
+  max_bytes_spec_url:
+  "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
+  url_bytes_included: true
+
+  # This is a legacy requirement. Style tags support all declarations while
+  # style attributes limit the allowed declarations. Support for style
+  # attributes was added at a later date than style tags, and more validation
+  # requirements were able to be enforced at that time.
+  allow_all_declaration_in_style_tag: true
+  allow_important: false
+  image_url_spec: {
+    protocol: "https"
+    protocol: "http"
+    protocol: "data"
+    allow_empty: true
+  }
+  font_url_spec: {
+    protocol: "https"
+    protocol: "http"
+    protocol: "data"
+    allow_empty: true
+  }
+  expand_vendor_prefixes: true
+  declaration_list: "BASIC_DECLARATIONS"
+  declaration_list_svg: "SVG_BASIC_DECLARATIONS"
+  declaration_list: "AMP_ONLY_DECLARATIONS"
+}
+
+# DocCss Rules for AMP transformed documents.
+# Identical to the non-transformed specification except that
+# `url_bytes_included` is false.
+css {
+  html_format: AMP
+  enabled_by: "transformed"
+  spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#stylesheets"
+
+  max_bytes: 75000
+  max_bytes_per_inline_style: 1000
+  max_bytes_spec_url:
+  "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
+  url_bytes_included: false
+
+  allow_all_declaration_in_style_tag: true
+  allow_important: false
+  image_url_spec: {
+    protocol: "https"
+    protocol: "http"
+    protocol: "data"
+    allow_empty: true
+  }
+  font_url_spec: {
+    protocol: "https"
+    protocol: "http"
+    protocol: "data"
+    allow_empty: true
+  }
+  expand_vendor_prefixes: true
+  declaration_list: "BASIC_DECLARATIONS"
+  declaration_list_svg: "SVG_BASIC_DECLARATIONS"
+  declaration_list: "AMP_ONLY_DECLARATIONS"
+}
+
+# DocCss rules for ACTIONS
+css {
+  html_format: ACTIONS
+  spec_url:
+  "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#stylesheets"
+
+  # Due to a bug, we allowed unlimited doc-level bytes for CSS in ACTIONS
+  # formats. To avoid breaking existing docs, this is temporarily allowed until
+  # we determine the next course of action.
+  # max_bytes: 75000
+  # max_bytes_per_inline_style: 1000
+  max_bytes_spec_url:
+  "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
+  url_bytes_included: true
+
+  allow_all_declaration_in_style_tag: true
+  allow_important: false
+  image_url_spec: {
+    protocol: "https"
+    protocol: "http"
+    protocol: "data"
+    allow_empty: true
+  }
+  font_url_spec: {
+    protocol: "https"
+    protocol: "http"
+    protocol: "data"
+    allow_empty: true
+  }
+  expand_vendor_prefixes: true
+  declaration_list: "BASIC_DECLARATIONS"
+  declaration_list_svg: "SVG_BASIC_DECLARATIONS"
+}
+
+# DocCss rules for AMP4ADS.
+css {
+  html_format: AMP4ADS
+  spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/a4a_spec#css"
+
+  # AMP4ADS currently allows unlimited inline style, and only limits style in
+  # the <style amp-custom> tag in the document head.
+  # max_bytes: 20000
+  # max_bytes_per_inline_style: 1000
+  max_bytes_spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/a4a_spec#css"
+
+  allow_all_declaration_in_style_tag: true
+  allow_important: false
+  image_url_spec: {
+    protocol: "https"
+    protocol: "http"
+    protocol: "data"
+    allow_empty: true
+  }
+  font_url_spec: {
+    protocol: "https"
+    protocol: "http"
+    protocol: "data"
+    allow_empty: true
+  }
+  expand_vendor_prefixes: true
+  declaration_list: "BASIC_DECLARATIONS"
+  declaration_list_svg: "SVG_BASIC_DECLARATIONS"
+}
+
+# DocCss rules for AMP4EMAIL
+css {
+  html_format: AMP4EMAIL
+  spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#stylesheets"
+
+  # Due to a bug, we allowed unlimited doc-level bytes for CSS in AMP4EMAIL
+  # formats. To avoid breaking emails, this is temporarily set to a warning but
+  # we intend to change to an error at a later time.
+  max_bytes_is_warning: true
+  max_bytes: 75000
+  max_bytes_per_inline_style: 1000
+
+  max_bytes_spec_url:
+  "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
+  url_bytes_included: true
+
+  # This is a legacy requirement. Style tags support all declarations while
+  # style attributes limit the allowed declarations. Support for style
+  # attributes was added at a later date than style tags, and more validation
+  # requirements were able to be enforced at that time.
+  allow_all_declaration_in_style_tag: true
+  allow_important: false
+  image_url_spec: {
+    protocol: "https"
+  }
+  expand_vendor_prefixes: true
+  declaration_list: "BASIC_DECLARATIONS"
+  declaration_list_svg: "SVG_BASIC_DECLARATIONS"
 }
 
 # This variant of <style amp-custom> is designed to help developers see how
@@ -62,7 +501,6 @@ tags: {  # <style amp-custom-length-check>, ALL FORMATS
 tags: {  # <style amp-custom>, [AMP, ACTIONS]
   html_format: AMP
   html_format: ACTIONS
-  disabled_by: "transformed"
   tag_name: "STYLE"
   spec_name: "style amp-custom"
   named_id: STYLE_AMP_CUSTOM
@@ -84,127 +522,17 @@ tags: {  # <style amp-custom>, [AMP, ACTIONS]
     value_casei: "text/css"
   }
   cdata: {
+    doc_css_bytes: true
     max_bytes: 75000
-    max_bytes_spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
-    css_spec: {
-      at_rule_spec: {
-        name: 'font-face'
-        type: PARSE_AS_DECLARATIONS
-      }
-      at_rule_spec: {
-        name: 'keyframes'
-        type: PARSE_AS_RULES
-      }
-      at_rule_spec: {
-        name: 'media'
-        type: PARSE_AS_RULES
-      }
-      at_rule_spec: {
-        name: 'page'
-        type: PARSE_AS_DECLARATIONS
-      }
-      at_rule_spec: {
-        name: 'supports'
-        type: PARSE_AS_RULES
-      }
-      at_rule_spec: {
-        name: '$DEFAULT'  # matches if none of the above match
-        type: PARSE_AS_ERROR
-      }
-      image_url_spec: {
-        protocol: "https"
-        protocol: "http"
-        protocol: "data"
-        protocol: "absolute"  # Temporary / will go away.
-        allow_empty: true
-      }
-      font_url_spec: {
-        protocol: "https"
-        protocol: "http"
-        protocol: "data"
-        allow_empty: true
-      }
-    }
-    blacklisted_cdata_regex: {
-      regex: "<!--"
-      error_message: "html comments"
-    }
-    # These regex blacklists are temporary hacks to validate essential CSS
-    # rules. They will be replaced later with more principled solutions.
-    blacklisted_cdata_regex: {
-      regex: "(^|\\W)i-amphtml-"
-      error_message: "CSS i-amphtml- name prefix"
-    }
-  }
-  spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#stylesheets"
-}
+    max_bytes_spec_url:
+    "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
 
-tags: {  # `<style amp-custom>`, transformed [AMP, ACTIONS]
-  html_format: AMP
-  html_format: ACTIONS
-  enabled_by: "transformed"
-  tag_name: "STYLE"
-  spec_name: "style amp-custom (transformed)"
-  unique: true
-  mandatory_parent: "HEAD"
-  attrs: {
-    name: "amp-custom"
-    mandatory: true
-    value: ""
-    # This is a fine dispatch key, but we would prefer that this tagspec
-    # is used for errors related to <style> tags missing the amp-custom
-    # attribute rather than the boilerplate tagspec which doesn't have an
-    # attribute and thus can't have a dispatch_key.
-    # dispatch_key: NAME_DISPATCH
-  }
-  attrs: {  # Allow the default.
-    name: "type"
-    value_casei: "text/css"
-  }
-  cdata: {
-    max_bytes: 75000
-    # For transformed AMP non-data URLs are not counted against the total byte
-    # limit for <style amp-custom>.
-    url_bytes_included: false
-    max_bytes_spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
     css_spec: {
-      at_rule_spec: {
-        name: 'font-face'
-        type: PARSE_AS_DECLARATIONS
-      }
-      at_rule_spec: {
-        name: 'keyframes'
-        type: PARSE_AS_RULES
-      }
-      at_rule_spec: {
-        name: 'media'
-        type: PARSE_AS_RULES
-      }
-      at_rule_spec: {
-        name: 'page'
-        type: PARSE_AS_DECLARATIONS
-      }
-      at_rule_spec: {
-        name: 'supports'
-        type: PARSE_AS_RULES
-      }
-      at_rule_spec: {
-        name: '$DEFAULT'  # matches if none of the above match
-        type: PARSE_AS_ERROR
-      }
-      image_url_spec: {
-        protocol: "https"
-        protocol: "http"
-        protocol: "data"
-        protocol: "absolute"  # Temporary / will go away.
-        allow_empty: true
-      }
-      font_url_spec: {
-        protocol: "https"
-        protocol: "http"
-        protocol: "data"
-        allow_empty: true
-      }
+      at_rule_spec: { name: 'font-face' }
+      at_rule_spec: { name: 'keyframes' }
+      at_rule_spec: { name: 'media' }
+      at_rule_spec: { name: 'page' }
+      at_rule_spec: { name: 'supports' }
     }
     blacklisted_cdata_regex: {
       regex: "<!--"
@@ -242,20 +570,15 @@ tags: {  # <style amp-custom>, AMP4ADS
     value_casei: "text/css"
   }
   cdata: {
+    doc_css_bytes: true
     max_bytes: 20000  # Smaller than AMP, which is 75,000.
-    max_bytes_spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/a4a_spec#css"
+    max_bytes_spec_url:
+    "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
     css_spec: {
-      at_rule_spec: {
-        name: 'font-face'
-        type: PARSE_AS_DECLARATIONS
-      }
-      at_rule_spec: {
-        name: 'keyframes'
-        type: PARSE_AS_RULES
-      }
+      at_rule_spec: { name: 'font-face' }
+      at_rule_spec: { name: 'keyframes' }
       at_rule_spec: {
         name: 'media'
-        type: PARSE_AS_RULES
         media_query_spec: {
           issues_as_error: false
           type: 'all'
@@ -269,27 +592,7 @@ tags: {  # <style amp-custom>, AMP4ADS
           feature: 'resolution'
         }
       }
-      at_rule_spec: {
-        name: 'supports'
-        type: PARSE_AS_RULES
-      }
-      at_rule_spec: {
-        name: '$DEFAULT'  # matches if none of the above match
-        type: PARSE_AS_ERROR
-      }
-      image_url_spec: {
-        protocol: "https"
-        protocol: "http"
-        protocol: "data"
-        protocol: "absolute"  # Temporary / will go away.
-        allow_empty: true
-      }
-      font_url_spec: {
-        protocol: "https"
-        protocol: "http"
-        protocol: "data"
-        allow_empty: true
-      }
+      at_rule_spec: { name: 'supports' }
       validate_amp4ads: true
     }
     blacklisted_cdata_regex: {
@@ -328,12 +631,14 @@ tags: {  # <style amp-custom>, AMP4EMAIL
   }
   # TODO(b/68756045): Whitelist CSS properties allowed in Dynamic Mail.
   cdata: {
+    doc_css_bytes: true
     max_bytes: 75000
-    max_bytes_spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
+    max_bytes_spec_url:
+    "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size"
+
     css_spec: {
       at_rule_spec: {
         name: 'media'
-        type: PARSE_AS_RULES
         media_query_spec: {
           issues_as_error: true
           type: 'all'
@@ -345,17 +650,7 @@ tags: {  # <style amp-custom>, AMP4EMAIL
           feature: 'width'
         }
       }
-      at_rule_spec: {
-        name: 'page'
-        type: PARSE_AS_DECLARATIONS
-      }
-      at_rule_spec: {
-        name: '$DEFAULT'  # matches if none of the above match
-        type: PARSE_AS_ERROR
-      }
-      image_url_spec: {
-        protocol: "https"
-      }
+      at_rule_spec: { name: 'page' }
     }
     blacklisted_cdata_regex: {
       regex: "<!--"
@@ -388,6 +683,7 @@ tags: {  # `<style amp-boilerplate>`, [AMP, ACTIONS]
     dispatch_key: NAME_VALUE_PARENT_DISPATCH
   }
   cdata: {
+    doc_css_bytes: false
     # This regex allows arbitrary whitespace whenever some whitespace
     # is required in the boilerplate. It was made by replacing ' ' with \\s+.
     cdata_regex: "\\s*body\\s*{\\s*-webkit-animation:\\s*-amp-start\\s+8s\\s+steps\\(1,\\s*end\\)\\s+0s\\s+1\\s+normal\\s+both;\\s*-moz-animation:\\s*-amp-start\\s+8s\\s+steps\\s*\\(1\\s*,\\s*end\\s*\\)\\s+0s\\s+1\\s+normal\\s+both;\\s*-ms-animation:\\s*-amp-start\\s+8s\\s+steps\\s*\\(1\\s*,\\s*end\\s*\\)\\s+0s\\s+1\\s+normal\\s+both;\\s*animation:\\s*-amp-start\\s+8s\\s+steps\\(1,\\s*end\\)\\s+0s\\s+1\\s+normal\\s+both;?\\s*}\\s*@-webkit-keyframes\\s+-amp-start\\s*{\\s*from\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*to\\s*{\\s*visibility:\\s*visible;?\\s*}\\s*}\\s*@-moz-keyframes\\s+-amp-start\\s*{\\s*from\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*to\\s*{\\s*visibility:\\s*visible;?\\s*}\\s*}\\s*@-ms-keyframes\\s+-amp-start\\s*{\\s*from\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*to\\s*{\\s*visibility:\\s*visible;?\\s*}\\s*}\\s*@-o-keyframes\\s+-amp-start\\s*{\\s*from\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*to\\s*{\\s*visibility:\\s*visible;?\\s*}\\s*}\\s*@keyframes\\s+-amp-start\\s*{\\s*from\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*to\\s*{\\s*visibility:\\s*visible;?\\s*}\\s*}\\s*"
@@ -410,6 +706,7 @@ tags: {  # `<style amp-boilerplate>`, transformed [AMP, ACTIONS]
     dispatch_key: NAME_VALUE_PARENT_DISPATCH
   }
   cdata: {
+    doc_css_bytes: false
     # This regex allows arbitrary whitespace whenever some whitespace
     # is required in the boilerplate. It was made by replacing ' ' with \\s+.
     cdata_regex: "\\s*body\\s*{\\s*-webkit-animation:\\s*-amp-start\\s+8s\\s+steps\\(1,\\s*end\\)\\s+0s\\s+1\\s+normal\\s+both;\\s*-moz-animation:\\s*-amp-start\\s+8s\\s+steps\\s*\\(1\\s*,\\s*end\\s*\\)\\s+0s\\s+1\\s+normal\\s+both;\\s*-ms-animation:\\s*-amp-start\\s+8s\\s+steps\\s*\\(1\\s*,\\s*end\\s*\\)\\s+0s\\s+1\\s+normal\\s+both;\\s*animation:\\s*-amp-start\\s+8s\\s+steps\\(1,\\s*end\\)\\s+0s\\s+1\\s+normal\\s+both;?\\s*}\\s*@-webkit-keyframes\\s+-amp-start\\s*{\\s*from\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*to\\s*{\\s*visibility:\\s*visible;?\\s*}\\s*}\\s*@-moz-keyframes\\s+-amp-start\\s*{\\s*from\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*to\\s*{\\s*visibility:\\s*visible;?\\s*}\\s*}\\s*@-ms-keyframes\\s+-amp-start\\s*{\\s*from\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*to\\s*{\\s*visibility:\\s*visible;?\\s*}\\s*}\\s*@-o-keyframes\\s+-amp-start\\s*{\\s*from\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*to\\s*{\\s*visibility:\\s*visible;?\\s*}\\s*}\\s*@keyframes\\s+-amp-start\\s*{\\s*from\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*to\\s*{\\s*visibility:\\s*visible;?\\s*}\\s*}\\s*"
@@ -432,6 +729,7 @@ tags: {  # `<style amp4ads-boilerplate>`, AMP4ADS
     dispatch_key: NAME_VALUE_PARENT_DISPATCH
   }
   cdata: {
+    doc_css_bytes: false
     # This regex allows arbitrary whitespace around the body statement, but
     # not inside it. This is a compromise to keep things simple, but allow
     # pretty printing tools some latitude.
@@ -454,6 +752,7 @@ tags: {  # `<style amp4email-boilerplate>`, AMP4EMAIL
     dispatch_key: NAME_VALUE_PARENT_DISPATCH
   }
   cdata: {
+    doc_css_bytes: false
     # This regex allows arbitrary whitespace whenever some whitespace
     # is required in the boilerplate. It was made by replacing ' ' with \\s+.
     cdata_regex: "\\s*body\\s*{\\s*visibility:\\s*hidden;?\\s*}\\s*"
@@ -479,6 +778,7 @@ tags: {  # `<style amp-boilerplate>`, [AMP, ACTIONS]
     dispatch_key: NAME_VALUE_PARENT_DISPATCH
   }
   cdata {
+    doc_css_bytes: false
     cdata_regex: "\\s*body\\s*{\\s*-webkit-animation:\\s*none;\\s*-moz-animation:\\s*none;\\s*-ms-animation:\\s*none;\\s*animation:\\s*none;?\\s*}\\s*"
   }
   spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amp-boilerplate?format=websites"
@@ -500,6 +800,7 @@ tags: {  # `<style amp-boilerplate>`, transformed [AMP, ACTIONS]
     dispatch_key: NAME_VALUE_PARENT_DISPATCH
   }
   cdata {
+    doc_css_bytes: false
     cdata_regex: "\\s*body\\s*{\\s*-webkit-animation:\\s*none;\\s*-moz-animation:\\s*none;\\s*-ms-animation:\\s*none;\\s*animation:\\s*none;?\\s*}\\s*"
   }
   spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amp-boilerplate?format=websites"
@@ -521,25 +822,16 @@ tags: {  # `<style amp-keyframes>`, [AMP, AMP4ADS, ACTIONS]
      dispatch_key: NAME_DISPATCH
   }
   cdata: {
+    # amp-keyframes CSS bytes do not count against the document CSS byte limit
+    # as these style tags must come at the end of the document and are limited
+    # to animation keyframes rules.
+    doc_css_bytes: false
     max_bytes: 500000
     max_bytes_spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#keyframes-stylesheet"
     css_spec: {
-      at_rule_spec: {
-        name: 'keyframes'
-        type: PARSE_AS_RULES
-      }
-      at_rule_spec: {
-        name: 'media'
-        type: PARSE_AS_RULES
-      }
-      at_rule_spec: {
-        name: 'supports'
-        type: PARSE_AS_RULES
-      }
-      at_rule_spec: {
-        name: '$DEFAULT'  # matches if none of the above match
-        type: PARSE_AS_ERROR
-      }
+      at_rule_spec: { name: 'keyframes' }
+      at_rule_spec: { name: 'media' }
+      at_rule_spec: { name: 'supports' }
       validate_keyframes: true
       declaration: "animation-timing-function"
       declaration: "offset-distance"
diff --git a/validator/validator-main.protoascii b/validator/validator-main.protoascii
index ce170315b..db8f115ba 100644
--- a/validator/validator-main.protoascii
+++ b/validator/validator-main.protoascii
@@ -20,13 +20,13 @@
 # in production from crashing. This id is not relevant to validator.js
 # because thus far, engine (validator.js) and spec file
 # (validator-main.protoascii, etc) are always released together.
-min_validator_revision_required: 375
+min_validator_revision_required: 455
 
 # The spec file revision allows the validator engine to distinguish
 # newer versions of the spec file. This is currently a Google internal
 # mechanism, validator.js does not use this facility. However, any
 # change to this file (validator-main.js) requires updating this revision id.
-spec_file_revision: 1028
+spec_file_revision: 1043
 
 styles_spec_url: "https://amp.dev/documentation/guides-and-tutorials/develop/style_and_layout/style_pages"
 script_spec_url: "https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#html-tags"
@@ -322,14 +322,14 @@ tags: {
                  "https://cloud\\.typography\\.com/"
                  "[0-9]*/[0-9]*/css/fonts\\.css|"
                  "https://fast\\.fonts\\.net/.*|"
-                 "https://fonts\\.googleapis\\.com/css\\?.*|"
+                 "https://fonts\\.googleapis\\.com/css2?\\?.*|"
                  "https://fonts\\.googleapis\\.com/icon\\?.*|"
                  "https://fonts\\.googleapis\\.com/earlyaccess/.*\\.css|"
                  "https://maxcdn\\.bootstrapcdn\\.com/font-awesome/"
                  "([0-9]+\\.?)+/css/font-awesome\\.min\\.css(\\?.*)?|"
                  "https://(use|pro)\\.fontawesome\\.com/releases/v([0-9]+\\.?)+"
-                 "/css/(all|brands|solid|regular|light|fontawesome)\\.css|"
-                 "https://(use|pro)\\.fontawesome\\.com/[0-9a-zA-Z]+\\.css|"
+                 "/css/[0-9a-zA-Z-]+\\.css|"
+                 "https://(use|pro)\\.fontawesome\\.com/[0-9a-zA-Z-]+\\.css|"
                  "https://use\\.typekit\\.net/[\\w\\p{L}\\p{N}_]+\\.css"
   }
   attrs: { name: "integrity" }  # SRI attribute (https://www.w3.org/TR/SRI/)
@@ -1421,6 +1421,11 @@ tags: {
     value_casei: "text/html"
     value_casei: "application/rss+xml"
   }
+  attrs: {
+    name: "show-tooltip"
+    value: "auto"
+    value: "true"
+  }
   attr_lists: "name-attr"
   # <amp-bind>
   attrs: { name: "[href]" }
@@ -2339,288 +2344,9 @@ attr_lists: {
   name: "svg-style-attr"
   attrs: {
     name: "style"
-    blacklisted_value_regex: "!\\s*important"
-    # CSS proprities: https://www.w3schools.com/cssref/default.asp
-    # SVG specific properties: https://www.w3.org/TR/SVG11/styling.html#SVGStylingProperties
-    # Disallowed:
-    # css_declaration: { name: "@charset" }
-    # css_declaration: { name: "@font-face" }
-    # css_declaration: { name: "@font-feature-values" }
-    # css_declaration: { name: "@import" }
-    # css_declaration: { name: "@keyframes" }
-    # css_declaration: { name: "@media" }
-    # Allowed:
-    css_declaration: { name: "align-content" }
-    css_declaration: { name: "align-items" }
-    css_declaration: { name: "align-self" }
-    css_declaration: { name: "alignment-baseline" } # SVG
-    css_declaration: { name: "all" }
-    css_declaration: { name: "animation" }
-    css_declaration: { name: "animation-delay" }
-    css_declaration: { name: "animation-direction" }
-    css_declaration: { name: "animation-duration" }
-    css_declaration: { name: "animation-fill-mode" }
-    css_declaration: { name: "animation-iteration-count" }
-    css_declaration: { name: "animation-name" }
-    css_declaration: { name: "animation-play-state" }
-    css_declaration: { name: "animation-timing-function" }
-    css_declaration: { name: "backface-visibility" }
-    css_declaration: { name: "background" }
-    css_declaration: { name: "background-attachment" }
-    css_declaration: { name: "background-blend-mode" }
-    css_declaration: { name: "background-clip" }
-    css_declaration: { name: "background-color" }
-    css_declaration: { name: "background-image" }
-    css_declaration: { name: "background-origin" }
-    css_declaration: { name: "background-position" }
-    css_declaration: { name: "background-repeat" }
-    css_declaration: { name: "background-size" }
-    css_declaration: { name: "baseline-shift" } # SVG
-    css_declaration: { name: "border" }
-    css_declaration: { name: "border-bottom" }
-    css_declaration: { name: "border-bottom-color" }
-    css_declaration: { name: "border-bottom-left-radius" }
-    css_declaration: { name: "border-bottom-right-radius" }
-    css_declaration: { name: "border-bottom-style" }
-    css_declaration: { name: "border-bottom-width" }
-    css_declaration: { name: "border-collapse" }
-    css_declaration: { name: "border-color" }
-    css_declaration: { name: "border-image" }
-    css_declaration: { name: "border-image-outset" }
-    css_declaration: { name: "border-image-repeat" }
-    css_declaration: { name: "border-image-slice" }
-    css_declaration: { name: "border-image-source" }
-    css_declaration: { name: "border-image-width" }
-    css_declaration: { name: "border-left" }
-    css_declaration: { name: "border-left-color" }
-    css_declaration: { name: "border-left-style" }
-    css_declaration: { name: "border-left-width" }
-    css_declaration: { name: "border-radius" }
-    css_declaration: { name: "border-right" }
-    css_declaration: { name: "border-right-color" }
-    css_declaration: { name: "border-right-style" }
-    css_declaration: { name: "border-right-width" }
-    css_declaration: { name: "border-spacing" }
-    css_declaration: { name: "border-style" }
-    css_declaration: { name: "border-top" }
-    css_declaration: { name: "border-top-color" }
-    css_declaration: { name: "border-top-left-radius" }
-    css_declaration: { name: "border-top-right-radius" }
-    css_declaration: { name: "border-top-style" }
-    css_declaration: { name: "border-top-width" }
-    css_declaration: { name: "border-width" }
-    css_declaration: { name: "bottom" }
-    css_declaration: { name: "box-decoration-break" }
-    css_declaration: { name: "box-shadow" }
-    css_declaration: { name: "box-sizing" }
-    css_declaration: { name: "break-after" }
-    css_declaration: { name: "break-before" }
-    css_declaration: { name: "break-inside" }
-    css_declaration: { name: "caption-side" }
-    css_declaration: { name: "caret-color" }
-    css_declaration: { name: "clear" }
-    css_declaration: { name: "clip" }
-    css_declaration: { name: "clip-path" } # SVG
-    css_declaration: { name: "clip-rule" } # SVG
-    css_declaration: { name: "color" }
-    css_declaration: { name: "color-interpolation" } # SVG
-    css_declaration: { name: "color-interpolation-filters" } # SVG
-    css_declaration: { name: "color-profile" } # SVG
-    css_declaration: { name: "color-rendering" } # SVG
-    css_declaration: { name: "column-count" }
-    css_declaration: { name: "column-fill" }
-    css_declaration: { name: "column-gap" }
-    css_declaration: { name: "column-rule" }
-    css_declaration: { name: "column-rule-color" }
-    css_declaration: { name: "column-rule-style" }
-    css_declaration: { name: "column-rule-width" }
-    css_declaration: { name: "column-span" }
-    css_declaration: { name: "column-width" }
-    css_declaration: { name: "columns" }
-    css_declaration: { name: "content" }
-    css_declaration: { name: "counter-increment" }
-    css_declaration: { name: "counter-reset" }
-    css_declaration: { name: "cursor" }
-    css_declaration: { name: "direction" }
-    css_declaration: { name: "display" }
-    css_declaration: { name: "dominant-baseline" } # SVG
-    css_declaration: { name: "empty-cells" }
-    css_declaration: { name: "enable-background" } # SVG
-    css_declaration: { name: "fill" } # SVG
-    css_declaration: { name: "fill-opacity" } # SVG
-    css_declaration: { name: "fill-rule" } # SVG
-    css_declaration: { name: "filter" }
-    css_declaration: { name: "flex" }
-    css_declaration: { name: "flex-basis" }
-    css_declaration: { name: "flex-direction" }
-    css_declaration: { name: "flex-flow" }
-    css_declaration: { name: "flex-grow" }
-    css_declaration: { name: "flex-shrink" }
-    css_declaration: { name: "flex-wrap" }
-    css_declaration: { name: "float" }
-    css_declaration: { name: "flood-color" } # SVG
-    css_declaration: { name: "flood-opacity" } # SVG
-    css_declaration: { name: "font" }
-    css_declaration: { name: "font-family" }
-    css_declaration: { name: "font-feature-settings" }
-    css_declaration: { name: "font-kerning" }
-    css_declaration: { name: "font-language-override" }
-    css_declaration: { name: "font-size" }
-    css_declaration: { name: "font-size-adjust" }
-    css_declaration: { name: "font-stretch" }
-    css_declaration: { name: "font-style" }
-    css_declaration: { name: "font-synthesis" }
-    css_declaration: { name: "font-variant" }
-    css_declaration: { name: "font-variant-alternates" }
-    css_declaration: { name: "font-variant-caps" }
-    css_declaration: { name: "font-variant-east-asian" }
-    css_declaration: { name: "font-variant-ligatures" }
-    css_declaration: { name: "font-variant-numeric" }
-    css_declaration: { name: "font-variant-position" }
-    css_declaration: { name: "font-weight" }
-    css_declaration: { name: "glyph-orientation-horizontal" } # SVG
-    css_declaration: { name: "glyph-orientation-vertical" } # SVG
-    css_declaration: { name: "grid" }
-    css_declaration: { name: "grid-area" }
-    css_declaration: { name: "grid-auto-columns" }
-    css_declaration: { name: "grid-auto-flow" }
-    css_declaration: { name: "grid-auto-rows" }
-    css_declaration: { name: "grid-column" }
-    css_declaration: { name: "grid-column-end" }
-    css_declaration: { name: "grid-column-gap" }
-    css_declaration: { name: "grid-column-start" }
-    css_declaration: { name: "grid-gap" }
-    css_declaration: { name: "grid-row" }
-    css_declaration: { name: "grid-row-end" }
-    css_declaration: { name: "grid-row-gap" }
-    css_declaration: { name: "grid-row-start" }
-    css_declaration: { name: "grid-template" }
-    css_declaration: { name: "grid-template-areas" }
-    css_declaration: { name: "grid-template-columns" }
-    css_declaration: { name: "grid-template-rows" }
-    css_declaration: { name: "hanging-punctuation" }
-    css_declaration: { name: "height" }
-    css_declaration: { name: "hyphens" }
-    css_declaration: { name: "image-rendering" }
-    css_declaration: { name: "isolation" }
-    css_declaration: { name: "justify-content" }
-    css_declaration: { name: "kerning" } # SVG
-    css_declaration: { name: "left" }
-    css_declaration: { name: "letter-spacing" }
-    css_declaration: { name: "lighting-color" } # SVG
-    css_declaration: { name: "line-break" }
-    css_declaration: { name: "line-height" }
-    css_declaration: { name: "list-style" }
-    css_declaration: { name: "list-style-image" }
-    css_declaration: { name: "list-style-position" }
-    css_declaration: { name: "list-style-type" }
-    css_declaration: { name: "margin" }
-    css_declaration: { name: "margin-bottom" }
-    css_declaration: { name: "margin-left" }
-    css_declaration: { name: "margin-right" }
-    css_declaration: { name: "margin-top" }
-    css_declaration: { name: "marker" } # SVG
-    css_declaration: { name: "marker-end" } # SVG
-    css_declaration: { name: "marker-mid" } # SVG
-    css_declaration: { name: "marker-start" } # SVG
-    css_declaration: { name: "mask" } # SVG
-    css_declaration: { name: "max-height" }
-    css_declaration: { name: "max-width" }
-    css_declaration: { name: "min-height" }
-    css_declaration: { name: "min-width" }
-    css_declaration: { name: "mix-blend-mode" }
-    css_declaration: { name: "object-fit" }
-    css_declaration: { name: "object-position" }
-    css_declaration: { name: "opacity" }
-    css_declaration: { name: "order" }
-    css_declaration: { name: "orphans" }
-    css_declaration: { name: "outline" }
-    css_declaration: { name: "outline-color" }
-    css_declaration: { name: "outline-offset" }
-    css_declaration: { name: "outline-style" }
-    css_declaration: { name: "outline-width" }
-    css_declaration: { name: "overflow" }
-    css_declaration: { name: "overflow-wrap" }
-    css_declaration: { name: "overflow-x" }
-    css_declaration: { name: "overflow-y" }
-    css_declaration: { name: "padding" }
-    css_declaration: { name: "padding-bottom" }
-    css_declaration: { name: "padding-left" }
-    css_declaration: { name: "padding-right" }
-    css_declaration: { name: "padding-top" }
-    css_declaration: { name: "page-break-after" }
-    css_declaration: { name: "page-break-before" }
-    css_declaration: { name: "page-break-inside" }
-    css_declaration: { name: "perspective" }
-    css_declaration: { name: "perspective-origin" }
-    css_declaration: { name: "pointer-events" }
-    # CSS property `position` with values `fixed` and `sticky are not allowed.
-    # From https://www.w3schools.com/cssref/pr_class_position.asp
-    css_declaration: {
-      name: "position"
-      value_casei: "absolute"
-      value_casei: "inherit"
-      value_casei: "initial"
-      value_casei: "relative"
-      value_casei: "static"
-    }
-    css_declaration: { name: "quotes" }
-    css_declaration: { name: "resize" }
-    css_declaration: { name: "right" }
-    css_declaration: { name: "shape-rendering" } # SVG
-    css_declaration: { name: "stop-color" } # SVG
-    css_declaration: { name: "stop-opacity" } # SVG
-    css_declaration: { name: "stroke" } # SVG
-    css_declaration: { name: "stroke-dasharray" } # SVG
-    css_declaration: { name: "stroke-dashoffset" } # SVG
-    css_declaration: { name: "stroke-linecap" } # SVG
-    css_declaration: { name: "stroke-linejoin" } # SVG
-    css_declaration: { name: "stroke-miterlimit" } # SVG
-    css_declaration: { name: "stroke-opacity" } # SVG
-    css_declaration: { name: "stroke-width" } # SVG
-    css_declaration: { name: "tab-size" }
-    css_declaration: { name: "table-layout" }
-    css_declaration: { name: "text-align" }
-    css_declaration: { name: "text-align-last" }
-    css_declaration: { name: "text-anchor" } # SVG
-    css_declaration: { name: "text-combine-upright" }
-    css_declaration: { name: "text-decoration" }
-    css_declaration: { name: "text-decoration-color" }
-    css_declaration: { name: "text-decoration-line" }
-    css_declaration: { name: "text-decoration-style" }
-    css_declaration: { name: "text-fill-color" }
-    css_declaration: { name: "text-indent" }
-    css_declaration: { name: "text-justify" }
-    css_declaration: { name: "text-orientation" }
-    css_declaration: { name: "text-overflow" }
-    css_declaration: { name: "text-rendering" } # SVG
-    css_declaration: { name: "text-shadow" }
-    css_declaration: { name: "text-stroke" }
-    css_declaration: { name: "text-stroke-color" }
-    css_declaration: { name: "text-stroke-width" }
-    css_declaration: { name: "text-transform" }
-    css_declaration: { name: "text-underline-position" }
-    css_declaration: { name: "top" }
-    css_declaration: { name: "transform" }
-    css_declaration: { name: "transform-origin" }
-    css_declaration: { name: "transform-style" }
-    css_declaration: { name: "transition" }
-    css_declaration: { name: "transition-delay" }
-    css_declaration: { name: "transition-duration" }
-    css_declaration: { name: "transition-property" }
-    css_declaration: { name: "transition-timing-function" }
-    css_declaration: { name: "unicode-bidi" }
-    css_declaration: { name: "user-select" }
-    css_declaration: { name: "vertical-align" }
-    css_declaration: { name: "visibility" }
-    css_declaration: { name: "white-space" }
-    css_declaration: { name: "widows" }
-    css_declaration: { name: "width" }
-    css_declaration: { name: "word-break" }
-    css_declaration: { name: "word-spacing" }
-    css_declaration: { name: "word-wrap" }
-    css_declaration: { name: "writing-mode" }
-    css_declaration: { name: "z-index" }
+    # `style` attribute value validated against Document Level CSS rules defined
+    # in validator-css.protoascii.
+    value_doc_svg_css: true
   }
 }
 
@@ -3888,6 +3614,14 @@ attr_lists: {
     disabled_by: "amp4email"
     name: "list"
   }
+  attrs: { 
+    disabled_by: "amp4email"
+    name: "enterkeyhint"
+  }
+  attrs: {
+    disabled_by: "amp4email"
+    name: "enterkeyhint"
+  }
   attrs: { name: "max" }
   attrs: { name: "maxlength" }
   attrs: { name: "min" }
@@ -5712,250 +5446,9 @@ attr_lists: {
   attrs: { name: "rev" }
   attrs: {
     name: "style"
-    blacklisted_value_regex: "(!\\s*important|<!--)"
-    # CSS proprities: https://www.w3schools.com/cssref/default.asp
-    # Disallowed:
-    # css_declaration: { name: "@charset" }
-    # css_declaration: { name: "@font-face" }
-    # css_declaration: { name: "@font-feature-values" }
-    # css_declaration: { name: "@import" }
-    # css_declaration: { name: "@keyframes" }
-    # css_declaration: { name: "@media" }
-    # Allowed:
-    css_declaration: { name: "align-content" }
-    css_declaration: { name: "align-items" }
-    css_declaration: { name: "align-self" }
-    css_declaration: { name: "all" }
-    css_declaration: { name: "animation" }
-    css_declaration: { name: "animation-delay" }
-    css_declaration: { name: "animation-direction" }
-    css_declaration: { name: "animation-duration" }
-    css_declaration: { name: "animation-fill-mode" }
-    css_declaration: { name: "animation-iteration-count" }
-    css_declaration: { name: "animation-name" }
-    css_declaration: { name: "animation-play-state" }
-    css_declaration: { name: "animation-timing-function" }
-    css_declaration: { name: "backface-visibility" }
-    css_declaration: { name: "background" }
-    css_declaration: { name: "background-attachment" }
-    css_declaration: { name: "background-blend-mode" }
-    css_declaration: { name: "background-clip" }
-    css_declaration: { name: "background-color" }
-    css_declaration: { name: "background-image" }
-    css_declaration: { name: "background-origin" }
-    css_declaration: { name: "background-position" }
-    css_declaration: { name: "background-repeat" }
-    css_declaration: { name: "background-size" }
-    css_declaration: { name: "border" }
-    css_declaration: { name: "border-bottom" }
-    css_declaration: { name: "border-bottom-color" }
-    css_declaration: { name: "border-bottom-left-radius" }
-    css_declaration: { name: "border-bottom-right-radius" }
-    css_declaration: { name: "border-bottom-style" }
-    css_declaration: { name: "border-bottom-width" }
-    css_declaration: { name: "border-collapse" }
-    css_declaration: { name: "border-color" }
-    css_declaration: { name: "border-image" }
-    css_declaration: { name: "border-image-outset" }
-    css_declaration: { name: "border-image-repeat" }
-    css_declaration: { name: "border-image-slice" }
-    css_declaration: { name: "border-image-source" }
-    css_declaration: { name: "border-image-width" }
-    css_declaration: { name: "border-left" }
-    css_declaration: { name: "border-left-color" }
-    css_declaration: { name: "border-left-style" }
-    css_declaration: { name: "border-left-width" }
-    css_declaration: { name: "border-radius" }
-    css_declaration: { name: "border-right" }
-    css_declaration: { name: "border-right-color" }
-    css_declaration: { name: "border-right-style" }
-    css_declaration: { name: "border-right-width" }
-    css_declaration: { name: "border-spacing" }
-    css_declaration: { name: "border-style" }
-    css_declaration: { name: "border-top" }
-    css_declaration: { name: "border-top-color" }
-    css_declaration: { name: "border-top-left-radius" }
-    css_declaration: { name: "border-top-right-radius" }
-    css_declaration: { name: "border-top-style" }
-    css_declaration: { name: "border-top-width" }
-    css_declaration: { name: "border-width" }
-    css_declaration: { name: "bottom" }
-    css_declaration: { name: "box-decoration-break" }
-    css_declaration: { name: "box-shadow" }
-    css_declaration: { name: "box-sizing" }
-    css_declaration: { name: "break-after" }
-    css_declaration: { name: "break-before" }
-    css_declaration: { name: "break-inside" }
-    css_declaration: { name: "caption-side" }
-    css_declaration: { name: "caret-color" }
-    css_declaration: { name: "clear" }
-    css_declaration: { name: "clip" }
-    css_declaration: { name: "color" }
-    css_declaration: { name: "column-count" }
-    css_declaration: { name: "column-fill" }
-    css_declaration: { name: "column-gap" }
-    css_declaration: { name: "column-rule" }
-    css_declaration: { name: "column-rule-color" }
-    css_declaration: { name: "column-rule-style" }
-    css_declaration: { name: "column-rule-width" }
-    css_declaration: { name: "column-span" }
-    css_declaration: { name: "column-width" }
-    css_declaration: { name: "columns" }
-    css_declaration: { name: "content" }
-    css_declaration: { name: "counter-increment" }
-    css_declaration: { name: "counter-reset" }
-    css_declaration: { name: "cursor" }
-    css_declaration: { name: "direction" }
-    css_declaration: { name: "display" }
-    css_declaration: { name: "empty-cells" }
-    css_declaration: { name: "filter" }
-    css_declaration: { name: "flex" }
-    css_declaration: { name: "flex-basis" }
-    css_declaration: { name: "flex-direction" }
-    css_declaration: { name: "flex-flow" }
-    css_declaration: { name: "flex-grow" }
-    css_declaration: { name: "flex-shrink" }
-    css_declaration: { name: "flex-wrap" }
-    css_declaration: { name: "float" }
-    css_declaration: { name: "font" }
-    css_declaration: { name: "font-family" }
-    css_declaration: { name: "font-feature-settings" }
-    css_declaration: { name: "font-kerning" }
-    css_declaration: { name: "font-language-override" }
-    css_declaration: { name: "font-size" }
-    css_declaration: { name: "font-size-adjust" }
-    css_declaration: { name: "font-stretch" }
-    css_declaration: { name: "font-style" }
-    css_declaration: { name: "font-synthesis" }
-    css_declaration: { name: "font-variant" }
-    css_declaration: { name: "font-variant-alternates" }
-    css_declaration: { name: "font-variant-caps" }
-    css_declaration: { name: "font-variant-east-asian" }
-    css_declaration: { name: "font-variant-ligatures" }
-    css_declaration: { name: "font-variant-numeric" }
-    css_declaration: { name: "font-variant-position" }
-    css_declaration: { name: "font-weight" }
-    css_declaration: { name: "grid" }
-    css_declaration: { name: "grid-area" }
-    css_declaration: { name: "grid-auto-columns" }
-    css_declaration: { name: "grid-auto-flow" }
-    css_declaration: { name: "grid-auto-rows" }
-    css_declaration: { name: "grid-column" }
-    css_declaration: { name: "grid-column-end" }
-    css_declaration: { name: "grid-column-gap" }
-    css_declaration: { name: "grid-column-start" }
-    css_declaration: { name: "grid-gap" }
-    css_declaration: { name: "grid-row" }
-    css_declaration: { name: "grid-row-end" }
-    css_declaration: { name: "grid-row-gap" }
-    css_declaration: { name: "grid-row-start" }
-    css_declaration: { name: "grid-template" }
-    css_declaration: { name: "grid-template-areas" }
-    css_declaration: { name: "grid-template-columns" }
-    css_declaration: { name: "grid-template-rows" }
-    css_declaration: { name: "hanging-punctuation" }
-    css_declaration: { name: "height" }
-    css_declaration: { name: "hyphens" }
-    css_declaration: { name: "image-rendering" }
-    css_declaration: { name: "isolation" }
-    css_declaration: { name: "justify-content" }
-    css_declaration: { name: "left" }
-    css_declaration: { name: "letter-spacing" }
-    css_declaration: { name: "line-break" }
-    css_declaration: { name: "line-height" }
-    css_declaration: { name: "list-style" }
-    css_declaration: { name: "list-style-image" }
-    css_declaration: { name: "list-style-position" }
-    css_declaration: { name: "list-style-type" }
-    css_declaration: { name: "margin" }
-    css_declaration: { name: "margin-bottom" }
-    css_declaration: { name: "margin-left" }
-    css_declaration: { name: "margin-right" }
-    css_declaration: { name: "margin-top" }
-    css_declaration: { name: "max-height" }
-    css_declaration: { name: "max-width" }
-    css_declaration: { name: "min-height" }
-    css_declaration: { name: "min-width" }
-    css_declaration: { name: "mix-blend-mode" }
-    css_declaration: { name: "object-fit" }
-    css_declaration: { name: "object-position" }
-    css_declaration: { name: "opacity" }
-    css_declaration: { name: "order" }
-    css_declaration: { name: "orphans" }
-    css_declaration: { name: "outline" }
-    css_declaration: { name: "outline-color" }
-    css_declaration: { name: "outline-offset" }
-    css_declaration: { name: "outline-style" }
-    css_declaration: { name: "outline-width" }
-    css_declaration: { name: "overflow" }
-    css_declaration: { name: "overflow-wrap" }
-    css_declaration: { name: "overflow-x" }
-    css_declaration: { name: "overflow-y" }
-    css_declaration: { name: "padding" }
-    css_declaration: { name: "padding-bottom" }
-    css_declaration: { name: "padding-left" }
-    css_declaration: { name: "padding-right" }
-    css_declaration: { name: "padding-top" }
-    css_declaration: { name: "page-break-after" }
-    css_declaration: { name: "page-break-before" }
-    css_declaration: { name: "page-break-inside" }
-    css_declaration: { name: "perspective" }
-    css_declaration: { name: "perspective-origin" }
-    css_declaration: { name: "pointer-events" }
-    # CSS property `position` with values `fixed` and `sticky are not allowed.
-    # From https://www.w3schools.com/cssref/pr_class_position.asp
-    css_declaration: {
-      name: "position"
-      value_casei: "absolute"
-      value_casei: "inherit"
-      value_casei: "initial"
-      value_casei: "relative"
-      value_casei: "static"
-    }
-    css_declaration: { name: "quotes" }
-    css_declaration: { name: "resize" }
-    css_declaration: { name: "right" }
-    css_declaration: { name: "tab-size" }
-    css_declaration: { name: "table-layout" }
-    css_declaration: { name: "text-align" }
-    css_declaration: { name: "text-align-last" }
-    css_declaration: { name: "text-combine-upright" }
-    css_declaration: { name: "text-decoration" }
-    css_declaration: { name: "text-decoration-color" }
-    css_declaration: { name: "text-decoration-line" }
-    css_declaration: { name: "text-decoration-style" }
-    css_declaration: { name: "text-fill-color" }
-    css_declaration: { name: "text-indent" }
-    css_declaration: { name: "text-justify" }
-    css_declaration: { name: "text-orientation" }
-    css_declaration: { name: "text-overflow" }
-    css_declaration: { name: "text-shadow" }
-    css_declaration: { name: "text-stroke" }
-    css_declaration: { name: "text-stroke-color" }
-    css_declaration: { name: "text-stroke-width" }
-    css_declaration: { name: "text-transform" }
-    css_declaration: { name: "text-underline-position" }
-    css_declaration: { name: "top" }
-    css_declaration: { name: "transform" }
-    css_declaration: { name: "transform-origin" }
-    css_declaration: { name: "transform-style" }
-    css_declaration: { name: "transition" }
-    css_declaration: { name: "transition-delay" }
-    css_declaration: { name: "transition-duration" }
-    css_declaration: { name: "transition-property" }
-    css_declaration: { name: "transition-timing-function" }
-    css_declaration: { name: "unicode-bidi" }
-    css_declaration: { name: "user-select" }
-    css_declaration: { name: "vertical-align" }
-    css_declaration: { name: "visibility" }
-    css_declaration: { name: "white-space" }
-    css_declaration: { name: "widows" }
-    css_declaration: { name: "width" }
-    css_declaration: { name: "word-break" }
-    css_declaration: { name: "word-spacing" }
-    css_declaration: { name: "word-wrap" }
-    css_declaration: { name: "writing-mode" }
-    css_declaration: { name: "z-index" }
+    # `style` attribute value validated against Document Level CSS rules defined
+    # in validator-css.protoascii.
+    value_doc_css: true
   }
   attrs: { name: "typeof" }
   attrs: { name: "vocab" }

</details>

+127 -174

0 comment

3 changed files

westonruter

pr closed time in 5 days

issue commentafragen/github-updater

Updating plugins stored in Gists

I find providing the URL to the Zipfile not the best user experience. I also tried providing the URL to a Gist after selecting the Gist host option:

image

But this failed for some reason:

image

westonruter

comment created time in 5 days

more