diff --git a/includes/amp-helper-functions.php b/includes/amp-helper-functions.php
index b5dbd116ee4..d5f53c694f4 100644
--- a/includes/amp-helper-functions.php
+++ b/includes/amp-helper-functions.php
@@ -361,8 +361,23 @@ function amp_get_asset_url( $file ) {
* @return string Boilerplate code.
*/
function amp_get_boilerplate_code() {
- return ''
- . '';
+ $stylesheets = amp_get_boilerplate_stylesheets();
+ return sprintf( '', $stylesheets[0], $stylesheets[1] );
+}
+
+/**
+ * Get AMP boilerplate stylesheets.
+ *
+ * @since 1.3
+ * @link https://www.ampproject.org/docs/reference/spec#boilerplate
+ *
+ * @return string[] Stylesheets, where first is contained in style[amp-boilerplate] and the second in noscript>style[amp-boilerplate].
+ */
+function amp_get_boilerplate_stylesheets() {
+ return [
+ 'body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}',
+ 'body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}',
+ ];
}
/**
diff --git a/includes/class-amp-story-post-type.php b/includes/class-amp-story-post-type.php
index 5f8ba10dc2b..79b499ea4ad 100644
--- a/includes/class-amp-story-post-type.php
+++ b/includes/class-amp-story-post-type.php
@@ -197,6 +197,30 @@ public static function register() {
add_action( 'wp_enqueue_scripts', [ __CLASS__, 'add_custom_stories_styles' ] );
+ add_action(
+ 'amp_story_head',
+ function() {
+ // Theme support for title-tag is implied for stories. See _wp_render_title_tag().
+ echo '
' . esc_html( wp_get_document_title() ) . '' . "\n";
+ },
+ 1
+ );
+ add_action( 'amp_story_head', 'wp_enqueue_scripts', 1 );
+ add_action(
+ 'amp_story_head',
+ function() {
+ /*
+ * Same as wp_print_styles() but importantly omitting the wp_print_styles action, which themes/plugins
+ * can use to output arbitrary styling. Styling is constrained in story template via the
+ * \AMP_Story_Post_Type::filter_frontend_print_styles_array() method.
+ */
+ wp_styles()->do_items();
+ },
+ 8
+ );
+ add_action( 'amp_story_head', 'rel_canonical' );
+ add_action( 'amp_story_head', 'amp_add_generator_metadata' );
+
// Remove unnecessary settings.
add_filter( 'block_editor_settings', [ __CLASS__, 'filter_block_editor_settings' ], 10, 2 );
diff --git a/includes/class-amp-theme-support.php b/includes/class-amp-theme-support.php
index e02cf242fbf..799a6ec4354 100644
--- a/includes/class-amp-theme-support.php
+++ b/includes/class-amp-theme-support.php
@@ -1016,34 +1016,6 @@ static function( $html ) {
1
);
- /*
- * "AMP HTML documents MUST contain the AMP boilerplate code (head > style[amp-boilerplate] and noscript > style[amp-boilerplate])
- * in their head tag." {@link https://www.ampproject.org/docs/fundamentals/spec#required-markup AMP Required markup}
- *
- * After "Specify the tag for your favicon.", then
- * "Specify any custom styles by using the ';
- },
- 0
- );
- add_action(
- 'wp_head',
- static function() {
- echo amp_get_boilerplate_code(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- },
- PHP_INT_MAX
- );
-
add_action( 'admin_bar_init', [ __CLASS__, 'init_admin_bar' ] );
add_action( 'wp_head', 'amp_add_generator_metadata', 20 );
add_action( 'wp_enqueue_scripts', [ __CLASS__, 'enqueue_assets' ], 0 ); // Enqueue before theme's styles.
@@ -1434,8 +1406,9 @@ static function( $body_classes ) {
*
* @since 0.7
* @link https://www.ampproject.org/docs/reference/spec#required-markup
- * @link https://docs.google.com/document/d/169XUxtSSEJb16NfkrCr9y5lqhUR7vxXEAsNxBzg07fM/edit#heading=h.2ha259c3ffos
+ * @link https://amp.dev/documentation/guides-and-tutorials/optimize-and-measure/optimize_amp/
* @todo All of this might be better placed inside of a sanitizer.
+ * @todo Consider removing any scripts that are not among the $script_handles.
*
* @param DOMDocument $dom Document.
* @param string[] $script_handles AMP script handles for components identified during output buffering.
@@ -1447,6 +1420,8 @@ public static function ensure_required_markup( DOMDocument $dom, $script_handles
* @var DOMElement $meta
* @var DOMElement $script
* @var DOMElement $link
+ * @var DOMElement $style
+ * @var DOMElement $noscript
*/
$xpath = new DOMXPath( $dom );
@@ -1496,9 +1471,9 @@ public static function ensure_required_markup( DOMDocument $dom, $script_handles
* there are a few basic optimizations that you can apply. The key is to structure the section
* in a way so that all render-blocking scripts and custom fonts load as fast as possible."
*
- * "The first tag should be the meta charset tag, followed by any remaining meta tags."
+ * "1. The first tag should be the meta charset tag, followed by any remaining meta tags."
*
- * {@link https://docs.google.com/document/d/169XUxtSSEJb16NfkrCr9y5lqhUR7vxXEAsNxBzg07fM/edit#heading=h.2ha259c3ffos Optimize the AMP Runtime loading}
+ * {@link https://amp.dev/documentation/guides-and-tutorials/optimize-and-measure/optimize_amp/ Optimize the AMP Runtime loading}
*/
$meta_charset = null;
$meta_viewport = null;
@@ -1606,10 +1581,10 @@ public static function ensure_required_markup( DOMDocument $dom, $script_handles
/* phpcs:ignore Squiz.PHP.CommentedOutCode.Found
*
- * "Next, preload the AMP runtime v0.js
-
assertStringEndsWith( '/assets/foo.jpg', amp_get_asset_url( 'foo.jpg' ) );
+ }
+
+ /**
+ * Test amp_get_boilerplate_code.
+ *
+ * @covers ::amp_get_boilerplate_code()
+ */
+ public function test_amp_get_boilerplate_code() {
+ $boilerplate_code = amp_get_boilerplate_code();
+ $this->assertStringStartsWith( '#s',
- '##s',
'',
- '