diff --git a/bin/amphtml-update.py b/bin/amphtml-update.py index 4c468fe9932..bf6071200c4 100644 --- a/bin/amphtml-update.py +++ b/bin/amphtml-update.py @@ -100,7 +100,7 @@ def GeneratePHP(out_dir): logging.info('entering ...') assert re.match(r'^[a-zA-Z_\-0-9]+$', out_dir), 'bad out_dir: %s' % out_dir - allowed_tags, attr_lists, versions = ParseRules(out_dir) + allowed_tags, attr_lists, reference_points, versions = ParseRules(out_dir) #Generate the output out = [] @@ -109,6 +109,7 @@ def GeneratePHP(out_dir): GenerateAllowedTagsPHP(out, allowed_tags) GenerateLayoutAttributesPHP(out, attr_lists) GenerateGlobalAttributesPHP(out, attr_lists) + GenerateReferencePointsPHP(out, reference_points) GenerateFooterPHP(out) # join out array into a single string and remove unneeded whitespace @@ -188,6 +189,15 @@ def GenerateGlobalAttributesPHP(out, attr_lists): out.append('') logging.info('... done') +def GenerateReferencePointsPHP(out, reference_points): + logging.info('entering ...') + + # Output the reference points. + out.append('') + out.append('\tprivate static $reference_points = %s;' % Phpize( reference_points, 1 ).lstrip() ) + out.append('') + logging.info('... done') + def GenerateFooterPHP(out): logging.info('entering ...') @@ -219,6 +229,20 @@ def GenerateFooterPHP(out): return null; } + /** + * Get reference point spec. + * + * @since 1.0 + * @param string $tag_spec_name Tag spec name. + * @return array|null Reference point spec, or null if does not exist. + */ + public static function get_reference_point_spec( $tag_spec_name ) { + if ( isset( self::$reference_points[ $tag_spec_name ] ) ) { + return self::$reference_points[ $tag_spec_name ]; + } + return null; + } + /** * Get list of globally-allowed attributes. * @@ -258,6 +282,7 @@ def ParseRules(out_dir): allowed_tags = {} attr_lists = {} + reference_points = {} versions = {} specfile='%s/validator.protoascii' % out_dir @@ -301,14 +326,15 @@ def ParseRules(out_dir): if tag_spec.HasField('mandatory_parent') and tag_spec.mandatory_parent in mandatory_parent_blacklist and tag_spec.tag_name != 'HTML': continue - # Ignore the special $REFERENCE_POINT tag - if '$REFERENCE_POINT' == tag_spec.tag_name: - continue - # Ignore deprecated tags if tag_spec.HasField('deprecation'): continue + # Handle the special $REFERENCE_POINT tag + if '$REFERENCE_POINT' == tag_spec.tag_name: + reference_points[ tag_spec.spec_name ] = GetTagSpec(tag_spec, attr_lists) + continue + # If we made it here, then start adding the tag_spec if tag_spec.tag_name.lower() not in allowed_tags: tag_list = [] @@ -322,7 +348,7 @@ def ParseRules(out_dir): allowed_tags[UnicodeEscape(tag_spec.tag_name).lower()] = tag_list logging.info('... done') - return allowed_tags, attr_lists, versions + return allowed_tags, attr_lists, reference_points, versions def GetTagSpec(tag_spec, attr_lists): @@ -400,6 +426,16 @@ def GetTagRules(tag_spec): requires_extension_list.append(requires_extension) tag_rules['requires_extension'] = requires_extension_list + if hasattr(tag_spec, 'reference_points') and len( tag_spec.reference_points ) != 0: + tag_reference_points = {} + for reference_point_spec in tag_spec.reference_points: + tag_reference_points[ reference_point_spec.tag_spec_name ] = { + "mandatory": reference_point_spec.mandatory, + "unique": reference_point_spec.unique + } + if len( tag_reference_points ) > 0: + tag_rules['reference_points'] = tag_reference_points + if hasattr(tag_spec, 'also_requires_tag_warning') and len( tag_spec.also_requires_tag_warning ) != 0: also_requires_tag_warning_list = [] for also_requires_tag_warning in tag_spec.also_requires_tag_warning: diff --git a/includes/sanitizers/class-amp-allowed-tags-generated.php b/includes/sanitizers/class-amp-allowed-tags-generated.php index c12d90067c7..5fce609f9d3 100644 --- a/includes/sanitizers/class-amp-allowed-tags-generated.php +++ b/includes/sanitizers/class-amp-allowed-tags-generated.php @@ -13,7 +13,7 @@ */ class AMP_Allowed_Tags_Generated { - private static $spec_file_revision = 712; + private static $spec_file_revision = 720; private static $minimum_validator_revision_required = 348; private static $allowed_tags = array( @@ -1133,6 +1133,16 @@ class AMP_Allowed_Tags_Generated { 4, ), ), + 'reference_points' => array( + 'AMP-CAROUSEL lightbox [child]' => array( + 'mandatory' => false, + 'unique' => false, + ), + 'AMP-CAROUSEL lightbox [lightbox-exclude]' => array( + 'mandatory' => false, + 'unique' => false, + ), + ), 'requires_extension' => array( 'amp-carousel', 'amp-lightbox-gallery', @@ -1284,6 +1294,7 @@ class AMP_Allowed_Tags_Generated { ), 'when-ended' => array( 'value_casei' => array( + 'continue', 'stop', ), ), @@ -2340,6 +2351,33 @@ class AMP_Allowed_Tags_Generated { ), ), ), + 'amp-image-slider' => array( + array( + 'attr_spec_list' => array( + 'disable-hint-reappear' => array(), + 'media' => array(), + 'noloading' => array( + 'value' => array( + '', + ), + ), + ), + 'tag_spec' => array( + 'amp_layout' => array( + 'supported_layouts' => array( + 2, + 9, + 1, + 4, + ), + ), + 'requires_extension' => array( + 'amp-image-slider', + ), + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-image-slider', + ), + ), + ), 'amp-img' => array( array( 'attr_spec_list' => array( @@ -2598,6 +2636,7 @@ class AMP_Allowed_Tags_Generated { 9, 1, 4, + 5, ), ), 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-layout', @@ -2607,6 +2646,7 @@ class AMP_Allowed_Tags_Generated { 'amp-lightbox' => array( array( 'attr_spec_list' => array( + '[open]' => array(), 'animate-in' => array( 'value_casei' => array( 'fade-in', @@ -2724,6 +2764,20 @@ class AMP_Allowed_Tags_Generated { 3, ), ), + 'reference_points' => array( + 'AMP-LIVE-LIST [items]' => array( + 'mandatory' => true, + 'unique' => true, + ), + 'AMP-LIVE-LIST [pagination]' => array( + 'mandatory' => false, + 'unique' => true, + ), + 'AMP-LIVE-LIST [update]' => array( + 'mandatory' => true, + 'unique' => true, + ), + ), 'requires_extension' => array( 'amp-live-list', ), @@ -2760,6 +2814,16 @@ class AMP_Allowed_Tags_Generated { array( 'attr_spec_list' => array(), 'tag_spec' => array( + 'reference_points' => array( + 'AMP-NEXT-PAGE > [separator]' => array( + 'mandatory' => false, + 'unique' => true, + ), + 'amp-next-page extension .json configuration' => array( + 'mandatory' => true, + 'unique' => true, + ), + ), 'requires_extension' => array( 'amp-next-page', ), @@ -2782,6 +2846,12 @@ class AMP_Allowed_Tags_Generated { ), ), 'tag_spec' => array( + 'reference_points' => array( + 'AMP-NEXT-PAGE > [separator]' => array( + 'mandatory' => false, + 'unique' => true, + ), + ), 'requires_extension' => array( 'amp-next-page', ), @@ -2951,6 +3021,11 @@ class AMP_Allowed_Tags_Generated { 'amp-pan-zoom' => array( array( 'attr_spec_list' => array( + 'disable-double-tap' => array( + 'value' => array( + '', + ), + ), 'initial-scale' => array( 'value_regex' => '[0-9]+(\\.[0-9]+)?', ), @@ -3292,6 +3367,16 @@ class AMP_Allowed_Tags_Generated { 'disallowed_ancestor' => array( 'amp-selector', ), + 'reference_points' => array( + 'AMP-SELECTOR child' => array( + 'mandatory' => false, + 'unique' => false, + ), + 'AMP-SELECTOR option' => array( + 'mandatory' => false, + 'unique' => false, + ), + ), 'requires_extension' => array( 'amp-selector', ), @@ -3661,6 +3746,12 @@ class AMP_Allowed_Tags_Generated { 'attr_spec_list' => array(), 'tag_spec' => array( 'mandatory_ancestor' => 'amp-story-page', + 'reference_points' => array( + 'AMP-STORY-CTA-LAYER animate-in' => array( + 'mandatory' => false, + 'unique' => false, + ), + ), ), ), ), @@ -3679,6 +3770,16 @@ class AMP_Allowed_Tags_Generated { ), 'tag_spec' => array( 'mandatory_ancestor' => 'amp-story-page', + 'reference_points' => array( + 'AMP-STORY-GRID-LAYER animate-in' => array( + 'mandatory' => false, + 'unique' => false, + ), + 'AMP-STORY-GRID-LAYER default' => array( + 'mandatory' => false, + 'unique' => false, + ), + ), ), ), ), @@ -4097,6 +4198,40 @@ class AMP_Allowed_Tags_Generated { ), ), ), + 'amp-viqeo-player' => array( + array( + 'attr_spec_list' => array( + 'autoplay' => array(), + 'data-profileid' => array( + 'mandatory' => true, + 'value_regex' => '[0-9a-f]*', + ), + 'data-videoid' => array( + 'mandatory' => true, + ), + 'media' => array(), + 'noloading' => array( + 'value' => array( + '', + ), + ), + ), + 'tag_spec' => array( + 'amp_layout' => array( + 'supported_layouts' => array( + 6, + 2, + 3, + 7, + 4, + ), + ), + 'requires_extension' => array( + 'amp-viqeo-player', + ), + ), + ), + ), 'amp-vk' => array( array( 'attr_spec_list' => array( @@ -4961,6 +5096,30 @@ class AMP_Allowed_Tags_Generated { 'spec_name' => 'FORM DIV [submit-error][template]', ), ), + array( + 'attr_spec_list' => array( + 'first' => array( + 'mandatory' => true, + ), + ), + 'tag_spec' => array( + 'mandatory_parent' => 'amp-image-slider', + 'spec_name' => 'AMP-IMAGE-SLIDER > DIV [first]', + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-image-slider', + ), + ), + array( + 'attr_spec_list' => array( + 'second' => array( + 'mandatory' => true, + ), + ), + 'tag_spec' => array( + 'mandatory_parent' => 'amp-image-slider', + 'spec_name' => 'AMP-IMAGE-SLIDER > DIV [second]', + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-image-slider', + ), + ), ), 'dl' => array( array( @@ -10082,6 +10241,31 @@ class AMP_Allowed_Tags_Generated { ), ), ), + array( + 'attr_spec_list' => array( + 'async' => array( + 'mandatory' => true, + 'value' => array( + '', + ), + ), + 'nonce' => array(), + 'type' => array( + 'value_casei' => array( + 'text/javascript', + ), + ), + ), + 'tag_spec' => array( + 'extension_spec' => array( + 'name' => 'amp-image-slider', + 'version' => array( + '0.1', + 'latest', + ), + ), + ), + ), array( 'attr_spec_list' => array( 'async' => array( @@ -11247,6 +11431,31 @@ class AMP_Allowed_Tags_Generated { ), ), ), + array( + 'attr_spec_list' => array( + 'async' => array( + 'mandatory' => true, + 'value' => array( + '', + ), + ), + 'nonce' => array(), + 'type' => array( + 'value_casei' => array( + 'text/javascript', + ), + ), + ), + 'tag_spec' => array( + 'extension_spec' => array( + 'name' => 'amp-viqeo-player', + 'version' => array( + '0.1', + 'latest', + ), + ), + ), + ), array( 'attr_spec_list' => array( 'async' => array( @@ -11916,6 +12125,9 @@ class AMP_Allowed_Tags_Generated { 'stroke-miterlimit' => array(), 'stroke-opacity' => array(), 'stroke-width' => array(), + 'style' => array( + 'blacklisted_value_regex' => '!important', + ), 'systemlanguage' => array(), 'text-anchor' => array(), 'text-decoration' => array(), @@ -12230,6 +12442,16 @@ class AMP_Allowed_Tags_Generated { ), 'tag_spec' => array( 'mandatory_parent' => 'amp-story-auto-ads', + 'reference_points' => array( + 'AMP-STORY-GRID-LAYER animate-in' => array( + 'mandatory' => false, + 'unique' => false, + ), + 'AMP-STORY-GRID-LAYER default' => array( + 'mandatory' => false, + 'unique' => false, + ), + ), 'requires_extension' => array( 'amp-mustache', ), @@ -13449,6 +13671,305 @@ class AMP_Allowed_Tags_Generated { ); + private static $reference_points = array( + 'AMP-CAROUSEL lightbox [child]' => array( + 'attr_spec_list' => array( + 'lightbox-thumbnail-id' => array( + 'value_regex_casei' => '^[a-z][a-z\\d_-]*', + ), + ), + 'tag_spec' => array( + 'spec_name' => 'AMP-CAROUSEL lightbox [child]', + ), + ), + 'AMP-CAROUSEL lightbox [lightbox-exclude]' => array( + 'attr_spec_list' => array( + 'lightbox-exclude' => array( + 'mandatory' => true, + ), + ), + 'tag_spec' => array( + 'spec_name' => 'AMP-CAROUSEL lightbox [lightbox-exclude]', + ), + ), + 'AMP-LIVE-LIST [items]' => array( + 'attr_spec_list' => array( + 'items' => array( + 'mandatory' => true, + ), + ), + 'tag_spec' => array( + 'reference_points' => array( + 'AMP-LIVE-LIST [items] item' => array( + 'mandatory' => false, + 'unique' => false, + ), + ), + 'spec_name' => 'AMP-LIVE-LIST [items]', + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-live-list#items', + ), + ), + 'AMP-LIVE-LIST [items] item' => array( + 'attr_spec_list' => array( + 'data-sort-time' => array( + 'mandatory' => true, + ), + 'data-tombstone' => array(), + 'data-update-time' => array(), + 'id' => array( + 'mandatory' => true, + ), + ), + 'tag_spec' => array( + 'spec_name' => 'AMP-LIVE-LIST [items] item', + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-live-list#items', + ), + ), + 'AMP-LIVE-LIST [pagination]' => array( + 'attr_spec_list' => array( + 'pagination' => array( + 'mandatory' => true, + ), + ), + 'tag_spec' => array( + 'spec_name' => 'AMP-LIVE-LIST [pagination]', + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-live-list#pagination', + ), + ), + 'AMP-LIVE-LIST [update]' => array( + 'attr_spec_list' => array( + 'update' => array( + 'mandatory' => true, + ), + ), + 'tag_spec' => array( + 'spec_name' => 'AMP-LIVE-LIST [update]', + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-live-list#update', + ), + ), + 'AMP-NEXT-PAGE > [separator]' => array( + 'attr_spec_list' => array( + 'separator' => array( + 'mandatory' => true, + ), + ), + 'tag_spec' => array( + 'mandatory_parent' => 'amp-next-page', + 'spec_name' => 'AMP-NEXT-PAGE > [separator]', + ), + ), + 'AMP-SELECTOR child' => array( + 'attr_spec_list' => array(), + 'tag_spec' => array( + 'reference_points' => array( + 'AMP-SELECTOR child' => array( + 'mandatory' => false, + 'unique' => false, + ), + 'AMP-SELECTOR option' => array( + 'mandatory' => false, + 'unique' => false, + ), + ), + 'spec_name' => 'AMP-SELECTOR child', + ), + ), + 'AMP-SELECTOR option' => array( + 'attr_spec_list' => array( + 'disabled' => array( + 'value' => array( + '', + ), + ), + 'option' => array( + 'mandatory' => true, + ), + 'selected' => array( + 'value' => array( + '', + ), + ), + ), + 'tag_spec' => array( + 'spec_name' => 'AMP-SELECTOR option', + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-selector', + ), + ), + 'AMP-STORY-CTA-LAYER animate-in' => array( + 'attr_spec_list' => array( + 'animate-in' => array( + 'value' => array( + 'drop', + 'fade-in', + 'fly-in-bottom', + 'fly-in-left', + 'fly-in-right', + 'fly-in-top', + 'pan-down', + 'pan-left', + 'pan-right', + 'pan-up', + 'pulse', + 'rotate-in-left', + 'rotate-in-right', + 'twirl-in', + 'whoosh-in-left', + 'whoosh-in-right', + 'zoom-in', + 'zoom-out', + ), + ), + 'animate-in-after' => array(), + 'animate-in-delay' => array(), + 'animate-in-duration' => array(), + ), + 'tag_spec' => array( + 'reference_points' => array( + 'AMP-STORY-CTA-LAYER animate-in' => array( + 'mandatory' => false, + 'unique' => false, + ), + ), + 'spec_name' => 'AMP-STORY-CTA-LAYER animate-in', + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-story', + ), + ), + 'AMP-STORY-GRID-LAYER animate-in' => array( + 'attr_spec_list' => array( + 'animate-in' => array( + 'value' => array( + 'drop', + 'fade-in', + 'fly-in-bottom', + 'fly-in-left', + 'fly-in-right', + 'fly-in-top', + 'pan-down', + 'pan-left', + 'pan-right', + 'pan-up', + 'pulse', + 'rotate-in-left', + 'rotate-in-right', + 'twirl-in', + 'whoosh-in-left', + 'whoosh-in-right', + 'zoom-in', + 'zoom-out', + ), + ), + 'animate-in-after' => array(), + 'animate-in-delay' => array(), + 'animate-in-duration' => array(), + ), + 'tag_spec' => array( + 'reference_points' => array( + 'AMP-STORY-GRID-LAYER animate-in' => array( + 'mandatory' => false, + 'unique' => false, + ), + ), + 'spec_name' => 'AMP-STORY-GRID-LAYER animate-in', + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-story', + ), + ), + 'AMP-STORY-GRID-LAYER default' => array( + 'attr_spec_list' => array( + 'align-content' => array( + 'value' => array( + 'center', + 'end', + 'space-around', + 'space-between', + 'space-evenly', + 'start', + 'stretch', + ), + ), + 'align-items' => array( + 'value' => array( + 'center', + 'end', + 'start', + 'stretch', + ), + ), + 'align-self' => array( + 'value' => array( + 'center', + 'end', + 'start', + 'stretch', + ), + ), + 'animate-in' => array( + 'value' => array( + 'drop', + 'fade-in', + 'fly-in-bottom', + 'fly-in-left', + 'fly-in-right', + 'fly-in-top', + 'pan-down', + 'pan-left', + 'pan-right', + 'pan-up', + 'pulse', + 'rotate-in-left', + 'rotate-in-right', + 'twirl-in', + 'whoosh-in-left', + 'whoosh-in-right', + 'zoom-in', + 'zoom-out', + ), + ), + 'animate-in-after' => array(), + 'animate-in-delay' => array(), + 'animate-in-duration' => array(), + 'grid-area' => array(), + 'justify-content' => array( + 'value' => array( + 'center', + 'end', + 'space-around', + 'space-between', + 'space-evenly', + 'start', + 'stretch', + ), + ), + 'justify-items' => array( + 'value' => array( + 'center', + 'end', + 'start', + 'stretch', + ), + ), + 'justify-self' => array( + 'value' => array( + 'center', + 'end', + 'start', + 'stretch', + ), + ), + ), + 'tag_spec' => array( + 'reference_points' => array( + 'AMP-STORY-GRID-LAYER animate-in' => array( + 'mandatory' => false, + 'unique' => false, + ), + ), + 'spec_name' => 'AMP-STORY-GRID-LAYER default', + 'spec_url' => 'https://www.ampproject.org/docs/reference/components/amp-story', + ), + ), + ); + + /** * Get allowed tags. * @@ -13475,6 +13996,20 @@ public static function get_allowed_tag( $node_name ) { return null; } + /** + * Get reference point spec. + * + * @since 1.0 + * @param string $tag_spec_name Tag spec name. + * @return array|null Reference point spec, or null if does not exist. + */ + public static function get_reference_point_spec( $tag_spec_name ) { + if ( isset( self::$reference_points[ $tag_spec_name ] ) ) { + return self::$reference_points[ $tag_spec_name ]; + } + return null; + } + /** * Get list of globally-allowed attributes. * diff --git a/includes/sanitizers/class-amp-rule-spec.php b/includes/sanitizers/class-amp-rule-spec.php index b3b61181f21..a09fb94eb5b 100644 --- a/includes/sanitizers/class-amp-rule-spec.php +++ b/includes/sanitizers/class-amp-rule-spec.php @@ -70,39 +70,6 @@ abstract class AMP_Rule_Spec { 9 => 'intrinsic', ); - /** - * If a node type listed here is invalid, it and it's subtree will be - * removed if it is invalid. This is mainly because any children will be - * non-functional without this parent. - * - * If a tag is not listed here, it will be replaced by its children if it - * is invalid. - * - * @todo There are other nodes that should probably be listed here as well. - * - * @var array - */ - public static $node_types_to_remove_if_invalid = array( - 'form', - 'input', - 'link', - 'meta', - 'style', - // Include 'script' here? - ); - - /** - * It is mentioned in the documentation in several places that data-* - * is generally allowed, but there is no specific rule for it in the - * protoascii file, so we include it here. - * - * @var array - */ - public static $whitelisted_attr_regex = array( - '@^data-[a-zA-Z][\\w:.-]*$@uis', - '(update|item|pagination|option|selected|disabled)', // Allowed for live reference points. - ); - /** * List of boolean attributes. * diff --git a/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php b/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php index b52e7dfcfb9..ec300c31675 100644 --- a/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php +++ b/includes/sanitizers/class-amp-tag-and-attribute-sanitizer.php @@ -240,7 +240,7 @@ private function process_alternate_names( $attr_spec_list ) { } /** - * Sanitize the