diff --git a/validator/engine/validator.js b/validator/engine/validator.js index a826215144ab..9947001de159 100644 --- a/validator/engine/validator.js +++ b/validator/engine/validator.js @@ -1763,18 +1763,12 @@ class TagStack { * @return {boolean} */ function isAtRuleValid(cssSpec, atRuleName) { - let defaultType = ''; - for (const atRuleSpec of cssSpec.atRuleSpec) { - if (atRuleSpec.name === '$DEFAULT') { - defaultType = atRuleSpec.type; - } else if (atRuleSpec.name === parse_css.stripVendorPrefix(atRuleName)) { - return atRuleSpec.type !== generated.AtRuleSpec.BlockType.PARSE_AS_ERROR; + if (atRuleSpec.name === parse_css.stripVendorPrefix(atRuleName)) { + return true; } } - - asserts.assert(defaultType !== ''); - return defaultType !== generated.AtRuleSpec.BlockType.PARSE_AS_ERROR; + return false; } /** @@ -1864,41 +1858,24 @@ class InvalidRuleVisitor extends parse_css.RuleVisitor { let CssParsingConfig; /** - * Generates a CssParsingConfig from a CssSpec. - * @param {!generated.CssSpec} cssSpec + * Generates a CssParsingConfig. * @return {!CssParsingConfig} */ -function computeCssParsingConfig(cssSpec) { +function GenCssParsingConfig() { /** @type {!Object} */ const ampAtRuleParsingSpec = Object.create(null); - for (const atRuleSpec of cssSpec.atRuleSpec) { - if (atRuleSpec.type === generated.AtRuleSpec.BlockType.PARSE_AS_ERROR || - atRuleSpec.type === generated.AtRuleSpec.BlockType.PARSE_AS_IGNORE) { - ampAtRuleParsingSpec[atRuleSpec.name] = - parse_css.BlockType.PARSE_AS_IGNORE; - } else if ( - atRuleSpec.type === generated.AtRuleSpec.BlockType.PARSE_AS_RULES) { - ampAtRuleParsingSpec[atRuleSpec.name] = - parse_css.BlockType.PARSE_AS_RULES; - } else if ( - atRuleSpec.type === - generated.AtRuleSpec.BlockType.PARSE_AS_DECLARATIONS) { - ampAtRuleParsingSpec[atRuleSpec.name] = - parse_css.BlockType.PARSE_AS_DECLARATIONS; - } else { - asserts.fail('Unrecognized atRuleSpec type: ' + atRuleSpec.type); - } - } + ampAtRuleParsingSpec['font-face'] = parse_css.BlockType.PARSE_AS_DECLARATIONS; + ampAtRuleParsingSpec['keyframes'] = parse_css.BlockType.PARSE_AS_RULES; + ampAtRuleParsingSpec['media'] = parse_css.BlockType.PARSE_AS_RULES; + ampAtRuleParsingSpec['page'] = parse_css.BlockType.PARSE_AS_DECLARATIONS; + ampAtRuleParsingSpec['supports'] = parse_css.BlockType.PARSE_AS_RULES; const config = { atRuleSpec: ampAtRuleParsingSpec, defaultSpec: parse_css.BlockType.PARSE_AS_IGNORE, }; - if (cssSpec.atRuleSpec.length > 0) { - config.defaultSpec = - /** @type {!parse_css.BlockType} */ (ampAtRuleParsingSpec['$DEFAULT']); - } return config; } +exports.GenCssParsingConfig = GenCssParsingConfig; /** * CdataMatcher maintains a constraint to check which an opening tag @@ -2098,7 +2075,7 @@ class CdataMatcher { cdata, this.getLineCol().getLine(), this.getLineCol().getCol(), cssErrors); /** @type {!CssParsingConfig} */ - const cssParsingConfig = computeCssParsingConfig(cssSpec); + const cssParsingConfig = GenCssParsingConfig(); /** @type {!parse_css.Stylesheet} */ const stylesheet = parse_css.parseAStylesheet( tokenList, cssParsingConfig.atRuleSpec, cssParsingConfig.defaultSpec, @@ -4388,6 +4365,13 @@ function validateAttrDeclaration( validationResult) { /** @type {!Array} */ const cssErrors = []; + // The line/col we are passing in here is not the actual start point in the + // text for the attribute string. It's the start point for the tag. This means + // that any line/col values for tokens are also similarly offset incorrectly. + // For error messages, this means we just use the line/col of the tag instead + // of the token so as to minimize confusion. This could be improved further. + // TODO(https://github.com/ampproject/amphtml/issues/27507): Compute attribute + // offsets for use in CSS error messages. /** @type {!Array} */ const tokenList = tokenize_css.tokenize( attrValue, context.getLineCol().getLine(), context.getLineCol().getCol(), @@ -4402,8 +4386,8 @@ function validateAttrDeclaration( // Override the first parameter with the name of this style tag. params[0] = tagSpecName; context.addError( - errorToken.code, new LineCol(errorToken.line, errorToken.col), params, - /* url */ '', validationResult); + errorToken.code, context.getLineCol(), params, /* url */ '', + validationResult); } // If there were errors parsing, exit from validating further. @@ -5769,11 +5753,6 @@ class ParsedValidatorRules { * @param {!generated.ValidationResult} validationResult */ maybeEmitCssLengthSpecErrors(context, validationResult) { - // Only emit an error if there have been inline styles used. Otherwise - // if there was to be an error it would have been caught by - // CdataMatcher::Match(). - if (context.getInlineStyleByteSize() == 0) {return;} - const bytesUsed = context.getInlineStyleByteSize() + context.getStyleAmpCustomByteSize(); @@ -6499,3 +6478,4 @@ goog.exportSymbol( goog.exportSymbol('amp.validator.categorizeError', categorizeError); goog.exportSymbol( 'amp.validator.annotateWithErrorCategories', annotateWithErrorCategories); +goog.exportSymbol('amp.validator.isSeverityWarning', isSeverityWarning); diff --git a/validator/engine/validator_test.js b/validator/engine/validator_test.js index a80515457ff6..436f33f978db 100644 --- a/validator/engine/validator_test.js +++ b/validator/engine/validator_test.js @@ -467,8 +467,7 @@ describe('Validatorvalidator.CssLength', () => { // construct stylesheets of any length that we want. const validStyleBlob = 'h1 {a: b}\n'; assertStrictEqual(10, validStyleBlob.length); - const validInlineStyleBlob = 'width:1px;'; - assertStrictEqual(10, validInlineStyleBlob.length); + const validInlineStyleBlob = ''; it('accepts 75000 bytes in author stylesheet and 0 bytes in inline style', () => { @@ -525,70 +524,300 @@ describe('Validatorvalidator.CssLength', () => { test.run(); }); - it('fails on 0 bytes in author stylesheet and 75000 bytes in inline style', + it('allows 0 bytes in author stylesheet and 75000 bytes in inline style', () => { const inlineStyle = Array(7501).join(validInlineStyleBlob); - assertStrictEqual(75000, inlineStyle.length); const test = new ValidatorTestCase('feature_tests/css_length.html'); test.inlineOutput = false; test.ampHtmlFileContents = test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') .replace('replace_inline_style', inlineStyle); + test.expectedOutput = 'PASS'; + test.run(); + }); + + it('will not accept 0 bytes in author stylesheet and 75010 bytes in ' + + 'inline style', + () => { + const inlineStyle = Array(7502).join(validInlineStyleBlob); + const test = new ValidatorTestCase('feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') + .replace('replace_inline_style', inlineStyle); + test.expectedOutputFile = null; + test.expectedOutput = + 'FAIL\nfeature_tests/css_length.html:36:6 The author stylesheet specified in tag \'style amp-custom\' and the combined inline styles is too large - document contains 75010 bytes whereas the limit is 75000 bytes. (see https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size)'; + test.run(); + }); + + it('will not accept 75000 bytes in author stylesheet and 14 bytes in ' + + 'inline style', + () => { + const stylesheet = Array(7501).join(validStyleBlob); + assertStrictEqual(75000, stylesheet.length); + const test = new ValidatorTestCase('feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents + .replace('.replace_amp_custom {}', stylesheet) + .replace('replace_inline_style', ''); + test.expectedOutput = + 'FAIL\nfeature_tests/css_length.html:7536:6 The author stylesheet specified in tag \'style amp-custom\' and the combined inline styles is too large - document contains 75014 bytes whereas the limit is 75000 bytes. (see https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size)'; + test.run(); + }); +}); + +describe('Validatorvalidator.CssLengthAmpActions', () => { + if (process.env['UPDATE_VALIDATOR_TEST'] === '1') { + return; + } + // Rather than encoding some really long author stylesheets in + // testcases, which would be difficult to read/verify that the + // testcase is valid, we modify a valid testcase + // (feature_tests/css_length.html) designed for this purpose in code. + + // We use a blob of length 10 (both bytes and chars) to make it easy to + // construct stylesheets of any length that we want. + const validStyleBlob = 'h1 {a: b}\n'; + assertStrictEqual(10, validStyleBlob.length); + const validInlineStyleBlob = ''; + + it('accepts 75000 bytes in author stylesheet and 0 bytes in inline style', + () => { + const stylesheet = Array(7501).join(validStyleBlob); + assertStrictEqual(75000, stylesheet.length); + const test = + new ValidatorTestCase('actions_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents + .replace('.replace_amp_custom {}', stylesheet) + .replace('replace_inline_style', ''); + test.expectedOutput = 'PASS'; + test.run(); + }); + + it('will not accept 75001 bytes in author stylesheet and 0 bytes in ' + + 'inline style', + () => { + const stylesheet = Array(7501).join(validStyleBlob) + ' '; + assertStrictEqual(75001, stylesheet.length); + const test = + new ValidatorTestCase('actions_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents + .replace('.replace_amp_custom {}', stylesheet) + .replace('replace_inline_style', ''); + test.expectedOutputFile = null; test.expectedOutput = 'FAIL\n' + - 'feature_tests/css_length.html:34:2 The inline style specified in ' + - 'tag \'div\' is too long - it contains 75000 bytes whereas the ' + - 'limit is 1000 bytes. (see https://amp.dev/documentation/guides' + - '-and-tutorials/learn/spec/amphtml#maximum-size)'; + 'actions_feature_tests/css_length.html:28:2 The author stylesheet ' + + 'specified in tag \'style amp-custom\' is too long - document ' + + 'contains 75001 bytes whereas the limit is 75000 bytes. ' + + '(see https://amp.dev/documentation/guides-and-tutorials/' + + 'learn/spec/amphtml#maximum-size)'; test.run(); }); - it('will not accept 0 bytes in author stylesheet and 75001 bytes in ' + - 'inline style', - () => { - const inlineStyle = Array(7501).join(validInlineStyleBlob) + ' '; - assertStrictEqual(75001, inlineStyle.length); - const test = new ValidatorTestCase('feature_tests/css_length.html'); - test.inlineOutput = false; - test.ampHtmlFileContents = - test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') - .replace('replace_inline_style', inlineStyle); - test.expectedOutputFile = null; - test.expectedOutput = 'FAIL\n' + - 'feature_tests/css_length.html:34:2 The inline style specified in ' + - 'tag \'div\' is too long - it contains 75001 bytes whereas the ' + - 'limit is 1000 bytes. (see https://amp.dev/documentation/guides' + - '-and-tutorials/learn/spec/amphtml#maximum-size)\n' + - 'feature_tests/css_length.html:36:6 The author stylesheet ' + - 'specified in tag \'style amp-custom\' and the combined inline ' + - 'styles is too large - document contains 75001 bytes whereas the ' + - 'limit is 75000 bytes. ' + - '(see https://amp.dev/documentation/guides-and-tutorials/' + - 'learn/spec/amphtml#maximum-size)'; - test.run(); - }); + it('knows utf8 and rejects file with 75002 bytes but 74999 characters ' + + 'and 0 bytes in inline style', + () => { + const stylesheet = Array(7500).join(validStyleBlob) + 'h {a: 😺}'; + assertStrictEqual(74999, stylesheet.length); // character length + const test = + new ValidatorTestCase('actions_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents + .replace('.replace_amp_custom {}', stylesheet) + .replace('replace_inline_style', ''); + test.expectedOutputFile = null; + test.expectedOutput = 'FAIL\n' + + 'actions_feature_tests/css_length.html:28:2 The author stylesheet ' + + 'specified in tag \'style amp-custom\' is too long - document ' + + 'contains 75002 bytes whereas the limit is 75000 bytes. ' + + '(see https://amp.dev/documentation/guides-and-tutorials/' + + 'learn/spec/amphtml#maximum-size)'; + test.run(); + }); + + it('allows 0 bytes in author stylesheet and 75000 bytes in inline style', + () => { + const inlineStyle = Array(7501).join(validInlineStyleBlob); + const test = + new ValidatorTestCase('actions_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') + .replace('replace_inline_style', inlineStyle); + test.expectedOutput = 'PASS'; + test.run(); + }); + + it('will not accept 0 bytes in author stylesheet and 75010 bytes in ' + + 'inline style', + () => { + const inlineStyle = Array(7502).join(validInlineStyleBlob); + const test = + new ValidatorTestCase('actions_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') + .replace('replace_inline_style', inlineStyle); + test.expectedOutputFile = null; + // TODO(gregable): This should not pass, as we have more than 75,000 + // bytes of inline style. + test.expectedOutput = 'PASS'; + test.run(); + }); it('will not accept 75000 bytes in author stylesheet and 14 bytes in ' + - 'inline style', - () => { - const stylesheet = Array(7501).join(validStyleBlob); - assertStrictEqual(75000, stylesheet.length); - const test = new ValidatorTestCase('feature_tests/css_length.html'); - test.inlineOutput = false; - test.ampHtmlFileContents = - test.ampHtmlFileContents - .replace('.replace_amp_custom {}', stylesheet) - .replace('replace_inline_style', 'display:block;'); - test.expectedOutput = 'FAIL\n' + - 'feature_tests/css_length.html:7536:6 The author stylesheet ' + - 'specified in tag \'style amp-custom\' and the combined inline ' + - 'styles is too large - document contains 75014 bytes whereas the ' + - 'limit is 75000 bytes. ' + - '(see https://amp.dev/documentation/guides-and-tutorials/' + - 'learn/spec/amphtml#maximum-size)'; - test.run(); - }); + 'inline style', + () => { + const stylesheet = Array(7501).join(validStyleBlob); + assertStrictEqual(75000, stylesheet.length); + const test = + new ValidatorTestCase('actions_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents + .replace('.replace_amp_custom {}', stylesheet) + .replace('replace_inline_style', ''); + // TODO(gregable): This should not pass, as we have more than 75,000 + // bytes of total style. + test.expectedOutput = 'PASS'; + test.run(); + }); +}); + +describe('Validatorvalidator.CssLengthAmpEmail', () => { + if (process.env['UPDATE_VALIDATOR_TEST'] === '1') { + return; + } + // Rather than encoding some really long author stylesheets in + // testcases, which would be difficult to read/verify that the + // testcase is valid, we modify a valid testcase + // (feature_tests/css_length.html) designed for this purpose in code. + + // We use a blob of length 10 (both bytes and chars) to make it easy to + // construct stylesheets of any length that we want. + const validStyleBlob = 'h1 {a: b}\n'; + assertStrictEqual(10, validStyleBlob.length); + const validInlineStyleBlob = ''; + + it('accepts 75000 bytes in author stylesheet and 0 bytes in inline style', + () => { + const stylesheet = Array(7501).join(validStyleBlob); + assertStrictEqual(75000, stylesheet.length); + const test = + new ValidatorTestCase('amp4email_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents + .replace('.replace_amp_custom {}', stylesheet) + .replace('replace_inline_style', ''); + test.expectedOutput = 'PASS'; + test.run(); + }); + + it('will not accept 75001 bytes in author stylesheet and 0 bytes in ' + + 'inline style', + () => { + const stylesheet = Array(7501).join(validStyleBlob) + ' '; + assertStrictEqual(75001, stylesheet.length); + const test = + new ValidatorTestCase('amp4email_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents + .replace('.replace_amp_custom {}', stylesheet) + .replace('replace_inline_style', ''); + test.expectedOutputFile = null; + test.expectedOutput = 'FAIL\n' + + 'amp4email_feature_tests/css_length.html:28:2 The author stylesheet ' + + 'specified in tag \'style amp-custom (AMP4EMAIL)\' is too long - document ' + + 'contains 75001 bytes whereas the limit is 75000 bytes. ' + + '(see https://amp.dev/documentation/guides-and-tutorials/' + + 'learn/spec/amphtml#maximum-size)'; + test.run(); + }); + + it('knows utf8 and rejects file with 75002 bytes but 74999 characters ' + + 'and 0 bytes in inline style', + () => { + const stylesheet = Array(7500).join(validStyleBlob) + 'h {a: 😺}'; + assertStrictEqual(74999, stylesheet.length); // character length + const test = + new ValidatorTestCase('amp4email_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents + .replace('.replace_amp_custom {}', stylesheet) + .replace('replace_inline_style', ''); + test.expectedOutputFile = null; + test.expectedOutput = 'FAIL\n' + + 'amp4email_feature_tests/css_length.html:28:2 The author stylesheet ' + + 'specified in tag \'style amp-custom (AMP4EMAIL)\' is too long - document ' + + 'contains 75002 bytes whereas the limit is 75000 bytes. ' + + '(see https://amp.dev/documentation/guides-and-tutorials/' + + 'learn/spec/amphtml#maximum-size)'; + test.run(); + }); + + it('allows 0 bytes in author stylesheet and 75000 bytes in inline style', + () => { + const inlineStyle = Array(7501).join(validInlineStyleBlob); + const test = + new ValidatorTestCase('amp4email_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') + .replace('replace_inline_style', inlineStyle); + test.expectedOutput = 'PASS'; + test.run(); + }); + + it('will not accept 0 bytes in author stylesheet and 75010 bytes in ' + + 'inline style', + () => { + const inlineStyle = Array(7502).join(validInlineStyleBlob); + const test = + new ValidatorTestCase('amp4email_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') + .replace('replace_inline_style', inlineStyle); + test.expectedOutputFile = null; + // TODO(gregable): This should not pass, as we have more than 75,000 + // bytes of inline style. + test.expectedOutput = 'PASS'; + test.run(); + }); + + it('will not accept 75000 bytes in author stylesheet and 14 bytes in ' + + 'inline style', + () => { + const stylesheet = Array(7501).join(validStyleBlob); + assertStrictEqual(75000, stylesheet.length); + const test = + new ValidatorTestCase('amp4email_feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents + .replace('.replace_amp_custom {}', stylesheet) + .replace('replace_inline_style', ''); + // TODO(gregable): This should not pass, as we have more than 75,000 + // bytes of total style. + test.expectedOutput = 'PASS'; + test.run(); + }); }); +// Similar to ValidatorTransformedAmpvalidator.CssLengthWithUrls, but for +// non-transformed AMP, to show that behavior should differ from non-transformed +// in that URLs are not counted towards the URL length, unless they are data: +// urls. describe('Validatorvalidator.CssLengthWithUrls', () => { if (process.env['UPDATE_VALIDATOR_TEST'] === '1') { return; @@ -680,6 +909,42 @@ describe('Validatorvalidator.CssLengthWithUrls', () => { 'learn/spec/amphtml#maximum-size)'; test.run(); }); + + it(`will reject exactly 0 bytes in stylesheet and 75009 bytes inline style, including a relative URL of 19 bytes`, + () => { + // This string is 33 bytes of inline style inside a B tag. + const inline33Bytes = + ''; + // 2273 x 33 = 75009 + const inlineStyle = Array(2274).join(inline33Bytes); + const test = new ValidatorTestCase('feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') + .replace('replace_inline_style', inlineStyle); + test.expectedOutputFile = null; + test.expectedOutput = 'FAIL\n' + + 'feature_tests/css_length.html:36:6 The author stylesheet specified in tag \'style amp-custom\' and the combined inline styles is too large - document contains 75009 bytes whereas the limit is 75000 bytes. (see https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size)'; + test.run(); + }); + + it(`will reject exactly 0 bytes in stylesheet and 75009 bytes inline style, including a data URL of 19 bytes`, + () => { + // This string is 33 bytes of inline style inside a B tag. + const inline33Bytes = + ''; + // 2273 x 33 = 75009 + const inlineStyle = Array(2274).join(inline33Bytes); + const test = new ValidatorTestCase('feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') + .replace('replace_inline_style', inlineStyle); + test.expectedOutputFile = null; + test.expectedOutput = 'FAIL\n' + + 'feature_tests/css_length.html:36:6 The author stylesheet specified in tag \'style amp-custom\' and the combined inline styles is too large - document contains 75009 bytes whereas the limit is 75000 bytes. (see https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size)'; + test.run(); + }); }); describe('ValidatorTransformedAmpvalidator.CssLengthWithUrls', () => { @@ -768,6 +1033,42 @@ describe('ValidatorTransformedAmpvalidator.CssLengthWithUrls', () => { 'learn/spec/amphtml#maximum-size)'; test.run(); }); + + it(`will reject exactly 0 bytes in stylesheet and 75009 bytes inline style, including a relative URL of 19 bytes`, + () => { + // This string is 33 bytes of inline style inside a B tag. + const inline33Bytes = + ''; + // 2273 x 33 = 75009 + const inlineStyle = Array(2274).join(inline33Bytes); + const test = new ValidatorTestCase('feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') + .replace('replace_inline_style', inlineStyle); + test.expectedOutputFile = null; + test.expectedOutput = 'FAIL\n' + + 'feature_tests/css_length.html:36:6 The author stylesheet specified in tag \'style amp-custom\' and the combined inline styles is too large - document contains 75009 bytes whereas the limit is 75000 bytes. (see https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size)'; + test.run(); + }); + + it(`will reject exactly 0 bytes in stylesheet and 75009 bytes inline style, including a data URL of 19 bytes`, + () => { + // This string is 33 bytes of inline style inside a B tag. + const inline33Bytes = + ''; + // 2273 x 33 = 75009 + const inlineStyle = Array(2274).join(inline33Bytes); + const test = new ValidatorTestCase('feature_tests/css_length.html'); + test.inlineOutput = false; + test.ampHtmlFileContents = + test.ampHtmlFileContents.replace('.replace_amp_custom {}', '') + .replace('replace_inline_style', inlineStyle); + test.expectedOutputFile = null; + test.expectedOutput = 'FAIL\n' + + 'feature_tests/css_length.html:36:6 The author stylesheet specified in tag \'style amp-custom\' and the combined inline styles is too large - document contains 75009 bytes whereas the limit is 75000 bytes. (see https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#maximum-size)'; + test.run(); + }); }); describe('validator.CssLength', () => { @@ -1523,18 +1824,19 @@ describe('ValidatorRulesMakeSense', () => { // css_spec if (tagSpec.cdata.cssSpec !== null) { usefulCdataSpec = true; - let hasDefaultAtRuleSpec = false; const atRuleSpecNameIsUnique = {}; const atRuleSpecRegex = new RegExp('[a-z-_]*'); + const parsingSpec = validator.GenCssParsingConfig().atRuleSpec; for (const atRuleSpec of tagSpec.cdata.cssSpec.atRuleSpec) { - if (atRuleSpec.name === '$DEFAULT') { - hasDefaultAtRuleSpec = true; - } else { - // Must be a lower case alphabetic name. - it('at_rule_spec must be lower case alphabetic', () => { - expect(atRuleSpecRegex.test(atRuleSpec.name)).toBe(true); - }); - } + // Must be a lower case alphabetic name. + it('at_rule_spec must be lower case alphabetic', () => { + expect(atRuleSpecRegex.test(atRuleSpec.name)).toBe(true); + }); + it('at_rule_spec must have matching css parsing spec', () => { + // If this fails, you probably need to update the mapping in + // GenCssParsingConfig to add support for your new at rule. + expect(parsingSpec[atRuleSpec.name]).toBeDefined(); + }); if (atRuleSpec.mediaQuerySpec !== null) { it('only media atrule contains mediaQuerySpec', () => { expect(atRuleSpec.name === 'media'); @@ -1545,13 +1847,7 @@ describe('ValidatorRulesMakeSense', () => { .toBe(false); atRuleSpecNameIsUnique[atRuleSpec.name] = 0; }); - it('at_rule_spec must have type defined', () => { - expect(atRuleSpec.type).toBeDefined(); - }); } - it('at_rule_spec has default defined', () => { - expect(hasDefaultAtRuleSpec).toBe(true); - }); it('at_rule_spec has image_url_spec defined', () => { expect(tagSpec.cdata.cssSpec.imageUrlSpec).toBeDefined(); }); diff --git a/validator/testdata/actions_feature_tests/css_length.html b/validator/testdata/actions_feature_tests/css_length.html new file mode 100644 index 000000000000..f0c0b9e8e5a0 --- /dev/null +++ b/validator/testdata/actions_feature_tests/css_length.html @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + Hello, world. + replace_inline_style + + diff --git a/validator/testdata/actions_feature_tests/css_length.out b/validator/testdata/actions_feature_tests/css_length.out new file mode 100644 index 000000000000..f1a6e3257817 --- /dev/null +++ b/validator/testdata/actions_feature_tests/css_length.out @@ -0,0 +1,37 @@ +PASS +| +| +| +| +| +| +| +| +| +| +| +| +| +| Hello, world. +| replace_inline_style +| +| diff --git a/validator/testdata/amp4email_feature_tests/css_length.html b/validator/testdata/amp4email_feature_tests/css_length.html new file mode 100644 index 000000000000..83f06fafb1c6 --- /dev/null +++ b/validator/testdata/amp4email_feature_tests/css_length.html @@ -0,0 +1,34 @@ + + + + + + + + + + + + Hello, world. + replace_inline_style + + diff --git a/validator/testdata/amp4email_feature_tests/css_length.out b/validator/testdata/amp4email_feature_tests/css_length.out new file mode 100644 index 000000000000..43782637e713 --- /dev/null +++ b/validator/testdata/amp4email_feature_tests/css_length.out @@ -0,0 +1,35 @@ +PASS +| +| +| +| +| +| +| +| +| +| +| +| Hello, world. +| replace_inline_style +| +| diff --git a/validator/testdata/feature_tests/css_length.html b/validator/testdata/feature_tests/css_length.html index d0af3132b4da..bc17524a5189 100644 --- a/validator/testdata/feature_tests/css_length.html +++ b/validator/testdata/feature_tests/css_length.html @@ -31,6 +31,6 @@ Hello, world. -
+ replace_inline_style diff --git a/validator/testdata/feature_tests/css_length.out b/validator/testdata/feature_tests/css_length.out index f00503e51e0a..64e19ee0b61f 100644 --- a/validator/testdata/feature_tests/css_length.out +++ b/validator/testdata/feature_tests/css_length.out @@ -1,4 +1,4 @@ -FAIL +PASS | + diff --git a/validator/testdata/feature_tests/incorrect_custom_style.out b/validator/testdata/feature_tests/incorrect_custom_style.out index ef73d54e8a1a..22c89975d604 100644 --- a/validator/testdata/feature_tests/incorrect_custom_style.out +++ b/validator/testdata/feature_tests/incorrect_custom_style.out @@ -54,7 +54,6 @@ feature_tests/incorrect_custom_style.html:37:24 CSS syntax error in tag 'style a feature_tests/incorrect_custom_style.html:41:28 CSS syntax error in tag 'style amp-custom' - invalid url protocol 'invalid:'. (see https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#stylesheets) | foo { background-image: url('https://valid.com/1.jpg') } | foo { background-image: url('http://valid.com/1.jpg') } -| foo { background-image: url('absolute://disallow.com/soon.jpg') } | foo { background-image: url('://valid.jpg') } | foo { background-image: url('valid.jpg') } | @@ -62,12 +61,9 @@ feature_tests/incorrect_custom_style.html:41:28 CSS syntax error in tag 'style a | @font-face { src: url(''); } /* allowed for now */ | @font-face { src: url('invalid://invalid.com/1.ttf') } >> ^~~~~~~~~ -feature_tests/incorrect_custom_style.html:50:22 CSS syntax error in tag 'style amp-custom' - invalid url protocol 'invalid:'. (see https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#stylesheets) +feature_tests/incorrect_custom_style.html:49:22 CSS syntax error in tag 'style amp-custom' - invalid url protocol 'invalid:'. (see https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#stylesheets) | @font-face { src: url('https://valid.com/1.ttf') } | @font-face { src: url('http://valid.com/1.ttf') } -| @font-face { src: url('absolute://invalid.com/1.ttf') } ->> ^~~~~~~~~ -feature_tests/incorrect_custom_style.html:53:22 CSS syntax error in tag 'style amp-custom' - invalid url protocol 'absolute:'. (see https://amp.dev/documentation/guides-and-tutorials/learn/spec/amphtml#stylesheets) | @font-face { src: url('://valid.ttf') } | @font-face { src: url('valid.ttf') } | @@ -75,5 +71,9 @@ feature_tests/incorrect_custom_style.html:53:22 CSS syntax error in tag 'style a | | | +| +| +>> ^~~~~~~~~ +feature_tests/incorrect_custom_style.html:60:2 The attribute 'style' in tag 'b' is set to the invalid value 'color: red !important'. | | diff --git a/validator/testdata/feature_tests/inline_style.html b/validator/testdata/feature_tests/inline_style.html index 7d9b466a910e..5853e2a595ca 100644 --- a/validator/testdata/feature_tests/inline_style.html +++ b/validator/testdata/feature_tests/inline_style.html @@ -38,5 +38,7 @@ + + diff --git a/validator/testdata/feature_tests/inline_style.out b/validator/testdata/feature_tests/inline_style.out index 956030b9713a..8a79f0932acc 100644 --- a/validator/testdata/feature_tests/inline_style.out +++ b/validator/testdata/feature_tests/inline_style.out @@ -49,5 +49,7 @@ feature_tests/inline_style.html:38:2 The property 'lemur' in attribute 'style' i | >> ^~~~~~~~~ feature_tests/inline_style.html:40:2 The attribute '[style]' may not appear in tag 'amp-img'. (see https://amp.dev/documentation/components/amp-img) +| +| | | diff --git a/validator/testdata/transformed_feature_tests/css_length.html b/validator/testdata/transformed_feature_tests/css_length.html index edd93c75edf3..5896b58892ef 100644 --- a/validator/testdata/transformed_feature_tests/css_length.html +++ b/validator/testdata/transformed_feature_tests/css_length.html @@ -31,6 +31,6 @@ Hello, world. -
+ replace_inline_style diff --git a/validator/testdata/transformed_feature_tests/css_length.out b/validator/testdata/transformed_feature_tests/css_length.out index a0cedf8d3c89..0317a53427b5 100644 --- a/validator/testdata/transformed_feature_tests/css_length.out +++ b/validator/testdata/transformed_feature_tests/css_length.out @@ -1,4 +1,4 @@ -FAIL +PASS |