{ __( 'Try out a more flexible AMP by generating pages that use AMP components without requiring AMP validity! By selecting a sandboxing level, you are indicating the minimum degree of sanitization. For example, if you selected the loose level but have a page without any POST form and no custom scripts, it will still be served as valid AMP—the same as if you had selected the strict level.', 'amp' ) }
@@ -112,9 +88,6 @@ export function Sandboxing( { focusedSection } ) {
) }
-
+ >
);
}
-Sandboxing.propTypes = {
- focusedSection: PropTypes.string,
-};
diff --git a/includes/amp-helper-functions.php b/includes/amp-helper-functions.php
index ce125f0dfc7..68a1ad21405 100644
--- a/includes/amp-helper-functions.php
+++ b/includes/amp-helper-functions.php
@@ -404,7 +404,7 @@ function amp_is_available() {
}
$message = sprintf(
- /* translators: 1: amp_is_available() function, 2: amp_is_request() function, 3: is_amp_endpoint() function */
+ /* translators: 1: amp_is_available function, 2: amp_is_request function, 3: is_amp_endpoint function */
__( '%1$s (or %2$s, formerly %3$s) was called too early and so it will not work properly.', 'amp' ),
'`amp_is_available()`',
'`amp_is_request()`',
@@ -741,9 +741,14 @@ function amp_add_amphtml_link() {
}
$amp_url = amp_add_paired_endpoint( amp_get_current_url() );
+
if ( $amp_url ) {
- $amp_url = remove_query_arg( QueryVar::NOAMP, $amp_url );
- printf( '', esc_url( $amp_url ) );
+ $amp_url = remove_query_arg( QueryVar::NOAMP, $amp_url );
+ $sandboxing_level = amp_get_sandboxing_level();
+
+ if ( 0 === $sandboxing_level || 3 === $sandboxing_level ) {
+ printf( '', esc_url( $amp_url ) );
+ }
}
}
@@ -2163,3 +2168,21 @@ function amp_remove_paired_endpoint( $url ) {
return $url;
}
}
+
+/**
+ * Determine sandboxing level if enabled.
+ *
+ * @since 2.3.1
+ *
+ * @return int Following values are possible:
+ * 0: Sandbox is disabled.
+ * 1: Sandboxing level: Loose.
+ * 2: Sandboxing level: Moderate.
+ * 3: Sandboxing level: Strict.
+ */
+function amp_get_sandboxing_level() {
+ if ( ! AMP_Options_Manager::get_option( Option::SANDBOXING_ENABLED ) ) {
+ return 0;
+ }
+ return AMP_Options_Manager::get_option( Option::SANDBOXING_LEVEL );
+}
diff --git a/includes/validation/class-amp-validation-manager.php b/includes/validation/class-amp-validation-manager.php
index 090bd0283aa..f0c61aeef85 100644
--- a/includes/validation/class-amp-validation-manager.php
+++ b/includes/validation/class-amp-validation-manager.php
@@ -2032,6 +2032,8 @@ public static function finalize_validation( Document $dom ) {
return true;
}
+ $sandboxing_level = amp_get_sandboxing_level();
+
/*
* In AMP-first, documents with invalid AMP markup can still be served. The amp attribute will be omitted in
* order to prevent GSC from complaining about a validation error already surfaced inside of WordPress.
@@ -2041,8 +2043,10 @@ public static function finalize_validation( Document $dom ) {
* Otherwise, if in Paired AMP then redirect to the non-AMP version if the current user isn't an user who
* can manage validation error statuses (access developer tools) and change the AMP options for the template
* mode. Such users should be able to see kept invalid markup on the AMP page even though it is invalid.
+ *
+ * Also, if sandboxing is not set to strict mode, then the page should be displayed to the user.
*/
- if ( amp_is_canonical() ) {
+ if ( amp_is_canonical() || ( 1 === $sandboxing_level || 2 === $sandboxing_level ) ) {
return true;
}
diff --git a/src/MobileRedirection.php b/src/MobileRedirection.php
index 89f23f29743..0ba02f9bc62 100644
--- a/src/MobileRedirection.php
+++ b/src/MobileRedirection.php
@@ -64,7 +64,15 @@ public function register() {
add_filter( 'amp_default_options', [ $this, 'filter_default_options' ] );
add_filter( 'amp_options_updating', [ $this, 'sanitize_options' ], 10, 2 );
- if ( AMP_Options_Manager::get_option( Option::MOBILE_REDIRECT ) && ! amp_is_canonical() ) {
+ $is_mobile_redirect_enabled = AMP_Options_Manager::get_option( Option::MOBILE_REDIRECT );
+ $sandboxing_level = amp_get_sandboxing_level();
+
+ // Add alternative link if mobile redirection is enabled or sandboxing level is set to loose or moderate.
+ if ( ! amp_is_canonical() && ( $is_mobile_redirect_enabled || ( 1 === $sandboxing_level || 2 === $sandboxing_level ) ) ) {
+ add_action( 'wp_head', [ $this, 'add_mobile_alternative_link' ] );
+ }
+
+ if ( $is_mobile_redirect_enabled && ! amp_is_canonical() ) {
add_action( 'template_redirect', [ $this, 'redirect' ], PHP_INT_MAX );
// Enable AMP-to-AMP linking by default to avoid redirecting to AMP version when navigating.
@@ -144,11 +152,9 @@ public function redirect() {
}
// Print the mobile switcher styles.
- add_action( 'wp_head', [ $this, 'add_mobile_version_switcher_styles' ] );
- add_action( 'amp_post_template_head', [ $this, 'add_mobile_version_switcher_styles' ] ); // For legacy Reader mode theme.
+ $this->add_mobile_switcher_head_hooks();
if ( ! amp_is_request() ) {
- add_action( 'wp_head', [ $this, 'add_mobile_alternative_link' ] );
if ( $js ) {
// Add mobile redirection script.
add_action( 'wp_head', [ $this, 'add_mobile_redirect_script' ], ~PHP_INT_MAX );
@@ -165,21 +171,43 @@ public function redirect() {
}
// Add a link to the footer to allow for navigation to the AMP version.
- add_action( 'wp_footer', [ $this, 'add_mobile_version_switcher_link' ] );
+ $this->add_mobile_switcher_footer_hooks();
} else {
if ( ! $js && $this->is_redirection_disabled_via_cookie() ) {
$this->set_mobile_redirection_disabled_cookie( false );
}
- add_filter( 'amp_to_amp_linking_element_excluded', [ $this, 'filter_amp_to_amp_linking_element_excluded' ], 100, 2 );
- add_filter( 'amp_to_amp_linking_element_query_vars', [ $this, 'filter_amp_to_amp_linking_element_query_vars' ], 10, 2 );
+ $this->add_a2a_linking_hooks();
// Add a link to the footer to allow for navigation to the non-AMP version.
- add_action( 'wp_footer', [ $this, 'add_mobile_version_switcher_link' ] );
- add_action( 'amp_post_template_footer', [ $this, 'add_mobile_version_switcher_link' ] ); // For legacy Reader mode theme.
+ $this->add_mobile_switcher_footer_hooks();
}
}
+ /**
+ * Add mobile version switcher head hooks.
+ */
+ private function add_mobile_switcher_head_hooks() {
+ add_action( 'wp_head', [ $this, 'add_mobile_version_switcher_styles' ] );
+ add_action( 'amp_post_template_head', [ $this, 'add_mobile_version_switcher_styles' ] ); // For legacy Reader mode theme.
+ }
+
+ /**
+ * Add mobile version switcher footer hooks.
+ */
+ private function add_mobile_switcher_footer_hooks() {
+ add_action( 'wp_footer', [ $this, 'add_mobile_version_switcher_link' ] );
+ add_action( 'amp_post_template_footer', [ $this, 'add_mobile_version_switcher_link' ] ); // For legacy Reader mode theme.
+ }
+
+ /**
+ * Add AMP-to-AMP linking hooks.
+ */
+ private function add_a2a_linking_hooks() {
+ add_filter( 'amp_to_amp_linking_element_excluded', [ $this, 'filter_amp_to_amp_linking_element_excluded' ], 100, 2 );
+ add_filter( 'amp_to_amp_linking_element_query_vars', [ $this, 'filter_amp_to_amp_linking_element_query_vars' ], 10, 2 );
+ }
+
/**
* Ensure that links/forms which point to ?noamp up-front are excluded from AMP-to-AMP linking.
*
@@ -481,10 +509,12 @@ private function sanitize_script_attributes( $attributes ) {
* @link https://developers.google.com/search/mobile-sites/mobile-seo/separate-urls#annotation-in-the-html
*/
public function add_mobile_alternative_link() {
- printf(
- '',
- esc_url( $this->get_current_amp_url() )
- );
+ if ( amp_is_available() && ! amp_is_request() ) {
+ printf(
+ '',
+ esc_url( $this->get_current_amp_url() )
+ );
+ }
}
/**
@@ -574,6 +604,10 @@ public function add_mobile_version_switcher_link() {
*/
$text = apply_filters( 'amp_mobile_version_switcher_link_text', $text );
+ if ( empty( $text ) ) {
+ return;
+ }
+
$hide_switcher = (
// The switcher must always be shown in the AMP version to allow accessing the non-AMP version.
! $is_amp
diff --git a/src/Sandboxing.php b/src/Sandboxing.php
index a2ba1118582..0cc8505a6d5 100644
--- a/src/Sandboxing.php
+++ b/src/Sandboxing.php
@@ -134,19 +134,12 @@ public function sanitize_options( $options, $new_options ) {
* Add hooks.
*/
public function add_hooks() {
- // Limit to Standard mode for now. To support in Transitional/Reader we'd need to discontinue redirecting invalid
- // AMP to non-AMP and omit the amphtml link (in which case it would only be relevant when mobile redirection is
- // enabled).
- if ( ! amp_is_canonical() ) {
- return;
- }
+ $sandboxing_level = amp_get_sandboxing_level();
- if ( ! AMP_Options_Manager::get_option( Option::SANDBOXING_ENABLED ) ) {
+ if ( 0 === $sandboxing_level ) {
return;
}
- $sandboxing_level = AMP_Options_Manager::get_option( Option::SANDBOXING_LEVEL );
-
// Opt-in to the new script sanitization logic in the script sanitizer.
add_filter(
'amp_content_sanitizers',
@@ -212,6 +205,10 @@ private function remove_required_amp_markup_if_not_used( Document $dom, $effecti
return;
}
+ if ( $dom->ampElements->length > 0 ) {
+ return;
+ }
+
$amp_scripts = $dom->xpath->query( '//script[ @custom-element or @custom-template ]' );
if ( $amp_scripts->length > 0 ) {
return;
diff --git a/tests/php/src/MobileRedirectionTest.php b/tests/php/src/MobileRedirectionTest.php
index 8ccd3b27717..ec1eb45002d 100644
--- a/tests/php/src/MobileRedirectionTest.php
+++ b/tests/php/src/MobileRedirectionTest.php
@@ -115,6 +115,98 @@ public function test_register_enabled_but_standard_mode() {
$this->assert_hooks_not_added( $instance );
}
+ /**
+ * Get data for test_add_mobile_alternate_link
+ *
+ * @return array
+ */
+ public function get_add_mobile_alternate_link() {
+ return [
+ 'mobile_redirection_enabled' => [
+ [
+ Option::MOBILE_REDIRECT => true,
+ ],
+ 10,
+ ],
+ 'mobile_redirection_enabled_in_canonical_mode' => [
+ [
+ Option::MOBILE_REDIRECT => true,
+ Option::THEME_SUPPORT => AMP_Theme_Support::STANDARD_MODE_SLUG,
+ ],
+ false,
+ ],
+ 'sandboxing_set_to_loose' => [
+ [
+ Option::MOBILE_REDIRECT => false,
+ Option::SANDBOXING_ENABLED => true,
+ Option::SANDBOXING_LEVEL => 1,
+ ],
+ 10,
+ ],
+ 'sandboxing_set_to_loose_in_canonical_mode' => [
+ [
+ Option::MOBILE_REDIRECT => false,
+ Option::SANDBOXING_ENABLED => true,
+ Option::SANDBOXING_LEVEL => 1,
+ Option::THEME_SUPPORT => AMP_Theme_Support::STANDARD_MODE_SLUG,
+ ],
+ false,
+ ],
+ 'sandboxing_set_to_moderate' => [
+ [
+ Option::MOBILE_REDIRECT => false,
+ Option::SANDBOXING_ENABLED => true,
+ Option::SANDBOXING_LEVEL => 2,
+ ],
+ 10,
+ ],
+ 'sandboxing_set_to_moderate_in_canonical_mode' => [
+ [
+ Option::MOBILE_REDIRECT => false,
+ Option::SANDBOXING_ENABLED => true,
+ Option::SANDBOXING_LEVEL => 2,
+ Option::THEME_SUPPORT => AMP_Theme_Support::STANDARD_MODE_SLUG,
+ ],
+ false,
+ ],
+ 'sandboxing_set_to_strict' => [
+ [
+ Option::MOBILE_REDIRECT => false,
+ Option::SANDBOXING_ENABLED => true,
+ Option::SANDBOXING_LEVEL => 3,
+ ],
+ false,
+ ],
+ 'sandboxing_and_mobile_redirection_disabled' => [
+ [
+ Option::MOBILE_REDIRECT => false,
+ Option::SANDBOXING_ENABLED => false,
+ ],
+ false,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider get_add_mobile_alternate_link
+ *
+ * Test action which adds mobile alternative link to head if:
+ * - mobile redirection is enabled.
+ * - sandboxing level is set to Loose or Moderate.
+ *
+ * @covers ::register()
+ *
+ * @param array $options AMP options.
+ * @param bool|int $expected Expected result.
+ */
+ public function test_add_mobile_alternate_link( $options, $expected ) {
+ AMP_Options_Manager::update_options( $options );
+
+ $this->instance->register();
+
+ $this->assertSame( $expected, has_action( 'wp_head', [ $this->instance, 'add_mobile_alternative_link' ] ) );
+ }
+
/**
* Assert the service hooks were not added.
*
@@ -273,7 +365,6 @@ public function test_redirect_not_amp_endpoint_with_client_side_redirection() {
$this->assertTrue( amp_is_available() );
$this->instance->redirect();
$this->assertEquals( 10, has_action( 'wp_head', [ $this->instance, 'add_mobile_version_switcher_styles' ] ) );
- $this->assertEquals( 10, has_action( 'wp_head', [ $this->instance, 'add_mobile_alternative_link' ] ) );
$this->assertEquals( 10, has_action( 'wp_footer', [ $this->instance, 'add_mobile_version_switcher_link' ] ) );
}
@@ -360,6 +451,26 @@ public function test_redirect_on_transitional_and_available_and_server_side_on_a
$this->assertEquals( 10, has_action( 'amp_post_template_footer', [ $this->instance, 'add_mobile_version_switcher_link' ] ) );
}
+ /**
+ * @covers ::add_mobile_switcher_head_hooks()
+ * @covers ::add_mobile_switcher_footer_hooks()
+ * @covers ::add_a2a_linking_hooks()
+ */
+ public function test_add_mobile_switcher_hooks() {
+ $this->call_private_method( $this->instance, 'add_mobile_switcher_head_hooks' );
+ $this->assertEquals( 10, has_action( 'wp_head', [ $this->instance, 'add_mobile_version_switcher_styles' ] ) );
+ $this->assertEquals( 10, has_action( 'amp_post_template_head', [ $this->instance, 'add_mobile_version_switcher_styles' ] ) );
+
+ $this->call_private_method( $this->instance, 'add_mobile_switcher_footer_hooks' );
+ $this->assertEquals( 10, has_action( 'wp_footer', [ $this->instance, 'add_mobile_version_switcher_link' ] ) );
+ $this->assertEquals( 10, has_action( 'amp_post_template_footer', [ $this->instance, 'add_mobile_version_switcher_link' ] ) );
+
+ $this->call_private_method( $this->instance, 'add_a2a_linking_hooks' );
+ $this->assertEquals( 0, has_filter( 'amp_to_amp_linking_enabled', '__return_true' ) );
+ $this->assertEquals( 100, has_filter( 'amp_to_amp_linking_element_excluded', [ $this->instance, 'filter_amp_to_amp_linking_element_excluded' ] ) );
+ $this->assertEquals( 10, has_filter( 'amp_to_amp_linking_element_query_vars', [ $this->instance, 'filter_amp_to_amp_linking_element_query_vars' ] ) );
+ }
+
/** @covers ::filter_amp_to_amp_linking_element_excluded() */
public function test_filter_amp_to_amp_linking_element_excluded() {
$home_url_without_noamp = home_url( '/' );
@@ -554,10 +665,20 @@ public function test_add_noamp_mobile_query_var() {
/** @covers ::add_mobile_alternative_link() */
public function test_add_mobile_alternative_link() {
+ AMP_Options_Manager::update_option( Option::THEME_SUPPORT, AMP_Theme_Support::STANDARD_MODE_SLUG );
+ $this->go_to( '/' );
ob_start();
$this->instance->add_mobile_alternative_link();
$output = ob_get_clean();
+ $this->assertTrue( amp_is_request() );
+ $this->assertEmpty( $output );
+ AMP_Options_Manager::update_option( Option::THEME_SUPPORT, AMP_Theme_Support::TRANSITIONAL_MODE_SLUG );
+ $this->go_to( '/' );
+ ob_start();
+ $this->instance->add_mobile_alternative_link();
+ $output = ob_get_clean();
+ $this->assertFalse( amp_is_request() );
$this->assertStringStartsWith( 'assertStringNotContainsString( '
+
+
+
+