diff --git a/Source/WebCore/css/CSSProperties.json b/Source/WebCore/css/CSSProperties.json index adb43319141c..c101feaa3eab 100644 --- a/Source/WebCore/css/CSSProperties.json +++ b/Source/WebCore/css/CSSProperties.json @@ -112,10 +112,6 @@ " (https://drafts.csswg.org/css-logical-1/#mapping-logic).", " Examples: \"top-left\", \"block-start\", \"horizontal\".", "", - "* descriptor-only:", - "Indicates that this CSS property is descriptor only (populates.", - "CSSProperty::isDescriptorOnly().", - "", "* font-property:", "Indicates that this CSS property is font-related. It must have corresponding", "methods on the FontDescription class.", @@ -417,7 +413,7 @@ "codegen-properties": { "custom": "All", "high-priority": true, - "custom-parser": true + "parser-function": "consumeFontFamily" }, "specification": { "category": "css-fonts", @@ -461,9 +457,9 @@ "custom": "All", "font-property": true, "high-priority": true, - "custom-parser": true, "parser-requires-context-mode": true, - "parser-requires-value-pool": true + "parser-requires-value-pool": true, + "parser-function": "consumeFontStyle" }, "specification": { "category": "css-fonts", @@ -477,7 +473,7 @@ "font-property": true, "high-priority": true, "converter": "FontWeight", - "custom-parser": true + "parser-function": "consumeFontWeight" }, "specification": { "category": "css-fonts", @@ -502,8 +498,8 @@ "font-property": true, "high-priority": true, "converter": "FontStretch", - "custom-parser": true, - "parser-requires-value-pool": true + "parser-requires-value-pool": true, + "parser-function": "consumeFontStretch" }, "specification": { "category": "css-fonts", @@ -547,7 +543,7 @@ "custom": "Initial|Inherit", "font-property": true, "high-priority": true, - "custom-parser": true, + "parser-function": "consumeFontFeatureSettings", "parser-requires-value-pool": true }, "specification": { @@ -625,7 +621,7 @@ "custom": "All", "font-property": true, "high-priority": true, - "custom-parser": true + "parser-function": "consumeFontVariantLigatures" }, "specification": { "category": "css-fonts", @@ -683,7 +679,7 @@ "custom": "All", "font-property": true, "high-priority": true, - "custom-parser": true + "parser-function": "consumeFontVariantNumeric" }, "specification": { "category": "css-fonts", @@ -697,7 +693,7 @@ "custom": "All", "font-property": true, "high-priority": true, - "custom-parser": true + "parser-function": "consumeFontVariantAlternates" }, "specification": { "category": "css-fonts", @@ -711,7 +707,7 @@ "custom": "All", "font-property": true, "high-priority": true, - "custom-parser": true + "parser-function": "consumeFontVariantEastAsian" }, "specification": { "category": "css-fonts", @@ -1029,18 +1025,6 @@ "url": "https://www.w3.org/TR/css-ruby-1/#rubypos" } }, - "additive-symbols": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCounterStyleAtRulesEnabled", - "skip-builder": true, - "custom-parser": true - }, - "specification": { - "category": "css-counter-styles", - "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-symbols" - } - }, "alignment-baseline": { "values": [ "auto", @@ -2947,18 +2931,6 @@ "url": "https://www.w3.org/TR/CSS2/tables.html#empty-cells" } }, - "fallback": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCounterStyleAtRulesEnabled", - "skip-builder": true, - "custom-parser": true - }, - "specification": { - "category": "css-counter-styles", - "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-fallback" - } - }, "fill": { "inherited": true, "codegen-properties": { @@ -4478,18 +4450,6 @@ "url": "https://drafts.csswg.org/css-overscroll-1/#propdef-overscroll-behavior-x" } }, - "pad": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCounterStyleAtRulesEnabled", - "skip-builder": true, - "custom-parser": true - }, - "specification": { - "category": "css-counter-styles", - "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-pad" - } - }, "padding": { "codegen-properties": { "longhands": [ @@ -4879,39 +4839,6 @@ "url": "https://www.w3.org/TR/css3-page/#page-size-prop" } }, - "src": { - "codegen-properties": { - "skip-builder": true, - "descriptor-only": true, - "custom-parser": true - }, - "specification": { - "category": "css-fonts", - "url": "https://www.w3.org/TR/css-fonts-3/#src-desc" - } - }, - "base-palette": { - "codegen-properties": { - "skip-builder": true, - "descriptor-only": true, - "custom-parser": true - }, - "specification": { - "category": "css-fonts", - "url": "https://drafts.csswg.org/css-fonts-4/#base-palette-desc" - } - }, - "override-colors": { - "codegen-properties": { - "skip-builder": true, - "descriptor-only": true, - "custom-parser": true - }, - "specification": { - "category": "css-fonts", - "url": "https://drafts.csswg.org/css-fonts-4/#override-color" - } - }, "stop-color": { "codegen-properties": { "svg": true, @@ -5071,25 +4998,13 @@ "inherited": true, "codegen-properties": { "converter": "SpeakAs", - "custom-parser": true + "parser-function": "consumeSpeakAs" }, "specification": { "category": "css-speech", "url": "https://www.w3.org/TR/css3-speech/#speak-as" } }, - "symbols": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCounterStyleAtRulesEnabled", - "skip-builder": true, - "custom-parser": true - }, - "specification": { - "category": "css-counter-styles", - "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-symbols" - } - }, "table-layout": { "values": [ "auto", @@ -5607,34 +5522,6 @@ "url": "https://www.w3.org/TR/CSS22/visuren.html#propdef-unicode-bidi" } }, - "unicode-range": { - "codegen-properties": { - "skip-builder": true, - "descriptor-only": true, - "custom-parser": true - }, - "specification": { - "category": "css-fonts", - "url": "https://www.w3.org/TR/css-fonts-3/#descdef-unicode-range" - } - }, - "font-display": { - "values": [ - "auto", - "block", - "swap", - "fallback", - "optional" - ], - "codegen-properties": { - "skip-builder": true, - "descriptor-only": true - }, - "specification": { - "category": "css-fonts-4", - "url": "https://drafts.csswg.org/css-fonts-4/#font-display-desc" - } - }, "vector-effect": { "values": [ "none", @@ -7434,18 +7321,6 @@ }, "status": "non-standard" }, - "negative": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCounterStyleAtRulesEnabled", - "skip-builder": true, - "custom-parser": true - }, - "specification": { - "category": "css-counter-styles", - "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-negative" - } - }, "color-scheme": { "inherited": true, "values": [ @@ -7556,18 +7431,6 @@ "url": "https://www.w3.org/TR/css-transforms-1/#propdef-perspective-origin" } }, - "prefix": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCounterStyleAtRulesEnabled", - "skip-builder": true, - "custom-parser": true - }, - "specification": { - "category": "css-counter-styles", - "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-prefix" - } - }, "print-color-adjust": { "inherited": true, "values": [ @@ -7585,18 +7448,6 @@ "url": "https://www.w3.org/TR/css-color-adjust/#print-color-adjust" } }, - "range": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCounterStyleAtRulesEnabled", - "skip-builder": true, - "custom-parser": true - }, - "specification": { - "category": "css-counter-styles", - "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-range" - } - }, "-webkit-rtl-ordering": { "inherited": true, "values": [ @@ -7609,18 +7460,6 @@ }, "status": "non-standard" }, - "suffix": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCounterStyleAtRulesEnabled", - "skip-builder": true, - "custom-parser": true - }, - "specification": { - "category": "css-counter-styles", - "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-suffix" - } - }, "-webkit-text-combine": { "inherited": true, "values": [ @@ -8552,18 +8391,6 @@ "url": "https://www.w3.org/TR/css-shapes/#propdef-shape-image-threshold" } }, - "system": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCounterStyleAtRulesEnabled", - "skip-builder": true, - "custom-parser": true - }, - "specification": { - "category": "css-counter-styles", - "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-system" - } - }, "-webkit-tap-highlight-color": { "inherited": true, "codegen-properties": { @@ -8746,41 +8573,387 @@ "category": "css-round-display", "url": "https://www.w3.org/TR/css-round-display-1/#border-boundary-property" } + } + }, + "descriptors": { + "@counter-style": { + "additive-symbols": { + "codegen-properties": { + "settings-flag": "cssCounterStyleAtRulesEnabled", + "parser-function": "consumeCounterStyleAdditiveSymbols", + "parser-requires-context": true + }, + "specification": { + "category": "css-counter-styles", + "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-symbols" + } + }, + "fallback": { + "codegen-properties": { + "settings-flag": "cssCounterStyleAtRulesEnabled", + "parser-function": "consumeCounterStyleName" + }, + "specification": { + "category": "css-counter-styles", + "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-fallback" + } + }, + "negative": { + "codegen-properties": { + "settings-flag": "cssCounterStyleAtRulesEnabled", + "parser-function": "consumeCounterStyleNegative", + "parser-requires-context": true + }, + "specification": { + "category": "css-counter-styles", + "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-negative" + } + }, + "pad": { + "codegen-properties": { + "settings-flag": "cssCounterStyleAtRulesEnabled", + "parser-function": "consumeCounterStylePad", + "parser-requires-context": true + }, + "specification": { + "category": "css-counter-styles", + "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-pad" + } + }, + "prefix": { + "codegen-properties": { + "settings-flag": "cssCounterStyleAtRulesEnabled", + "parser-function": "consumeCounterStyleSymbol", + "parser-requires-context": true + }, + "specification": { + "category": "css-counter-styles", + "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-prefix" + } + }, + "range": { + "codegen-properties": { + "settings-flag": "cssCounterStyleAtRulesEnabled", + "parser-function": "consumeCounterStyleRange" + }, + "specification": { + "category": "css-counter-styles", + "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-range" + } + }, + "suffix": { + "codegen-properties": { + "settings-flag": "cssCounterStyleAtRulesEnabled", + "parser-function": "consumeCounterStyleSymbol", + "parser-requires-context": true + }, + "specification": { + "category": "css-counter-styles", + "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-suffix" + } + }, + "system": { + "codegen-properties": { + "settings-flag": "cssCounterStyleAtRulesEnabled", + "parser-function": "consumeCounterStyleSystem" + }, + "specification": { + "category": "css-counter-styles", + "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-system" + } + }, + "speak-as": { + "codegen-properties": { + "settings-flag": "cssCounterStyleAtRulesEnabled", + "parser-function": "consumeCounterStyleSpeakAs" + }, + "specification": { + "category": "css-counter-styles", + "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-speak-as" + } + }, + "symbols": { + "codegen-properties": { + "settings-flag": "cssCounterStyleAtRulesEnabled", + "parser-function": "consumeCounterStyleSymbols", + "parser-requires-context": true + }, + "specification": { + "category": "css-counter-styles", + "url": "https://www.w3.org/TR/css-counter-styles-3/#counter-style-symbols" + } + } }, - "syntax": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCustomPropertiesAndValuesEnabled", - "skip-builder": true, - "custom-parser": true + "@font-face": { + "font-family": { + "codegen-properties": { + "parser-function": "consumeFontFaceFontFamily" + }, + "specification": { + "category": "css-fonts-4", + "url": "https://www.w3.org/TR/css-fonts-4/#font-family-desc" + } }, - "specification": { - "category": "css-properties-and-values", - "url": "https://drafts.css-houdini.org/css-properties-values-api/#the-syntax-descriptor" + "font-display": { + "values": [ + "auto", + "block", + "swap", + "fallback", + "optional" + ], + "codegen-properties": { + "parser-exported": true + }, + "specification": { + "category": "css-fonts-4", + "url": "https://drafts.csswg.org/css-fonts-4/#font-display-desc" + } + }, + "font-feature-settings": { + "codegen-properties": { + "parser-function": "consumeFontFeatureSettings", + "parser-requires-value-pool": true + }, + "specification": { + "category": "css-fonts-4", + "url": "https://www.w3.org/TR/css-fonts-4/#descdef-font-face-font-feature-settings" + } + }, + "font-stretch": { + "values": [ + "normal", + "ultra-condensed", + "extra-condensed", + "condensed", + "semi-condensed", + "semi-expanded", + "expanded", + "extra-expanded", + "ultra-expanded" + ], + "codegen-properties": [ + { + "enable-if": "ENABLE_VARIATION_FONTS", + "parser-requires-value-pool": true, + "parser-function": "consumeFontStretchRange" + }, + { + "enable-if": "!ENABLE_VARIATION_FONTS", + "parser-requires-value-pool": true, + "parser-function": "consumeFontStretch" + } + ], + "specification": { + "category": "css-fonts-4", + "url": "https://www.w3.org/TR/css-fonts-4/#descdef-font-face-font-stretch" + } + }, + "font-style": { + "values": [ + { + "value": "auto", + "status": "unimplemented" + }, + "normal", + "italic", + "oblique" + ], + "codegen-properties": [ + { + "enable-if": "ENABLE_VARIATION_FONTS", + "parser-requires-context-mode": true, + "parser-requires-value-pool": true, + "parser-function": "consumeFontStyleRange" + }, + { + "enable-if": "!ENABLE_VARIATION_FONTS", + "parser-requires-context-mode": true, + "parser-requires-value-pool": true, + "parser-function": "consumeFontStyle" + } + ], + "specification": { + "category": "css-fonts-4", + "url": "https://www.w3.org/TR/css-fonts-4/#descdef-font-face-font-style" + } + }, + "font-variant": { + "codegen-properties": { + "longhands": [ + "font-variant-ligatures", + "font-variant-caps", + "font-variant-alternates", + "font-variant-numeric", + "font-variant-east-asian", + "font-variant-position" + ] + }, + "specification": { + "category": "css-fonts", + "url": "https://www.w3.org/TR/css-fonts-3/#propdef-font-variant" + } + }, + "font-variant-alternates": { + "codegen-properties": { + "parser-function": "consumeFontVariantAlternates" + }, + "specification": { + "category": "css-fonts", + "url": "https://drafts.csswg.org/css-fonts-3/#font-variant-alternates-prop" + } + }, + "font-variant-caps": { + "values": [ + "normal", + "small-caps", + "all-small-caps", + "petite-caps", + "all-petite-caps", + "unicase", + "titling-caps" + ], + "specification": { + "category": "css-fonts", + "url": "https://drafts.csswg.org/css-fonts-3/#font-variant-caps-prop" + } + }, + "font-variant-east-asian": { + "codegen-properties": { + "parser-function": "consumeFontVariantEastAsian" + }, + "specification": { + "category": "css-fonts", + "url": "https://drafts.csswg.org/css-fonts-3/#font-variant-east-asian-prop" + } + }, + "font-variant-ligatures": { + "codegen-properties": { + "parser-function": "consumeFontVariantLigatures" + }, + "specification": { + "category": "css-fonts-4", + "url": "https://drafts.csswg.org/css-fonts-4/#font-variant-ligatures-prop" + } + }, + "font-variant-numeric": { + "codegen-properties": { + "parser-function": "consumeFontVariantNumeric" + }, + "specification": { + "category": "css-fonts", + "url": "https://drafts.csswg.org/css-fonts-3/#font-variant-numeric-prop" + } + }, + "font-variant-position": { + "values": [ + "normal", + "sub", + "super" + ], + "specification": { + "category": "css-fonts-4", + "url": "https://drafts.csswg.org/css-fonts-4/#propdef-font-variant-position" + } + }, + "font-weight": { + "codegen-properties": [ + { + "enable-if": "ENABLE_VARIATION_FONTS", + "parser-requires-value-pool": true, + "parser-function": "consumeFontWeightAbsoluteRange" + }, + { + "enable-if": "!ENABLE_VARIATION_FONTS", + "parser-requires-value-pool": true, + "parser-function": "consumeFontWeightAbsolute" + } + ], + "specification": { + "category": "css-fonts-4", + "url": "https://www.w3.org/TR/css-fonts-4/#descdef-font-face-font-weight" + } + }, + "src": { + "codegen-properties": { + "parser-function": "consumeFontFaceSrc", + "parser-requires-context": true + }, + "specification": { + "category": "css-fonts-4", + "url": "https://www.w3.org/TR/css-fonts-4/#src-desc" + } + }, + "unicode-range": { + "codegen-properties": { + "parser-function": "consumeFontFaceUnicodeRange" + }, + "specification": { + "category": "css-fonts", + "url": "https://www.w3.org/TR/css-fonts-3/#descdef-unicode-range" + } } }, - "inherits": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCustomPropertiesAndValuesEnabled", - "skip-builder": true, - "custom-parser": true + "@font-palette-values": { + "font-family": { + "codegen-properties": { + "parser-function": "consumeFamilyName" + }, + "specification": { + "category": "css-fonts-4", + "url": "https://www.w3.org/TR/css-fonts-4/#font-family-2-desc" + } }, - "specification": { - "category": "css-properties-and-values", - "url": "https://drafts.css-houdini.org/css-properties-values-api/#inherits-descriptor" + "base-palette": { + "codegen-properties": { + "parser-grammar": "light | dark | " + }, + "specification": { + "category": "css-fonts-4", + "url": "https://www.w3.org/TR/css-fonts-4/#font-family-2-desc" + } + }, + "override-colors": { + "codegen-properties": { + "parser-function": "consumeFontPaletteValuesOverrideColors", + "parser-requires-context": true + }, + "specification": { + "category": "css-fonts-4", + "url": "https://www.w3.org/TR/css-fonts-4/#override-color" + } } }, - "initial-value": { - "codegen-properties": { - "descriptor-only": true, - "settings-flag": "cssCustomPropertiesAndValuesEnabled", - "skip-builder": true, - "custom-parser": true + "@property": { + "syntax": { + "codegen-properties": { + "settings-flag": "cssCustomPropertiesAndValuesEnabled", + "parser-grammar": "" + }, + "specification": { + "category": "css-properties-and-values", + "url": "https://drafts.css-houdini.org/css-properties-values-api/#the-syntax-descriptor" + } }, - "specification": { - "category": "css-properties-and-values", - "url": "https://drafts.css-houdini.org/css-properties-values-api/#initial-value-descriptor" + "inherits": { + "codegen-properties": { + "settings-flag": "cssCustomPropertiesAndValuesEnabled", + "parser-grammar": "true | false" + }, + "specification": { + "category": "css-properties-and-values", + "url": "https://drafts.css-houdini.org/css-properties-values-api/#inherits-descriptor" + } + }, + "initial-value": { + "codegen-properties": { + "settings-flag": "cssCustomPropertiesAndValuesEnabled", + "parser-function": "consumePropertyInitialValue" + }, + "specification": { + "category": "css-properties-and-values", + "url": "https://drafts.css-houdini.org/css-properties-values-api/#initial-value-descriptor" + } } } }, diff --git a/Source/WebCore/css/parser/CSSParserFastPaths.cpp b/Source/WebCore/css/parser/CSSParserFastPaths.cpp index 2eadd3ca3e0f..7f662406488c 100644 --- a/Source/WebCore/css/parser/CSSParserFastPaths.cpp +++ b/Source/WebCore/css/parser/CSSParserFastPaths.cpp @@ -586,14 +586,14 @@ std::optional> CSSParserFastPaths::parseNamedColor(StringView str return parseNamedColorInternal(string.characters16(), string.length()); } -bool CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyID property, CSSValueID value, const CSSParserContext& context) +bool CSSParserFastPaths::isKeywordValidForStyleProperty(CSSPropertyID property, CSSValueID value, const CSSParserContext& context) { - return CSSPropertyParsing::isKeywordValidForProperty(property, value, context); + return CSSPropertyParsing::isKeywordValidForStyleProperty(property, value, context); } -bool CSSParserFastPaths::isKeywordPropertyID(CSSPropertyID property) +bool CSSParserFastPaths::isKeywordFastPathEligibleStyleProperty(CSSPropertyID property) { - return CSSPropertyParsing::isKeywordProperty(property); + return CSSPropertyParsing::isKeywordFastPathEligibleStyleProperty(property); } static bool isUniversalKeyword(StringView string) @@ -614,7 +614,7 @@ static RefPtr parseKeywordValue(CSSPropertyID propertyId, StringView s // FIXME: The "!context.enclosingRuleType" is suspicious. ASSERT(!CSSProperty::isDescriptorOnly(propertyId) || parsingDescriptor || !context.enclosingRuleType); - if (!CSSParserFastPaths::isKeywordPropertyID(propertyId)) { + if (!CSSParserFastPaths::isKeywordFastPathEligibleStyleProperty(propertyId)) { // All properties, including non-keyword properties, accept the CSS-wide keywords. if (!isUniversalKeyword(string)) return nullptr; @@ -636,7 +636,7 @@ static RefPtr parseKeywordValue(CSSPropertyID propertyId, StringView s if (!parsingDescriptor && isCSSWideKeyword(valueID)) return CSSValuePool::singleton().createIdentifierValue(valueID); - if (CSSParserFastPaths::isValidKeywordPropertyAndValue(propertyId, valueID, context)) + if (CSSParserFastPaths::isKeywordValidForStyleProperty(propertyId, valueID, context)) return CSSValuePool::singleton().createIdentifierValue(valueID); return nullptr; } diff --git a/Source/WebCore/css/parser/CSSParserFastPaths.h b/Source/WebCore/css/parser/CSSParserFastPaths.h index 7e2f3162c961..550723d7c912 100644 --- a/Source/WebCore/css/parser/CSSParserFastPaths.h +++ b/Source/WebCore/css/parser/CSSParserFastPaths.h @@ -47,8 +47,8 @@ class CSSParserFastPaths { static RefPtr maybeParseValue(CSSPropertyID, StringView, const CSSParserContext&); // Properties handled here shouldn't be explicitly handled in CSSPropertyParser. - static bool isKeywordPropertyID(CSSPropertyID); - static bool isValidKeywordPropertyAndValue(CSSPropertyID, CSSValueID, const CSSParserContext&); + static bool isKeywordFastPathEligibleStyleProperty(CSSPropertyID); + static bool isKeywordValidForStyleProperty(CSSPropertyID, CSSValueID, const CSSParserContext&); // Parses numeric and named colors. static std::optional> parseSimpleColor(StringView, bool strict = false); diff --git a/Source/WebCore/css/parser/CSSPropertyParser.cpp b/Source/WebCore/css/parser/CSSPropertyParser.cpp index ada94f721af0..59fd8aeb9050 100644 --- a/Source/WebCore/css/parser/CSSPropertyParser.cpp +++ b/Source/WebCore/css/parser/CSSPropertyParser.cpp @@ -222,7 +222,7 @@ bool CSSPropertyParser::parseValue(CSSPropertyID propertyID, bool important, con else if (ruleType == StyleRuleType::FontPaletteValues) parseSuccess = parser.parseFontPaletteValuesDescriptor(propertyID); else if (ruleType == StyleRuleType::CounterStyle) - parseSuccess = parser.parseCounterStyleDescriptor(propertyID, context); + parseSuccess = parser.parseCounterStyleDescriptor(propertyID); else if (ruleType == StyleRuleType::Keyframe) parseSuccess = parser.parseKeyframeDescriptor(propertyID, important); else if (ruleType == StyleRuleType::Property) @@ -335,7 +335,7 @@ bool CSSPropertyParser::consumeCSSWideKeyword(CSSPropertyID propertyID, bool imp RefPtr CSSPropertyParser::parseSingleValue(CSSPropertyID property, CSSPropertyID currentShorthand) { - return CSSPropertyParsing::parse(m_range, property, currentShorthand, m_context); + return CSSPropertyParsing::parseStyleProperty(m_range, property, currentShorthand, m_context); } std::pair, CSSCustomPropertySyntax::Type> CSSPropertyParser::consumeCustomPropertyValueWithSyntax(const CSSCustomPropertySyntax& syntax) @@ -531,298 +531,48 @@ RefPtr CSSPropertyParser::parseTypedCustomPropertyValue( return CSSCustomPropertyValue::createForSyntaxValue(name, WTFMove(*syntaxValue)); } -// https://www.w3.org/TR/css-counter-styles-3/#counter-style-system -static RefPtr consumeCounterStyleSystem(CSSParserTokenRange& range) +RefPtr CSSPropertyParser::parseCounterStyleDescriptor(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context) { - if (auto ident = consumeIdent(range)) - return ident; - - if (auto ident = consumeIdent(range)) { - if (range.atEnd()) - return ident; - // If we have the `fixed` keyword but the range is not at the end, the next token must be a integer. - // If it's not, this value is invalid. - auto firstSymbolValue = consumeInteger(range); - if (!firstSymbolValue) - return nullptr; - return createPrimitiveValuePair(ident.releaseNonNull(), firstSymbolValue.releaseNonNull()); - } - - if (auto ident = consumeIdent(range)) { - // There must be a `` following the `extends` keyword. If there isn't, this value is invalid. - auto parsedCounterStyleName = consumeCounterStyleName(range); - if (!parsedCounterStyleName) - return nullptr; - return createPrimitiveValuePair(ident.releaseNonNull(), parsedCounterStyleName.releaseNonNull()); - } - return nullptr; -} - -// https://www.w3.org/TR/css-counter-styles-3/#typedef-symbol -static RefPtr consumeCounterStyleSymbol(CSSParserTokenRange& range, const CSSParserContext& context) -{ - if (auto string = consumeString(range)) - return string; - if (auto customIdent = consumeCustomIdent(range)) - return customIdent; - // There are inherent difficulties in supporting symbols in @counter-styles, so gate them behind a - // flag for now. https://bugs.webkit.org/show_bug.cgi?id=167645 - if (context.counterStyleAtRuleImageSymbolsEnabled) { - if (auto image = consumeImage(range, context, { AllowedImageType::URLFunction, AllowedImageType::GeneratedImage })) - return image; - } - return nullptr; -} - -// https://www.w3.org/TR/css-counter-styles-3/#counter-style-negative -static RefPtr consumeCounterStyleNegative(CSSParserTokenRange& range, const CSSParserContext& context) -{ - auto prependValue = consumeCounterStyleSymbol(range, context); - if (!prependValue) - return nullptr; - if (range.atEnd()) - return prependValue; - - auto appendValue = consumeCounterStyleSymbol(range, context); - if (!appendValue || !range.atEnd()) - return nullptr; - - RefPtr values = CSSValueList::createSpaceSeparated(); - values->append(prependValue.releaseNonNull()); - values->append(appendValue.releaseNonNull()); - return values; -} - -// https://www.w3.org/TR/css-counter-styles-3/#counter-style-range -static RefPtr consumeCounterStyleRangeBound(CSSParserTokenRange& range) -{ - if (auto infinite = consumeIdent(range)) - return infinite; - if (auto integer = consumeInteger(range)) - return integer; - return nullptr; -} - -// https://www.w3.org/TR/css-counter-styles-3/#counter-style-range -static RefPtr consumeCounterStyleRange(CSSParserTokenRange& range) -{ - if (auto autoValue = consumeIdent(range)) - return autoValue; - - auto rangeList = consumeCommaSeparatedListWithoutSingleValueOptimization(range, [](auto& range) -> RefPtr { - auto lowerBound = consumeCounterStyleRangeBound(range); - if (!lowerBound) - return nullptr; - auto upperBound = consumeCounterStyleRangeBound(range); - if (!upperBound) - return nullptr; - - // If the lower bound of any range is higher than the upper bound, the entire descriptor is invalid and must be - // ignored. - if (lowerBound->isInteger() && upperBound->isInteger() && lowerBound->intValue() > upperBound->intValue()) - return nullptr; - - return createPrimitiveValuePair(lowerBound.releaseNonNull(), upperBound.releaseNonNull(), Pair::IdenticalValueEncoding::DoNotCoalesce); - }); - - if (!range.atEnd() || !rangeList || !rangeList->length()) - return nullptr; - return rangeList; -} - -// https://www.w3.org/TR/css-counter-styles-3/#counter-style-pad -static RefPtr consumeCounterStylePad(CSSParserTokenRange& range, const CSSParserContext& context) -{ - RefPtr integer; - RefPtr symbol; - while (!integer || !symbol) { - if (!integer) { - integer = consumeNonNegativeInteger(range); - if (integer) - continue; - } - if (!symbol) { - symbol = consumeCounterStyleSymbol(range, context); - if (symbol) - continue; - } - return nullptr; - } - if (!range.atEnd()) - return nullptr; - auto values = CSSValueList::createSpaceSeparated(); - values->append(integer.releaseNonNull()); - values->append(symbol.releaseNonNull()); - return values; -} + ASSERT(context.propertySettings.cssCounterStyleAtRulesEnabled); -// https://www.w3.org/TR/css-counter-styles-3/#counter-style-symbols -static RefPtr consumeCounterStyleSymbols(CSSParserTokenRange& range, const CSSParserContext& context) -{ - auto symbols = CSSValueList::createSpaceSeparated(); - while (!range.atEnd()) { - auto symbol = consumeCounterStyleSymbol(range, context); - if (!symbol) - return nullptr; - symbols->append(symbol.releaseNonNull()); - } - if (!symbols->length()) - return nullptr; - return symbols; + return CSSPropertyParsing::parseCounterStyleDescriptor(range, property, context); } -// https://www.w3.org/TR/css-counter-styles-3/#counter-style-symbols -static RefPtr consumeCounterStyleAdditiveSymbols(CSSParserTokenRange& range, const CSSParserContext& context) +bool CSSPropertyParser::parseCounterStyleDescriptor(CSSPropertyID property) { - std::optional lastWeight; - auto values = consumeCommaSeparatedListWithoutSingleValueOptimization(range, [&lastWeight](auto& range, auto& context) -> RefPtr { - auto integer = consumeNonNegativeInteger(range); - auto symbol = consumeCounterStyleSymbol(range, context); - if (!integer) { - if (!symbol) - return nullptr; - integer = consumeNonNegativeInteger(range); - if (!integer) - return nullptr; - } - - // Additive tuples must be specified in order of strictly descending weight. - auto weight = integer->intValue(); - if (lastWeight && !(weight < lastWeight)) - return nullptr; - lastWeight = weight; - - auto pair = CSSValueList::createSpaceSeparated(); - pair->append(integer.releaseNonNull()); - pair->append(symbol.releaseNonNull()); - return pair; - }, context); + ASSERT(m_context.propertySettings.cssCounterStyleAtRulesEnabled); - if (!range.atEnd() || !values || !values->length()) - return nullptr; - return values; -} + auto parsedValue = CSSPropertyParsing::parseCounterStyleDescriptor(m_range, property, m_context); + if (!parsedValue || !m_range.atEnd()) + return false; -// https://www.w3.org/TR/css-counter-styles-3/#counter-style-speak-as -static RefPtr consumeCounterStyleSpeakAs(CSSParserTokenRange& range) -{ - if (auto speakAsIdent = consumeIdent(range)) - return speakAsIdent; - return consumeCounterStyleName(range); + addProperty(property, CSSPropertyInvalid, *parsedValue, false); + return true; } -RefPtr CSSPropertyParser::parseCounterStyleDescriptor(CSSPropertyID propId, CSSParserTokenRange& range, const CSSParserContext& context) +bool CSSPropertyParser::parseFontFaceDescriptor(CSSPropertyID property) { - ASSERT(context.propertySettings.cssCounterStyleAtRulesEnabled); - ASSERT(isExposed(propId, &context.propertySettings)); - - switch (propId) { - case CSSPropertySystem: - return consumeCounterStyleSystem(range); - case CSSPropertyNegative: - return consumeCounterStyleNegative(range, context); - case CSSPropertyPrefix: - case CSSPropertySuffix: - return consumeCounterStyleSymbol(range, context); - case CSSPropertyRange: - return consumeCounterStyleRange(range); - case CSSPropertyPad: - return consumeCounterStylePad(range, context); - case CSSPropertyFallback: - return consumeCounterStyleName(range); - case CSSPropertySymbols: - return consumeCounterStyleSymbols(range, context); - case CSSPropertyAdditiveSymbols: - return consumeCounterStyleAdditiveSymbols(range, context); - case CSSPropertySpeakAs: - return consumeCounterStyleSpeakAs(range); - default: - ASSERT_NOT_REACHED(); - return nullptr; - } -} + if (isShorthandCSSProperty(property)) + return parseFontFaceDescriptorShorthand(property); -bool CSSPropertyParser::parseCounterStyleDescriptor(CSSPropertyID propId, const CSSParserContext& context) -{ - auto parsedValue = parseCounterStyleDescriptor(propId, m_range, context); + auto parsedValue = CSSPropertyParsing::parseFontFaceDescriptor(m_range, property, m_context); if (!parsedValue || !m_range.atEnd()) return false; - addProperty(propId, CSSPropertyInvalid, *parsedValue, false); + addProperty(property, CSSPropertyInvalid, *parsedValue, false); return true; } -bool CSSPropertyParser::parseFontFaceDescriptor(CSSPropertyID propId) +bool CSSPropertyParser::parseFontFaceDescriptorShorthand(CSSPropertyID property) { - ASSERT(isExposed(propId, &m_context.propertySettings)); + ASSERT(isExposed(property, m_context.propertySettings)); - RefPtr parsedValue; - switch (propId) { - case CSSPropertyFontFamily: - parsedValue = consumeFontFamilyDescriptor(m_range); - break; - case CSSPropertySrc: // This is a list of urls or local references. - parsedValue = consumeFontFaceSrc(m_range, m_context); - break; - case CSSPropertyUnicodeRange: - parsedValue = consumeFontFaceUnicodeRange(m_range); - break; - case CSSPropertyFontDisplay: - parsedValue = consumeFontFaceFontDisplay(m_range, CSSValuePool::singleton()); - break; - case CSSPropertyFontWeight: -#if ENABLE(VARIATION_FONTS) - parsedValue = consumeFontWeightAbsoluteRange(m_range, CSSValuePool::singleton()); -#else - parsedValue = consumeFontWeightAbsolute(m_range, CSSValuePool::singleton()); -#endif - break; - case CSSPropertyFontStretch: -#if ENABLE(VARIATION_FONTS) - parsedValue = consumeFontStretchRange(m_range, CSSValuePool::singleton()); -#else - parsedValue = consumeFontStretch(m_range, CSSValuePool::singleton()); -#endif - break; - case CSSPropertyFontStyle: -#if ENABLE(VARIATION_FONTS) - parsedValue = consumeFontStyleRange(m_range, m_context.mode, CSSValuePool::singleton()); -#else - parsedValue = consumeFontStyle(m_range, m_context.mode, CSSValuePool::singleton()); -#endif - break; - case CSSPropertyFontVariantCaps: - parsedValue = CSSPropertyParsing::consumeFontVariantCaps(m_range); - break; - case CSSPropertyFontVariantLigatures: - parsedValue = consumeFontVariantLigatures(m_range); - break; - case CSSPropertyFontVariantNumeric: - parsedValue = consumeFontVariantNumeric(m_range); - break; - case CSSPropertyFontVariantEastAsian: - parsedValue = consumeFontVariantEastAsian(m_range); - break; - case CSSPropertyFontVariantAlternates: - parsedValue = consumeFontVariantAlternates(m_range); - break; - case CSSPropertyFontVariantPosition: - parsedValue = CSSPropertyParsing::consumeFontVariantPosition(m_range); - break; + switch (property) { case CSSPropertyFontVariant: return consumeFontVariantShorthand(false); - case CSSPropertyFontFeatureSettings: - parsedValue = consumeFontFeatureSettings(m_range, CSSValuePool::singleton()); - break; default: - break; - } - - if (!parsedValue || !m_range.atEnd()) return false; - - addProperty(propId, CSSPropertyInvalid, *parsedValue, false); - return true; + } } bool CSSPropertyParser::parseKeyframeDescriptor(CSSPropertyID propertyID, bool important) @@ -846,78 +596,23 @@ bool CSSPropertyParser::parseKeyframeDescriptor(CSSPropertyID propertyID, bool i } } -bool CSSPropertyParser::parsePropertyDescriptor(CSSPropertyID propertyID) +bool CSSPropertyParser::parsePropertyDescriptor(CSSPropertyID property) { - auto parsedValue = [&]() -> RefPtr { - switch (propertyID) { - case CSSPropertySyntax: - return consumeString(m_range); - case CSSPropertyInherits: - return consumeIdent(m_range); - case CSSPropertyInitialValue: - return CSSCustomPropertyValue::createSyntaxAll(nullAtom(), CSSVariableData::create(m_range.consumeAll())); - default: - return nullptr; - } - }(); - + auto parsedValue = CSSPropertyParsing::parsePropertyDescriptor(m_range, property, m_context); if (!parsedValue || !m_range.atEnd()) return false; - addProperty(propertyID, CSSPropertyInvalid, *parsedValue, false); + addProperty(property, CSSPropertyInvalid, *parsedValue, false); return true; } -static RefPtr consumeBasePaletteDescriptor(CSSParserTokenRange& range) +bool CSSPropertyParser::parseFontPaletteValuesDescriptor(CSSPropertyID property) { - if (auto result = consumeIdent(range)) - return result; - return consumeNonNegativeInteger(range); -} - -static RefPtr consumeOverrideColorsDescriptor(CSSParserTokenRange& range, const CSSParserContext& context) -{ - auto list = consumeCommaSeparatedListWithoutSingleValueOptimization(range, [](auto& range, auto& context) -> RefPtr { - auto key = consumeNonNegativeInteger(range); - if (!key) - return nullptr; - - auto color = consumeColor(range, context, false, { StyleColor::CSSColorType::Absolute }); - if (!color) - return nullptr; - - return CSSFontPaletteValuesOverrideColorsValue::create(key.releaseNonNull(), color.releaseNonNull()); - }, context); - - if (!range.atEnd() || !list || !list->length()) - return nullptr; - - return list; -} - -bool CSSPropertyParser::parseFontPaletteValuesDescriptor(CSSPropertyID propId) -{ - ASSERT(isExposed(propId, &m_context.propertySettings)); - - RefPtr parsedValue; - switch (propId) { - case CSSPropertyFontFamily: - parsedValue = consumeFamilyName(m_range); - break; - case CSSPropertyBasePalette: - parsedValue = consumeBasePaletteDescriptor(m_range); - break; - case CSSPropertyOverrideColors: - parsedValue = consumeOverrideColorsDescriptor(m_range, m_context); - break; - default: - break; - } - + auto parsedValue = CSSPropertyParsing::parseFontPaletteValuesDescriptor(m_range, property, m_context); if (!parsedValue || !m_range.atEnd()) return false; - addProperty(propId, CSSPropertyInvalid, *parsedValue, false); + addProperty(property, CSSPropertyInvalid, *parsedValue, false); return true; } @@ -1734,7 +1429,7 @@ bool CSSPropertyParser::consumeLegacyTextOrientation(bool important) if (valueID == CSSValueSidewaysRight) { keyword = CSSValuePool::singleton().createIdentifierValue(CSSValueSideways); consumeIdentRaw(m_range); - } else if (CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyTextOrientation, valueID, m_context)) + } else if (CSSParserFastPaths::isKeywordValidForStyleProperty(CSSPropertyTextOrientation, valueID, m_context)) keyword = consumeIdent(m_range); if (!keyword || !m_range.atEnd()) return false; @@ -2009,7 +1704,7 @@ bool CSSPropertyParser::consumeBackgroundShorthand(const StylePropertyShorthand& bool CSSPropertyParser::consumeOverflowShorthand(bool important) { CSSValueID xValueID = m_range.consumeIncludingWhitespace().id(); - if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyOverflowY, xValueID, m_context)) + if (!CSSParserFastPaths::isKeywordValidForStyleProperty(CSSPropertyOverflowY, xValueID, m_context)) return false; CSSValueID yValueID; @@ -2025,7 +1720,7 @@ bool CSSPropertyParser::consumeOverflowShorthand(bool important) } else yValueID = m_range.consumeIncludingWhitespace().id(); - if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyOverflowY, yValueID, m_context)) + if (!CSSParserFastPaths::isKeywordValidForStyleProperty(CSSPropertyOverflowY, yValueID, m_context)) return false; if (!m_range.atEnd()) return false; diff --git a/Source/WebCore/css/parser/CSSPropertyParser.h b/Source/WebCore/css/parser/CSSPropertyParser.h index d0c4362c7360..543f8d592b8f 100644 --- a/Source/WebCore/css/parser/CSSPropertyParser.h +++ b/Source/WebCore/css/parser/CSSPropertyParser.h @@ -71,11 +71,20 @@ class CSSPropertyParser { bool inQuirksMode() const { return m_context.mode == HTMLQuirksMode; } - bool parseViewportDescriptor(CSSPropertyID propId, bool important); + // @font-face descriptors. bool parseFontFaceDescriptor(CSSPropertyID); + bool parseFontFaceDescriptorShorthand(CSSPropertyID); + + // @font-palette-values descriptors. bool parseFontPaletteValuesDescriptor(CSSPropertyID); - bool parseCounterStyleDescriptor(CSSPropertyID, const CSSParserContext&); + + // @counter-style descriptors. + bool parseCounterStyleDescriptor(CSSPropertyID); + + // @keyframe descriptors. bool parseKeyframeDescriptor(CSSPropertyID, bool important); + + // @property descriptors. bool parsePropertyDescriptor(CSSPropertyID); void addProperty(CSSPropertyID longhand, CSSPropertyID shorthand, Ref&&, bool important, bool implicit = false); diff --git a/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp b/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp index d748318a1bf4..a1b878e71d3b 100644 --- a/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp +++ b/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp @@ -43,6 +43,7 @@ #include "CSSContentDistributionValue.h" #include "CSSCrossfadeValue.h" #include "CSSCursorImageValue.h" +#include "CSSCustomPropertyValue.h" #include "CSSFilterImageValue.h" #include "CSSFontVariantAlternatesValue.h" #include "CSSFontVariantLigaturesParser.h" @@ -5434,24 +5435,6 @@ RefPtr consumeFontFamily(CSSParserTokenRange& range) }); } -RefPtr consumeFontFamilyDescriptor(CSSParserTokenRange& range) -{ - // FIXME-NEWPARSER: https://bugs.webkit.org/show_bug.cgi?id=196381 For compatibility with the old parser, we have to make - // a list here, even though the list always contains only a single family name. - // Once the old parser is gone, we can delete this function, make the caller - // use consumeFamilyName instead, and then patch the @font-face code to - // not expect a list with a single name in it. - RefPtr list = CSSValueList::createCommaSeparated(); - RefPtr parsedValue = consumeFamilyName(range); - if (parsedValue) - list->append(parsedValue.releaseNonNull()); - - if (!range.atEnd() || !list->length()) - return nullptr; - - return list; -} - static RefPtr consumeCounter(CSSParserTokenRange& range, int defaultValue) { if (range.peek().id() == CSSValueNone) @@ -7262,7 +7245,7 @@ template RefPtr consumeBackgroundSize(CSSParse vertical = consumeIdent(range); if (!vertical) vertical = consumeLengthOrPercent(range, cssParserMode, ValueRange::NonNegative, UnitlessQuirk::Allow); - } + } if (!vertical) { if constexpr (property == CSSPropertyWebkitBackgroundSize) { @@ -8218,6 +8201,254 @@ RefPtr consumeOffsetRotate(CSSParserTokenRange& range, CSSParserMode m return CSSOffsetRotateValue::create(WTFMove(modifier), WTFMove(angle)); } -} // namespace CSSPropertyParserHelpers +// MARK: - @-rule descriptor consumers: + +// MARK: @font-face + +RefPtr consumeFontFaceFontFamily(CSSParserTokenRange& range) +{ + // FIXME-NEWPARSER: https://bugs.webkit.org/show_bug.cgi?id=196381 For compatibility with the old parser, we have to make + // a list here, even though the list always contains only a single family name. + // Once the old parser is gone, we can delete this function, make the caller + // use consumeFamilyName instead, and then patch the @font-face code to + // not expect a list with a single name in it. + RefPtr list = CSSValueList::createCommaSeparated(); + RefPtr parsedValue = consumeFamilyName(range); + if (parsedValue) + list->append(parsedValue.releaseNonNull()); + + if (!range.atEnd() || !list->length()) + return nullptr; + + return list; +} + +// MARK: @font-palette-values + +RefPtr consumeFontPaletteValuesOverrideColors(CSSParserTokenRange& range, const CSSParserContext& context) +{ + auto list = consumeCommaSeparatedListWithoutSingleValueOptimization(range, [](auto& range, auto& context) -> RefPtr { + auto key = consumeNonNegativeInteger(range); + if (!key) + return nullptr; + + auto color = consumeColor(range, context, false, { StyleColor::CSSColorType::Absolute }); + if (!color) + return nullptr; + + return CSSFontPaletteValuesOverrideColorsValue::create(key.releaseNonNull(), color.releaseNonNull()); + }, context); + + if (!range.atEnd() || !list || !list->length()) + return nullptr; + + return list; +} + +// MARK: @counter-style + +// https://www.w3.org/TR/css-counter-styles-3/#counter-style-system +RefPtr consumeCounterStyleSystem(CSSParserTokenRange& range) +{ + // cyclic | numeric | alphabetic | symbolic | additive | [fixed ?] | [ extends ] + + if (auto ident = consumeIdent(range)) + return ident; + + if (auto ident = consumeIdent(range)) { + if (range.atEnd()) + return ident; + // If we have the `fixed` keyword but the range is not at the end, the next token must be a integer. + // If it's not, this value is invalid. + auto firstSymbolValue = consumeInteger(range); + if (!firstSymbolValue) + return nullptr; + return createPrimitiveValuePair(ident.releaseNonNull(), firstSymbolValue.releaseNonNull()); + } + + if (auto ident = consumeIdent(range)) { + // There must be a `` following the `extends` keyword. If there isn't, this value is invalid. + auto parsedCounterStyleName = consumeCounterStyleName(range); + if (!parsedCounterStyleName) + return nullptr; + return createPrimitiveValuePair(ident.releaseNonNull(), parsedCounterStyleName.releaseNonNull()); + } + return nullptr; +} +// https://www.w3.org/TR/css-counter-styles-3/#typedef-symbol +RefPtr consumeCounterStyleSymbol(CSSParserTokenRange& range, const CSSParserContext& context) +{ + // [ | | ] + + if (auto string = consumeString(range)) + return string; + if (auto customIdent = consumeCustomIdent(range)) + return customIdent; + // There are inherent difficulties in supporting symbols in @counter-styles, so gate them behind a + // flag for now. https://bugs.webkit.org/show_bug.cgi?id=167645 + if (context.counterStyleAtRuleImageSymbolsEnabled) { + if (auto image = consumeImage(range, context, { AllowedImageType::URLFunction, AllowedImageType::GeneratedImage })) + return image; + } + return nullptr; +} + +// https://www.w3.org/TR/css-counter-styles-3/#counter-style-negative +RefPtr consumeCounterStyleNegative(CSSParserTokenRange& range, const CSSParserContext& context) +{ + // ? + + auto prependValue = consumeCounterStyleSymbol(range, context); + if (!prependValue) + return nullptr; + if (range.atEnd()) + return prependValue; + + auto appendValue = consumeCounterStyleSymbol(range, context); + if (!appendValue || !range.atEnd()) + return nullptr; + + RefPtr values = CSSValueList::createSpaceSeparated(); + values->append(prependValue.releaseNonNull()); + values->append(appendValue.releaseNonNull()); + return values; +} + +static RefPtr consumeCounterStyleRangeBound(CSSParserTokenRange& range) +{ + if (auto infinite = consumeIdent(range)) + return infinite; + if (auto integer = consumeInteger(range)) + return integer; + return nullptr; +} + +// https://www.w3.org/TR/css-counter-styles-3/#counter-style-range +RefPtr consumeCounterStyleRange(CSSParserTokenRange& range) +{ + // [ [ | infinite ]{2} ]# | auto + + if (auto autoValue = consumeIdent(range)) + return autoValue; + + auto rangeList = consumeCommaSeparatedListWithoutSingleValueOptimization(range, [](auto& range) -> RefPtr { + auto lowerBound = consumeCounterStyleRangeBound(range); + if (!lowerBound) + return nullptr; + auto upperBound = consumeCounterStyleRangeBound(range); + if (!upperBound) + return nullptr; + + // If the lower bound of any range is higher than the upper bound, the entire descriptor is invalid and must be + // ignored. + if (lowerBound->isInteger() && upperBound->isInteger() && lowerBound->intValue() > upperBound->intValue()) + return nullptr; + + return createPrimitiveValuePair(lowerBound.releaseNonNull(), upperBound.releaseNonNull(), Pair::IdenticalValueEncoding::DoNotCoalesce); + }); + + if (!range.atEnd() || !rangeList || !rangeList->length()) + return nullptr; + return rangeList; +} + +// https://www.w3.org/TR/css-counter-styles-3/#counter-style-pad +RefPtr consumeCounterStylePad(CSSParserTokenRange& range, const CSSParserContext& context) +{ + // && + + RefPtr integer; + RefPtr symbol; + while (!integer || !symbol) { + if (!integer) { + integer = consumeNonNegativeInteger(range); + if (integer) + continue; + } + if (!symbol) { + symbol = consumeCounterStyleSymbol(range, context); + if (symbol) + continue; + } + return nullptr; + } + if (!range.atEnd()) + return nullptr; + auto values = CSSValueList::createSpaceSeparated(); + values->append(integer.releaseNonNull()); + values->append(symbol.releaseNonNull()); + return values; +} + +// https://www.w3.org/TR/css-counter-styles-3/#descdef-counter-style-symbols +RefPtr consumeCounterStyleSymbols(CSSParserTokenRange& range, const CSSParserContext& context) +{ + // + + + auto symbols = CSSValueList::createSpaceSeparated(); + while (!range.atEnd()) { + auto symbol = consumeCounterStyleSymbol(range, context); + if (!symbol) + return nullptr; + symbols->append(symbol.releaseNonNull()); + } + if (!symbols->length()) + return nullptr; + return symbols; +} + +// https://www.w3.org/TR/css-counter-styles-3/#descdef-counter-style-additive-symbols +RefPtr consumeCounterStyleAdditiveSymbols(CSSParserTokenRange& range, const CSSParserContext& context) +{ + // [ && ]# + + std::optional lastWeight; + auto values = consumeCommaSeparatedListWithoutSingleValueOptimization(range, [&lastWeight](auto& range, auto& context) -> RefPtr { + auto integer = consumeNonNegativeInteger(range); + auto symbol = consumeCounterStyleSymbol(range, context); + if (!integer) { + if (!symbol) + return nullptr; + integer = consumeNonNegativeInteger(range); + if (!integer) + return nullptr; + } + + // Additive tuples must be specified in order of strictly descending weight. + auto weight = integer->intValue(); + if (lastWeight && !(weight < lastWeight)) + return nullptr; + lastWeight = weight; + + auto pair = CSSValueList::createSpaceSeparated(); + pair->append(integer.releaseNonNull()); + pair->append(symbol.releaseNonNull()); + return pair; + }, context); + + if (!range.atEnd() || !values || !values->length()) + return nullptr; + return values; +} + +// https://www.w3.org/TR/css-counter-styles-3/#counter-style-speak-as +RefPtr consumeCounterStyleSpeakAs(CSSParserTokenRange& range) +{ + // auto | bullets | numbers | words | spell-out | + + if (auto speakAsIdent = consumeIdent(range)) + return speakAsIdent; + return consumeCounterStyleName(range); +} + +// MARK: @property + +// https://drafts.css-houdini.org/css-properties-values-api/#initial-value-descriptor +RefPtr consumePropertyInitialValue(CSSParserTokenRange& range) +{ + return CSSCustomPropertyValue::createSyntaxAll(nullAtom(), CSSVariableData::create(range.consumeAll())); +} + +} // namespace CSSPropertyParserHelpers } // namespace WebCore diff --git a/Source/WebCore/css/parser/CSSPropertyParserHelpers.h b/Source/WebCore/css/parser/CSSPropertyParserHelpers.h index eb91a031d058..518a2f0c985f 100644 --- a/Source/WebCore/css/parser/CSSPropertyParserHelpers.h +++ b/Source/WebCore/css/parser/CSSPropertyParserHelpers.h @@ -234,7 +234,6 @@ RefPtr consumeFontVariantNumeric(CSSParserTokenRange&); RefPtr consumeFontWeight(CSSParserTokenRange&); RefPtr consumeFamilyName(CSSParserTokenRange&); RefPtr consumeFontFamily(CSSParserTokenRange&); -RefPtr consumeFontFamilyDescriptor(CSSParserTokenRange&); RefPtr consumeCounterIncrement(CSSParserTokenRange&); RefPtr consumeCounterReset(CSSParserTokenRange&); RefPtr consumeSize(CSSParserTokenRange&, CSSParserMode); @@ -319,6 +318,30 @@ RefPtr consumeColorScheme(CSSParserTokenRange&); RefPtr consumeOffsetRotate(CSSParserTokenRange&, CSSParserMode); +// @font-face descriptor consumers: + +RefPtr consumeFontFaceFontFamily(CSSParserTokenRange&); + +// @font-palette-values descriptor consumers: + +RefPtr consumeFontPaletteValuesOverrideColors(CSSParserTokenRange&, const CSSParserContext&); + +// @counter-style descriptor consumers: + +RefPtr consumeCounterStyleSystem(CSSParserTokenRange&); +RefPtr consumeCounterStyleSymbol(CSSParserTokenRange&, const CSSParserContext&); +RefPtr consumeCounterStyleNegative(CSSParserTokenRange&, const CSSParserContext&); +RefPtr consumeCounterStyleRange(CSSParserTokenRange&); +RefPtr consumeCounterStylePad(CSSParserTokenRange&, const CSSParserContext&); +RefPtr consumeCounterStyleSymbols(CSSParserTokenRange&, const CSSParserContext&); +RefPtr consumeCounterStyleAdditiveSymbols(CSSParserTokenRange&, const CSSParserContext&); +RefPtr consumeCounterStyleSpeakAs(CSSParserTokenRange&); + +// @property descriptor consumers: + +RefPtr consumePropertyInitialValue(CSSParserTokenRange&); + + // Template and inline implementations are at the bottom of the file for readability. template bool identMatches(CSSValueID) diff --git a/Source/WebCore/css/parser/CSSPropertyParserWorkerSafe.cpp b/Source/WebCore/css/parser/CSSPropertyParserWorkerSafe.cpp index 99cf41efeb9c..607fe973af39 100644 --- a/Source/WebCore/css/parser/CSSPropertyParserWorkerSafe.cpp +++ b/Source/WebCore/css/parser/CSSPropertyParserWorkerSafe.cpp @@ -275,7 +275,7 @@ static RefPtr consumeFontStyleAngle(CSSParserTokenRange& rang return angle; } -RefPtr consumeFontStyleRange(CSSParserTokenRange& range, CSSParserMode mode, CSSValuePool& pool) +RefPtr consumeFontStyleRange(CSSParserTokenRange& range, CSSParserMode mode, CSSValuePool& pool) { auto keyword = CSSPropertyParserHelpers::consumeIdentWorkerSafe(range, pool); if (!keyword) diff --git a/Source/WebCore/css/parser/CSSPropertyParserWorkerSafe.h b/Source/WebCore/css/parser/CSSPropertyParserWorkerSafe.h index 316b13632dc5..3d7b28adb166 100644 --- a/Source/WebCore/css/parser/CSSPropertyParserWorkerSafe.h +++ b/Source/WebCore/css/parser/CSSPropertyParserWorkerSafe.h @@ -32,7 +32,6 @@ namespace WebCore { -class CSSFontStyleRangeValue; class CSSPrimitiveValue; class CSSValue; class CSSValueList; @@ -65,7 +64,7 @@ RefPtr consumeFontFeatureSettings(CSSParserTokenRange&, CSSValuePool&) RefPtr consumeFontFaceFontDisplay(CSSParserTokenRange&, CSSValuePool&); #if ENABLE(VARIATION_FONTS) -RefPtr consumeFontStyleRange(CSSParserTokenRange&, CSSParserMode, CSSValuePool&); +RefPtr consumeFontStyleRange(CSSParserTokenRange&, CSSParserMode, CSSValuePool&); RefPtr consumeFontWeightAbsoluteRange(CSSParserTokenRange&, CSSValuePool&); RefPtr consumeFontStretchRange(CSSParserTokenRange&, CSSValuePool&); #endif diff --git a/Source/WebCore/css/process-css-properties.py b/Source/WebCore/css/process-css-properties.py index 149edaddde00..70c1a36adced 100755 --- a/Source/WebCore/css/process-css-properties.py +++ b/Source/WebCore/css/process-css-properties.py @@ -35,7 +35,6 @@ import sys import textwrap - def quote_iterable(iterable, suffix=""): return (f'"{x}"{suffix}' for x in iterable) @@ -181,7 +180,7 @@ def id_without_prefix_with_lowercase_first_letter(self): class PropertyName(Name): - def __init__(self, name, *, name_for_methods): + def __init__(self, name, *, name_for_methods=None): super().__init__(name) self.name_for_methods = PropertyName._compute_name_for_methods(name_for_methods, self.id_without_prefix) @@ -450,7 +449,7 @@ def from_json(parsing_context, key_path, json_value): return Longhand(**json_value) -class CodeGenProperties: +class StylePropertyCodeGenProperties: schema = Schema( Schema.Entry("aliases", allowed_types=[list], default_value=[]), Schema.Entry("auto-functions", allowed_types=[bool], default_value=False), @@ -461,7 +460,6 @@ class CodeGenProperties: Schema.Entry("converter", allowed_types=[str]), Schema.Entry("custom", allowed_types=[str]), Schema.Entry("custom-parser", allowed_types=[bool]), - Schema.Entry("descriptor-only", allowed_types=[bool], default_value=False), Schema.Entry("enable-if", allowed_types=[str]), Schema.Entry("fast-path-inherited", allowed_types=[bool], default_value=False), Schema.Entry("fill-layer-property", allowed_types=[bool], default_value=False), @@ -501,11 +499,11 @@ class CodeGenProperties: ) def __init__(self, property_name, **dictionary): - CodeGenProperties.schema.set_attributes_from_dictionary(dictionary, instance=self) + StylePropertyCodeGenProperties.schema.set_attributes_from_dictionary(dictionary, instance=self) self.property_name = property_name def __str__(self): - return f"CodeGenProperties {vars(self)}" + return f"StylePropertyCodeGenProperties {vars(self)}" def __repr__(self): return self.__str__() @@ -516,7 +514,7 @@ def from_json(parsing_context, key_path, name, json_value): json_value = parsing_context.select_enabled_variant(json_value, label=f"{key_path}.codegen-properties") assert(type(json_value) is dict) - CodeGenProperties.schema.validate_dictionary(parsing_context, f"{key_path}.codegen-properties", json_value, label=f"CodeGenProperties") + StylePropertyCodeGenProperties.schema.validate_dictionary(parsing_context, f"{key_path}.codegen-properties", json_value, label=f"StylePropertyCodeGenProperties") property_name = PropertyName(name, name_for_methods=json_value.get("name-for-methods")) @@ -546,7 +544,7 @@ def from_json(parsing_context, key_path, name, json_value): if "longhands" in json_value: json_value["longhands"] = list(compact_map(lambda value: Longhand.from_json(parsing_context, f"{key_path}.codegen-properties", value), json_value["longhands"])) if not json_value["longhands"]: - longhands = None + del json_value["longhands"] if "computable" in json_value: if json_value["computable"]: @@ -587,7 +585,21 @@ def from_json(parsing_context, key_path, name, json_value): grammar.perform_fixups(parsing_context.parsed_shared_grammar_rules) json_value["parser-grammar"] = grammar - return CodeGenProperties(property_name, **json_value) + if json_value.get("parser-function"): + for entry_name in ["skip-parser", "longhands", "custom-parser", "parser-grammar"]: + if entry_name in json_value: + raise Exception(f"{key_path} can't have both 'parser-function' and '{entry_name}'.") + + if json_value.get("custom-parser"): + for entry_name in ["skip-parser"]: + if entry_name in json_value: + raise Exception(f"{key_path} can't have both 'custom-parser' and '{entry_name}'.") + + # Canonicalize to 'parser-function' so that the rest of the code only has to deal with one of them. + json_value["parser-function"] = f"consume{property_name.id_without_prefix}" + del json_value["custom-parser"] + + return StylePropertyCodeGenProperties(property_name, **json_value) @property def is_logical(self): @@ -620,7 +632,6 @@ def __init__(self, **dictionary): Property.schema.set_attributes_from_dictionary(dictionary, instance=self) self.property_name = self.codegen_properties.property_name self.synonymous_properties = [] - self._fast_path_keyword_terms_sorted_by_name = None def __str__(self): return self.name @@ -633,7 +644,7 @@ def from_json(parsing_context, key_path, name, json_value): assert(type(json_value) is dict) Property.schema.validate_dictionary(parsing_context, f"{key_path}.{name}", json_value, label=f"Property") - codegen_properties = CodeGenProperties.from_json(parsing_context, f"{key_path}.{name}", name, json_value.get("codegen-properties", {})) + codegen_properties = StylePropertyCodeGenProperties.from_json(parsing_context, f"{key_path}.{name}", name, json_value.get("codegen-properties", {})) json_value["codegen-properties"] = codegen_properties if codegen_properties.enable_if is not None and not parsing_context.is_enabled(conditional=codegen_properties.enable_if): @@ -650,6 +661,8 @@ def from_json(parsing_context, key_path, name, json_value): values = list(filter(lambda value: value is not None, map(lambda value: Value.from_json(parsing_context, f"{key_path}.{name}", value), json_value["values"]))) if codegen_properties.parser_grammar: codegen_properties.parser_grammar.perform_fixups_for_values_references(values) + else: + codegen_properties.parser_grammar = Grammar.from_values(parsing_context, key_path, name, values) json_value["values"] = values return Property(**json_value) @@ -657,10 +670,10 @@ def from_json(parsing_context, key_path, name, json_value): def perform_fixups_for_synonyms(self, all_properties): # If 'synonym' was specified, replace the name with references to the Property object, and vice-versa a back-reference on that Property object back to this. if self.codegen_properties.synonym: - if self.codegen_properties.synonym not in all_properties.properties_by_name: + if self.codegen_properties.synonym not in all_properties.all_by_name: raise Exception(f"Property {self.name} has an unknown synonym: {self.codegen_properties.synonym}.") - original = all_properties.properties_by_name[self.codegen_properties.synonym] + original = all_properties.all_by_name[self.codegen_properties.synonym] original.synonymous_properties.append(self) self.codegen_properties.synonym = original @@ -668,15 +681,15 @@ def perform_fixups_for_synonyms(self, all_properties): def perform_fixups_for_longhands(self, all_properties): # If 'longhands' was specified, replace the names with references to the Property objects. if self.codegen_properties.longhands: - self.codegen_properties.longhands = [all_properties.properties_by_name[longhand.value] for longhand in self.codegen_properties.longhands] + self.codegen_properties.longhands = [all_properties.all_by_name[longhand.value] for longhand in self.codegen_properties.longhands] def perform_fixups_for_related_properties(self, all_properties): # If 'related-property' was specified, validate the relationship and replace the name with a reference to the Property object. if self.codegen_properties.related_property: - if self.codegen_properties.related_property not in all_properties.properties_by_name: + if self.codegen_properties.related_property not in all_properties.all_by_name: raise Exception(f"Property {self.name} has an unknown related property: {self.codegen_properties.related_property}.") - related_property = all_properties.properties_by_name[self.codegen_properties.related_property] + related_property = all_properties.all_by_name[self.codegen_properties.related_property] if type(related_property.codegen_properties.related_property) is str: if related_property.codegen_properties.related_property != self.name: raise Exception(f"Property {self.name} has {related_property.name} as a related property, but it's not reciprocal.") @@ -730,6 +743,14 @@ def id_without_scope(self): def id(self): return self.property_name.id + # Used for parsing and consume methods. It is prefixed with a 'kind' for descriptors, and left unprefixed for style properties. + # Examples: + # style property 'column-width' would generate a consume method called `consumeColumnWidth` + # @font-face descriptor 'font-display' would generate a consume method called `consumeFontFaceFontDisplay` + @property + def name_for_parsing_methods(self): + return self.property_name.id_without_prefix + @property def name_for_methods(self): return self.property_name.name_for_methods @@ -760,50 +781,6 @@ def is_skipped_from_computed_style(self): return False - @property - def is_eligible_for_fast_path(self): - if self.codegen_properties.longhands or self.codegen_properties.descriptor_only or self.codegen_properties.skip_parser: - return False - return True - - @property - def _fast_path_keyword_terms(self): - if not self.is_eligible_for_fast_path: - return [] - if self.codegen_properties.parser_grammar: - return self.codegen_properties.parser_grammar.fast_path_keyword_terms - return (value.keyword_term for value in self.values if value.keyword_term.is_eligible_for_fast_path) - - @property - def fast_path_keyword_terms_sorted_by_name(self): - if not self.is_eligible_for_fast_path: - return [] - if not self._fast_path_keyword_terms_sorted_by_name: - self._fast_path_keyword_terms_sorted_by_name = sorted(self._fast_path_keyword_terms, key=functools.cmp_to_key(Properties._sort_with_prefixed_properties_last)) - return self._fast_path_keyword_terms_sorted_by_name - - @property - def has_only_keyword_terms(self): - if self.codegen_properties.parser_grammar: - return self.codegen_properties.parser_grammar.has_only_keyword_terms - return self.values is not None - - @property - def has_fast_path_keyword_terms(self): - if not self.is_eligible_for_fast_path: - return False - if self.codegen_properties.parser_grammar: - return self.codegen_properties.parser_grammar.has_fast_path_keyword_terms - return self.values and any(value.keyword_term.is_eligible_for_fast_path for value in self.values) - - @property - def has_only_fast_path_keyword_terms(self): - if not self.is_eligible_for_fast_path: - return False - if self.codegen_properties.parser_grammar: - return self.codegen_properties.parser_grammar.has_only_fast_path_keyword_terms - return self.values and all(value.keyword_term.is_eligible_for_fast_path for value in self.values) - # Specialized properties to compute method names. @property @@ -847,7 +824,7 @@ def enum_name_for_layers_type(self): raise Exception(f"Unrecognized FillLayer property name: '{self.name}") -class Properties: +class StyleProperties: def __init__(self, *properties): self.properties = properties self.properties_by_name = {property.name: property for property in properties} @@ -859,11 +836,25 @@ def __init__(self, *properties): self._perform_fixups() def __str__(self): - return "Properties" + return "StyleProperties" def __repr__(self): return self.__str__() + @staticmethod + def from_json(parsing_context, key_path, json_value): + return StyleProperties( + *list( + filter( + lambda value: value is not None, + map( + lambda item: Property.from_json(parsing_context, key_path, item[0], item[1]), + json_value.items() + ) + ) + ) + ) + # Updates any references to other properties that were by name (e.g. string) with a direct # reference to the property object. def _perform_fixups(self): @@ -874,31 +865,21 @@ def _perform_fixups(self): @property def all(self): if not self._all: - self._all = sorted(self.properties, key=functools.cmp_to_key(Properties._sort_by_descending_priority_and_name)) + self._all = sorted(self.properties, key=functools.cmp_to_key(StyleProperties._sort_by_descending_priority_and_name)) return self._all + # Returns the map of property names to properties. + @property + def all_by_name(self): + return self.properties_by_name + # Returns the set of all properties that are included in computed styles. Sorted lexically by name with prefixed properties last. @property def all_computed(self): if not self._all_computed: - self._all_computed = sorted([property for property in self.all if not property.is_skipped_from_computed_style], key=functools.cmp_to_key(Properties._sort_with_prefixed_properties_last)) + self._all_computed = sorted([property for property in self.all if not property.is_skipped_from_computed_style], key=functools.cmp_to_key(StyleProperties._sort_with_prefixed_properties_last)) return self._all_computed - # Returns a generator for the set of properties that are conditionally included depending on settings. Default decreasing priority and name sorting. - @property - def all_with_settings_flag(self): - return (property for property in self.all if property.codegen_properties.settings_flag) - - # Returns a generator for the set of properties that are marked internal-only. Default decreasing priority and name sorting. - @property - def all_internal_only(self): - return (property for property in self.all if property.codegen_properties.internal_only) - - # Returns a generator for the set properties that are NOT marked internal. Default decreasing priority and name sorting. - @property - def all_non_internal_only(self): - return (property for property in self.all if not property.codegen_properties.internal_only) - # Returns a generator for the set of properties that have an associate longhand, the so-called shorthands. Default decreasing priority and name sorting. @property def all_shorthands(self): @@ -909,11 +890,6 @@ def all_shorthands(self): def all_non_shorthands(self): return (property for property in self.all if not property.codegen_properties.longhands) - # Returns a generator for the set of properties that can accept a single value keyword. Default decreasing priority and name sorting. - @property - def all_with_fast_path_keyword_terms(self): - return (property for property in self.all if property.has_fast_path_keyword_terms) - # Returns a generator for the set of properties that are direction-aware (aka flow-sensative). Sorted first by property group name and then by property name. @property def all_direction_aware_properties(self): @@ -929,13 +905,6 @@ def all_in_logical_property_group(self): for resolver, property in sorted(property_group[kind].items(), key=lambda x: x[1].name): yield property - # Returns the set of settings-flags used by any property. Uniqued and sorted lexically. - @property - def settings_flags(self): - if not self._settings_flags: - self._settings_flags = sorted(list(set([property.codegen_properties.settings_flag for property in self.properties if property.codegen_properties.settings_flag]))) - return self._settings_flags - # Default sorting algorithm for properties. def _sort_by_descending_priority_and_name(a, b): # Sort shorthands to the back @@ -978,7 +947,7 @@ def _sort_by_descending_priority_and_name(a, b): if not a_is_sink_priority and b_is_sink_priority: return -1 - return Properties._sort_with_prefixed_properties_last(a, b) + return StyleProperties._sort_with_prefixed_properties_last(a, b) def _sort_with_prefixed_properties_last(a, b): # Sort prefixed names to the back. @@ -997,6 +966,366 @@ def _sort_with_prefixed_properties_last(a, b): return 0 +class DescriptorCodeGenProperties: + schema = Schema( + Schema.Entry("aliases", allowed_types=[list], default_value=[]), + Schema.Entry("comment", allowed_types=[str]), + Schema.Entry("enable-if", allowed_types=[str]), + Schema.Entry("internal-only", allowed_types=[bool], default_value=False), + Schema.Entry("longhands", allowed_types=[list]), + Schema.Entry("parser-function", allowed_types=[str]), + Schema.Entry("parser-exported", allowed_types=[bool]), + Schema.Entry("parser-grammar", allowed_types=[list, dict, str]), + Schema.Entry("parser-grammar-comment", allowed_types=[str]), + Schema.Entry("parser-requires-additional-parameters", allowed_types=[list], default_value=[]), + Schema.Entry("parser-requires-context", allowed_types=[bool], default_value=False), + Schema.Entry("parser-requires-context-mode", allowed_types=[bool], default_value=False), + Schema.Entry("parser-requires-current-shorthand", allowed_types=[bool], default_value=False), + Schema.Entry("parser-requires-current-property", allowed_types=[bool], default_value=False), + Schema.Entry("parser-requires-quirks-mode", allowed_types=[bool], default_value=False), + Schema.Entry("parser-requires-value-pool", allowed_types=[bool], default_value=False), + Schema.Entry("settings-flag", allowed_types=[str]), + Schema.Entry("skip-codegen", allowed_types=[bool], default_value=False), + Schema.Entry("skip-parser", allowed_types=[bool], default_value=False), + ) + + def __init__(self, descriptor_name, **dictionary): + DescriptorCodeGenProperties.schema.set_attributes_from_dictionary(dictionary, instance=self) + self.descriptor_name = descriptor_name + + # By defining these to None, we can utilize the shared sorting method, StyleProperties._sort_by_descending_priority_and_name. + self.top_priority = None + self.high_priority = None + self.sink_priority = None + self.is_deferred = None + + def __str__(self): + return f"DescriptorCodeGenProperties {vars(self)}" + + def __repr__(self): + return self.__str__() + + @staticmethod + def from_json(parsing_context, key_path, name, json_value): + if type(json_value) is list: + json_value = parsing_context.select_enabled_variant(json_value, label=f"{key_path}.codegen-properties") + + assert(type(json_value) is dict) + DescriptorCodeGenProperties.schema.validate_dictionary(parsing_context, f"{key_path}.codegen-properties", json_value, label=f"DescriptorCodeGenProperties") + + descriptor_name = PropertyName(name) + + if "longhands" in json_value: + json_value["longhands"] = list(compact_map(lambda value: Longhand.from_json(parsing_context, f"{key_path}.codegen-properties", value), json_value["longhands"])) + if not json_value["longhands"]: + del json_value["longhands"] + + if json_value.get("parser-grammar"): + for entry_name in ["parser-function", "parser-requires-additional-parameters", "parser-requires-context", "parser-requires-context-mode", "parser-requires-current-shorthand", "parser-requires-current-property", "parser-requires-quirks-mode", "parser-requires-value-pool", "skip-parser", "longhands"]: + if entry_name in json_value: + raise Exception(f"{key_path} can't have both 'parser-grammar' and '{entry_name}.") + grammar = Grammar.from_json(parsing_context, f"{key_path}", name, json_value["parser-grammar"]) + grammar.perform_fixups(parsing_context.parsed_shared_grammar_rules) + json_value["parser-grammar"] = grammar + + if json_value.get("parser-function"): + for entry_name in ["skip-parser", "longhands"]: + if entry_name in json_value: + raise Exception(f"{key_path} can't have both 'parser-function' and '{entry_name}'.") + + return DescriptorCodeGenProperties(descriptor_name, **json_value) + + +class Descriptor: + schema = Schema( + Schema.Entry("codegen-properties", allowed_types=[dict, list]), + Schema.Entry("specification", allowed_types=[dict], convert_to=Specification), + Schema.Entry("status", allowed_types=[dict, str], convert_to=Status), + Schema.Entry("values", allowed_types=[list]), + ) + + def __init__(self, kind, descriptor_method_name_modifier, **dictionary): + Descriptor.schema.set_attributes_from_dictionary(dictionary, instance=self) + self.kind = kind + self.descriptor_method_name_modifier = descriptor_method_name_modifier + self.descriptor_name = self.codegen_properties.descriptor_name + + def __str__(self): + return f"{self.name} ({self.kind})" + + def __repr__(self): + return self.__str__() + + @staticmethod + def from_json(parsing_context, key_path, name, json_value, kind, descriptor_method_name_modifier): + assert(type(json_value) is dict) + Descriptor.schema.validate_dictionary(parsing_context, f"{key_path}.{name}", json_value, label=f"Descriptor") + + codegen_properties = DescriptorCodeGenProperties.from_json(parsing_context, f"{key_path}.{name}", name, json_value.get("codegen-properties", {})) + json_value["codegen-properties"] = codegen_properties + + if codegen_properties.enable_if is not None and not parsing_context.is_enabled(conditional=codegen_properties.enable_if): + if parsing_context.verbose: + print(f"SKIPPED {name} due to failing to satisfy 'enable-if' condition, '{json_value['codegen-properties'].enable_if}', with active macro set") + return None + + if codegen_properties.skip_codegen is not None and codegen_properties.skip_codegen: + if parsing_context.verbose: + print(f"SKIPPED {name} due to 'skip-codegen'") + return None + + if "values" in json_value: + values = list(filter(lambda value: value is not None, map(lambda value: Value.from_json(parsing_context, f"{key_path}.{name}", value), json_value["values"]))) + if codegen_properties.parser_grammar: + codegen_properties.parser_grammar.perform_fixups_for_values_references(values) + else: + codegen_properties.parser_grammar = Grammar.from_values(parsing_context, key_path, name, values) + json_value["values"] = values + + return Descriptor(kind, descriptor_method_name_modifier, **json_value) + + def perform_fixups_for_longhands(self, all_descriptors): + # If 'longhands' was specified, replace the names with references to the Descriptor objects. + if self.codegen_properties.longhands: + self.codegen_properties.longhands = [all_descriptors.all_by_name[longhand.value] for longhand in self.codegen_properties.longhands] + + def perform_fixups(self, all_descriptors): + self.perform_fixups_for_longhands(all_descriptors) + + @property + def id_without_prefix(self): + return self.descriptor_name.id_without_prefix + + # Used for parsing and consume methods. It is prefixed with a 'kind' for descriptors, and left unprefixed for style properties. + # Examples: + # style property 'column-width' would generate a consume method called `consumeColumnWidth` + # @font-face descriptor 'font-display' would generate a consume method called `consumeFontFaceFontDisplay` + @property + def name_for_parsing_methods(self): + return self.descriptor_method_name_modifier + self.descriptor_name.id_without_prefix + + @property + def id_without_prefix_with_lowercase_first_letter(self): + return self.descriptor_name.id_without_prefix_with_lowercase_first_letter + + @property + def id_without_scope(self): + return self.descriptor_name.id_without_scope + + @property + def id(self): + return self.descriptor_name.id + + @property + def name(self): + return self.descriptor_name.name + + @property + def aliases(self): + return self.codegen_properties.aliases + + +class Descriptors: + def __init__(self, kind, descriptor_method_name_modifier, descriptors): + self.kind = kind + self.descriptor_method_name_modifierdescriptor_method_name_modifier = descriptor_method_name_modifier + self.descriptors = descriptors + self.descriptors_by_name = {descriptor.name: descriptor for descriptor in descriptors} + self._all = None + self._perform_fixups() + + def __str__(self): + return f"{self.kind} descriptors" + + def __repr__(self): + return self.__str__() + + @staticmethod + def from_json(parsing_context, key_path, json_value, Subclass, kind, descriptor_method_name_modifier): + return Subclass(kind, descriptor_method_name_modifier, + *list( + filter( + lambda value: value is not None, + map( + lambda item: Descriptor.from_json(parsing_context, key_path, item[0], item[1], kind, descriptor_method_name_modifier), + json_value.items() + ) + ) + ) + ) + + # Updates any references to other properties that were by name (e.g. string) with a direct + # reference to the descriptor object. + def _perform_fixups(self): + for descriptor in self.all: + descriptor.perform_fixups(self) + + @property + def all(self): + if not self._all: + self._all = sorted(self.descriptors, key=functools.cmp_to_key(StyleProperties._sort_by_descending_priority_and_name)) + return self._all + + @property + def all_by_name(self): + return self.descriptors_by_name + + +class CounterStyleDescriptors(Descriptors): + def __init__(self, kind, descriptor_method_name_modifier, *descriptors): + super().__init__(kind, descriptor_method_name_modifier, descriptors) + + @staticmethod + def from_json(parsing_context, key_path, json_value): + return Descriptors.from_json(parsing_context, key_path, json_value, CounterStyleDescriptors, "@counter-style", "CounterStyle") + + +class FontFaceDescriptors(Descriptors): + def __init__(self, kind, descriptor_method_name_modifier, *descriptors): + super().__init__(kind, descriptor_method_name_modifier, descriptors) + + @staticmethod + def from_json(parsing_context, key_path, json_value): + return Descriptors.from_json(parsing_context, key_path, json_value, FontFaceDescriptors, "@font-face", "FontFace") + + +class FontPaletteValuesDescriptors(Descriptors): + def __init__(self, kind, descriptor_method_name_modifier, *descriptors): + super().__init__(kind, descriptor_method_name_modifier, descriptors) + + @staticmethod + def from_json(parsing_context, key_path, json_value): + return Descriptors.from_json(parsing_context, key_path, json_value, FontPaletteValuesDescriptors, "@font-palette-values", "FontPaletteValues") + + +class PropertyDescriptors(Descriptors): + def __init__(self, kind, descriptor_method_name_modifier, *descriptors): + super().__init__(kind, descriptor_method_name_modifier, descriptors) + + @staticmethod + def from_json(parsing_context, key_path, json_value): + return Descriptors.from_json(parsing_context, key_path, json_value, PropertyDescriptors, "@property", "Property") + + +class PropertiesAndDescriptors: + def __init__(self, style, counter_style, font_face, font_palette_values, property): + self.style = style + self.counter_style = counter_style + self.font_face = font_face + self.font_palette_values = font_palette_values + self.property = property + self._all_grouped_by_name = None + self._all_by_name = None + self._all_unique = None + self._settings_flags = None + + def __str__(self): + return "PropertiesAndDescriptors" + + def __repr__(self): + return self.__str__() + + @staticmethod + def from_json(parsing_context, *, properties_json_value, descriptors_json_value): + return PropertiesAndDescriptors( + StyleProperties.from_json(parsing_context, "properties", properties_json_value), + CounterStyleDescriptors.from_json(parsing_context, "descriptors.@counter-style", descriptors_json_value["@counter-style"]), + FontFaceDescriptors.from_json(parsing_context, "descriptors.@font-face", descriptors_json_value["@font-face"]), + FontPaletteValuesDescriptors.from_json(parsing_context, "descriptors.@font-palette-values", descriptors_json_value["@font-palette-values"]), + PropertyDescriptors.from_json(parsing_context, "descriptors.@property", descriptors_json_value["@property"]) + ) + + def _compute_all_grouped_by_name(self): + return [self.all_by_name[property.name] for property in self.all_unique] + + def _compute_all_by_name(self): + result = {} + for property in self.all_properties_and_descriptors: + result.setdefault(property.name, []).append(property) + return result + + def _compute_all_unique(self): + # NOTE: This is computes the ordered set of properties and descriptors that correspond to the CSSPropertyID + # enumeration and related lookup tables and functions. + + result = list(self.style.all) + name_set = set(self.style.all_by_name.keys()) + + for descriptor in self.all_descriptors: + if descriptor.name in name_set: + continue + result.append(descriptor) + name_set.add(descriptor.name) + + # FIXME: It doesn't make a lot of sense to sort the descriptors like this, but this maintains + # the current behavior and has no negative side effect. In the future, we should either separate + # the descriptors out of CSSPropertyID or the descriptor-only ones together in some fashion. + return sorted(result, key=functools.cmp_to_key(StyleProperties._sort_by_descending_priority_and_name)) + + # Returns a generator for the set of all properties and descriptors. + @property + def all_properties_and_descriptors(self): + return itertools.chain(self.style.all, self.counter_style.all, self.font_face.all, self.font_palette_values.all, self.property.all) + + # Returns a generator for the set of all properties and descriptors. + @property + def all_descriptors(self): + return itertools.chain(self.counter_style.all, self.font_face.all, self.font_palette_values.all, self.property.all) + + # Returns the set of properties and descriptors that have unique names, preferring style properties when + # there is a conflict. This set corresponds one-to-one in membership and order with CSSPropertyID. + @property + def all_unique(self): + if not self._all_unique: + self._all_unique = self._compute_all_unique() + return self._all_unique + + # Returns a parallel list to `all_unique`, but rather than containing the canonical property, each entry + # in this list is a list of all properties or descriptors with the unique name. + @property + def all_grouped_by_name(self): + if not self._all_grouped_by_name: + self._all_grouped_by_name = self._compute_all_grouped_by_name() + return self._all_grouped_by_name + + # Returns a map of names to lists of the properties or descriptors with that name. + @property + def all_by_name(self): + if not self._all_by_name: + self._all_by_name = self._compute_all_by_name() + return self._all_by_name + + # Returns a generator for the set of properties and descriptors that are conditionally included depending on settings. If two properties + # or descriptors have the same, we only return the canonical one and only if all the variants have settings flags. + # + # For example, there are two "speak-as" entries. One is a style property and the other is @counter-style descriptor. Only the one of the + # two, the @counter-style descriptor, has settings_flags set, so we don't return anything for that name. + @property + def all_unique_with_settings_flag(self): + return (property_set[0] for property_set in self.all_grouped_by_name if all(property.codegen_properties.settings_flag for property in property_set)) + + # Returns a generator for the subset of `self.all_unique` that are marked internal-only. + @property + def all_unique_internal_only(self): + return (property for property in self.all_unique if property.codegen_properties.internal_only) + + # Returns a generator for the subset of `self.all_unique` that are NOT marked internal. + @property + def all_unique_non_internal_only(self): + return (property for property in self.all_unique if not property.codegen_properties.internal_only) + + @property + def all_descriptor_only(self): + return (descriptor for descriptor in self.all_descriptors if descriptor.name not in self.style.all_by_name) + + # Returns the set of settings-flags used by any property or descriptor. Uniqued and sorted lexically. + @property + def settings_flags(self): + if not self._settings_flags: + self._settings_flags = sorted(list(set([property.codegen_properties.settings_flag for property in self.all_properties_and_descriptors if property.codegen_properties.settings_flag]))) + return self._settings_flags + + # MARK: - Property Parsing class Term: @@ -1427,6 +1756,14 @@ def from_node(node): return MatchOneTerm(**dictionary) + @staticmethod + def from_values(parsing_context, key_path, values): + dictionary = { + "value": list(compact_map(lambda value: value.keyword_term, values)) + } + + return MatchOneTerm(**dictionary) + @staticmethod def from_json(parsing_context, key_path, json_value): assert(type(json_value) is dict) @@ -1552,6 +1889,7 @@ class Grammar: def __init__(self, name, root_term): self.name = name self.root_term = root_term + self._fast_path_keyword_terms_sorted_by_name = None def __str__(self): return f"{self.name} {self.root_term}" @@ -1559,6 +1897,10 @@ def __str__(self): def __repr__(self): return self.__str__() + @staticmethod + def from_values(parsing_context, key_path, name, values): + return Grammar(name, MatchOneTerm.from_values(parsing_context, key_path, values)) + @staticmethod def from_json(parsing_context, key_path, name, json_value): if type(json_value) is str: @@ -1595,6 +1937,12 @@ def fast_path_keyword_terms(self): return self.root_term.fast_path_keyword_terms return [] + @property + def fast_path_keyword_terms_sorted_by_name(self): + if not self._fast_path_keyword_terms_sorted_by_name: + self._fast_path_keyword_terms_sorted_by_name = sorted(self.fast_path_keyword_terms, key=functools.cmp_to_key(StyleProperties._sort_with_prefixed_properties_last)) + return self._fast_path_keyword_terms_sorted_by_name + # A shared grammar rule and metadata describing it. Part of the set of rules tracked by SharedGrammarRules. class SharedGrammarRule: @@ -1664,6 +2012,7 @@ class TopLevelObject: Schema.Entry("categories", allowed_types=[dict], required=True), Schema.Entry("instructions", allowed_types=[list], required=True), Schema.Entry("properties", allowed_types=[dict], required=True), + Schema.Entry("descriptors", allowed_types=[dict], required=True), Schema.Entry("shared-grammar-rules", allowed_types=[dict], required=True), ) @@ -1675,7 +2024,7 @@ def __init__(self, json_value, *, defines_string, parsing_for_codegen, verbose): self.parsing_for_codegen = parsing_for_codegen self.verbose = verbose self.parsed_shared_grammar_rules = None - self.parsed_properties = None + self.parsed_properties_and_descriptors = None def parse_shared_grammar_rules(self): self.parsed_shared_grammar_rules = SharedGrammarRules( @@ -1690,18 +2039,8 @@ def parse_shared_grammar_rules(self): ) ) - def parse_properties(self): - self.parsed_properties = Properties( - *list( - filter( - lambda value: value is not None, - map( - lambda item: Property.from_json(self, "$properties", item[0], item[1]), - self.json_value["properties"].items() - ) - ) - ) - ) + def parse_properties_and_descriptors(self): + self.parsed_properties_and_descriptors = PropertiesAndDescriptors.from_json(self, properties_json_value=self.json_value["properties"], descriptors_json_value=self.json_value["descriptors"]) def is_enabled(self, *, conditional): if conditional[0] == '!': @@ -1722,8 +2061,8 @@ def select_enabled_variant(self, variants, *, label): # MARK: - Code Generation class GenerationContext: - def __init__(self, properties, shared_grammar_rules, *, verbose, gperf_executable): - self.properties = properties + def __init__(self, properties_and_descriptors, shared_grammar_rules, *, verbose, gperf_executable): + self.properties_and_descriptors = properties_and_descriptors self.shared_grammar_rules = shared_grammar_rules self.verbose = verbose self.gperf_executable = gperf_executable @@ -1804,7 +2143,7 @@ def generate_forward_declarations(self, *, to, structs=[], classes=[]): to.write(f"class {class_};") to.newline() - def generate_property_id_switch_function(self, *, to, signature, properties, mapping, default, prologue=None, epilogue=None): + def generate_property_id_switch_function(self, *, to, signature, iterable, mapping, default, mapping_to_property=lambda p: p, prologue=None, epilogue=None): to.write(f"{signature}") to.write(f"{{") @@ -1814,10 +2153,10 @@ def generate_property_id_switch_function(self, *, to, signature, properties, map to.write(f"switch (id) {{") - for property in properties: - to.write(f"case {property.id}:") + for item in iterable: + to.write(f"case {mapping_to_property(item).id}:") with to.indent(): - to.write(f"{mapping(property)}") + to.write(f"{mapping(item)}") to.write(f"default:") with to.indent(): @@ -1830,15 +2169,15 @@ def generate_property_id_switch_function(self, *, to, signature, properties, map to.write(f"}}") to.newline() - def generate_property_id_switch_function_bool(self, *, to, signature, properties): + def generate_property_id_switch_function_bool(self, *, to, signature, iterable, mapping_to_property=lambda p: p): to.write(f"{signature}") to.write(f"{{") with to.indent(): to.write(f"switch (id) {{") - for property in properties: - to.write(f"case {property.id}:") + for item in iterable: + to.write(f"case {mapping_to_property(item).id}:") with to.indent(): to.write(f"return true;") @@ -1857,9 +2196,13 @@ class GenerateCSSPropertyNames: def __init__(self, generation_context): self.generation_context = generation_context + @property + def properties_and_descriptors(self): + return self.generation_context.properties_and_descriptors + @property def properties(self): - return self.generation_context.properties + return self.generation_context.properties_and_descriptors.style def generate(self): self.generate_css_property_names_h() @@ -1920,14 +2263,14 @@ def _generate_css_property_names_gperf_prelude(self, *, to): static_assert(numCSSProperties + 1 <= 65535, "CSSPropertyID should fit into uint16_t."); """) - all_computed_property_ids = (f"{property.id}," for property in self.properties.all_computed) - to.write(f"const std::array computedPropertyIDs {{") + all_computed_property_ids = (f"{property.id}," for property in self.properties_and_descriptors.style.all_computed) + to.write(f"const std::array computedPropertyIDs {{") with to.indent(): to.write_lines(all_computed_property_ids) to.write("};") to.newline() - all_property_name_strings = quote_iterable(self.properties.all, "_s,") + all_property_name_strings = quote_iterable((f"{property.name}" for property in self.properties_and_descriptors.all_unique), "_s,") to.write(f"constexpr ASCIILiteral propertyNameStrings[numCSSProperties] = {{") with to.indent(): to.write_lines(all_property_name_strings) @@ -1961,10 +2304,10 @@ def _generate_gperf_declarations(self, *, to): """) def _generate_gperf_keywords(self, *, to): - # Concatenates a list of 'propererty-name, property-id' strings with a second list of 'property-alias, property-id' strings. + # Concatenates a list of unique 'propererty-name, property-id' strings with a second list of all 'property-alias, property-id' strings. all_property_names_and_aliases_with_ids = itertools.chain( - [f'{property.name}, {property.id}' for property in self.properties.all], - *[[f'{alias}, {property.id}' for alias in property.aliases] for property in self.properties.all] + [f'{property.name}, {property.id}' for property in self.properties_and_descriptors.all_unique], + *[[f'{alias}, {property.id}' for alias in property.aliases] for property in self.properties_and_descriptors.all_properties_and_descriptors] ) to.write("%%") @@ -2038,7 +2381,7 @@ def _generate_physical_logical_conversion_function(self, *, to, signature, sourc to.write(f"auto textflow = makeTextFlow(writingMode, direction);") to.write(f"switch (id) {{") - for group_name, property_group in sorted(self.properties.logical_property_groups.items(), key=lambda x: x[0]): + for group_name, property_group in sorted(self.properties_and_descriptors.style.logical_property_groups.items(), key=lambda x: x[0]): kind = property_group["kind"] kind_as_id = PropertyName.convert_name_to_id(kind) @@ -2067,7 +2410,7 @@ def _generate_is_exposed_functions(self, *, to): self.generation_context.generate_property_id_switch_function( to=to, signature="static bool isExposedNotInvalidAndNotInternal(CSSPropertyID id, const CSSPropertySettings& settings)", - properties=self.properties.all_with_settings_flag, + iterable=self.properties_and_descriptors.all_unique_with_settings_flag, mapping=lambda p: f"return settings.{p.codegen_properties.settings_flag};", default="return true;" ) @@ -2075,7 +2418,7 @@ def _generate_is_exposed_functions(self, *, to): self.generation_context.generate_property_id_switch_function( to=to, signature="static bool isExposedNotInvalidAndNotInternal(CSSPropertyID id, const Settings& settings)", - properties=self.properties.all_with_settings_flag, + iterable=self.properties_and_descriptors.all_unique_with_settings_flag, mapping=lambda p: f"return settings.{p.codegen_properties.settings_flag}();", default="return true;" ) @@ -2115,7 +2458,7 @@ def _generate_is_exposed_functions(self, *, to): """) def _generate_is_inherited_property(self, *, to): - all_inherited_and_ids = (f'{"true " if property.inherited else "false"}, // {property.id}' for property in self.properties.all) + all_inherited_and_ids = (f'{"true " if hasattr(property, "inherited") and property.inherited else "false"}, // {property.id}' for property in self.properties_and_descriptors.all_unique) to.write(f"constexpr bool isInheritedPropertyTable[numCSSProperties + {GenerationContext.number_of_predefined_properties}] = {{") with to.indent(): @@ -2139,7 +2482,7 @@ def _generate_are_in_same_logical_property_group_with_different_mappings_logic(s with to.indent(): to.write(f"switch (id1) {{") - for group_name, property_group in sorted(self.properties.logical_property_groups.items(), key=lambda x: x[0]): + for group_name, property_group in sorted(self.properties_and_descriptors.style.logical_property_groups.items(), key=lambda x: x[0]): logical = property_group["logical"] physical = property_group["physical"] for first in [logical, physical]: @@ -2166,7 +2509,7 @@ def _generate_are_in_same_logical_property_group_with_different_mappings_logic(s to.newline() def _generate_css_property_settings_constructor(self, *, to): - first_settings_initializer, *remaining_settings_initializers = [f"{flag} {{ settings.{flag}() }}" for flag in self.properties.settings_flags] + first_settings_initializer, *remaining_settings_initializers = [f"{flag} {{ settings.{flag}() }}" for flag in self.properties_and_descriptors.settings_flags] to.write(f"CSSPropertySettings::CSSPropertySettings(const Settings& settings)") with to.indent(): @@ -2178,7 +2521,7 @@ def _generate_css_property_settings_constructor(self, *, to): to.newline() def _generate_css_property_settings_operator_equal(self, *, to): - first, *middle, last = (f"a.{flag} == b.{flag}" for flag in self.properties.settings_flags) + first, *middle, last = (f"a.{flag} == b.{flag}" for flag in self.properties_and_descriptors.settings_flags) to.write(f"bool operator==(const CSSPropertySettings& a, const CSSPropertySettings& b)") to.write(f"{{") @@ -2192,7 +2535,7 @@ def _generate_css_property_settings_operator_equal(self, *, to): to.newline() def _generate_css_property_settings_hasher(self, *, to): - first, *middle, last = (f"settings.{flag} << {i}" for (i, flag) in enumerate(self.properties.settings_flags)) + first, *middle, last = (f"settings.{flag} << {i}" for (i, flag) in enumerate(self.properties_and_descriptors.settings_flags)) to.write(f"void add(Hasher& hasher, const CSSPropertySettings& settings)") to.write(f"{{") @@ -2229,7 +2572,7 @@ def generate_css_property_names_gperf(self): self.generation_context.generate_property_id_switch_function_bool( to=writer, signature="bool isInternal(CSSPropertyID id)", - properties=self.properties.all_internal_only + iterable=(p for p in self.properties_and_descriptors.all_unique if p.codegen_properties.internal_only) ) self._generate_is_exposed_functions( @@ -2243,7 +2586,7 @@ def generate_css_property_names_gperf(self): self.generation_context.generate_property_id_switch_function( to=writer, signature="CSSPropertyID relatedProperty(CSSPropertyID id)", - properties=(p for p in self.properties.all if p.codegen_properties.related_property), + iterable=(p for p in self.properties_and_descriptors.style.all if p.codegen_properties.related_property), mapping=lambda p: f"return {p.codegen_properties.related_property.id};", default="return CSSPropertyID::CSSPropertyInvalid;" ) @@ -2251,7 +2594,7 @@ def generate_css_property_names_gperf(self): self.generation_context.generate_property_id_switch_function( to=writer, signature="Vector CSSProperty::aliasesForProperty(CSSPropertyID id)", - properties=(p for p in self.properties.all if p.codegen_properties.aliases), + iterable=(p for p in self.properties_and_descriptors.style.all if p.codegen_properties.aliases), mapping=lambda p: f"return {{ {', '.join(quote_iterable(p.codegen_properties.aliases, '_s'))} }};", default="return { };" ) @@ -2259,13 +2602,13 @@ def generate_css_property_names_gperf(self): self.generation_context.generate_property_id_switch_function_bool( to=writer, signature="bool CSSProperty::isColorProperty(CSSPropertyID id)", - properties=(p for p in self.properties.all if p.codegen_properties.color_property) + iterable=(p for p in self.properties_and_descriptors.style.all if p.codegen_properties.color_property) ) self.generation_context.generate_property_id_switch_function( to=writer, signature="UChar CSSProperty::listValuedPropertySeparator(CSSPropertyID id)", - properties=(p for p in self.properties.all if p.codegen_properties.separator), + iterable=(p for p in self.properties_and_descriptors.style.all if p.codegen_properties.separator), mapping=lambda p: f"return '{ p.codegen_properties.separator[0] }';", default="break;", epilogue="return '\\0';" @@ -2274,13 +2617,13 @@ def generate_css_property_names_gperf(self): self.generation_context.generate_property_id_switch_function_bool( to=writer, signature="bool CSSProperty::isDirectionAwareProperty(CSSPropertyID id)", - properties=self.properties.all_direction_aware_properties + iterable=self.properties_and_descriptors.style.all_direction_aware_properties ) self.generation_context.generate_property_id_switch_function_bool( to=writer, signature="bool CSSProperty::isInLogicalPropertyGroup(CSSPropertyID id)", - properties=self.properties.all_in_logical_property_group + iterable=self.properties_and_descriptors.style.all_in_logical_property_group ) self._generate_are_in_same_logical_property_group_with_different_mappings_logic( @@ -2306,7 +2649,7 @@ def generate_css_property_names_gperf(self): self.generation_context.generate_property_id_switch_function_bool( to=writer, signature="bool CSSProperty::isDescriptorOnly(CSSPropertyID id)", - properties=(p for p in self.properties.all if p.codegen_properties.descriptor_only) + iterable=self.properties_and_descriptors.all_descriptor_only ) self._generate_css_property_settings_constructor( @@ -2347,7 +2690,7 @@ def _generate_css_property_names_h_property_constants(self, *, to): first_deferred_property = None last_deferred_property = None - for property in self.properties.all: + for property in self.properties_and_descriptors.all_unique: if property.codegen_properties.longhands: if not first_shorthand_property: first_shorthand_property = property @@ -2394,11 +2737,11 @@ def _generate_css_property_names_h_property_constants(self, *, to): to.write(f"constexpr auto lastShorthandProperty = {last_shorthand_property.id};") to.write(f"constexpr uint16_t numCSSPropertyLonghands = firstShorthandProperty - firstCSSProperty;") - to.write(f"extern const std::array computedPropertyIDs;") + to.write(f"extern const std::array computedPropertyIDs;") to.newline() def _generate_css_property_names_h_property_settings(self, *, to): - settings_variable_declarations = (f"bool {flag} {{ false }};" for flag in self.properties.settings_flags) + settings_variable_declarations = (f"bool {flag} {{ false }};" for flag in self.properties_and_descriptors.settings_flags) to.write(f"struct CSSPropertySettings {{") with to.indent(): @@ -2530,8 +2873,8 @@ def __init__(self, generation_context): self.generation_context = generation_context @property - def properties(self): - return self.generation_context.properties + def properties_and_descriptors(self): + return self.generation_context.properties_and_descriptors def generate(self): self.generate_css_style_declaration_property_names_idl() @@ -2592,7 +2935,7 @@ def generate_css_style_declaration_property_names_idl(self): ) name_or_alias_to_property = {} - for property in self.properties.all_non_internal_only: + for property in self.properties_and_descriptors.all_unique_non_internal_only: name_or_alias_to_property[property.name] = property for alias in property.aliases: name_or_alias_to_property[alias] = property @@ -2680,8 +3023,12 @@ def __init__(self, generation_context): self.generation_context = generation_context @property - def properties(self): - return self.generation_context.properties + def properties_and_descriptors(self): + return self.generation_context.properties_and_descriptors + + @property + def style_properties(self): + return self.generation_context.properties_and_descriptors.style def generate(self): self.generate_style_builder_generated_cpp() @@ -2923,7 +3270,7 @@ def converted_value(property): else: return "downcast(value)" - if property in self.properties.properties_by_name["font"].codegen_properties.longhands and "Initial" not in property.codegen_properties.custom and not property.codegen_properties.converter: + if property in self.style_properties.all_by_name["font"].codegen_properties.longhands and "Initial" not in property.codegen_properties.custom and not property.codegen_properties.converter: to.write(f"if (is(value) && CSSPropertyParserHelpers::isSystemFontShorthand(downcast(value).valueID())) {{") with to.indent(): to.write(f"applyInitial{property.id_without_prefix}(builderState);") @@ -2973,7 +3320,7 @@ def _generate_style_builder_generated_cpp_builder_functions_class(self, *, to): to.write(f"public:") with to.indent(): - for property in self.properties.all: + for property in self.style_properties.all: if property.codegen_properties.longhands: continue if property.codegen_properties.skip_builder: @@ -3015,7 +3362,13 @@ def scope_for_function(property, function): return "BuilderCustom" return "BuilderFunctions" - for property in self.properties.all: + for property in self.properties_and_descriptors.all_unique: + if not isinstance(property, Property): + to.write(f"case {property.id}:") + with to.indent(): + to.write(f"break;") + continue + if property.codegen_properties.synonym: continue @@ -3093,8 +3446,8 @@ def __init__(self, generation_context): self.generation_context = generation_context @property - def properties(self): - return self.generation_context.properties + def style_properties(self): + return self.generation_context.properties_and_descriptors.style def generate(self): self.generate_style_property_shorthand_functions_h() @@ -3104,7 +3457,7 @@ def generate(self): def _generate_style_property_shorthand_functions_declarations(self, *, to): # Skip non-shorthand properties (aka properties WITH longhands). - for property in self.properties.all_shorthands: + for property in self.style_properties.all_shorthands: to.write(f"StylePropertyShorthand {property.id_without_prefix_with_lowercase_first_letter}Shorthand();") to.newline() @@ -3133,7 +3486,7 @@ def generate_style_property_shorthand_functions_h(self): # MARK: - Helper generator functions for StylePropertyShorthandFunctions.cpp def _generate_style_property_shorthand_functions_accessors(self, *, to, longhand_to_shorthands, shorthand_to_longhand_count): - for property in self.properties.all_shorthands: + for property in self.style_properties.all_shorthands: to.write(f"StylePropertyShorthand {property.id_without_prefix_with_lowercase_first_letter}Shorthand()") to.write(f"{{") with to.indent(): @@ -3143,7 +3496,7 @@ def _generate_style_property_shorthand_functions_accessors(self, *, to, longhand shorthand_to_longhand_count[property] = 0 for longhand in property.codegen_properties.longhands: if longhand.name == "all": - for inner_property in self.properties.all_non_shorthands: + for inner_property in self.style_properties.all_non_shorthands: if inner_property.name == "direction" or inner_property.name == "unicode-bidi": continue longhand_to_shorthands.setdefault(inner_property, []) @@ -3225,7 +3578,7 @@ def generate_style_property_shorthand_functions_cpp(self): self.generation_context.generate_property_id_switch_function( to=writer, signature="StylePropertyShorthand shorthandForProperty(CSSPropertyID id)", - properties=self.properties.all_shorthands, + iterable=self.style_properties.all_shorthands, mapping=lambda p: f"return {p.id_without_prefix_with_lowercase_first_letter}Shorthand();", default="return { };" ) @@ -3243,7 +3596,7 @@ def __init__(self, generation_context): self.generation_context = generation_context # Create a handler for each property and add it to the `property_consumers` map. - self.property_consumers = {property: PropertyConsumer.make(property) for property in generation_context.properties.all} + self.property_consumers = {property: PropertyConsumer.make(property) for property in generation_context.properties_and_descriptors.all_properties_and_descriptors} self.shared_grammar_rule_consumers = {shared_grammar_rule: SharedGrammarRuleConsumer.make(shared_grammar_rule) for shared_grammar_rule in generation_context.shared_grammar_rules.all} def generate(self): @@ -3251,8 +3604,8 @@ def generate(self): self.generate_css_property_parsing_cpp() @property - def properties(self): - return self.generation_context.properties + def properties_and_descriptors(self): + return self.generation_context.properties_and_descriptors @property def shared_grammar_rules(self): @@ -3260,12 +3613,60 @@ def shared_grammar_rules(self): @property def all_property_consumers(self): - return (self.property_consumers[property] for property in self.properties.all) + return (self.property_consumers[property] for property in self.properties_and_descriptors.all_properties_and_descriptors) + + @property + def all_style_property_consumers(self): + return (self.property_consumers[property] for property in self.properties_and_descriptors.style.all) + + @property + def all_counter_style_descriptor_property_consumers(self): + return (self.property_consumers[property] for property in self.properties_and_descriptors.counter_style.all) + + @property + def all_font_face_descriptor_property_consumers(self): + return (self.property_consumers[property] for property in self.properties_and_descriptors.font_face.all) + + @property + def all_font_palette_values_descriptor_property_consumers(self): + return (self.property_consumers[property] for property in self.properties_and_descriptors.font_palette_values.all) + + @property + def all_property_descriptor_property_consumers(self): + return (self.property_consumers[property] for property in self.properties_and_descriptors.property.all) @property def all_shared_grammar_rule_consumers(self): return (self.shared_grammar_rule_consumers[shared_grammar_rule] for shared_grammar_rule in self.shared_grammar_rules.all) + @property + def all_property_parsing_collections(self): + ParsingCollection = collections.namedtuple('ParsingCollection', ['parsing_suffix', 'rule', 'noun', 'supports_current_shorthand', 'property_consumers']) + + return [ + ParsingCollection('StyleProperty', 'style', 'style', True, list(self.all_style_property_consumers)), + ParsingCollection('CounterStyleDescriptor', '@counter-style', 'descriptor', False, list(self.all_counter_style_descriptor_property_consumers)), + ParsingCollection('FontFaceDescriptor', '@font-face', 'descriptor', False, list(self.all_font_face_descriptor_property_consumers)), + ParsingCollection('FontPaletteValuesDescriptor', '@font-palette-values', 'descriptor', False, list(self.all_font_palette_values_descriptor_property_consumers)), + ParsingCollection('PropertyDescriptor', '@property', 'descriptor', False, list(self.all_property_descriptor_property_consumers)), + ] + + @property + def all_property_consumers_grouped_by_kind(self): + return [ + ("style property", list(self.all_style_property_consumers)), + ("@counter-style", list(self.all_counter_style_descriptor_property_consumers)), + ("@font-face", list(self.all_font_face_descriptor_property_consumers)), + ("@font-palette-values", list(self.all_font_palette_values_descriptor_property_consumers)), + ("@property", list(self.all_property_descriptor_property_consumers)), + ] + + @property + def all_consumers_grouped_by_kind(self): + return self.all_property_consumers_grouped_by_kind + [ + ("shared", list(self.all_shared_grammar_rule_consumers)) + ] + def generate_css_property_parsing_h(self): with open('CSSPropertyParsing.h', 'w') as output_file: writer = Writer(output_file) @@ -3337,19 +3738,25 @@ def generate_css_property_parsing_cpp(self): to=writer ) - self._generate_css_property_parsing_cpp_parse( - to=writer - ) + for parsing_collection in self.all_property_parsing_collections: + self._generate_css_property_parsing_cpp_parse_property( + to=writer, + parsing_collection=parsing_collection + ) - self._generate_css_property_parsing_cpp_is_keyword_valid_aggregate( - to=writer - ) + keyword_fast_path_eligible_property_consumers = [property_consumer for property_consumer in parsing_collection.property_consumers if property_consumer.keyword_fast_path_generator] - self.generation_context.generate_property_id_switch_function_bool( - to=writer, - signature="bool CSSPropertyParsing::isKeywordProperty(CSSPropertyID id)", - properties=self.properties.all_with_fast_path_keyword_terms, - ) + self._generate_css_property_parsing_cpp_is_keyword_valid_for_property( + to=writer, + parsing_collection=parsing_collection, + keyword_fast_path_eligible_property_consumers=keyword_fast_path_eligible_property_consumers + ) + + self._generate_css_property_parsing_cpp_is_keyword_fast_path_eligible_for_property( + to=writer, + parsing_collection=parsing_collection, + keyword_fast_path_eligible_property_consumers=keyword_fast_path_eligible_property_consumers + ) # MARK: - Helper generator functions for CSSPropertyParsing.h @@ -3357,56 +3764,68 @@ def _generate_css_property_parsing_h_property_parsing_declaration(self, *, to): to.write(f"struct CSSPropertyParsing {{") with to.indent(): - to.write_block("""\ - // Parse and return a single value. - static RefPtr parse(CSSParserTokenRange&, CSSPropertyID id, CSSPropertyID currentShorthand, const CSSParserContext&); - - // Returns true if the bare keyword value forms a valid construction when used with the - // provided property. - static bool isKeywordValidForProperty(CSSPropertyID, CSSValueID, const CSSParserContext&); - - // Returns true for properties that are valid to pass to `isKeywordValidForProperty`. This - // corresponds to the set of properties where a bare keyword value is a valid construction. - // NOTE: This will return true for properties that allow values that aren't keywords. All - // that it validates is that the property can be valid with a keyword value. (For example, - // 'list-style-type' supports a litany of keyword values, but also supports a string value.) - static bool isKeywordProperty(CSSPropertyID); - """) + for parsing_collection in self.all_property_parsing_collections: + to.write(f"// Parse and return a single longhand {parsing_collection.rule} {parsing_collection.noun}.") + if parsing_collection.supports_current_shorthand: + to.write(f"static RefPtr parse{parsing_collection.parsing_suffix}(CSSParserTokenRange&, CSSPropertyID id, CSSPropertyID currentShorthand, const CSSParserContext&);") + else: + to.write(f"static RefPtr parse{parsing_collection.parsing_suffix}(CSSParserTokenRange&, CSSPropertyID id, const CSSParserContext&);") + to.write(f"// Fast path bare-keyword support.") + to.write(f"static bool isKeywordValidFor{parsing_collection.parsing_suffix}(CSSPropertyID, CSSValueID, const CSSParserContext&);") + to.write(f"static bool isKeywordFastPathEligible{parsing_collection.parsing_suffix}(CSSPropertyID);") + to.newline() - for property_consumer in self.all_property_consumers: - property_consumer.generate_export_declaration(to=to) + to.write(f"// Direct consumers.") - for shared_grammar_rule_consumer in self.all_shared_grammar_rule_consumers: - shared_grammar_rule_consumer.generate_export_declaration(to=to) + for description, consumers in self.all_consumers_grouped_by_kind: + if any(consumer.is_exported for consumer in consumers): + to.newline() + to.write(f"// Exported {description} consumers.") + for consumer in (consumer for consumer in consumers if consumer.is_exported): + consumer.generate_export_declaration(to=to) to.write(f"}};") to.newline() # MARK: - Helper generator functions for CSSPropertyParsing.cpp - def _generate_css_property_parsing_cpp_is_keyword_valid_aggregate(self, *, to): - to.write(f"bool CSSPropertyParsing::isKeywordValidForProperty(CSSPropertyID id, CSSValueID keyword, const CSSParserContext& context)") - to.write(f"{{") - - with to.indent(): - to.write(f"switch (id) {{") + def _generate_css_property_parsing_cpp_is_keyword_valid_for_property(self, *, to, parsing_collection, keyword_fast_path_eligible_property_consumers): + if not keyword_fast_path_eligible_property_consumers: + to.write(f"bool CSSPropertyParsing::isKeywordValidFor{parsing_collection.parsing_suffix}(CSSPropertyID, CSSValueID, const CSSParserContext&)") + to.write(f"{{") + with to.indent(): + to.write(f"return false;") + to.write(f"}}") + to.newline() + return - for property_consumer in self.all_property_consumers: - keyword_fast_path_generator = property_consumer.keyword_fast_path_generator - if not keyword_fast_path_generator: - continue + requires_context = any(propopery_consumer.keyword_fast_path_generator.requires_context for propopery_consumer in keyword_fast_path_eligible_property_consumers) - to.write(f"case {property_consumer.property.id}:") - with to.indent(): - to.write(f"return {keyword_fast_path_generator.generate_call_string(keyword_string='keyword', context_string='context')};") + self.generation_context.generate_property_id_switch_function( + to=to, + signature=f"bool CSSPropertyParsing::isKeywordValidFor{parsing_collection.parsing_suffix}(CSSPropertyID id, CSSValueID keyword, const CSSParserContext&{' context' if requires_context else ''})", + iterable=keyword_fast_path_eligible_property_consumers, + mapping=lambda property_consumer: f"return {property_consumer.keyword_fast_path_generator.generate_call_string(keyword_string='keyword', context_string='context')};", + default="return false;", + mapping_to_property=lambda property_consumer: property_consumer.property + ) - to.write(f"default:") + def _generate_css_property_parsing_cpp_is_keyword_fast_path_eligible_for_property(self, *, to, parsing_collection, keyword_fast_path_eligible_property_consumers): + if not keyword_fast_path_eligible_property_consumers: + to.write(f"bool CSSPropertyParsing::isKeywordFastPathEligible{parsing_collection.parsing_suffix}(CSSPropertyID)") + to.write(f"{{") with to.indent(): to.write(f"return false;") - to.write(f"}}") - to.write(f"}}") - to.newline() + to.newline() + return + + self.generation_context.generate_property_id_switch_function_bool( + to=to, + signature=f"bool CSSPropertyParsing::isKeywordFastPathEligible{parsing_collection.parsing_suffix}(CSSPropertyID id)", + iterable=keyword_fast_path_eligible_property_consumers, + mapping_to_property=lambda property_consumer: property_consumer.property + ) def _generate_css_property_parsing_cpp_property_parsing_functions(self, *, to): # First generate definitions for all the keyword-only fast path predicate functions. @@ -3426,12 +3845,18 @@ def _generate_css_property_parsing_cpp_property_parsing_functions(self, *, to): if property_consumer.property.codegen_properties.parser_exported: property_consumer.generate_definition(to=to) - # And finally all the exported shared grammar rule consumers. + # And finally all the exported shared grammar rule consumers (these will be static members of the CSSPropertyParsing struct). for shared_grammar_rule_consumer in self.all_shared_grammar_rule_consumers: shared_grammar_rule_consumer.generate_definition(to=to) - def _generate_css_property_parsing_cpp_parse(self, *, to): - to.write(f"RefPtr CSSPropertyParsing::parse(CSSParserTokenRange& range, CSSPropertyID id, CSSPropertyID currentShorthand, const CSSParserContext& context)") + def _generate_css_property_parsing_cpp_parse_property(self, *, to, parsing_collection): + if parsing_collection.supports_current_shorthand: + to.write(f"RefPtr CSSPropertyParsing::parse{parsing_collection.parsing_suffix}(CSSParserTokenRange& range, CSSPropertyID id, CSSPropertyID currentShorthand, const CSSParserContext& context)") + current_shorthand_string = "currentShorthand" + else: + to.write(f"RefPtr CSSPropertyParsing::parse{parsing_collection.parsing_suffix}(CSSParserTokenRange& range, CSSPropertyID id, const CSSParserContext& context)") + current_shorthand_string = None + to.write(f"{{") with to.indent(): to.write(f"if (!isExposed(id, context.propertySettings) && !isInternal(id)) {{") @@ -3447,11 +3872,11 @@ def _generate_css_property_parsing_cpp_parse(self, *, to): PropertyReturnExpression = collections.namedtuple('PropertyReturnExpression', ['property', 'return_expression']) property_and_return_expressions = [] - for property_consumer in self.all_property_consumers: + for property_consumer in parsing_collection.property_consumers: return_expression = property_consumer.generate_call_string( range_string="range", id_string="id", - current_shorthand_string="currentShorthand", + current_shorthand_string=current_shorthand_string, context_string="context") if return_expression is None: @@ -3473,7 +3898,7 @@ def _generate_css_property_parsing_cpp_parse(self, *, to): property_and_return_expressions_grouped_by_expression.append(PropertiesReturnExpression(properties, return_expression)) def _sort_by_first_property(a, b): - return Properties._sort_by_descending_priority_and_name(a.properties[0], b.properties[0]) + return StyleProperties._sort_by_descending_priority_and_name(a.properties[0], b.properties[0]) to.write(f"switch (id) {{") for properties, return_expression in sorted(property_and_return_expressions_grouped_by_expression, key=functools.cmp_to_key(_sort_by_first_property)): @@ -3920,6 +4345,7 @@ def generate_definition(self, *, to): # # def generate_export_declaration(self, *, to): # def generate_definition(self, *, to): +# var is_exported # class SharedGrammarRuleConsumer(object): @staticmethod @@ -3939,6 +4365,10 @@ def __str__(self): def __repr__(self): return self.__str__() + @property + def is_exported(self): + return False + def generate_export_declaration(self, *, to): pass @@ -3973,6 +4403,10 @@ def _build_signature(shared_grammar_rule, requires_context): name=f"consume{shared_grammar_rule.name_for_methods.id_without_prefix}", parameters=GeneratedSharedGrammarRuleConsumer._build_parameters(requires_context)) + @property + def is_exported(self): + return True + def generate_export_declaration(self, *, to): to.write(f"static {self.signature.declaration_string};") @@ -4021,22 +4455,23 @@ def generate_definition(self, *, to): # called from the main `parse` function. If the parser grammar allows for any keyword only valid # parses (e.g. for the grammar [ none | ], "none" is a valid keyword only parse), these # property consumers will also emit a `keyword-only fast path` function (e.g. `isKeywordValidFor*`) -# and ensure that it is called from the main `isKeywordValidForProperty` function. +# and ensure that it is called from the main `isKeywordValidForStyleProperty` function. # # `PropertyConsumer` abstract interface: # # def generate_call_string(self, *, range_string, id_string, current_shorthand_string, context_string): # def generate_export_declaration(self, *, to): # def generate_definition(self, *, to): +# var is_exported # var keyword_fast_path_generator class PropertyConsumer(object): @staticmethod def make(property): - if property.codegen_properties.longhands or property.codegen_properties.descriptor_only or property.codegen_properties.skip_parser: + if property.codegen_properties.longhands or property.codegen_properties.skip_parser: return SkipPropertyConsumer(property) - if property.codegen_properties.custom_parser or property.codegen_properties.parser_function: + if property.codegen_properties.parser_function: return CustomPropertyConsumer(property) if property.codegen_properties.parser_grammar: @@ -4046,11 +4481,6 @@ def make(property): return DirectPropertyConsumer(property) return GeneratedPropertyConsumer(property) - if property.has_only_keyword_terms: - if property.has_only_fast_path_keyword_terms: - return FastPathKeywordOnlyPropertyConsumer(property) - return GeneratedPropertyConsumer(property) - raise Exception(f"Invalid property definition for '{property.id}'. Style properties must either specify values or a custom parser.") @@ -4060,7 +4490,7 @@ def __init__(self, property): self.property = property def __str__(self): - return "SkipPropertyConsumer" + return f"SkipPropertyConsumer for {self.property}" def __repr__(self): return self.__str__() @@ -4074,6 +4504,10 @@ def generate_export_declaration(self, *, to): def generate_definition(self, *, to): pass + @property + def is_exported(self): + return False + @property def keyword_fast_path_generator(self): return None @@ -4085,7 +4519,7 @@ def __init__(self, property): self.property = property def __str__(self): - return "CustomPropertyConsumer" + return f"CustomPropertyConsumer for {self.property}" def __repr__(self): return self.__str__() @@ -4107,8 +4541,7 @@ def generate_call_string(self, *, range_string, id_string, current_shorthand_str parameters.append("CSSValuePool::singleton()") parameters += self.property.codegen_properties.parser_requires_additional_parameters - # If a "parser-function" has been specified, use that, otherwise assume the 'consume' function uses the property name. - function = self.property.codegen_properties.parser_function or f"consume{self.property.id_without_prefix}" + function = self.property.codegen_properties.parser_function # Merge the scope, function and parameters to form the final invocation. return f"{function}({', '.join(parameters)})" @@ -4119,6 +4552,10 @@ def generate_export_declaration(self, *, to): def generate_definition(self, *, to): pass + @property + def is_exported(self): + return False + @property def keyword_fast_path_generator(self): return None @@ -4128,12 +4565,12 @@ def keyword_fast_path_generator(self): class FastPathKeywordOnlyPropertyConsumer(PropertyConsumer): def __init__(self, property): self.property = property - self.keyword_fast_path_generator = KeywordFastPathGenerator(f"isKeywordValidFor{property.id_without_prefix}", property.fast_path_keyword_terms_sorted_by_name) + self.keyword_fast_path_generator = KeywordFastPathGenerator(f"isKeywordValidFor{property.name_for_parsing_methods}", property.codegen_properties.parser_grammar.fast_path_keyword_terms_sorted_by_name) self.term_generator = TermGeneratorFastPathKeywordTerms(self.keyword_fast_path_generator) self.signature = FastPathKeywordOnlyPropertyConsumer._build_signature(property, self.keyword_fast_path_generator) def __str__(self): - return "FastPathKeywordOnlyPropertyConsumer" + return f"FastPathKeywordOnlyPropertyConsumer for {self.property}" def __repr__(self): return self.__str__() @@ -4156,7 +4593,7 @@ def _build_signature(property, keyword_fast_path_generator): return FunctionSignature( result_type="RefPtr", scope=FastPathKeywordOnlyPropertyConsumer._build_scope(property), - name=f"consume{property.id_without_prefix}", + name=f"consume{property.name_for_parsing_methods}", parameters=FastPathKeywordOnlyPropertyConsumer._build_parameters(keyword_fast_path_generator)) def generate_call_string(self, *, range_string, id_string=None, current_shorthand_string=None, context_string): @@ -4167,12 +4604,16 @@ def generate_call_string(self, *, range_string, id_string=None, current_shorthan # For "direct" and "fast-path keyword only" consumers, we only generate the property specific # defintion if the property has been marked as exported. + @property + def is_exported(self): + return self.property.codegen_properties.parser_exported + def generate_export_declaration(self, *, to): - if self.property.codegen_properties.parser_exported: + if self.is_exported: to.write(f"static {self.signature.declaration_string};") def generate_definition(self, *, to): - if self.property.codegen_properties.parser_exported: + if self.is_exported: to.write(f"{self.signature.definition_string}") to.write(f"{{") with to.indent(): @@ -4190,7 +4631,7 @@ def __init__(self, property): self.signature = DirectPropertyConsumer._build_signature(self.property, self.term_generator) def __str__(self): - return "DirectPropertyConsumer" + return f"DirectPropertyConsumer for {self.property}" def __repr__(self): return self.__str__() @@ -4213,7 +4654,7 @@ def _build_signature(property, term_generator): return FunctionSignature( result_type="RefPtr", scope=DirectPropertyConsumer._build_scope(property), - name=f"consume{property.id_without_prefix}", + name=f"consume{property.name_for_parsing_methods}", parameters=DirectPropertyConsumer._build_parameters(term_generator)) def generate_call_string(self, *, range_string, id_string=None, current_shorthand_string=None, context_string): @@ -4225,12 +4666,16 @@ def generate_call_string(self, *, range_string, id_string=None, current_shorthan # For "direct" and "fast-path keyword only" consumers, we only generate the property specific # defintion if the property has been marked as exported. + @property + def is_exported(self): + return self.property.codegen_properties.parser_exported + def generate_export_declaration(self, *, to): - if self.property.codegen_properties.parser_exported: + if self.is_exported: to.write(f"static {self.signature.declaration_string};") def generate_definition(self, *, to): - if self.property.codegen_properties.parser_exported: + if self.is_exported: to.write(f"{self.signature.definition_string}") to.write(f"{{") with to.indent(): @@ -4253,7 +4698,7 @@ def __init__(self, property): self.signature = GeneratedPropertyConsumer._build_signature(property, self.requires_context) def __str__(self): - return "GeneratedPropertyConsumer" + return f"GeneratedPropertyConsumer for {self.property}" def __repr__(self): return self.__str__() @@ -4276,13 +4721,13 @@ def _build_signature(property, requires_context): return FunctionSignature( result_type="RefPtr", scope=GeneratedPropertyConsumer._build_scope(property), - name=f"consume{property.id_without_prefix}", + name=f"consume{property.name_for_parsing_methods}", parameters=GeneratedPropertyConsumer._build_parameters(property, requires_context)) @staticmethod def _build_keyword_fast_path_generator(property): - if property.has_fast_path_keyword_terms: - return KeywordFastPathGenerator(f"isKeywordValidFor{property.id_without_prefix}", property.fast_path_keyword_terms_sorted_by_name) + if property.codegen_properties.parser_grammar.has_fast_path_keyword_terms: + return KeywordFastPathGenerator(f"isKeywordValidFor{property.name_for_parsing_methods}", property.codegen_properties.parser_grammar.fast_path_keyword_terms_sorted_by_name) return None def generate_call_string(self, *, range_string, id_string, current_shorthand_string, context_string): @@ -4291,12 +4736,16 @@ def generate_call_string(self, *, range_string, id_string, current_shorthand_str parameters += [context_string] return self.signature.generate_call_string(parameters) + @property + def is_exported(self): + return self.property.codegen_properties.parser_exported + def generate_export_declaration(self, *, to): - if self.property.codegen_properties.parser_exported: + if self.is_exported: to.write(f"static {self.signature.declaration_string};") def generate_definition(self, *, to): - if self.property.codegen_properties.parser_exported: + if self.is_exported: to.write(f"{self.signature.definition_string}") else: to.write(f"static {self.signature.definition_string}") @@ -5015,12 +5464,18 @@ def main(): parsing_context = ParsingContext(properties_json, defines_string=args.defines, parsing_for_codegen=True, verbose=args.verbose) parsing_context.parse_shared_grammar_rules() - parsing_context.parse_properties() + parsing_context.parse_properties_and_descriptors() if args.verbose: - print(f"{len(parsing_context.parsed_properties.properties)} properties active for code generation") - - generation_context = GenerationContext(parsing_context.parsed_properties, parsing_context.parsed_shared_grammar_rules, verbose=args.verbose, gperf_executable=args.gperf_executable) + print(f"{len(parsing_context.parsed_shared_grammar_rules.rules)} shared grammar rules active for code generation") + print(f"{len(parsing_context.parsed_properties_and_descriptors.style.all)} style properties active for code generation") + print(f"{len(parsing_context.parsed_properties_and_descriptors.counter_style.all)} @counter-style descriptors active for code generation") + print(f"{len(parsing_context.parsed_properties_and_descriptors.font_face.all)} @font-face descriptors active for code generation") + print(f"{len(parsing_context.parsed_properties_and_descriptors.font_palette_values.all)} @font-palette-values descriptors active for code generation") + print(f"{len(parsing_context.parsed_properties_and_descriptors.property.all)} @property descriptors active for code generation") + print(f"{len(parsing_context.parsed_properties_and_descriptors.all_unique)} uniquely named properties and descriptors active for code generation") + + generation_context = GenerationContext(parsing_context.parsed_properties_and_descriptors, parsing_context.parsed_shared_grammar_rules, verbose=args.verbose, gperf_executable=args.gperf_executable) generators = [ GenerateCSSPropertyNames, diff --git a/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp b/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp index d9b4b88a3c7b..94c1236f5b4e 100644 --- a/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp +++ b/Source/WebCore/inspector/agents/InspectorCSSAgent.cpp @@ -866,10 +866,10 @@ Protocol::ErrorStringOr>> Insp property->setLonghands(WTFMove(longhands)); } - if (CSSParserFastPaths::isKeywordPropertyID(propertyID)) { + if (CSSParserFastPaths::isKeywordFastPathEligibleStyleProperty(propertyID)) { auto values = JSON::ArrayOf::create(); for (auto valueID : allCSSValueKeywords()) { - if (CSSParserFastPaths::isValidKeywordPropertyAndValue(propertyID, valueID, strictCSSParserContext())) + if (CSSParserFastPaths::isKeywordValidForStyleProperty(propertyID, valueID, strictCSSParserContext())) values->addItem(nameString(valueID)); } if (values->length()) diff --git a/Tools/Scripts/webkitpy/style/checkers/jsonchecker.py b/Tools/Scripts/webkitpy/style/checkers/jsonchecker.py index 665af0753afd..3fc9b2bd0478 100644 --- a/Tools/Scripts/webkitpy/style/checkers/jsonchecker.py +++ b/Tools/Scripts/webkitpy/style/checkers/jsonchecker.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011 Apple Inc. All rights reserved. +# Copyright (C) 2011-2022 Apple Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -131,6 +131,12 @@ def check(self, lines): self._shared_grammar_rules = properties_definition['shared-grammar-rules'] self.check_shared_grammar_rules() + if 'descriptors' not in properties_definition: + self._handle_style_error(0, 'json/syntax', 5, '"descriptors" key not found, the key is mandatory.') + return + self._descriptors = properties_definition['descriptors'] + self.check_descriptors() + if 'properties' not in properties_definition: self._handle_style_error(0, 'json/syntax', 5, '"properties" key not found, the key is mandatory.') return @@ -178,6 +184,42 @@ def check_shared_grammar_rule(self, rule_name, rule_value): keys_and_validators[key](rule_name, "", key, value) + def check_descriptor(self, descriptor_name, descriptor_value): + keys_and_validators = { + '*': self.validate_comment, + 'values': self.validate_array, + 'codegen-properties': self.validate_codegen_properties, + 'status': self.validate_status, + 'specification': self.validate_specification, + } + + for key, value in descriptor_value.items(): + if key not in keys_and_validators: + self._handle_style_error(0, 'json/syntax', 5, 'dictionary for descriptor "%s" has unexpected key "%s".' % (property_name, key)) + return + + keys_and_validators[key](descriptor_name, "", key, value) + + def check_descriptor_kind(self, descriptor_kind, descriptors): + if descriptor_kind[0] != "@": + self._handle_style_error(0, 'json/syntax', 5, '"descriptors" key "%s" does not begin with an "@".' % (descriptor_kind)) + return + + if not isinstance(descriptors, dict): + self._handle_style_error(0, 'json/syntax', 5, '"descriptors.%s" is not a dictionary.' % (descriptor_kind)) + return + + for descriptor_name, descriptor_value in descriptors.items(): + self.check_descriptor(descriptor_name, descriptor_value) + + def check_descriptors(self): + if not isinstance(self._descriptors, dict): + self._handle_style_error(0, 'json/syntax', 5, '"descriptors" is not a dictionary.') + return + + for descriptor_kind, descriptors in self._descriptors.items(): + self.check_descriptor_kind(descriptor_kind, descriptors) + def check_shared_grammar_rules(self): if not isinstance(self._shared_grammar_rules, dict): self._handle_style_error(0, 'json/syntax', 5, '"shared-grammar-rules" is not a dictionary.') @@ -357,7 +399,6 @@ def check_codegen_properties(self, property_name, codegen_properties): 'converter': self.validate_string, 'custom': self.validate_string, 'custom-parser': self.validate_boolean, - 'descriptor-only': self.validate_boolean, 'enable-if': self.validate_string, 'fast-path-inherited': self.validate_boolean, 'fill-layer-property': self.validate_boolean,