1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Copyright (C) 2016 Apple Inc. All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "CSSPropertyParser.h"
33 #include "CSSAspectRatioValue.h"
34 #include "CSSBasicShapes.h"
35 #include "CSSBorderImage.h"
36 #include "CSSBorderImageSliceValue.h"
37 #include "CSSContentDistributionValue.h"
38 #include "CSSCursorImageValue.h"
39 #include "CSSCustomIdentValue.h"
40 #include "CSSCustomPropertyValue.h"
41 #include "CSSFontFaceSrcValue.h"
42 #include "CSSFontFeatureValue.h"
43 #if ENABLE(VARIATION_FONTS)
44 #include "CSSFontVariationValue.h"
46 #include "CSSFontStyleRangeValue.h"
47 #include "CSSFontStyleValue.h"
48 #include "CSSFunctionValue.h"
49 #include "CSSGridAutoRepeatValue.h"
50 #include "CSSGridLineNamesValue.h"
51 #include "CSSGridTemplateAreasValue.h"
52 #include "CSSLineBoxContainValue.h"
53 #include "CSSParserFastPaths.h"
54 #include "CSSParserIdioms.h"
55 #include "CSSPendingSubstitutionValue.h"
56 #include "CSSPrimitiveValueMappings.h"
57 #include "CSSPropertyParserHelpers.h"
58 #include "CSSReflectValue.h"
59 #include "CSSShadowValue.h"
60 #include "CSSTimingFunctionValue.h"
61 #include "CSSUnicodeRangeValue.h"
62 #include "CSSVariableParser.h"
63 #include "CSSVariableReferenceValue.h"
65 #if ENABLE(DASHBOARD_SUPPORT)
66 #include "DashboardRegion.h"
69 #include "HashTools.h"
70 // FIXME-NEWPARSER: Replace Pair and Rect with actual CSSValue subclasses (CSSValuePair and CSSQuadValue).
73 #include "RenderTheme.h"
74 #include "RuntimeEnabledFeatures.h"
75 #include "SVGPathByteStream.h"
76 #include "SVGPathUtilities.h"
77 #include "StyleBuilderConverter.h"
78 #include "StylePropertyShorthand.h"
79 #include "StylePropertyShorthandFunctions.h"
80 #include "StyleResolver.h"
83 #include <wtf/text/StringBuilder.h>
90 bool isCustomPropertyName(const String& propertyName)
92 return propertyName.length() > 2 && propertyName.characterAt(0) == '-' && propertyName.characterAt(1) == '-';
95 static bool hasPrefix(const char* string, unsigned length, const char* prefix)
97 for (unsigned i = 0; i < length; ++i) {
100 if (string[i] != prefix[i])
106 #if PLATFORM(IOS_FAMILY)
107 void cssPropertyNameIOSAliasing(const char* propertyName, const char*& propertyNameAlias, unsigned& newLength)
109 if (!strcmp(propertyName, "-webkit-hyphenate-locale")) {
110 // Worked in iOS 4.2.
111 static const char webkitLocale[] = "-webkit-locale";
112 propertyNameAlias = webkitLocale;
113 newLength = strlen(webkitLocale);
118 template <typename CharacterType>
119 static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length)
121 char buffer[maxCSSPropertyNameLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
123 for (unsigned i = 0; i != length; ++i) {
124 CharacterType c = propertyName[i];
126 return CSSPropertyInvalid; // illegal character
127 buffer[i] = toASCIILower(c);
129 buffer[length] = '\0';
131 const char* name = buffer;
132 if (buffer[0] == '-') {
133 #if ENABLE(LEGACY_CSS_VENDOR_PREFIXES)
134 // If the prefix is -apple- or -khtml-, change it to -webkit-.
135 // This makes the string one character longer.
136 if (RuntimeEnabledFeatures::sharedFeatures().legacyCSSVendorPrefixesEnabled()
137 && (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-"))) {
138 memmove(buffer + 7, buffer + 6, length + 1 - 6);
139 memcpy(buffer, "-webkit", 7);
143 #if PLATFORM(IOS_FAMILY)
144 cssPropertyNameIOSAliasing(buffer, name, length);
148 const Property* hashTableEntry = findProperty(name, length);
149 if (hashTableEntry) {
150 auto propertyID = static_cast<CSSPropertyID>(hashTableEntry->id);
151 if (isEnabledCSSProperty(propertyID))
154 return CSSPropertyInvalid;
157 static bool isAppleLegacyCssValueKeyword(const char* valueKeyword, unsigned length)
159 static const char applePrefix[] = "-apple-";
160 static const char appleSystemPrefix[] = "-apple-system";
161 static const char applePayPrefix[] = "-apple-pay";
162 static const char* appleWirelessPlaybackTargetActive = getValueName(CSSValueAppleWirelessPlaybackTargetActive);
164 return hasPrefix(valueKeyword, length, applePrefix)
165 && !hasPrefix(valueKeyword, length, appleSystemPrefix)
166 && !hasPrefix(valueKeyword, length, applePayPrefix)
167 && !WTF::equal(reinterpret_cast<const LChar*>(valueKeyword), reinterpret_cast<const LChar*>(appleWirelessPlaybackTargetActive), length);
170 template <typename CharacterType>
171 static CSSValueID cssValueKeywordID(const CharacterType* valueKeyword, unsigned length)
173 char buffer[maxCSSValueKeywordLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
175 for (unsigned i = 0; i != length; ++i) {
176 CharacterType c = valueKeyword[i];
178 return CSSValueInvalid; // illegal keyword.
179 buffer[i] = WTF::toASCIILower(c);
181 buffer[length] = '\0';
183 if (buffer[0] == '-') {
184 // If the prefix is -apple- or -khtml-, change it to -webkit-.
185 // This makes the string one character longer.
186 // On iOS we don't want to change values starting with -apple-system to -webkit-system.
187 // FIXME: Remove this mangling without breaking the web.
188 if (isAppleLegacyCssValueKeyword(buffer, length) || hasPrefix(buffer, length, "-khtml-")) {
189 memmove(buffer + 7, buffer + 6, length + 1 - 6);
190 memcpy(buffer, "-webkit", 7);
195 const Value* hashTableEntry = findValue(buffer, length);
196 return hashTableEntry ? static_cast<CSSValueID>(hashTableEntry->id) : CSSValueInvalid;
199 CSSValueID cssValueKeywordID(StringView string)
201 unsigned length = string.length();
203 return CSSValueInvalid;
204 if (length > maxCSSValueKeywordLength)
205 return CSSValueInvalid;
207 return string.is8Bit() ? cssValueKeywordID(string.characters8(), length) : cssValueKeywordID(string.characters16(), length);
210 CSSPropertyID cssPropertyID(StringView string)
212 unsigned length = string.length();
215 return CSSPropertyInvalid;
216 if (length > maxCSSPropertyNameLength)
217 return CSSPropertyInvalid;
219 return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length);
222 using namespace CSSPropertyParserHelpers;
224 CSSPropertyParser::CSSPropertyParser(const CSSParserTokenRange& range, const CSSParserContext& context, Vector<CSSProperty, 256>* parsedProperties, bool consumeWhitespace)
227 , m_parsedProperties(parsedProperties)
229 if (consumeWhitespace)
230 m_range.consumeWhitespace();
233 void CSSPropertyParser::addProperty(CSSPropertyID property, CSSPropertyID currentShorthand, Ref<CSSValue>&& value, bool important, bool implicit)
235 if (!isEnabledCSSProperty(property))
238 int shorthandIndex = 0;
239 bool setFromShorthand = false;
241 if (currentShorthand) {
242 auto shorthands = matchingShorthandsForLonghand(property);
243 setFromShorthand = true;
244 if (shorthands.size() > 1)
245 shorthandIndex = indexOfShorthandForLonghand(currentShorthand, shorthands);
248 m_parsedProperties->append(CSSProperty(property, WTFMove(value), important, setFromShorthand, shorthandIndex, implicit));
251 void CSSPropertyParser::addExpandedPropertyForValue(CSSPropertyID property, Ref<CSSValue>&& value, bool important)
253 const StylePropertyShorthand& shorthand = shorthandForProperty(property);
254 unsigned shorthandLength = shorthand.length();
255 ASSERT(shorthandLength);
256 const CSSPropertyID* longhands = shorthand.properties();
257 for (unsigned i = 0; i < shorthandLength; ++i)
258 addProperty(longhands[i], property, value.copyRef(), important);
261 bool CSSPropertyParser::parseValue(CSSPropertyID propertyID, bool important, const CSSParserTokenRange& range, const CSSParserContext& context, ParsedPropertyVector& parsedProperties, StyleRule::Type ruleType)
263 int parsedPropertiesSize = parsedProperties.size();
265 CSSPropertyParser parser(range, context, &parsedProperties);
268 #if ENABLE(CSS_DEVICE_ADAPTATION)
269 if (ruleType == StyleRule::Viewport)
270 parseSuccess = parser.parseViewportDescriptor(propertyID, important);
273 if (ruleType == StyleRule::FontFace)
274 parseSuccess = parser.parseFontFaceDescriptor(propertyID);
276 parseSuccess = parser.parseValueStart(propertyID, important);
279 parsedProperties.shrink(parsedPropertiesSize);
284 RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, const CSSParserTokenRange& range, const CSSParserContext& context)
286 CSSPropertyParser parser(range, context, nullptr);
287 RefPtr<CSSValue> value = parser.parseSingleValue(property);
288 if (!value || !parser.m_range.atEnd())
293 bool CSSPropertyParser::canParseTypedCustomPropertyValue(const String& syntax, const CSSParserTokenRange& tokens, const CSSParserContext& context)
295 CSSPropertyParser parser(tokens, context, nullptr);
296 return parser.canParseTypedCustomPropertyValue(syntax);
299 RefPtr<CSSCustomPropertyValue> CSSPropertyParser::parseTypedCustomPropertyValue(const String& name, const String& syntax, const CSSParserTokenRange& tokens, const StyleResolver& styleResolver, const CSSParserContext& context)
301 CSSPropertyParser parser(tokens, context, nullptr, false);
302 RefPtr<CSSCustomPropertyValue> value = parser.parseTypedCustomPropertyValue(name, syntax, styleResolver);
303 if (!value || !parser.m_range.atEnd())
308 void CSSPropertyParser::collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies, const CSSParserTokenRange& tokens, const CSSParserContext& context)
310 CSSPropertyParser parser(tokens, context, nullptr);
311 parser.collectParsedCustomPropertyValueDependencies(syntax, isRoot, dependencies);
314 static bool isLegacyBreakProperty(CSSPropertyID propertyID)
316 switch (propertyID) {
317 case CSSPropertyPageBreakAfter:
318 case CSSPropertyPageBreakBefore:
319 case CSSPropertyPageBreakInside:
320 case CSSPropertyWebkitColumnBreakAfter:
321 case CSSPropertyWebkitColumnBreakBefore:
322 case CSSPropertyWebkitColumnBreakInside:
330 bool CSSPropertyParser::parseValueStart(CSSPropertyID propertyID, bool important)
332 if (consumeCSSWideKeyword(propertyID, important))
335 CSSParserTokenRange originalRange = m_range;
336 bool isShorthand = isShorthandCSSProperty(propertyID);
339 // Variable references will fail to parse here and will fall out to the variable ref parser below.
340 if (parseShorthand(propertyID, important))
342 } else if (isLegacyBreakProperty(propertyID)) {
343 // FIXME-NEWPARSER: Can turn this into a shorthand once old parser is gone, and then
344 // we don't need the special case.
345 if (consumeLegacyBreakProperty(propertyID, important))
348 RefPtr<CSSValue> parsedValue = parseSingleValue(propertyID);
349 if (parsedValue && m_range.atEnd()) {
350 addProperty(propertyID, CSSPropertyInvalid, *parsedValue, important);
355 if (CSSVariableParser::containsValidVariableReferences(originalRange, m_context)) {
356 RefPtr<CSSVariableReferenceValue> variable = CSSVariableReferenceValue::create(originalRange);
359 RefPtr<CSSPendingSubstitutionValue> pendingValue = CSSPendingSubstitutionValue::create(propertyID, variable.releaseNonNull());
360 addExpandedPropertyForValue(propertyID, pendingValue.releaseNonNull(), important);
362 addProperty(propertyID, CSSPropertyInvalid, variable.releaseNonNull(), important);
369 bool CSSPropertyParser::consumeCSSWideKeyword(CSSPropertyID propertyID, bool important)
371 CSSParserTokenRange rangeCopy = m_range;
372 CSSValueID valueID = rangeCopy.consumeIncludingWhitespace().id();
373 if (!rangeCopy.atEnd())
376 RefPtr<CSSValue> value;
377 if (valueID == CSSValueInherit)
378 value = CSSValuePool::singleton().createInheritedValue();
379 else if (valueID == CSSValueInitial)
380 value = CSSValuePool::singleton().createExplicitInitialValue();
381 else if (valueID == CSSValueUnset)
382 value = CSSValuePool::singleton().createUnsetValue();
383 else if (valueID == CSSValueRevert)
384 value = CSSValuePool::singleton().createRevertValue();
388 const StylePropertyShorthand& shorthand = shorthandForProperty(propertyID);
389 if (!shorthand.length()) {
390 if (CSSProperty::isDescriptorOnly(propertyID))
392 addProperty(propertyID, CSSPropertyInvalid, value.releaseNonNull(), important);
394 addExpandedPropertyForValue(propertyID, value.releaseNonNull(), important);
399 bool CSSPropertyParser::consumeTransformOrigin(bool important)
401 RefPtr<CSSPrimitiveValue> resultX;
402 RefPtr<CSSPrimitiveValue> resultY;
403 if (consumeOneOrTwoValuedPosition(m_range, m_context.mode, UnitlessQuirk::Forbid, resultX, resultY)) {
404 m_range.consumeWhitespace();
405 bool atEnd = m_range.atEnd();
406 RefPtr<CSSPrimitiveValue> resultZ = consumeLength(m_range, m_context.mode, ValueRangeAll);
410 addProperty(CSSPropertyTransformOriginX, CSSPropertyTransformOrigin, resultX.releaseNonNull(), important);
411 addProperty(CSSPropertyTransformOriginY, CSSPropertyTransformOrigin, resultY.releaseNonNull(), important);
412 addProperty(CSSPropertyTransformOriginZ, CSSPropertyTransformOrigin, resultZ ? resultZ.releaseNonNull() : CSSValuePool::singleton().createValue(0, CSSPrimitiveValue::UnitType::CSS_PX), important, !hasZ);
419 bool CSSPropertyParser::consumePerspectiveOrigin(bool important)
421 RefPtr<CSSPrimitiveValue> resultX;
422 RefPtr<CSSPrimitiveValue> resultY;
423 if (consumePosition(m_range, m_context.mode, UnitlessQuirk::Forbid, resultX, resultY)) {
424 addProperty(CSSPropertyPerspectiveOriginX, CSSPropertyPerspectiveOrigin, resultX.releaseNonNull(), important);
425 addProperty(CSSPropertyPerspectiveOriginY, CSSPropertyPerspectiveOrigin, resultY.releaseNonNull(), important);
431 // Methods for consuming non-shorthand properties starts here.
432 static RefPtr<CSSValue> consumeWillChange(CSSParserTokenRange& range)
434 if (range.peek().id() == CSSValueAuto)
435 return consumeIdent(range);
437 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
438 // Every comma-separated list of identifiers is a valid will-change value,
439 // unless the list includes an explicitly disallowed identifier.
441 if (range.peek().type() != IdentToken)
443 CSSPropertyID propertyID = cssPropertyID(range.peek().value());
444 if (propertyID != CSSPropertyInvalid) {
445 // Now "all" is used by both CSSValue and CSSPropertyValue.
446 // Need to return nullptr when currentValue is CSSPropertyAll.
447 if (propertyID == CSSPropertyWillChange || propertyID == CSSPropertyAll)
449 // FIXME-NEWPARSER: Use CSSCustomIdentValue someday.
450 values->append(CSSValuePool::singleton().createIdentifierValue(propertyID));
451 range.consumeIncludingWhitespace();
453 switch (range.peek().id()) {
457 case CSSValueDefault:
458 case CSSValueInitial:
459 case CSSValueInherit:
461 case CSSValueContents:
462 case CSSValueScrollPosition:
463 values->append(consumeIdent(range).releaseNonNull());
466 // Append properties we don't recognize, but that are legal, as strings.
467 values->append(consumeCustomIdent(range).releaseNonNull());
474 if (!consumeCommaIncludingWhitespace(range))
481 static RefPtr<CSSFontFeatureValue> consumeFontFeatureTag(CSSParserTokenRange& range)
483 // Feature tag name consists of 4-letter characters.
484 static const unsigned tagNameLength = 4;
486 const CSSParserToken& token = range.consumeIncludingWhitespace();
487 // Feature tag name comes first
488 if (token.type() != StringToken)
490 if (token.value().length() != tagNameLength)
494 for (unsigned i = 0; i < tag.size(); ++i) {
495 // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
496 UChar character = token.value()[i];
497 if (character < 0x20 || character > 0x7E)
499 tag[i] = toASCIILower(character);
503 if (!range.atEnd() && range.peek().type() != CommaToken) {
504 // Feature tag values could follow: <integer> | on | off
505 if (auto primitiveValue = consumeInteger(range, 0))
506 tagValue = primitiveValue->intValue();
507 else if (range.peek().id() == CSSValueOn || range.peek().id() == CSSValueOff)
508 tagValue = range.consumeIncludingWhitespace().id() == CSSValueOn;
512 return CSSFontFeatureValue::create(WTFMove(tag), tagValue);
515 static RefPtr<CSSValue> consumeFontFeatureSettings(CSSParserTokenRange& range)
517 if (range.peek().id() == CSSValueNormal)
518 return consumeIdent(range);
519 RefPtr<CSSValueList> settings = CSSValueList::createCommaSeparated();
521 RefPtr<CSSFontFeatureValue> fontFeatureValue = consumeFontFeatureTag(range);
522 if (!fontFeatureValue)
524 settings->append(fontFeatureValue.releaseNonNull());
525 } while (consumeCommaIncludingWhitespace(range));
529 #if ENABLE(VARIATION_FONTS)
530 static RefPtr<CSSValue> consumeFontVariationTag(CSSParserTokenRange& range)
532 if (range.peek().type() != StringToken)
535 auto string = range.consumeIncludingWhitespace().value().toString();
538 if (string.length() != tag.size())
540 for (unsigned i = 0; i < tag.size(); ++i) {
541 // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
542 UChar character = string[i];
543 if (character < 0x20 || character > 0x7E)
552 auto success = consumeNumberRaw(range, tagValue);
556 return CSSFontVariationValue::create(tag, tagValue);
559 static RefPtr<CSSValue> consumeFontVariationSettings(CSSParserTokenRange& range)
561 if (range.peek().id() == CSSValueNormal)
562 return consumeIdent(range);
564 auto settings = CSSValueList::createCommaSeparated();
566 RefPtr<CSSValue> variationValue = consumeFontVariationTag(range);
569 settings->append(variationValue.releaseNonNull());
570 } while (consumeCommaIncludingWhitespace(range));
572 if (!settings->length())
575 return WTFMove(settings);
577 #endif // ENABLE(VARIATION_FONTS)
579 static RefPtr<CSSValue> consumePage(CSSParserTokenRange& range)
581 if (range.peek().id() == CSSValueAuto)
582 return consumeIdent(range);
583 return consumeCustomIdent(range);
586 static RefPtr<CSSValue> consumeQuotes(CSSParserTokenRange& range)
588 if (range.peek().id() == CSSValueNone)
589 return consumeIdent(range);
590 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
591 while (!range.atEnd()) {
592 RefPtr<CSSPrimitiveValue> parsedValue = consumeString(range);
595 values->append(parsedValue.releaseNonNull());
597 if (values->length() && values->length() % 2 == 0)
602 class FontVariantLigaturesParser {
604 FontVariantLigaturesParser()
605 : m_sawCommonLigaturesValue(false)
606 , m_sawDiscretionaryLigaturesValue(false)
607 , m_sawHistoricalLigaturesValue(false)
608 , m_sawContextualLigaturesValue(false)
609 , m_result(CSSValueList::createSpaceSeparated())
613 enum class ParseResult {
619 ParseResult consumeLigature(CSSParserTokenRange& range)
621 CSSValueID valueID = range.peek().id();
623 case CSSValueNoCommonLigatures:
624 case CSSValueCommonLigatures:
625 if (m_sawCommonLigaturesValue)
626 return ParseResult::DisallowedValue;
627 m_sawCommonLigaturesValue = true;
629 case CSSValueNoDiscretionaryLigatures:
630 case CSSValueDiscretionaryLigatures:
631 if (m_sawDiscretionaryLigaturesValue)
632 return ParseResult::DisallowedValue;
633 m_sawDiscretionaryLigaturesValue = true;
635 case CSSValueNoHistoricalLigatures:
636 case CSSValueHistoricalLigatures:
637 if (m_sawHistoricalLigaturesValue)
638 return ParseResult::DisallowedValue;
639 m_sawHistoricalLigaturesValue = true;
641 case CSSValueNoContextual:
642 case CSSValueContextual:
643 if (m_sawContextualLigaturesValue)
644 return ParseResult::DisallowedValue;
645 m_sawContextualLigaturesValue = true;
648 return ParseResult::UnknownValue;
650 m_result->append(consumeIdent(range).releaseNonNull());
651 return ParseResult::ConsumedValue;
654 RefPtr<CSSValue> finalizeValue()
656 if (!m_result->length())
657 return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
658 return WTFMove(m_result);
662 bool m_sawCommonLigaturesValue;
663 bool m_sawDiscretionaryLigaturesValue;
664 bool m_sawHistoricalLigaturesValue;
665 bool m_sawContextualLigaturesValue;
666 RefPtr<CSSValueList> m_result;
669 static RefPtr<CSSValue> consumeFontVariantLigatures(CSSParserTokenRange& range)
671 if (range.peek().id() == CSSValueNormal || range.peek().id() == CSSValueNone)
672 return consumeIdent(range);
674 FontVariantLigaturesParser ligaturesParser;
676 if (ligaturesParser.consumeLigature(range) !=
677 FontVariantLigaturesParser::ParseResult::ConsumedValue)
679 } while (!range.atEnd());
681 return ligaturesParser.finalizeValue();
684 static RefPtr<CSSValue> consumeFontVariantEastAsian(CSSParserTokenRange& range)
686 if (range.peek().id() == CSSValueNormal)
687 return consumeIdent(range);
689 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
690 FontVariantEastAsianVariant variant = FontVariantEastAsianVariant::Normal;
691 FontVariantEastAsianWidth width = FontVariantEastAsianWidth::Normal;
692 FontVariantEastAsianRuby ruby = FontVariantEastAsianRuby::Normal;
694 while (!range.atEnd()) {
695 if (range.peek().type() != IdentToken)
698 auto id = range.peek().id();
702 variant = FontVariantEastAsianVariant::Jis78;
705 variant = FontVariantEastAsianVariant::Jis83;
708 variant = FontVariantEastAsianVariant::Jis90;
711 variant = FontVariantEastAsianVariant::Jis04;
713 case CSSValueSimplified:
714 variant = FontVariantEastAsianVariant::Simplified;
716 case CSSValueTraditional:
717 variant = FontVariantEastAsianVariant::Traditional;
719 case CSSValueFullWidth:
720 width = FontVariantEastAsianWidth::Full;
722 case CSSValueProportionalWidth:
723 width = FontVariantEastAsianWidth::Proportional;
726 ruby = FontVariantEastAsianRuby::Yes;
732 range.consumeIncludingWhitespace();
736 case FontVariantEastAsianVariant::Normal:
738 case FontVariantEastAsianVariant::Jis78:
739 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis78));
741 case FontVariantEastAsianVariant::Jis83:
742 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis83));
744 case FontVariantEastAsianVariant::Jis90:
745 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis90));
747 case FontVariantEastAsianVariant::Jis04:
748 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis04));
750 case FontVariantEastAsianVariant::Simplified:
751 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueSimplified));
753 case FontVariantEastAsianVariant::Traditional:
754 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueTraditional));
759 case FontVariantEastAsianWidth::Normal:
761 case FontVariantEastAsianWidth::Full:
762 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueFullWidth));
764 case FontVariantEastAsianWidth::Proportional:
765 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueProportionalWidth));
770 case FontVariantEastAsianRuby::Normal:
772 case FontVariantEastAsianRuby::Yes:
773 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueRuby));
776 if (!values->length())
782 static RefPtr<CSSPrimitiveValue> consumeFontVariantCaps(CSSParserTokenRange& range)
784 return consumeIdent<CSSValueNormal, CSSValueSmallCaps, CSSValueAllSmallCaps,
785 CSSValuePetiteCaps, CSSValueAllPetiteCaps,
786 CSSValueUnicase, CSSValueTitlingCaps>(range);
789 static RefPtr<CSSPrimitiveValue> consumeFontVariantAlternates(CSSParserTokenRange& range)
791 return consumeIdent<CSSValueNormal, CSSValueHistoricalForms>(range);
794 static RefPtr<CSSPrimitiveValue> consumeFontVariantPosition(CSSParserTokenRange& range)
796 return consumeIdent<CSSValueNormal, CSSValueSub, CSSValueSuper>(range);
799 class FontVariantNumericParser {
801 FontVariantNumericParser()
802 : m_sawNumericFigureValue(false)
803 , m_sawNumericSpacingValue(false)
804 , m_sawNumericFractionValue(false)
805 , m_sawOrdinalValue(false)
806 , m_sawSlashedZeroValue(false)
807 , m_result(CSSValueList::createSpaceSeparated())
811 enum class ParseResult {
817 ParseResult consumeNumeric(CSSParserTokenRange& range)
819 CSSValueID valueID = range.peek().id();
821 case CSSValueLiningNums:
822 case CSSValueOldstyleNums:
823 if (m_sawNumericFigureValue)
824 return ParseResult::DisallowedValue;
825 m_sawNumericFigureValue = true;
827 case CSSValueProportionalNums:
828 case CSSValueTabularNums:
829 if (m_sawNumericSpacingValue)
830 return ParseResult::DisallowedValue;
831 m_sawNumericSpacingValue = true;
833 case CSSValueDiagonalFractions:
834 case CSSValueStackedFractions:
835 if (m_sawNumericFractionValue)
836 return ParseResult::DisallowedValue;
837 m_sawNumericFractionValue = true;
839 case CSSValueOrdinal:
840 if (m_sawOrdinalValue)
841 return ParseResult::DisallowedValue;
842 m_sawOrdinalValue = true;
844 case CSSValueSlashedZero:
845 if (m_sawSlashedZeroValue)
846 return ParseResult::DisallowedValue;
847 m_sawSlashedZeroValue = true;
850 return ParseResult::UnknownValue;
852 m_result->append(consumeIdent(range).releaseNonNull());
853 return ParseResult::ConsumedValue;
856 RefPtr<CSSValue> finalizeValue()
858 if (!m_result->length())
859 return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
860 return WTFMove(m_result);
865 bool m_sawNumericFigureValue;
866 bool m_sawNumericSpacingValue;
867 bool m_sawNumericFractionValue;
868 bool m_sawOrdinalValue;
869 bool m_sawSlashedZeroValue;
870 RefPtr<CSSValueList> m_result;
873 static RefPtr<CSSValue> consumeFontVariantNumeric(CSSParserTokenRange& range)
875 if (range.peek().id() == CSSValueNormal)
876 return consumeIdent(range);
878 FontVariantNumericParser numericParser;
880 if (numericParser.consumeNumeric(range) !=
881 FontVariantNumericParser::ParseResult::ConsumedValue)
883 } while (!range.atEnd());
885 return numericParser.finalizeValue();
888 static RefPtr<CSSPrimitiveValue> consumeFontVariantCSS21(CSSParserTokenRange& range)
890 return consumeIdent<CSSValueNormal, CSSValueSmallCaps>(range);
893 static RefPtr<CSSPrimitiveValue> consumeFontWeightKeywordValue(CSSParserTokenRange& range)
895 return consumeIdent<CSSValueNormal, CSSValueBold, CSSValueBolder, CSSValueLighter>(range);
898 static RefPtr<CSSPrimitiveValue> consumeFontWeight(CSSParserTokenRange& range)
900 if (auto result = consumeFontWeightKeywordValue(range))
902 return consumeFontWeightNumber(range);
905 #if ENABLE(VARIATION_FONTS)
906 static RefPtr<CSSValue> consumeFontWeightRange(CSSParserTokenRange& range)
908 if (auto result = consumeFontWeightKeywordValue(range))
910 auto firstNumber = consumeFontWeightNumber(range);
915 auto secondNumber = consumeFontWeightNumber(range);
916 if (!secondNumber || firstNumber->floatValue() > secondNumber->floatValue())
918 auto result = CSSValueList::createSpaceSeparated();
919 result->append(firstNumber.releaseNonNull());
920 result->append(secondNumber.releaseNonNull());
921 return RefPtr<CSSValue>(WTFMove(result));
925 static RefPtr<CSSPrimitiveValue> consumeFontStretchKeywordValue(CSSParserTokenRange& range)
927 return consumeIdent<CSSValueUltraCondensed, CSSValueExtraCondensed, CSSValueCondensed, CSSValueSemiCondensed, CSSValueNormal, CSSValueSemiExpanded, CSSValueExpanded, CSSValueExtraExpanded, CSSValueUltraExpanded>(range);
930 #if ENABLE(VARIATION_FONTS)
931 static bool fontStretchIsWithinRange(float stretch)
937 static RefPtr<CSSPrimitiveValue> consumeFontStretch(CSSParserTokenRange& range)
939 if (auto result = consumeFontStretchKeywordValue(range))
941 #if ENABLE(VARIATION_FONTS)
942 if (auto percent = consumePercent(range, ValueRangeNonNegative))
943 return fontStretchIsWithinRange(percent->value<float>()) ? percent : nullptr;
948 #if ENABLE(VARIATION_FONTS)
949 static RefPtr<CSSValue> consumeFontStretchRange(CSSParserTokenRange& range)
951 if (auto result = consumeFontStretchKeywordValue(range))
953 auto firstPercent = consumePercent(range, ValueRangeNonNegative);
954 if (!firstPercent || !fontStretchIsWithinRange(firstPercent->value<float>()))
958 auto secondPercent = consumePercent(range, ValueRangeNonNegative);
959 if (!secondPercent || !fontStretchIsWithinRange(secondPercent->value<float>()) || firstPercent->floatValue() > secondPercent->floatValue())
961 auto result = CSSValueList::createSpaceSeparated();
962 result->append(firstPercent.releaseNonNull());
963 result->append(secondPercent.releaseNonNull());
964 return RefPtr<CSSValue>(WTFMove(result));
968 static RefPtr<CSSPrimitiveValue> consumeFontStyleKeywordValue(CSSParserTokenRange& range)
970 return consumeIdent<CSSValueNormal, CSSValueItalic, CSSValueOblique>(range);
973 #if ENABLE(VARIATION_FONTS)
974 static bool fontStyleIsWithinRange(float oblique)
976 return oblique > -90 && oblique < 90;
980 static RefPtr<CSSFontStyleValue> consumeFontStyle(CSSParserTokenRange& range, CSSParserMode cssParserMode)
982 auto result = consumeFontStyleKeywordValue(range);
986 auto valueID = result->valueID();
987 if (valueID == CSSValueNormal || valueID == CSSValueItalic)
988 return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(valueID));
989 ASSERT(result->valueID() == CSSValueOblique);
990 #if ENABLE(VARIATION_FONTS)
991 if (!range.atEnd()) {
992 if (auto angle = consumeAngle(range, cssParserMode)) {
993 if (fontStyleIsWithinRange(angle->value<float>(CSSPrimitiveValue::CSS_DEG)))
994 return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueOblique), WTFMove(angle));
999 UNUSED_PARAM(cssParserMode);
1001 return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueOblique));
1004 #if ENABLE(VARIATION_FONTS)
1005 static RefPtr<CSSFontStyleRangeValue> consumeFontStyleRange(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1007 auto keyword = consumeFontStyleKeywordValue(range);
1011 if (keyword->valueID() != CSSValueOblique || range.atEnd())
1012 return CSSFontStyleRangeValue::create(keyword.releaseNonNull());
1014 if (auto firstAngle = consumeAngle(range, cssParserMode)) {
1015 if (!fontStyleIsWithinRange(firstAngle->value<float>(CSSPrimitiveValue::CSS_DEG)))
1017 if (range.atEnd()) {
1018 auto result = CSSValueList::createSpaceSeparated();
1019 result->append(firstAngle.releaseNonNull());
1020 return CSSFontStyleRangeValue::create(keyword.releaseNonNull(), WTFMove(result));
1022 auto secondAngle = consumeAngle(range, cssParserMode);
1023 if (!secondAngle || !fontStyleIsWithinRange(secondAngle->value<float>(CSSPrimitiveValue::CSS_DEG)) || firstAngle->floatValue(CSSPrimitiveValue::CSS_DEG) > secondAngle->floatValue(CSSPrimitiveValue::CSS_DEG))
1025 auto result = CSSValueList::createSpaceSeparated();
1026 result->append(firstAngle.releaseNonNull());
1027 result->append(secondAngle.releaseNonNull());
1028 return CSSFontStyleRangeValue::create(keyword.releaseNonNull(), WTFMove(result));
1035 static String concatenateFamilyName(CSSParserTokenRange& range)
1037 StringBuilder builder;
1038 bool addedSpace = false;
1039 const CSSParserToken& firstToken = range.peek();
1040 while (range.peek().type() == IdentToken) {
1041 if (!builder.isEmpty()) {
1042 builder.append(' ');
1045 builder.append(range.consumeIncludingWhitespace().value());
1047 if (!addedSpace && isCSSWideKeyword(firstToken.id()))
1049 return builder.toString();
1052 static RefPtr<CSSValue> consumeFamilyName(CSSParserTokenRange& range)
1054 if (range.peek().type() == StringToken)
1055 return CSSValuePool::singleton().createFontFamilyValue(range.consumeIncludingWhitespace().value().toString());
1056 if (range.peek().type() != IdentToken)
1058 String familyName = concatenateFamilyName(range);
1059 if (familyName.isNull())
1061 return CSSValuePool::singleton().createFontFamilyValue(familyName);
1064 static RefPtr<CSSValue> consumeGenericFamily(CSSParserTokenRange& range)
1066 return consumeIdentRange(range, CSSValueSerif, CSSValueWebkitBody);
1069 static RefPtr<CSSValueList> consumeFontFamily(CSSParserTokenRange& range)
1071 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
1073 RefPtr<CSSValue> parsedValue = consumeGenericFamily(range);
1075 list->append(parsedValue.releaseNonNull());
1077 parsedValue = consumeFamilyName(range);
1079 list->append(parsedValue.releaseNonNull());
1084 } while (consumeCommaIncludingWhitespace(range));
1088 static RefPtr<CSSValueList> consumeFontFamilyDescriptor(CSSParserTokenRange& range)
1090 // FIXME-NEWPARSER: For compatibility with the old parser, we have to make
1091 // a list here, even though the list always contains only a single family name.
1092 // Once the old parser is gone, we can delete this function, make the caller
1093 // use consumeFamilyName instead, and then patch the @font-face code to
1094 // not expect a list with a single name in it.
1095 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
1096 RefPtr<CSSValue> parsedValue = consumeFamilyName(range);
1098 list->append(parsedValue.releaseNonNull());
1100 if (!range.atEnd() || !list->length())
1106 static RefPtr<CSSValue> consumeFontSynthesis(CSSParserTokenRange& range)
1108 // none | [ weight || style || small-caps ]
1109 CSSValueID id = range.peek().id();
1110 if (id == CSSValueNone)
1111 return consumeIdent(range);
1113 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1115 auto ident = consumeIdent<CSSValueWeight, CSSValueStyle, CSSValueSmallCaps>(range);
1118 if (list->hasValue(ident.get()))
1120 list->append(ident.releaseNonNull());
1123 if (!list->length())
1128 static RefPtr<CSSValue> consumeLetterSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1130 if (range.peek().id() == CSSValueNormal)
1131 return consumeIdent(range);
1133 return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1136 static RefPtr<CSSValue> consumeWordSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1138 if (range.peek().id() == CSSValueNormal)
1139 return consumeIdent(range);
1141 return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1144 static RefPtr<CSSValue> consumeTabSize(CSSParserTokenRange& range, CSSParserMode)
1146 return consumeInteger(range, 0);
1149 #if ENABLE(TEXT_AUTOSIZING)
1150 static RefPtr<CSSValue> consumeTextSizeAdjust(CSSParserTokenRange& range, CSSParserMode /* cssParserMode */)
1152 if (range.peek().id() == CSSValueAuto)
1153 return consumeIdent(range);
1154 if (range.peek().id() == CSSValueNone)
1155 return consumeIdent(range);
1156 return consumePercent(range, ValueRangeNonNegative);
1160 static RefPtr<CSSValue> consumeFontSize(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1162 if (range.peek().id() >= CSSValueXxSmall && range.peek().id() <= CSSValueLarger)
1163 return consumeIdent(range);
1164 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, unitless);
1167 static RefPtr<CSSPrimitiveValue> consumeLineHeight(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1169 if (range.peek().id() == CSSValueNormal)
1170 return consumeIdent(range);
1172 RefPtr<CSSPrimitiveValue> lineHeight = consumeNumber(range, ValueRangeNonNegative);
1175 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
1178 template<typename... Args>
1179 static Ref<CSSPrimitiveValue> createPrimitiveValuePair(Args&&... args)
1181 return CSSValuePool::singleton().createValue(Pair::create(std::forward<Args>(args)...));
1185 static RefPtr<CSSValue> consumeCounter(CSSParserTokenRange& range, int defaultValue)
1187 if (range.peek().id() == CSSValueNone)
1188 return consumeIdent(range);
1190 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1192 RefPtr<CSSPrimitiveValue> counterName = consumeCustomIdent(range);
1195 int i = defaultValue;
1196 if (RefPtr<CSSPrimitiveValue> counterValue = consumeInteger(range))
1197 i = counterValue->intValue();
1198 list->append(createPrimitiveValuePair(counterName.releaseNonNull(), CSSPrimitiveValue::create(i, CSSPrimitiveValue::UnitType::CSS_NUMBER), Pair::IdenticalValueEncoding::Coalesce));
1199 } while (!range.atEnd());
1203 static RefPtr<CSSValue> consumePageSize(CSSParserTokenRange& range)
1205 return consumeIdent<CSSValueA3, CSSValueA4, CSSValueA5, CSSValueB4, CSSValueB5, CSSValueLedger, CSSValueLegal, CSSValueLetter>(range);
1208 static RefPtr<CSSValueList> consumeSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1210 RefPtr<CSSValueList> result = CSSValueList::createSpaceSeparated();
1212 if (range.peek().id() == CSSValueAuto) {
1213 result->append(consumeIdent(range).releaseNonNull());
1217 if (RefPtr<CSSValue> width = consumeLength(range, cssParserMode, ValueRangeNonNegative)) {
1218 RefPtr<CSSValue> height = consumeLength(range, cssParserMode, ValueRangeNonNegative);
1219 result->append(width.releaseNonNull());
1221 result->append(height.releaseNonNull());
1225 RefPtr<CSSValue> pageSize = consumePageSize(range);
1226 RefPtr<CSSValue> orientation = consumeIdent<CSSValuePortrait, CSSValueLandscape>(range);
1228 pageSize = consumePageSize(range);
1230 if (!orientation && !pageSize)
1233 result->append(pageSize.releaseNonNull());
1235 result->append(orientation.releaseNonNull());
1239 static RefPtr<CSSValue> consumeTextIndent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1241 // [ <length> | <percentage> ] && hanging? && each-line?
1242 // Keywords only allowed when css3Text is enabled.
1243 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1245 bool hasLengthOrPercentage = false;
1246 // bool hasEachLine = false;
1247 bool hasHanging = false;
1250 if (!hasLengthOrPercentage) {
1251 if (RefPtr<CSSValue> textIndent = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow)) {
1252 list->append(*textIndent);
1253 hasLengthOrPercentage = true;
1258 CSSValueID id = range.peek().id();
1259 /* FIXME-NEWPARSER: We don't support this yet.
1260 if (!hasEachLine && id == CSSValueEachLine) {
1261 list->append(*consumeIdent(range));
1267 if (!hasHanging && id == CSSValueHanging) {
1268 list->append(consumeIdent(range).releaseNonNull());
1274 } while (!range.atEnd());
1276 if (!hasLengthOrPercentage)
1282 static bool validWidthOrHeightKeyword(CSSValueID id, const CSSParserContext& /*context*/)
1284 if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic || id == CSSValueMinContent || id == CSSValueWebkitMinContent || id == CSSValueMaxContent || id == CSSValueWebkitMaxContent || id == CSSValueWebkitFillAvailable || id == CSSValueFitContent || id == CSSValueWebkitFitContent) {
1290 static RefPtr<CSSValue> consumeMaxWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1292 if (range.peek().id() == CSSValueNone || validWidthOrHeightKeyword(range.peek().id(), context))
1293 return consumeIdent(range);
1294 return consumeLengthOrPercent(range, context.mode, ValueRangeNonNegative, unitless);
1297 static RefPtr<CSSValue> consumeWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1299 if (range.peek().id() == CSSValueAuto || validWidthOrHeightKeyword(range.peek().id(), context))
1300 return consumeIdent(range);
1301 return consumeLengthOrPercent(range, context.mode, ValueRangeNonNegative, unitless);
1304 static RefPtr<CSSValue> consumeMarginOrOffset(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1306 if (range.peek().id() == CSSValueAuto)
1307 return consumeIdent(range);
1308 return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, unitless);
1311 static RefPtr<CSSPrimitiveValue> consumeClipComponent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1313 if (range.peek().id() == CSSValueAuto)
1314 return consumeIdent(range);
1315 return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1318 static RefPtr<CSSValue> consumeClip(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1320 if (range.peek().id() == CSSValueAuto)
1321 return consumeIdent(range);
1323 if (range.peek().functionId() != CSSValueRect)
1326 CSSParserTokenRange args = consumeFunction(range);
1327 // rect(t, r, b, l) || rect(t r b l)
1328 RefPtr<CSSPrimitiveValue> top = consumeClipComponent(args, cssParserMode);
1331 bool needsComma = consumeCommaIncludingWhitespace(args);
1332 RefPtr<CSSPrimitiveValue> right = consumeClipComponent(args, cssParserMode);
1333 if (!right || (needsComma && !consumeCommaIncludingWhitespace(args)))
1335 RefPtr<CSSPrimitiveValue> bottom = consumeClipComponent(args, cssParserMode);
1336 if (!bottom || (needsComma && !consumeCommaIncludingWhitespace(args)))
1338 RefPtr<CSSPrimitiveValue> left = consumeClipComponent(args, cssParserMode);
1339 if (!left || !args.atEnd())
1342 auto rect = Rect::create();
1343 rect->setLeft(left.releaseNonNull());
1344 rect->setTop(top.releaseNonNull());
1345 rect->setRight(right.releaseNonNull());
1346 rect->setBottom(bottom.releaseNonNull());
1347 return CSSValuePool::singleton().createValue(WTFMove(rect));
1350 #if ENABLE(POINTER_EVENTS)
1351 static RefPtr<CSSValue> consumeTouchAction(CSSParserTokenRange& range)
1353 CSSValueID id = range.peek().id();
1354 if (id == CSSValueNone || id == CSSValueAuto || id == CSSValueManipulation)
1355 return consumeIdent(range);
1357 auto list = CSSValueList::createSpaceSeparated();
1359 auto ident = consumeIdent<CSSValuePanX, CSSValuePanY, CSSValuePinchZoom>(range);
1362 if (list->hasValue(ident.get()))
1364 list->append(ident.releaseNonNull());
1367 if (!list->length())
1373 static RefPtr<CSSPrimitiveValue> consumeLineClamp(CSSParserTokenRange& range)
1375 RefPtr<CSSPrimitiveValue> clampValue = consumePercent(range, ValueRangeNonNegative);
1378 // When specifying number of lines, don't allow 0 as a valid value.
1379 return consumePositiveInteger(range);
1382 static RefPtr<CSSValue> consumeAutoOrString(CSSParserTokenRange& range)
1384 if (range.peek().id() == CSSValueAuto)
1385 return consumeIdent(range);
1386 return consumeString(range);
1389 static RefPtr<CSSValue> consumeHyphenateLimit(CSSParserTokenRange& range, CSSValueID valueID)
1391 if (range.peek().id() == valueID)
1392 return consumeIdent(range);
1393 return consumeNumber(range, ValueRangeNonNegative);
1396 static RefPtr<CSSValue> consumeColumnWidth(CSSParserTokenRange& range)
1398 if (range.peek().id() == CSSValueAuto)
1399 return consumeIdent(range);
1400 // Always parse lengths in strict mode here, since it would be ambiguous otherwise when used in
1401 // the 'columns' shorthand property.
1402 RefPtr<CSSPrimitiveValue> columnWidth = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
1403 if (!columnWidth || (!columnWidth->isCalculated() && !columnWidth->doubleValue()) || (columnWidth->cssCalcValue() && !columnWidth->cssCalcValue()->doubleValue()))
1408 static RefPtr<CSSValue> consumeColumnCount(CSSParserTokenRange& range)
1410 if (range.peek().id() == CSSValueAuto)
1411 return consumeIdent(range);
1412 return consumePositiveInteger(range);
1415 static RefPtr<CSSValue> consumeGapLength(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1417 if (range.peek().id() == CSSValueNormal)
1418 return consumeIdent(range);
1419 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
1422 static RefPtr<CSSValue> consumeColumnSpan(CSSParserTokenRange& range)
1424 return consumeIdent<CSSValueAll, CSSValueNone>(range);
1427 static RefPtr<CSSValue> consumeZoom(CSSParserTokenRange& range, const CSSParserContext& /*context*/)
1429 const CSSParserToken& token = range.peek();
1430 RefPtr<CSSPrimitiveValue> zoom;
1431 if (token.type() == IdentToken)
1432 zoom = consumeIdent<CSSValueNormal, CSSValueReset, CSSValueDocument>(range);
1434 zoom = consumePercent(range, ValueRangeNonNegative);
1436 zoom = consumeNumber(range, ValueRangeNonNegative);
1441 static RefPtr<CSSValue> consumeAnimationIterationCount(CSSParserTokenRange& range)
1443 if (range.peek().id() == CSSValueInfinite)
1444 return consumeIdent(range);
1445 return consumeNumber(range, ValueRangeNonNegative);
1448 static RefPtr<CSSValue> consumeAnimationName(CSSParserTokenRange& range)
1450 if (range.peek().id() == CSSValueNone)
1451 return consumeIdent(range);
1453 if (range.peek().type() == StringToken) {
1454 const CSSParserToken& token = range.consumeIncludingWhitespace();
1455 if (equalIgnoringASCIICase(token.value(), "none"))
1456 return CSSValuePool::singleton().createIdentifierValue(CSSValueNone);
1457 // FIXME-NEWPARSER: Want to use a CSSCustomIdentValue here eventually.
1458 return CSSValuePool::singleton().createValue(token.value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
1461 return consumeCustomIdent(range);
1464 static RefPtr<CSSValue> consumeTransitionProperty(CSSParserTokenRange& range)
1466 const CSSParserToken& token = range.peek();
1467 if (token.type() != IdentToken)
1469 if (token.id() == CSSValueNone)
1470 return consumeIdent(range);
1472 if (CSSPropertyID property = token.parseAsCSSPropertyID()) {
1473 range.consumeIncludingWhitespace();
1475 // FIXME-NEWPARSER: No reason why we can't use the "all" property now that it exists.
1476 // The old parser used a value keyword for "all", though, since it predated support for
1478 if (property == CSSPropertyAll)
1479 return CSSValuePool::singleton().createIdentifierValue(CSSValueAll);
1481 // FIXME-NEWPARSER: Want to use a CSSCustomIdentValue here eventually.
1482 return CSSValuePool::singleton().createIdentifierValue(property);
1484 return consumeCustomIdent(range);
1488 static RefPtr<CSSValue> consumeSteps(CSSParserTokenRange& range)
1490 ASSERT(range.peek().functionId() == CSSValueSteps);
1491 CSSParserTokenRange rangeCopy = range;
1492 CSSParserTokenRange args = consumeFunction(rangeCopy);
1494 RefPtr<CSSPrimitiveValue> steps = consumePositiveInteger(args);
1498 // FIXME-NEWPARSER: Support the middle value and change from a boolean to an enum.
1499 bool stepAtStart = false;
1500 if (consumeCommaIncludingWhitespace(args)) {
1501 switch (args.consumeIncludingWhitespace().id()) {
1506 stepAtStart = false;
1517 return CSSStepsTimingFunctionValue::create(steps->intValue(), stepAtStart);
1520 static RefPtr<CSSValue> consumeCubicBezier(CSSParserTokenRange& range)
1522 ASSERT(range.peek().functionId() == CSSValueCubicBezier);
1523 CSSParserTokenRange rangeCopy = range;
1524 CSSParserTokenRange args = consumeFunction(rangeCopy);
1526 double x1, y1, x2, y2;
1527 if (consumeNumberRaw(args, x1)
1528 && x1 >= 0 && x1 <= 1
1529 && consumeCommaIncludingWhitespace(args)
1530 && consumeNumberRaw(args, y1)
1531 && consumeCommaIncludingWhitespace(args)
1532 && consumeNumberRaw(args, x2)
1533 && x2 >= 0 && x2 <= 1
1534 && consumeCommaIncludingWhitespace(args)
1535 && consumeNumberRaw(args, y2)
1538 return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2);
1544 static RefPtr<CSSValue> consumeSpringFunction(CSSParserTokenRange& range)
1546 ASSERT(range.peek().functionId() == CSSValueSpring);
1547 CSSParserTokenRange rangeCopy = range;
1548 CSSParserTokenRange args = consumeFunction(rangeCopy);
1550 // Mass must be greater than 0.
1552 if (!consumeNumberRaw(args, mass) || mass <= 0)
1555 // Stiffness must be greater than 0.
1557 if (!consumeNumberRaw(args, stiffness) || stiffness <= 0)
1560 // Damping coefficient must be greater than or equal to 0.
1562 if (!consumeNumberRaw(args, damping) || damping < 0)
1565 // Initial velocity may have any value.
1566 double initialVelocity;
1567 if (!consumeNumberRaw(args, initialVelocity))
1575 return CSSSpringTimingFunctionValue::create(mass, stiffness, damping, initialVelocity);
1578 static RefPtr<CSSValue> consumeAnimationTimingFunction(CSSParserTokenRange& range, const CSSParserContext& context)
1580 CSSValueID id = range.peek().id();
1581 if (id == CSSValueEase || id == CSSValueLinear || id == CSSValueEaseIn
1582 || id == CSSValueEaseOut || id == CSSValueEaseInOut || id == CSSValueStepStart || id == CSSValueStepEnd)
1583 return consumeIdent(range);
1585 CSSValueID function = range.peek().functionId();
1586 if (function == CSSValueCubicBezier)
1587 return consumeCubicBezier(range);
1588 if (function == CSSValueSteps)
1589 return consumeSteps(range);
1590 if (context.springTimingFunctionEnabled && function == CSSValueSpring)
1591 return consumeSpringFunction(range);
1595 static RefPtr<CSSValue> consumeAnimationValue(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
1598 case CSSPropertyAnimationDelay:
1599 case CSSPropertyTransitionDelay:
1600 return consumeTime(range, context.mode, ValueRangeAll, UnitlessQuirk::Forbid);
1601 case CSSPropertyAnimationDirection:
1602 return consumeIdent<CSSValueNormal, CSSValueAlternate, CSSValueReverse, CSSValueAlternateReverse>(range);
1603 case CSSPropertyAnimationDuration:
1604 case CSSPropertyTransitionDuration:
1605 return consumeTime(range, context.mode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
1606 case CSSPropertyAnimationFillMode:
1607 return consumeIdent<CSSValueNone, CSSValueForwards, CSSValueBackwards, CSSValueBoth>(range);
1608 case CSSPropertyAnimationIterationCount:
1609 return consumeAnimationIterationCount(range);
1610 case CSSPropertyAnimationName:
1611 return consumeAnimationName(range);
1612 case CSSPropertyAnimationPlayState:
1613 return consumeIdent<CSSValueRunning, CSSValuePaused>(range);
1614 case CSSPropertyTransitionProperty:
1615 return consumeTransitionProperty(range);
1616 case CSSPropertyAnimationTimingFunction:
1617 case CSSPropertyTransitionTimingFunction:
1618 return consumeAnimationTimingFunction(range, context);
1620 ASSERT_NOT_REACHED();
1625 static bool isValidAnimationPropertyList(CSSPropertyID property, const CSSValueList& valueList)
1627 if (property != CSSPropertyTransitionProperty || valueList.length() < 2)
1629 for (auto& value : valueList) {
1630 if (value->isPrimitiveValue() && downcast<CSSPrimitiveValue>(value.get()).isValueID()
1631 && downcast<CSSPrimitiveValue>(value.get()).valueID() == CSSValueNone)
1637 static RefPtr<CSSValue> consumeAnimationPropertyList(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
1639 RefPtr<CSSValueList> list;
1640 RefPtr<CSSValue> singleton;
1642 RefPtr<CSSValue> currentValue = consumeAnimationValue(property, range, context);
1646 if (singleton && !list) {
1647 list = CSSValueList::createCommaSeparated();
1648 list->append(singleton.releaseNonNull());
1652 list->append(currentValue.releaseNonNull());
1654 singleton = WTFMove(currentValue);
1656 } while (consumeCommaIncludingWhitespace(range));
1659 if (!isValidAnimationPropertyList(property, *list))
1662 ASSERT(list->length());
1669 bool CSSPropertyParser::consumeAnimationShorthand(const StylePropertyShorthand& shorthand, bool important)
1671 const unsigned longhandCount = shorthand.length();
1672 RefPtr<CSSValueList> longhands[8];
1673 ASSERT(longhandCount <= 8);
1674 for (size_t i = 0; i < longhandCount; ++i)
1675 longhands[i] = CSSValueList::createCommaSeparated();
1678 bool parsedLonghand[8] = { false };
1680 bool foundProperty = false;
1681 for (size_t i = 0; i < longhandCount; ++i) {
1682 if (parsedLonghand[i])
1685 if (RefPtr<CSSValue> value = consumeAnimationValue(shorthand.properties()[i], m_range, m_context)) {
1686 parsedLonghand[i] = true;
1687 foundProperty = true;
1688 longhands[i]->append(*value);
1694 } while (!m_range.atEnd() && m_range.peek().type() != CommaToken);
1696 // FIXME: This will make invalid longhands, see crbug.com/386459
1697 for (size_t i = 0; i < longhandCount; ++i) {
1698 if (!parsedLonghand[i])
1699 longhands[i]->append(CSSValuePool::singleton().createImplicitInitialValue());
1700 parsedLonghand[i] = false;
1702 } while (consumeCommaIncludingWhitespace(m_range));
1704 for (size_t i = 0; i < longhandCount; ++i) {
1705 if (!isValidAnimationPropertyList(shorthand.properties()[i], *longhands[i]))
1709 for (size_t i = 0; i < longhandCount; ++i)
1710 addProperty(shorthand.properties()[i], shorthand.id(), *longhands[i], important);
1712 return m_range.atEnd();
1715 static RefPtr<CSSValue> consumeZIndex(CSSParserTokenRange& range)
1717 if (range.peek().id() == CSSValueAuto)
1718 return consumeIdent(range);
1719 return consumeInteger(range);
1722 static RefPtr<CSSValue> consumeShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool isBoxShadowProperty)
1724 if (range.peek().id() == CSSValueNone)
1725 return consumeIdent(range);
1727 RefPtr<CSSValueList> shadowValueList = CSSValueList::createCommaSeparated();
1729 if (RefPtr<CSSShadowValue> shadowValue = consumeSingleShadow(range, cssParserMode, isBoxShadowProperty, isBoxShadowProperty))
1730 shadowValueList->append(*shadowValue);
1733 } while (consumeCommaIncludingWhitespace(range));
1734 return shadowValueList;
1737 static RefPtr<CSSValue> consumeTextDecorationLine(CSSParserTokenRange& range)
1739 CSSValueID id = range.peek().id();
1740 if (id == CSSValueNone)
1741 return consumeIdent(range);
1743 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1745 #if ENABLE(LETTERPRESS)
1746 RefPtr<CSSPrimitiveValue> ident = consumeIdent<CSSValueBlink, CSSValueUnderline, CSSValueOverline, CSSValueLineThrough, CSSValueWebkitLetterpress>(range);
1748 RefPtr<CSSPrimitiveValue> ident = consumeIdent<CSSValueBlink, CSSValueUnderline, CSSValueOverline, CSSValueLineThrough>(range);
1752 if (list->hasValue(ident.get()))
1754 list->append(ident.releaseNonNull());
1757 if (!list->length())
1762 static RefPtr<CSSValue> consumeTextDecorationSkip(CSSParserTokenRange& range)
1764 CSSValueID id = range.peek().id();
1765 if (id == CSSValueNone)
1766 return consumeIdent(range);
1768 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1770 auto ident = consumeIdent<CSSValueAuto, CSSValueInk, CSSValueObjects>(range);
1773 if (list->hasValue(ident.get()))
1775 list->append(ident.releaseNonNull());
1778 if (!list->length())
1783 static RefPtr<CSSValue> consumeTextEmphasisStyle(CSSParserTokenRange& range)
1785 CSSValueID id = range.peek().id();
1786 if (id == CSSValueNone)
1787 return consumeIdent(range);
1789 if (RefPtr<CSSValue> textEmphasisStyle = consumeString(range))
1790 return textEmphasisStyle;
1792 RefPtr<CSSPrimitiveValue> fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
1793 RefPtr<CSSPrimitiveValue> shape = consumeIdent<CSSValueDot, CSSValueCircle, CSSValueDoubleCircle, CSSValueTriangle, CSSValueSesame>(range);
1795 fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
1796 if (fill && shape) {
1797 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
1798 parsedValues->append(fill.releaseNonNull());
1799 parsedValues->append(shape.releaseNonNull());
1800 return parsedValues;
1809 static RefPtr<CSSPrimitiveValue> consumeCaretColor(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1811 if (range.peek().id() == CSSValueAuto)
1812 return consumeIdent(range);
1813 return consumeColor(range, cssParserMode);
1816 static RefPtr<CSSValue> consumeOutlineColor(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1818 // Allow the special focus color even in HTML Standard parsing mode.
1819 if (range.peek().id() == CSSValueWebkitFocusRingColor)
1820 return consumeIdent(range);
1821 return consumeColor(range, cssParserMode);
1824 static RefPtr<CSSPrimitiveValue> consumeLineWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1826 CSSValueID id = range.peek().id();
1827 if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick)
1828 return consumeIdent(range);
1829 return consumeLength(range, cssParserMode, ValueRangeNonNegative, unitless);
1832 static RefPtr<CSSPrimitiveValue> consumeBorderWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1834 return consumeLineWidth(range, cssParserMode, unitless);
1837 static RefPtr<CSSPrimitiveValue> consumeTextStrokeWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1839 return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
1842 static RefPtr<CSSPrimitiveValue> consumeColumnRuleWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1844 return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
1847 static bool consumeTranslate3d(CSSParserTokenRange& args, CSSParserMode cssParserMode, RefPtr<CSSFunctionValue>& transformValue)
1849 unsigned numberOfArguments = 2;
1850 RefPtr<CSSValue> parsedValue;
1852 parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1855 transformValue->append(*parsedValue);
1856 if (!consumeCommaIncludingWhitespace(args))
1858 } while (--numberOfArguments);
1859 parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
1862 transformValue->append(*parsedValue);
1866 static bool consumeNumbers(CSSParserTokenRange& args, RefPtr<CSSFunctionValue>& transformValue, unsigned numberOfArguments)
1869 RefPtr<CSSPrimitiveValue> parsedValue = consumeNumber(args, ValueRangeAll);
1872 transformValue->append(parsedValue.releaseNonNull());
1873 if (--numberOfArguments && !consumeCommaIncludingWhitespace(args))
1875 } while (numberOfArguments);
1879 static bool consumePerspective(CSSParserTokenRange& args, CSSParserMode cssParserMode, RefPtr<CSSFunctionValue>& transformValue)
1881 RefPtr<CSSPrimitiveValue> parsedValue = consumeLength(args, cssParserMode, ValueRangeNonNegative);
1884 if (!consumeNumberRaw(args, perspective) || perspective < 0)
1886 parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitType::CSS_PX);
1890 transformValue->append(parsedValue.releaseNonNull());
1894 static RefPtr<CSSValue> consumeTransformValue(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1896 CSSValueID functionId = range.peek().functionId();
1897 if (functionId == CSSValueInvalid)
1899 CSSParserTokenRange args = consumeFunction(range);
1903 RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(functionId);
1904 RefPtr<CSSValue> parsedValue;
1905 switch (functionId) {
1906 case CSSValueRotate:
1907 case CSSValueRotateX:
1908 case CSSValueRotateY:
1909 case CSSValueRotateZ:
1913 parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1916 if (functionId == CSSValueSkew && consumeCommaIncludingWhitespace(args)) {
1917 transformValue->append(*parsedValue);
1918 parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1923 case CSSValueScaleX:
1924 case CSSValueScaleY:
1925 case CSSValueScaleZ:
1927 parsedValue = consumeNumber(args, ValueRangeAll);
1930 if (functionId == CSSValueScale && consumeCommaIncludingWhitespace(args)) {
1931 transformValue->append(*parsedValue);
1932 parsedValue = consumeNumber(args, ValueRangeAll);
1937 case CSSValuePerspective:
1938 if (!consumePerspective(args, cssParserMode, transformValue))
1941 case CSSValueTranslateX:
1942 case CSSValueTranslateY:
1943 case CSSValueTranslate:
1944 parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1947 if (functionId == CSSValueTranslate && consumeCommaIncludingWhitespace(args)) {
1948 transformValue->append(*parsedValue);
1949 parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1954 case CSSValueTranslateZ:
1955 parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
1957 case CSSValueMatrix:
1958 case CSSValueMatrix3d:
1959 if (!consumeNumbers(args, transformValue, (functionId == CSSValueMatrix3d) ? 16 : 6))
1962 case CSSValueScale3d:
1963 if (!consumeNumbers(args, transformValue, 3))
1966 case CSSValueRotate3d:
1967 if (!consumeNumbers(args, transformValue, 3) || !consumeCommaIncludingWhitespace(args))
1969 parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1973 case CSSValueTranslate3d:
1974 if (!consumeTranslate3d(args, cssParserMode, transformValue))
1981 transformValue->append(*parsedValue);
1984 return transformValue;
1987 static RefPtr<CSSValue> consumeTransform(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1989 if (range.peek().id() == CSSValueNone)
1990 return consumeIdent(range);
1992 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1994 RefPtr<CSSValue> parsedTransformValue = consumeTransformValue(range, cssParserMode);
1995 if (!parsedTransformValue)
1997 list->append(parsedTransformValue.releaseNonNull());
1998 } while (!range.atEnd());
2003 template <CSSValueID start, CSSValueID end>
2004 static RefPtr<CSSPrimitiveValue> consumePositionLonghand(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2006 if (range.peek().type() == IdentToken) {
2007 CSSValueID id = range.peek().id();
2011 else if (id == CSSValueCenter)
2017 range.consumeIncludingWhitespace();
2018 return CSSPrimitiveValue::create(percent, CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
2020 return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
2023 static RefPtr<CSSPrimitiveValue> consumePositionX(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2025 return consumePositionLonghand<CSSValueLeft, CSSValueRight>(range, cssParserMode);
2028 static RefPtr<CSSPrimitiveValue> consumePositionY(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2030 return consumePositionLonghand<CSSValueTop, CSSValueBottom>(range, cssParserMode);
2033 static RefPtr<CSSValue> consumePaintStroke(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2035 if (range.peek().id() == CSSValueNone)
2036 return consumeIdent(range);
2037 RefPtr<CSSPrimitiveValue> url = consumeUrl(range);
2039 RefPtr<CSSValue> parsedValue;
2040 if (range.peek().id() == CSSValueNone)
2041 parsedValue = consumeIdent(range);
2043 parsedValue = consumeColor(range, cssParserMode);
2045 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
2046 values->append(url.releaseNonNull());
2047 values->append(parsedValue.releaseNonNull());
2052 return consumeColor(range, cssParserMode);
2055 static RefPtr<CSSValue> consumeGlyphOrientation(CSSParserTokenRange& range, CSSParserMode mode, CSSPropertyID property)
2057 if (range.peek().id() == CSSValueAuto) {
2058 if (property == CSSPropertyGlyphOrientationVertical)
2059 return consumeIdent(range);
2063 return consumeAngle(range, mode, UnitlessQuirk::Allow);
2066 static RefPtr<CSSValue> consumePaintOrder(CSSParserTokenRange& range)
2068 if (range.peek().id() == CSSValueNormal)
2069 return consumeIdent(range);
2071 Vector<CSSValueID, 3> paintTypeList;
2072 RefPtr<CSSPrimitiveValue> fill;
2073 RefPtr<CSSPrimitiveValue> stroke;
2074 RefPtr<CSSPrimitiveValue> markers;
2076 CSSValueID id = range.peek().id();
2077 if (id == CSSValueFill && !fill)
2078 fill = consumeIdent(range);
2079 else if (id == CSSValueStroke && !stroke)
2080 stroke = consumeIdent(range);
2081 else if (id == CSSValueMarkers && !markers)
2082 markers = consumeIdent(range);
2085 paintTypeList.append(id);
2086 } while (!range.atEnd());
2088 // After parsing we serialize the paint-order list. Since it is not possible to
2089 // pop a last list items from CSSValueList without bigger cost, we create the
2090 // list after parsing.
2091 CSSValueID firstPaintOrderType = paintTypeList.at(0);
2092 RefPtr<CSSValueList> paintOrderList = CSSValueList::createSpaceSeparated();
2093 switch (firstPaintOrderType) {
2095 case CSSValueStroke:
2096 paintOrderList->append(firstPaintOrderType == CSSValueFill ? fill.releaseNonNull() : stroke.releaseNonNull());
2097 if (paintTypeList.size() > 1) {
2098 if (paintTypeList.at(1) == CSSValueMarkers)
2099 paintOrderList->append(markers.releaseNonNull());
2102 case CSSValueMarkers:
2103 paintOrderList->append(markers.releaseNonNull());
2104 if (paintTypeList.size() > 1) {
2105 if (paintTypeList.at(1) == CSSValueStroke)
2106 paintOrderList->append(stroke.releaseNonNull());
2110 ASSERT_NOT_REACHED();
2113 return paintOrderList;
2116 static RefPtr<CSSValue> consumeNoneOrURI(CSSParserTokenRange& range)
2118 if (range.peek().id() == CSSValueNone)
2119 return consumeIdent(range);
2120 return consumeUrl(range);
2123 static RefPtr<CSSValue> consumeFlexBasis(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2125 // FIXME: Support intrinsic dimensions too.
2126 if (range.peek().id() == CSSValueAuto)
2127 return consumeIdent(range);
2128 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2131 static RefPtr<CSSValue> consumeKerning(CSSParserTokenRange& range, CSSParserMode mode)
2133 RefPtr<CSSValue> result = consumeIdent<CSSValueAuto, CSSValueNormal>(range);
2136 return consumeLength(range, mode, ValueRangeAll, UnitlessQuirk::Allow);
2139 static RefPtr<CSSValue> consumeStrokeDasharray(CSSParserTokenRange& range)
2141 CSSValueID id = range.peek().id();
2142 if (id == CSSValueNone)
2143 return consumeIdent(range);
2145 RefPtr<CSSValueList> dashes = CSSValueList::createCommaSeparated();
2147 RefPtr<CSSPrimitiveValue> dash = consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeNonNegative);
2148 if (!dash || (consumeCommaIncludingWhitespace(range) && range.atEnd()))
2150 dashes->append(dash.releaseNonNull());
2151 } while (!range.atEnd());
2155 static RefPtr<CSSPrimitiveValue> consumeBaselineShift(CSSParserTokenRange& range)
2157 CSSValueID id = range.peek().id();
2158 if (id == CSSValueBaseline || id == CSSValueSub || id == CSSValueSuper)
2159 return consumeIdent(range);
2160 return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll);
2163 static RefPtr<CSSPrimitiveValue> consumeRxOrRy(CSSParserTokenRange& range)
2165 // FIXME-NEWPARSER: We don't support auto values when mapping, so for now turn this
2166 // off until we can figure out if we're even supposed to support it.
2167 // if (range.peek().id() == CSSValueAuto)
2168 // return consumeIdent(range);
2169 return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
2172 static RefPtr<CSSValue> consumeCursor(CSSParserTokenRange& range, const CSSParserContext& context, bool inQuirksMode)
2174 RefPtr<CSSValueList> list;
2175 while (RefPtr<CSSValue> image = consumeImage(range, context, ConsumeGeneratedImage::Forbid)) {
2177 IntPoint hotSpot(-1, -1);
2178 bool hotSpotSpecified = false;
2179 if (consumeNumberRaw(range, num)) {
2180 hotSpot.setX(int(num));
2181 if (!consumeNumberRaw(range, num))
2183 hotSpot.setY(int(num));
2184 hotSpotSpecified = true;
2188 list = CSSValueList::createCommaSeparated();
2190 list->append(CSSCursorImageValue::create(image.releaseNonNull(), hotSpotSpecified, hotSpot, context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No));
2191 if (!consumeCommaIncludingWhitespace(range))
2195 CSSValueID id = range.peek().id();
2196 RefPtr<CSSValue> cursorType;
2197 if (id == CSSValueHand) {
2198 if (!inQuirksMode) // Non-standard behavior
2200 cursorType = CSSValuePool::singleton().createIdentifierValue(CSSValuePointer);
2201 range.consumeIncludingWhitespace();
2202 } else if ((id >= CSSValueAuto && id <= CSSValueWebkitZoomOut) || id == CSSValueCopy || id == CSSValueNone) {
2203 cursorType = consumeIdent(range);
2210 list->append(cursorType.releaseNonNull());
2214 static RefPtr<CSSValue> consumeAttr(CSSParserTokenRange args, CSSParserContext context)
2216 if (args.peek().type() != IdentToken)
2219 CSSParserToken token = args.consumeIncludingWhitespace();
2220 auto attrName = token.value().toAtomicString();
2221 if (context.isHTMLDocument)
2222 attrName = attrName.convertToASCIILowercase();
2227 // FIXME-NEWPARSER: We want to use a CSSCustomIdentValue here eventually for the attrName.
2228 // FIXME-NEWPARSER: We want to use a CSSFunctionValue rather than relying on a custom
2229 // attr() primitive value.
2230 return CSSValuePool::singleton().createValue(attrName, CSSPrimitiveValue::CSS_ATTR);
2233 static RefPtr<CSSValue> consumeCounterContent(CSSParserTokenRange args, bool counters)
2235 RefPtr<CSSPrimitiveValue> identifier = consumeCustomIdent(args);
2239 RefPtr<CSSPrimitiveValue> separator;
2241 separator = CSSPrimitiveValue::create(String(), CSSPrimitiveValue::UnitType::CSS_STRING);
2243 if (!consumeCommaIncludingWhitespace(args) || args.peek().type() != StringToken)
2245 separator = CSSPrimitiveValue::create(args.consumeIncludingWhitespace().value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
2248 RefPtr<CSSPrimitiveValue> listStyle;
2249 if (consumeCommaIncludingWhitespace(args)) {
2250 CSSValueID id = args.peek().id();
2251 if ((id != CSSValueNone && (id < CSSValueDisc || id > CSSValueKatakanaIroha)))
2253 listStyle = consumeIdent(args);
2255 listStyle = CSSValuePool::singleton().createIdentifierValue(CSSValueDecimal);
2260 // FIXME-NEWPARSER: Should just have a CSSCounterValue.
2261 return CSSValuePool::singleton().createValue(Counter::create(identifier.releaseNonNull(), listStyle.releaseNonNull(), separator.releaseNonNull()));
2264 static RefPtr<CSSValue> consumeContent(CSSParserTokenRange& range, CSSParserContext context)
2266 if (identMatches<CSSValueNone, CSSValueNormal>(range.peek().id()))
2267 return consumeIdent(range);
2269 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
2272 RefPtr<CSSValue> parsedValue = consumeImage(range, context);
2274 parsedValue = consumeIdent<CSSValueOpenQuote, CSSValueCloseQuote, CSSValueNoOpenQuote, CSSValueNoCloseQuote>(range);
2276 parsedValue = consumeString(range);
2278 if (range.peek().functionId() == CSSValueAttr)
2279 parsedValue = consumeAttr(consumeFunction(range), context);
2280 else if (range.peek().functionId() == CSSValueCounter)
2281 parsedValue = consumeCounterContent(consumeFunction(range), false);
2282 else if (range.peek().functionId() == CSSValueCounters)
2283 parsedValue = consumeCounterContent(consumeFunction(range), true);
2287 values->append(parsedValue.releaseNonNull());
2288 } while (!range.atEnd());
2293 static RefPtr<CSSPrimitiveValue> consumePerspective(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2295 if (range.peek().id() == CSSValueNone)
2296 return consumeIdent(range);
2297 RefPtr<CSSPrimitiveValue> parsedValue = consumeLength(range, cssParserMode, ValueRangeAll);
2299 // FIXME: Make this quirk only apply to the webkit prefixed version of the property.
2301 if (!consumeNumberRaw(range, perspective))
2303 parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitType::CSS_PX);
2305 if (parsedValue && (parsedValue->isCalculated() || parsedValue->doubleValue() > 0))
2310 #if ENABLE(CSS_SCROLL_SNAP)
2312 static RefPtr<CSSValueList> consumeScrollSnapAlign(CSSParserTokenRange& range)
2314 RefPtr<CSSValueList> alignmentValue = CSSValueList::createSpaceSeparated();
2315 if (RefPtr<CSSPrimitiveValue> firstValue = consumeIdent<CSSValueNone, CSSValueStart, CSSValueCenter, CSSValueEnd>(range)) {
2316 alignmentValue->append(firstValue.releaseNonNull());
2317 if (auto secondValue = consumeIdent<CSSValueNone, CSSValueStart, CSSValueCenter, CSSValueEnd>(range))
2318 alignmentValue->append(secondValue.releaseNonNull());
2320 return alignmentValue->length() ? alignmentValue : nullptr;
2323 static RefPtr<CSSValueList> consumeScrollSnapType(CSSParserTokenRange& range)
2325 RefPtr<CSSValueList> typeValue = CSSValueList::createSpaceSeparated();
2326 RefPtr<CSSPrimitiveValue> secondValue;
2328 auto firstValue = consumeIdent<CSSValueX, CSSValueY, CSSValueBlock, CSSValueInline, CSSValueBoth>(range);
2330 secondValue = consumeIdent<CSSValueProximity, CSSValueMandatory>(range);
2332 firstValue = consumeIdent<CSSValueNone, CSSValueProximity, CSSValueMandatory>(range);
2337 typeValue->append(firstValue.releaseNonNull());
2339 typeValue->append(secondValue.releaseNonNull());
2346 static RefPtr<CSSValue> consumeBorderRadiusCorner(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2348 RefPtr<CSSPrimitiveValue> parsedValue1 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2351 RefPtr<CSSPrimitiveValue> parsedValue2 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2353 parsedValue2 = parsedValue1;
2354 return createPrimitiveValuePair(parsedValue1.releaseNonNull(), parsedValue2.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2357 static RefPtr<CSSValue> consumeTextUnderlineOffset(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2359 if (auto value = consumeIdent<CSSValueAuto>(range))
2361 return consumeLength(range, cssParserMode, ValueRangeAll);
2364 static RefPtr<CSSValue> consumeTextDecorationThickness(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2366 if (auto value = consumeIdent<CSSValueAuto, CSSValueFromFont>(range))
2368 return consumeLength(range, cssParserMode, ValueRangeAll);
2371 static RefPtr<CSSPrimitiveValue> consumeVerticalAlign(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2373 RefPtr<CSSPrimitiveValue> parsedValue = consumeIdentRange(range, CSSValueBaseline, CSSValueWebkitBaselineMiddle);
2375 parsedValue = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2379 static RefPtr<CSSPrimitiveValue> consumeShapeRadius(CSSParserTokenRange& args, CSSParserMode cssParserMode)
2381 if (identMatches<CSSValueClosestSide, CSSValueFarthestSide>(args.peek().id()))
2382 return consumeIdent(args);
2383 return consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
2386 static RefPtr<CSSBasicShapeCircle> consumeBasicShapeCircle(CSSParserTokenRange& args, const CSSParserContext& context)
2388 // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
2389 // circle( [<shape-radius>]? [at <position>]? )
2390 RefPtr<CSSBasicShapeCircle> shape = CSSBasicShapeCircle::create();
2391 if (RefPtr<CSSPrimitiveValue> radius = consumeShapeRadius(args, context.mode))
2392 shape->setRadius(radius.releaseNonNull());
2393 if (consumeIdent<CSSValueAt>(args)) {
2394 RefPtr<CSSPrimitiveValue> centerX;
2395 RefPtr<CSSPrimitiveValue> centerY;
2396 if (!consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY))
2398 shape->setCenterX(centerX.releaseNonNull());
2399 shape->setCenterY(centerY.releaseNonNull());
2404 static RefPtr<CSSBasicShapeEllipse> consumeBasicShapeEllipse(CSSParserTokenRange& args, const CSSParserContext& context)
2406 // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
2407 // ellipse( [<shape-radius>{2}]? [at <position>]? )
2408 RefPtr<CSSBasicShapeEllipse> shape = CSSBasicShapeEllipse::create();
2409 if (RefPtr<CSSPrimitiveValue> radiusX = consumeShapeRadius(args, context.mode)) {
2410 shape->setRadiusX(radiusX.releaseNonNull());
2411 if (RefPtr<CSSPrimitiveValue> radiusY = consumeShapeRadius(args, context.mode))
2412 shape->setRadiusY(radiusY.releaseNonNull());
2414 if (consumeIdent<CSSValueAt>(args)) {
2415 RefPtr<CSSPrimitiveValue> centerX;
2416 RefPtr<CSSPrimitiveValue> centerY;
2417 if (!consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY))
2419 shape->setCenterX(centerX.releaseNonNull());
2420 shape->setCenterY(centerY.releaseNonNull());
2425 static RefPtr<CSSBasicShapePolygon> consumeBasicShapePolygon(CSSParserTokenRange& args, const CSSParserContext& context)
2427 RefPtr<CSSBasicShapePolygon> shape = CSSBasicShapePolygon::create();
2428 if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
2429 shape->setWindRule(args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? WindRule::EvenOdd : WindRule::NonZero);
2430 if (!consumeCommaIncludingWhitespace(args))
2435 RefPtr<CSSPrimitiveValue> xLength = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2438 RefPtr<CSSPrimitiveValue> yLength = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2441 shape->appendPoint(xLength.releaseNonNull(), yLength.releaseNonNull());
2442 } while (consumeCommaIncludingWhitespace(args));
2446 static RefPtr<CSSBasicShapePath> consumeBasicShapePath(CSSParserTokenRange& args)
2448 WindRule windRule = WindRule::NonZero;
2449 if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
2450 windRule = args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? WindRule::EvenOdd : WindRule::NonZero;
2451 if (!consumeCommaIncludingWhitespace(args))
2455 if (args.peek().type() != StringToken)
2458 auto byteStream = std::make_unique<SVGPathByteStream>();
2459 if (!buildSVGPathByteStreamFromString(args.consumeIncludingWhitespace().value().toString(), *byteStream, UnalteredParsing))
2462 auto shape = CSSBasicShapePath::create(WTFMove(byteStream));
2463 shape->setWindRule(windRule);
2465 return WTFMove(shape);
2468 static void complete4Sides(RefPtr<CSSPrimitiveValue> side[4])
2480 static bool consumeRadii(RefPtr<CSSPrimitiveValue> horizontalRadii[4], RefPtr<CSSPrimitiveValue> verticalRadii[4], CSSParserTokenRange& range, CSSParserMode cssParserMode, bool useLegacyParsing)
2483 for (; i < 4 && !range.atEnd() && range.peek().type() != DelimiterToken; ++i) {
2484 horizontalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2485 if (!horizontalRadii[i])
2488 if (!horizontalRadii[0])
2490 if (range.atEnd()) {
2491 // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2;
2492 if (useLegacyParsing && i == 2) {
2493 verticalRadii[0] = horizontalRadii[1];
2494 horizontalRadii[1] = nullptr;
2496 complete4Sides(horizontalRadii);
2497 for (unsigned i = 0; i < 4; ++i)
2498 verticalRadii[i] = horizontalRadii[i];
2502 if (!consumeSlashIncludingWhitespace(range))
2504 for (i = 0; i < 4 && !range.atEnd(); ++i) {
2505 verticalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2506 if (!verticalRadii[i])
2509 if (!verticalRadii[0] || !range.atEnd())
2512 complete4Sides(horizontalRadii);
2513 complete4Sides(verticalRadii);
2517 static RefPtr<CSSBasicShapeInset> consumeBasicShapeInset(CSSParserTokenRange& args, const CSSParserContext& context)
2519 RefPtr<CSSBasicShapeInset> shape = CSSBasicShapeInset::create();
2520 RefPtr<CSSPrimitiveValue> top = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2523 RefPtr<CSSPrimitiveValue> right = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2524 RefPtr<CSSPrimitiveValue> bottom;
2525 RefPtr<CSSPrimitiveValue> left;
2527 bottom = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2529 left = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2532 shape->updateShapeSize4Values(top.releaseNonNull(), right.releaseNonNull(), bottom.releaseNonNull(), left.releaseNonNull());
2534 shape->updateShapeSize3Values(top.releaseNonNull(), right.releaseNonNull(), bottom.releaseNonNull());
2536 shape->updateShapeSize2Values(top.releaseNonNull(), right.releaseNonNull());
2538 shape->updateShapeSize1Value(top.releaseNonNull());
2540 if (consumeIdent<CSSValueRound>(args)) {
2541 RefPtr<CSSPrimitiveValue> horizontalRadii[4] = { 0 };
2542 RefPtr<CSSPrimitiveValue> verticalRadii[4] = { 0 };
2543 if (!consumeRadii(horizontalRadii, verticalRadii, args, context.mode, false))
2545 shape->setTopLeftRadius(createPrimitiveValuePair(horizontalRadii[0].releaseNonNull(), verticalRadii[0].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2546 shape->setTopRightRadius(createPrimitiveValuePair(horizontalRadii[1].releaseNonNull(), verticalRadii[1].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2547 shape->setBottomRightRadius(createPrimitiveValuePair(horizontalRadii[2].releaseNonNull(), verticalRadii[2].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2548 shape->setBottomLeftRadius(createPrimitiveValuePair(horizontalRadii[3].releaseNonNull(), verticalRadii[3].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2553 static RefPtr<CSSValue> consumeBasicShape(CSSParserTokenRange& range, const CSSParserContext& context)
2555 RefPtr<CSSValue> result;
2556 if (range.peek().type() != FunctionToken)
2558 CSSValueID id = range.peek().functionId();
2559 CSSParserTokenRange rangeCopy = range;
2560 CSSParserTokenRange args = consumeFunction(rangeCopy);
2562 // FIXME-NEWPARSER: CSSBasicShape should be a CSSValue, and shapes should not be primitive values.
2563 RefPtr<CSSBasicShape> shape;
2564 if (id == CSSValueCircle)
2565 shape = consumeBasicShapeCircle(args, context);
2566 else if (id == CSSValueEllipse)
2567 shape = consumeBasicShapeEllipse(args, context);
2568 else if (id == CSSValuePolygon)
2569 shape = consumeBasicShapePolygon(args, context);
2570 else if (id == CSSValueInset)
2571 shape = consumeBasicShapeInset(args, context);
2572 else if (id == CSSValuePath)
2573 shape = consumeBasicShapePath(args);
2581 return CSSValuePool::singleton().createValue(shape.releaseNonNull());
2584 static RefPtr<CSSValue> consumeBasicShapeOrBox(CSSParserTokenRange& range, const CSSParserContext& context)
2586 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
2587 bool shapeFound = false;
2588 bool boxFound = false;
2589 while (!range.atEnd() && !(shapeFound && boxFound)) {
2590 RefPtr<CSSValue> componentValue;
2591 if (range.peek().type() == FunctionToken && !shapeFound) {
2592 componentValue = consumeBasicShape(range, context);
2594 } else if (range.peek().type() == IdentToken && !boxFound) {
2595 componentValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox, CSSValueFillBox, CSSValueStrokeBox, CSSValueViewBox>(range);
2598 if (!componentValue)
2600 list->append(componentValue.releaseNonNull());
2603 if (!range.atEnd() || !list->length())
2609 static RefPtr<CSSValue> consumeWebkitClipPath(CSSParserTokenRange& range, const CSSParserContext& context)
2611 if (range.peek().id() == CSSValueNone)
2612 return consumeIdent(range);
2613 if (RefPtr<CSSPrimitiveValue> url = consumeUrl(range))
2615 return consumeBasicShapeOrBox(range, context);
2618 static RefPtr<CSSValue> consumeShapeOutside(CSSParserTokenRange& range, const CSSParserContext& context)
2620 if (RefPtr<CSSValue> imageValue = consumeImageOrNone(range, context))
2622 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
2623 if (RefPtr<CSSValue> boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
2624 list->append(boxValue.releaseNonNull());
2625 if (RefPtr<CSSValue> shapeValue = consumeBasicShape(range, context)) {
2626 list->append(shapeValue.releaseNonNull());
2627 if (list->length() < 2) {
2628 if (RefPtr<CSSValue> boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
2629 list->append(boxValue.releaseNonNull());
2632 if (!list->length())
2637 static bool isAuto(CSSValueID id)
2639 return identMatches<CSSValueAuto>(id);
2642 static bool isNormalOrStretch(CSSValueID id)
2644 return identMatches<CSSValueNormal, CSSValueStretch>(id);
2647 static bool isLeftOrRightKeyword(CSSValueID id)
2649 return identMatches<CSSValueLeft, CSSValueRight>(id);
2652 static bool isContentDistributionKeyword(CSSValueID id)
2654 return identMatches<CSSValueSpaceBetween, CSSValueSpaceAround, CSSValueSpaceEvenly, CSSValueStretch>(id);
2657 static bool isContentPositionKeyword(CSSValueID id)
2659 return identMatches<CSSValueStart, CSSValueEnd, CSSValueCenter, CSSValueFlexStart, CSSValueFlexEnd>(id);
2662 static bool isContentPositionOrLeftOrRightKeyword(CSSValueID id)
2664 return isContentPositionKeyword(id) || isLeftOrRightKeyword(id);
2667 static bool isOverflowKeyword(CSSValueID id)
2669 return CSSPropertyParserHelpers::identMatches<CSSValueUnsafe, CSSValueSafe>(id);
2672 static bool isBaselineKeyword(CSSValueID id)
2674 return identMatches<CSSValueFirst, CSSValueLast, CSSValueBaseline>(id);
2677 static RefPtr<CSSPrimitiveValue> consumeOverflowPositionKeyword(CSSParserTokenRange& range)
2679 return isOverflowKeyword(range.peek().id()) ? consumeIdent(range) : nullptr;
2682 static CSSValueID getBaselineKeyword(RefPtr<CSSValue> value)
2684 auto& primitiveValue = downcast<CSSPrimitiveValue>(*value);
2685 if (primitiveValue.pairValue()) {
2686 ASSERT(primitiveValue.pairValue()->first()->valueID() == CSSValueLast);
2687 ASSERT(primitiveValue.pairValue()->second()->valueID() == CSSValueBaseline);
2688 return CSSValueLastBaseline;
2690 ASSERT(primitiveValue.valueID() == CSSValueBaseline);
2691 return CSSValueBaseline;
2694 static RefPtr<CSSValue> consumeBaselineKeyword(CSSParserTokenRange& range)
2696 RefPtr<CSSPrimitiveValue> preference = consumeIdent<CSSValueFirst, CSSValueLast>(range);
2697 RefPtr<CSSPrimitiveValue> baseline = consumeIdent<CSSValueBaseline>(range);
2700 if (preference && preference->valueID() == CSSValueLast)
2701 return createPrimitiveValuePair(preference.releaseNonNull(), baseline.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2705 using IsPositionKeyword = bool (*)(CSSValueID);
2707 static RefPtr<CSSValue> consumeContentDistributionOverflowPosition(CSSParserTokenRange& range, IsPositionKeyword isPositionKeyword)
2709 ASSERT(isPositionKeyword);
2710 CSSValueID id = range.peek().id();
2711 if (identMatches<CSSValueNormal>(id))
2712 return CSSContentDistributionValue::create(CSSValueInvalid, range.consumeIncludingWhitespace().id(), CSSValueInvalid);
2714 if (isBaselineKeyword(id)) {
2715 RefPtr<CSSValue> baseline = consumeBaselineKeyword(range);
2718 return CSSContentDistributionValue::create(CSSValueInvalid, getBaselineKeyword(baseline), CSSValueInvalid);
2721 if (isContentDistributionKeyword(id))
2722 return CSSContentDistributionValue::create(range.consumeIncludingWhitespace().id(), CSSValueInvalid, CSSValueInvalid);
2724 CSSValueID overflow = isOverflowKeyword(id) ? range.consumeIncludingWhitespace().id() : CSSValueInvalid;
2725 if (isPositionKeyword(range.peek().id()))
2726 return CSSContentDistributionValue::create(CSSValueInvalid, range.consumeIncludingWhitespace().id(), overflow);
2731 static RefPtr<CSSPrimitiveValue> consumeBorderImageRepeatKeyword(CSSParserTokenRange& range)
2733 return consumeIdent<CSSValueStretch, CSSValueRepeat, CSSValueSpace, CSSValueRound>(range);
2736 static RefPtr<CSSValue> consumeBorderImageRepeat(CSSParserTokenRange& range)
2738 RefPtr<CSSPrimitiveValue> horizontal = consumeBorderImageRepeatKeyword(range);
2741 RefPtr<CSSPrimitiveValue> vertical = consumeBorderImageRepeatKeyword(range);
2743 vertical = horizontal;
2744 return createPrimitiveValuePair(horizontal.releaseNonNull(), vertical.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2747 static RefPtr<CSSValue> consumeBorderImageSlice(CSSPropertyID property, CSSParserTokenRange& range)
2749 bool fill = consumeIdent<CSSValueFill>(range);
2750 RefPtr<CSSPrimitiveValue> slices[4] = { 0 };
2752 for (size_t index = 0; index < 4; ++index) {
2753 RefPtr<CSSPrimitiveValue> value = consumePercent(range, ValueRangeNonNegative);
2755 value = consumeNumber(range, ValueRangeNonNegative);
2758 slices[index] = value;
2762 if (consumeIdent<CSSValueFill>(range)) {
2767 complete4Sides(slices);
2768 // FIXME: For backwards compatibility, -webkit-border-image, -webkit-mask-box-image and -webkit-box-reflect have to do a fill by default.
2769 // FIXME: What do we do with -webkit-box-reflect and -webkit-mask-box-image? Probably just have to leave them filling...
2770 if (property == CSSPropertyWebkitBorderImage || property == CSSPropertyWebkitMaskBoxImage || property == CSSPropertyWebkitBoxReflect)
2773 // Now build a rect value to hold all four of our primitive values.
2774 // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2775 auto quad = Quad::create();
2776 quad->setTop(slices[0].releaseNonNull());
2777 quad->setRight(slices[1].releaseNonNull());
2778 quad->setBottom(slices[2].releaseNonNull());
2779 quad->setLeft(slices[3].releaseNonNull());
2781 // Make our new border image value now.
2782 return CSSBorderImageSliceValue::create(CSSValuePool::singleton().createValue(WTFMove(quad)), fill);
2785 static RefPtr<CSSValue> consumeBorderImageOutset(CSSParserTokenRange& range)
2787 RefPtr<CSSPrimitiveValue> outsets[4] = { 0 };
2789 RefPtr<CSSPrimitiveValue> value;
2790 for (size_t index = 0; index < 4; ++index) {
2791 value = consumeNumber(range, ValueRangeNonNegative);
2793 value = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
2796 outsets[index] = value;
2800 complete4Sides(outsets);
2802 // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2803 auto quad = Quad::create();
2804 quad->setTop(outsets[0].releaseNonNull());
2805 quad->setRight(outsets[1].releaseNonNull());
2806 quad->setBottom(outsets[2].releaseNonNull());
2807 quad->setLeft(outsets[3].releaseNonNull());
2809 return CSSValuePool::singleton().createValue(WTFMove(quad));
2812 static RefPtr<CSSValue> consumeBorderImageWidth(CSSParserTokenRange& range)
2814 RefPtr<CSSPrimitiveValue> widths[4];
2816 RefPtr<CSSPrimitiveValue> value;
2817 for (size_t index = 0; index < 4; ++index) {
2818 value = consumeNumber(range, ValueRangeNonNegative);
2820 value = consumeLengthOrPercent(range, HTMLStandardMode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
2822 value = consumeIdent<CSSValueAuto>(range);
2825 widths[index] = value;
2829 complete4Sides(widths);
2831 // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2832 auto quad = Quad::create();
2833 quad->setTop(widths[0].releaseNonNull());
2834 quad->setRight(widths[1].releaseNonNull());
2835 quad->setBottom(widths[2].releaseNonNull());
2836 quad->setLeft(widths[3].releaseNonNull());
2838 return CSSValuePool::singleton().createValue(WTFMove(quad));
2841 static bool consumeBorderImageComponents(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context, RefPtr<CSSValue>& source,
2842 RefPtr<CSSValue>& slice, RefPtr<CSSValue>& width, RefPtr<CSSValue>& outset, RefPtr<CSSValue>& repeat)
2846 source = consumeImageOrNone(range, context);
2851 repeat = consumeBorderImageRepeat(range);
2856 slice = consumeBorderImageSlice(property, range);
2858 ASSERT(!width && !outset);
2859 if (consumeSlashIncludingWhitespace(range)) {
2860 width = consumeBorderImageWidth(range);
2861 if (consumeSlashIncludingWhitespace(range)) {
2862 outset = consumeBorderImageOutset(range);
2865 } else if (!width) {
2875 } while (!range.atEnd());
2879 static RefPtr<CSSValue> consumeWebkitBorderImage(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2881 RefPtr<CSSValue> source;
2882 RefPtr<CSSValue> slice;
2883 RefPtr<CSSValue> width;
2884 RefPtr<CSSValue> outset;
2885 RefPtr<CSSValue> repeat;
2886 if (consumeBorderImageComponents(property, range, context, source, slice, width, outset, repeat))
2887 return createBorderImageValue(WTFMove(source), WTFMove(slice), WTFMove(width), WTFMove(outset), WTFMove(repeat));
2891 static RefPtr<CSSValue> consumeReflect(CSSParserTokenRange& range, const CSSParserContext& context)
2893 if (range.peek().id() == CSSValueNone)
2894 return consumeIdent(range);
2896 RefPtr<CSSPrimitiveValue> direction = consumeIdent<CSSValueAbove, CSSValueBelow, CSSValueLeft, CSSValueRight>(range);
2900 RefPtr<CSSPrimitiveValue> offset;
2902 offset = CSSValuePool::singleton().createValue(0, CSSPrimitiveValue::UnitType::CSS_PX);
2904 offset = consumeLengthOrPercent(range, context.mode, ValueRangeAll, UnitlessQuirk::Forbid);
2909 RefPtr<CSSValue> mask;
2910 if (!range.atEnd()) {
2911 mask = consumeWebkitBorderImage(CSSPropertyWebkitBoxReflect, range, context);
2915 return CSSReflectValue::create(direction.releaseNonNull(), offset.releaseNonNull(), WTFMove(mask));
2918 #if ENABLE(CSS_IMAGE_ORIENTATION)
2919 static RefPtr<CSSValue> consumeImageOrientation(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
2921 if (range.peek().type() != NumberToken) {
2922 RefPtr<CSSPrimitiveValue> angle = consumeAngle(range, cssParserMode, unitless);
2923 if (angle && angle->doubleValue() == 0)
2930 static RefPtr<CSSPrimitiveValue> consumeBackgroundBlendMode(CSSParserTokenRange& range)
2932 CSSValueID id = range.peek().id();
2933 if (id == CSSValueNormal || id == CSSValueOverlay || (id >= CSSValueMultiply && id <= CSSValueLuminosity))
2934 return consumeIdent(range);
2938 static RefPtr<CSSPrimitiveValue> consumeBackgroundAttachment(CSSParserTokenRange& range)
2940 return consumeIdent<CSSValueScroll, CSSValueFixed, CSSValueLocal>(range);
2943 static RefPtr<CSSPrimitiveValue> consumeBackgroundBox(CSSParserTokenRange& range)
2945 return consumeIdent<CSSValueBorderBox, CSSValuePaddingBox, CSSValueContentBox, CSSValueWebkitText>(range);
2948 static RefPtr<CSSPrimitiveValue> consumeBackgroundComposite(CSSParserTokenRange& range)
2950 return consumeIdentRange(range, CSSValueClear, CSSValuePlusLighter);
2953 static RefPtr<CSSPrimitiveValue> consumeWebkitMaskSourceType(CSSParserTokenRange& range)
2955 return consumeIdent<CSSValueAuto, CSSValueAlpha, CSSValueLuminance>(range);
2958 static RefPtr<CSSPrimitiveValue> consumePrefixedBackgroundBox(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& /*context*/)
2960 // The values 'border', 'padding' and 'content' are deprecated and do not apply to the version of the property that has the -webkit- prefix removed.
2961 if (RefPtr<CSSPrimitiveValue> value = consumeIdentRange(range, CSSValueBorder, CSSValuePaddingBox))
2963 if (range.peek().id() == CSSValueWebkitText || ((property == CSSPropertyWebkitBackgroundClip || property == CSSPropertyWebkitMaskClip) && range.peek().id() == CSSValueText))
2964 return consumeIdent(range);
2968 static RefPtr<CSSPrimitiveValue> consumeBackgroundSize(CSSPropertyID property, CSSParserTokenRange& range, CSSParserMode cssParserMode)
2970 if (identMatches<CSSValueContain, CSSValueCover>(range.peek().id()))
2971 return consumeIdent(range);
2973 // FIXME: We're allowing the unitless quirk on this property because our
2974 // tests assume that. Other browser engines don't allow it though.
2975 RefPtr<CSSPrimitiveValue> horizontal = consumeIdent<CSSValueAuto>(range);
2977 horizontal = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2979 RefPtr<CSSPrimitiveValue> vertical;
2980 if (!range.atEnd()) {
2981 if (range.peek().id() == CSSValueAuto) // `auto' is the default
2982 range.consumeIncludingWhitespace();
2984 vertical = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2985 } else if (!vertical && property == CSSPropertyWebkitBackgroundSize) {
2986 // Legacy syntax: "-webkit-background-size: 10px" is equivalent to "background-size: 10px 10px".
2987 vertical = horizontal;
2991 return createPrimitiveValuePair(horizontal.releaseNonNull(), vertical.releaseNonNull(), property == CSSPropertyWebkitBackgroundSize ? Pair::IdenticalValueEncoding::Coalesce : Pair::IdenticalValueEncoding::DoNotCoalesce);
2994 static RefPtr<CSSValueList> consumeGridAutoFlow(CSSParserTokenRange& range)
2996 RefPtr<CSSPrimitiveValue> rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
2997 RefPtr<CSSPrimitiveValue> denseAlgorithm = consumeIdent<CSSValueDense>(range);
2998 if (!rowOrColumnValue) {
2999 rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
3000 if (!rowOrColumnValue && !denseAlgorithm)
3003 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
3004 if (rowOrColumnValue)
3005 parsedValues->append(rowOrColumnValue.releaseNonNull());
3007 parsedValues->append(denseAlgorithm.releaseNonNull());
3008 return parsedValues;
3011 static RefPtr<CSSValue> consumeBackgroundComponent(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
3014 case CSSPropertyBackgroundClip:
3015 return consumeBackgroundBox(range);
3016 case CSSPropertyBackgroundBlendMode:
3017 return consumeBackgroundBlendMode(range);
3018 case CSSPropertyBackgroundAttachment:
3019 return consumeBackgroundAttachment(range);
3020 case CSSPropertyBackgroundOrigin:
3021 return consumeBackgroundBox(range);
3022 case CSSPropertyWebkitMaskComposite:
3023 case CSSPropertyWebkitBackgroundComposite:
3024 return consumeBackgroundComposite(range);
3025 case CSSPropertyWebkitBackgroundClip:
3026 case CSSPropertyWebkitBackgroundOrigin:
3027 case CSSPropertyWebkitMaskClip:
3028 case CSSPropertyWebkitMaskOrigin:
3029 return consumePrefixedBackgroundBox(property, range, context);
3030 case CSSPropertyBackgroundImage:
3031 case CSSPropertyWebkitMaskImage:
3032 return consumeImageOrNone(range, context);
3033 case CSSPropertyWebkitMaskSourceType:
3034 return consumeWebkitMaskSourceType(range);
3035 case CSSPropertyBackgroundPositionX:
3036 case CSSPropertyWebkitMaskPositionX:
3037 return consumePositionX(range, context.mode);
3038 case CSSPropertyBackgroundPositionY:
3039 case CSSPropertyWebkitMaskPositionY:
3040 return consumePositionY(range, context.mode);
3041 case CSSPropertyBackgroundSize:
3042 case CSSPropertyWebkitBackgroundSize:
3043 case CSSPropertyWebkitMaskSize:
3044 return consumeBackgroundSize(property, range, context.mode);
3045 case CSSPropertyBackgroundColor:
3046 return consumeColor(range, context.mode);
3053 static void addBackgroundValue(RefPtr<CSSValue>& list, Ref<CSSValue>&& value)
3056 if (!list->isBaseValueList()) {
3057 RefPtr<CSSValue> firstValue = list;
3058 list = CSSValueList::createCommaSeparated();
3059 downcast<CSSValueList>(*list).append(firstValue.releaseNonNull());
3061 downcast<CSSValueList>(*list).append(WTFMove(value));
3063 // To conserve memory we don't actually wrap a single value in a list.
3064 list = WTFMove(value);
3068 static RefPtr<CSSValue> consumeCommaSeparatedBackgroundComponent(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
3070 RefPtr<CSSValue> result;
3072 RefPtr<CSSValue> value = consumeBackgroundComponent(property, range, context);
3075 addBackgroundValue(result, value.releaseNonNull());
3076 } while (consumeCommaIncludingWhitespace(range));
3080 static bool isSelfPositionKeyword(CSSValueID id)
3082 return identMatches<CSSValueStart, CSSValueEnd, CSSValueCenter, CSSValueSelfStart, CSSValueSelfEnd, CSSValueFlexStart, CSSValueFlexEnd>(id);
3085 static bool isSelfPositionOrLeftOrRightKeyword(CSSValueID id)
3087 return isSelfPositionKeyword(id) || isLeftOrRightKeyword(id);
3090 static RefPtr<CSSValue> consumeSelfPositionOverflowPosition(CSSParserTokenRange& range, IsPositionKeyword isPositionKeyword)
3092 ASSERT(isPositionKeyword);
3093 CSSValueID id = range.peek().id();
3094 if (isAuto(id) || isNormalOrStretch(id))
3095 return consumeIdent(range);
3097 if (isBaselineKeyword(id))
3098 return consumeBaselineKeyword(range);
3100 RefPtr<CSSPrimitiveValue> overflowPosition = consumeOverflowPositionKeyword(range);
3101 if (!isPositionKeyword(range.peek().id()))
3103 RefPtr<CSSPrimitiveValue> selfPosition = consumeIdent(range);
3104 if (overflowPosition)
3105 return createPrimitiveValuePair(overflowPosition.releaseNonNull(), selfPosition.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
3106 return selfPosition;
3109 static RefPtr<CSSValue> consumeAlignItems(CSSParserTokenRange& range)
3111 // align-items property does not allow the 'auto' value.
3112 if (identMatches<CSSValueAuto>(range.peek().id()))
3114 return consumeSelfPositionOverflowPosition(range, isSelfPositionKeyword);
3117 static RefPtr<CSSValue> consumeJustifyItems(CSSParserTokenRange& range)
3119 // justify-items property does not allow the 'auto' value.
3120 if (identMatches<CSSValueAuto>(range.peek().id()))
3122 CSSParserTokenRange rangeCopy = range;
3123 RefPtr<CSSPrimitiveValue> legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
3124 RefPtr<CSSPrimitiveValue> positionKeyword = consumeIdent<CSSValueCenter, CSSValueLeft, CSSValueRight>(rangeCopy);
3126 legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
3129 if (positionKeyword)
3130 return createPrimitiveValuePair(legacy.releaseNonNull(), positionKeyword.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
3133 return consumeSelfPositionOverflowPosition(range, isSelfPositionOrLeftOrRightKeyword);
3136 static RefPtr<CSSValue> consumeFitContent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3138 CSSParserTokenRange rangeCopy = range;
3139 CSSParserTokenRange args = consumeFunction(rangeCopy);
3140 RefPtr<CSSPrimitiveValue> length = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3141 if (!length || !args.atEnd())
3144 RefPtr<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueFitContent);
3145 result->append(length.releaseNonNull());
3149 static RefPtr<CSSPrimitiveValue> consumeCustomIdentForGridLine(CSSParserTokenRange& range)
3151 if (range.peek().id() == CSSValueAuto || range.peek().id() == CSSValueSpan)
3153 return consumeCustomIdent(range);
3156 static RefPtr<CSSValue> consumeGridLine(CSSParserTokenRange& range)
3158 if (range.peek().id() == CSSValueAuto)
3159 return consumeIdent(range);
3161 RefPtr<CSSPrimitiveValue> spanValue;
3162 RefPtr<CSSPrimitiveValue> gridLineName;
3163 RefPtr<CSSPrimitiveValue> numericValue = consumeInteger(range);
3165 gridLineName = consumeCustomIdentForGridLine(range);
3166 spanValue = consumeIdent<CSSValueSpan>(range);
3168 spanValue = consumeIdent<CSSValueSpan>(range);
3170 numericValue = consumeInteger(range);
3171 gridLineName = consumeCustomIdentForGridLine(range);
3173 numericValue = consumeInteger(range);
3175 gridLineName = consumeCustomIdentForGridLine(range);
3177 numericValue = consumeInteger(range);
3178 spanValue = consumeIdent<CSSValueSpan>(range);
3179 if (!spanValue && !numericValue)
3180 return gridLineName;
3187 if (spanValue && !numericValue && !gridLineName)
3188 return nullptr; // "span" keyword alone is invalid.
3189 if (spanValue && numericValue && numericValue->intValue() < 0)
3190 return nullptr; // Negative numbers are not allowed for span.
3191 if (numericValue && numericValue->intValue() == 0)
3192 return nullptr; // An <integer> value of zero makes the declaration invalid.
3194 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3196 values->append(spanValue.releaseNonNull());
3198 values->append(numericValue.releaseNonNull());
3200 values->append(gridLineName.releaseNonNull());
3201 ASSERT(values->length());
3205 static bool isGridTrackFixedSized(const CSSPrimitiveValue& primitiveValue)
3207 CSSValueID valueID = primitiveValue.valueID();
3208 if (valueID == CSSValueMinContent || valueID == CSSValueWebkitMinContent || valueID == CSSValueMaxContent || valueID == CSSValueWebkitMaxContent || valueID == CSSValueAuto || primitiveValue.isFlex())
3214 static bool isGridTrackFixedSized(const CSSValue& value)
3216 if (value.isPrimitiveValue())
3217 return isGridTrackFixedSized(downcast<CSSPrimitiveValue>(value));
3219 ASSERT(value.isFunctionValue());
3220 auto& function = downcast<CSSFunctionValue>(value);
3221 if (function.name() == CSSValueFitContent || function.length() < 2)
3224 const CSSValue* minPrimitiveValue = downcast<CSSPrimitiveValue>(function.item(0));
3225 const CSSValue* maxPrimitiveValue = downcast<CSSPrimitiveValue>(function.item(1));
3226 return isGridTrackFixedSized(*minPrimitiveValue) || isGridTrackFixedSized(*maxPrimitiveValue);
3229 static Vector<String> parseGridTemplateAreasColumnNames(const String& gridRowNames)
3231 ASSERT(!gridRowNames.isEmpty());
3232 Vector<String> columnNames;
3233 // Using StringImpl to avoid checks and indirection in every call to String::operator[].
3234 StringImpl& text = *gridRowNames.impl();
3236 StringBuilder areaName;
3237 for (unsigned i = 0; i < text.length(); ++i) {
3238 if (isCSSSpace(text[i])) {
3239 if (!areaName.isEmpty()) {
3240 columnNames.append(areaName.toString());
3245 if (text[i] == '.') {
3246 if (areaName == ".")
3248 if (!areaName.isEmpty()) {
3249 columnNames.append(areaName.toString());
3253 if (!isNameCodePoint(text[i]))
3254 return Vector<String>();
3255 if (areaName == ".") {
3256 columnNames.append(areaName.toString());
3261 areaName.append(text[i]);
3264 if (!areaName.isEmpty())
3265 columnNames.append(areaName.toString());
3270 static bool parseGridTemplateAreasRow(const String& gridRowNames, NamedGridAreaMap& gridAreaMap, const size_t rowCount, size_t& columnCount)
3272 if (gridRowNames.isAllSpecialCharacters<isCSSSpace>())
3275 Vector<String> columnNames = parseGridTemplateAreasColumnNames(gridRowNames);
3276 if (rowCount == 0) {
3277 columnCount = columnNames.size();
3278 if (columnCount == 0)
3280 } else if (columnCount != columnNames.size()) {
3281 // The declaration is invalid if all the rows don't have the number of columns.
3285 for (size_t currentColumn = 0; currentColumn < columnCount; ++currentColumn) {
3286 const String& gridAreaName = columnNames[currentColumn];
3288 // Unamed areas are always valid (we consider them to be 1x1).
3289 if (gridAreaName == ".")
3292 size_t lookAheadColumn = currentColumn + 1;
3293 while (lookAheadColumn < columnCount && columnNames[lookAheadColumn] == gridAreaName)
3296 NamedGridAreaMap::iterator gridAreaIt = gridAreaMap.find(gridAreaName);
3297 if (gridAreaIt == gridAreaMap.end()) {
3298 gridAreaMap.add(gridAreaName, GridArea(GridSpan::translatedDefiniteGridSpan(rowCount, rowCount + 1), GridSpan::translatedDefiniteGridSpan(currentColumn, lookAheadColumn)));
3300 GridArea& gridArea = gridAreaIt->value;
3302 // The following checks test that the grid area is a single filled-in rectangle.
3303 // 1. The new row is adjacent to the previously parsed row.
3304 if (rowCount != gridArea.rows.endLine())
3307 // 2. The new area starts at the same position as the previously parsed area.
3308 if (currentColumn != gridArea.columns.startLine())
3311 // 3. The new area ends at the same position as the previously parsed area.
3312 if (lookAheadColumn != gridArea.columns.endLine())
3315 gridArea.rows = GridSpan::translatedDefiniteGridSpan(gridArea.rows.startLine(), gridArea.rows.endLine() + 1);
3317 currentColumn = lookAheadColumn - 1;
3323 static RefPtr<CSSPrimitiveValue> consumeGridBreadth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3325 const CSSParserToken& token = range.peek();
3326 if (identMatches<CSSValueMinContent, CSSValueWebkitMinContent, CSSValueMaxContent, CSSValueWebkitMaxContent, CSSValueAuto>(token.id()))
3327 return consumeIdent(range);
3328 if (token.type() == DimensionToken && token.unitType() == CSSPrimitiveValue::UnitType::CSS_FR) {
3329 if (range.peek().numericValue() < 0)
3331 return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::CSS_FR);
3333 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3336 static RefPtr<CSSValue> consumeGridTrackSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3338 const CSSParserToken& token = range.peek();
3339 if (identMatches<CSSValueAuto>(token.id()))
3340 return consumeIdent(range);
3342 if (token.functionId() == CSSValueMinmax) {
3343 CSSParserTokenRange rangeCopy = range;
3344 CSSParserTokenRange args = consumeFunction(rangeCopy);
3345 RefPtr<CSSPrimitiveValue> minTrackBreadth = consumeGridBreadth(args, cssParserMode);
3346 if (!minTrackBreadth || minTrackBreadth->isFlex() || !consumeCommaIncludingWhitespace(args))
3348 RefPtr<CSSPrimitiveValue> maxTrackBreadth = consumeGridBreadth(args, cssParserMode);
3349 if (!maxTrackBreadth || !args.atEnd())
3352 RefPtr<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueMinmax);
3353 result->append(minTrackBreadth.releaseNonNull());
3354 result->append(maxTrackBreadth.releaseNonNull());
3358 if (token.functionId() == CSSValueFitContent)
3359 return consumeFitContent(range, cssParserMode);
3361 return consumeGridBreadth(range, cssParserMode);
3364 // Appends to the passed in CSSGridLineNamesValue if any, otherwise creates a new one.
3365 static RefPtr<CSSGridLineNamesValue> consumeGridLineNames(CSSParserTokenRange& range, CSSGridLineNamesValue* lineNames = nullptr)
3367 CSSParserTokenRange rangeCopy = range;
3368 if (rangeCopy.consumeIncludingWhitespace().type() != LeftBracketToken)
3371 RefPtr<CSSGridLineNamesValue> result = lineNames;
3373 result = CSSGridLineNamesValue::create();
3374 while (RefPtr<CSSPrimitiveValue> lineName = consumeCustomIdentForGridLine(rangeCopy))
3375 result->append(lineName.releaseNonNull());
3376 if (rangeCopy.consumeIncludingWhitespace().type() != RightBracketToken)
3382 static bool consumeGridTrackRepeatFunction(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSValueList& list, bool& isAutoRepeat, bool& allTracksAreFixedSized)
3384 CSSParserTokenRange args = consumeFunction(range);
3385 // The number of repetitions for <auto-repeat> is not important at parsing level
3386 // because it will be computed later, let's set it to 1.
3387 size_t repetitions = 1;
3388 isAutoRepeat = identMatches<CSSValueAutoFill, CSSValueAutoFit>(args.peek().id());
3389 RefPtr<CSSValueList> repeatedValues;
3391 repeatedValues = CSSGridAutoRepeatValue::create(args.consumeIncludingWhitespace().id());
3393 // FIXME: a consumeIntegerRaw would be more efficient here.
3394 RefPtr<CSSPrimitiveValue> repetition = consumePositiveInteger(args);
3397 repetitions = clampTo<size_t>(repetition->doubleValue(), 0, GridPosition::max());
3398 repeatedValues = CSSValueList::createSpaceSeparated();
3400 if (!consumeCommaIncludingWhitespace(args))
3402 RefPtr<CSSGridLineNamesValue> lineNames = consumeGridLineNames(args);
3404 repeatedValues->append(lineNames.releaseNonNull());
3406 size_t numberOfTracks = 0;
3407 while (!args.atEnd()) {
3408 RefPtr<CSSValue> trackSize = consumeGridTrackSize(args, cssParserMode);
3411 if (allTracksAreFixedSized)
3412 allTracksAreFixedSized = isGridTrackFixedSized(*trackSize);
3413 repeatedValues->append(trackSize.releaseNonNull());
3415 lineNames = consumeGridLineNames(args);
3417 repeatedValues->append(lineNames.releaseNonNull());
3419 // We should have found at least one <track-size> or else it is not a valid <track-list>.
3420 if (!numberOfTracks)
3424 list.append(repeatedValues.releaseNonNull());
3426 // We clamp the repetitions to a multiple of the repeat() track list's size, while staying below the max grid size.
3427 repetitions = std::min(repetitions, GridPosition::max() / numberOfTracks);
3428 for (size_t i = 0; i < repetitions; ++i) {
3429 for (size_t j = 0; j < repeatedValues->length(); ++j)
3430 list.append(*repeatedValues->itemWithoutBoundsCheck(j));
3436 enum TrackListType { GridTemplate, GridTemplateNoRepeat, GridAuto };
3438 static RefPtr<CSSValue> consumeGridTrackList(CSSParserTokenRange& range, CSSParserMode cssParserMode, TrackListType trackListType)
3440 bool allowGridLineNames = trackListType != GridAuto;
3441 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3442 RefPtr<CSSGridLineNamesValue> lineNames = consumeGridLineNames(range);
3444 if (!allowGridLineNames)
3446 values->append(lineNames.releaseNonNull());
3449 bool allowRepeat = trackListType == GridTemplate;
3450 bool seenAutoRepeat = false;
3451 bool allTracksAreFixedSized = true;
3454 if (range.peek().functionId() == CSSValueRepeat) {
3457 if (!consumeGridTrackRepeatFunction(range, cssParserMode, *values, isAutoRepeat, allTracksAreFixedSized))
3459 if (isAutoRepeat && seenAutoRepeat)
3461 seenAutoRepeat = seenAutoRepeat || isAutoRepeat;
3462 } else if (RefPtr<CSSValue> value = consumeGridTrackSize(range, cssParserMode)) {
3463 if (allTracksAreFixedSized)
3464 allTracksAreFixedSized = isGridTrackFixedSized(*value);
3465 values->append(value.releaseNonNull());
3469 if (seenAutoRepeat && !allTracksAreFixedSized)
3471 lineNames = consumeGridLineNames(range);
3473 if (!allowGridLineNames)
3475 values->append(lineNames.releaseNonNull());
3477 } while (!range.atEnd() && range.peek().type() != DelimiterToken);
3481 static RefPtr<CSSValue> consumeGridTemplatesRowsOrColumns(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3483 if (range.peek().id() == CSSValueNone)
3484 return consumeIdent(range);
3485 return consumeGridTrackList(range, cssParserMode, GridTemplate);
3488 static RefPtr<CSSValue> consumeGridTemplateAreas(CSSParserTokenRange& range)
3490 if (range.peek().id() == CSSValueNone)
3491 return consumeIdent(range);
3493 NamedGridAreaMap gridAreaMap;
3494 size_t rowCount = 0;
3495 size_t columnCount = 0;
3497 while (range.peek().type() == StringToken) {
3498 if (!parseGridTemplateAreasRow(range.consumeIncludingWhitespace().value().toString(), gridAreaMap, rowCount, columnCount))
3505 ASSERT(columnCount);
3506 return CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount);
3509 static RefPtr<CSSValue> consumeLineBoxContain(CSSParserTokenRange& range)
3511 if (range.peek().id() == CSSValueNone)
3512 return consumeIdent(range);
3514 LineBoxContain lineBoxContain = LineBoxContainNone;
3516 while (range.peek().type() == IdentToken) {
3517 auto id = range.peek().id();
3518 if (id == CSSValueBlock) {
3519 if (lineBoxContain & LineBoxContainBlock)
3521 lineBoxContain |= LineBoxContainBlock;
3522 } else if (id == CSSValueInline) {
3523 if (lineBoxContain & LineBoxContainInline)
3525 lineBoxContain |= LineBoxContainInline;
3526 } else if (id == CSSValueFont) {
3527 if (lineBoxContain & LineBoxContainFont)
3529 lineBoxContain |= LineBoxContainFont;
3530 } else if (id == CSSValueGlyphs) {
3531 if (lineBoxContain & LineBoxContainGlyphs)
3533 lineBoxContain |= LineBoxContainGlyphs;
3534 } else if (id == CSSValueReplaced) {
3535 if (lineBoxContain & LineBoxContainReplaced)
3537 lineBoxContain |= LineBoxContainReplaced;
3538 } else if (id == CSSValueInlineBox) {
3539 if (lineBoxContain & LineBoxContainInlineBox)
3541 lineBoxContain |= LineBoxContainInlineBox;
3542 } else if (id == CSSValueInitialLetter) {
3543 if (lineBoxContain & LineBoxContainInitialLetter)
3545 lineBoxContain |= LineBoxContainInitialLetter;
3548 range.consumeIncludingWhitespace();
3551 if (!lineBoxContain)
3554 return CSSLineBoxContainValue::create(lineBoxContain);
3557 static RefPtr<CSSValue> consumeLineGrid(CSSParserTokenRange& range)
3559 if (range.peek().id() == CSSValueNone)
3560 return consumeIdent(range);
3561 return consumeCustomIdent(range);
3564 static RefPtr<CSSValue> consumeInitialLetter(CSSParserTokenRange& range)
3566 RefPtr<CSSValue> ident = consumeIdent<CSSValueNormal>(range);
3570 RefPtr<CSSPrimitiveValue> height = consumeNumber(range, ValueRangeNonNegative);
3574 RefPtr<CSSPrimitiveValue> position;
3575 if (!range.atEnd()) {
3576 position = consumeNumber(range, ValueRangeNonNegative);
3577 if (!position || !range.atEnd())
3580 position = height.copyRef();
3582 return createPrimitiveValuePair(position.releaseNonNull(), WTFMove(height));
3585 static RefPtr<CSSValue> consumeSpeakAs(CSSParserTokenRange& range)
3587 if (range.peek().id() == CSSValueNone)
3588 return consumeIdent(range);
3590 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3592 bool seenNormal = false;
3593 bool seenSpellOut = false;
3594 bool seenLiteralPunctuation = false;
3595 bool seenNoPunctuation = false;
3597 // normal | spell-out || digits || [ literal-punctuation | no-punctuation ]
3598 while (!range.atEnd()) {
3599 CSSValueID valueID = range.peek().id();
3600 if ((valueID == CSSValueNormal && seenSpellOut)
3601 || (valueID == CSSValueSpellOut && seenNormal)
3602 || (valueID == CSSValueLiteralPunctuation && seenNoPunctuation)
3603 || (valueID == CSSValueNoPunctuation && seenLiteralPunctuation))
3605 RefPtr<CSSValue> ident = consumeIdent<CSSValueNormal, CSSValueSpellOut, CSSValueDigits, CSSValueLiteralPunctuation, CSSValueNoPunctuation>(range);
3609 case CSSValueNormal:
3612 case CSSValueSpellOut:
3613 seenSpellOut = true;
3615 case CSSValueLiteralPunctuation:
3616 seenLiteralPunctuation = true;
3618 case CSSValueNoPunctuation:
3619 seenNoPunctuation = true;
3624 list->append(ident.releaseNonNull());
3627 return list->length() ? list : nullptr;
3630 static RefPtr<CSSValue> consumeHangingPunctuation(CSSParserTokenRange& range)
3632 if (range.peek().id() == CSSValueNone)
3633 return consumeIdent(range);
3635 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3637 bool seenForceEnd = false;
3638 bool seenAllowEnd = false;
3639 bool seenFirst = false;
3640 bool seenLast = false;
3642 while (!range.atEnd()) {
3643 CSSValueID valueID = range.peek().id();
3644 if ((valueID == CSSValueFirst && seenFirst)
3645 || (valueID == CSSValueLast && seenLast)
3646 || (valueID == CSSValueAllowEnd && (seenAllowEnd || seenForceEnd))
3647 || (valueID == CSSValueForceEnd && (seenAllowEnd || seenForceEnd)))
3649 RefPtr<CSSValue> ident = consumeIdent<CSSValueAllowEnd, CSSValueForceEnd, CSSValueFirst, CSSValueLast>(range);
3653 case CSSValueAllowEnd:
3654 seenAllowEnd = true;
3656 case CSSValueForceEnd:
3657 seenForceEnd = true;
3668 list->append(ident.releaseNonNull());
3671 return list->length() ? list : nullptr;
3674 static RefPtr<CSSValue> consumeWebkitMarqueeIncrement(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3676 if (range.peek().type() == IdentToken)
3677 return consumeIdent<CSSValueSmall, CSSValueMedium, CSSValueLarge>(range);
3678 return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
3681 static RefPtr<CSSValue> consumeWebkitMarqueeRepetition(CSSParserTokenRange& range)
3683 if (range.peek().type() == IdentToken)
3684 return consumeIdent<CSSValueInfinite>(range);
3685 return consumeNumber(range, ValueRangeNonNegative);
3688 static RefPtr<CSSValue> consumeWebkitMarqueeSpeed(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3690 if (range.peek().type() == IdentToken)
3691 return consumeIdent<CSSValueSlow, CSSValueNormal, CSSValueFast>(range);
3692 return consumeTime(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3695 static RefPtr<CSSValue> consumeAlt(CSSParserTokenRange& range, const CSSParserContext& context)
3697 if (range.peek().type() == StringToken)
3698 return consumeString(range);
3700 if (range.peek().functionId() != CSSValueAttr)
3703 return consumeAttr(consumeFunction(range), context);
3706 static RefPtr<CSSValue> consumeWebkitAspectRatio(CSSParserTokenRange& range)
3708 if (range.peek().type() == IdentToken)
3709 return consumeIdent<CSSValueAuto, CSSValueFromDimensions, CSSValueFromIntrinsic>(range);
3711 RefPtr<CSSPrimitiveValue> leftValue = consumeNumber(range, ValueRangeNonNegative);
3712 if (!leftValue || !leftValue->floatValue() || range.atEnd() || !consumeSlashIncludingWhitespace(range))
3714 RefPtr<CSSPrimitiveValue> rightValue = consumeNumber(range, ValueRangeNonNegative);
3715 if (!rightValue || !rightValue->floatValue())
3718 return CSSAspectRatioValue::create(leftValue->floatValue(), rightValue->floatValue());
3721 static RefPtr<CSSValue> consumeTextEmphasisPosition(CSSParserTokenRange& range)
3723 bool foundOverOrUnder = false;
3724 CSSValueID overUnderValueID = CSSValueOver;
3725 bool foundLeftOrRight = false;
3726 CSSValueID leftRightValueID = CSSValueRight;
3727 while (!range.atEnd()) {
3728 switch (range.peek().id()) {
3730 if (foundOverOrUnder)
3732 foundOverOrUnder = true;
3733 overUnderValueID = CSSValueOver;
3736 if (foundOverOrUnder)
3738 foundOverOrUnder = true;
3739 overUnderValueID = CSSValueUnder;
3742 if (foundLeftOrRight)
3744 foundLeftOrRight = true;
3745 leftRightValueID = CSSValueLeft;
3748 if (foundLeftOrRight)
3750 foundLeftOrRight = true;
3751 leftRightValueID = CSSValueRight;
3757 range.consumeIncludingWhitespace();
3759 if (!foundOverOrUnder)
3761 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3762 list->append(CSSValuePool::singleton().createIdentifierValue(overUnderValueID));
3763 if (foundLeftOrRight)
3764 list->append(CSSValuePool::singleton().createIdentifierValue(leftRightValueID));
3768 #if ENABLE(DARK_MODE_CSS)
3770 static RefPtr<CSSValue> consumeSupportedColorSchemes(CSSParserTokenRange& range)
3772 if (isAuto(range.peek().id()))
3773 return consumeIdent(range);
3775 Vector<CSSValueID, 3> identifiers;
3777 while (!range.atEnd()) {
3778 if (range.peek().type() != IdentToken)
3781 CSSValueID id = range.peek().id();
3785 // Auto is only allowed as a single value, and was handled earlier.
3786 // Don't allow it in the list.
3792 if (!identifiers.appendIfNotContains(id))
3797 // Unknown identifiers are allowed and ignored.
3801 range.consumeIncludingWhitespace();
3804 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3805 for (auto id : identifiers)
3806 list->append(CSSValuePool::singleton().createIdentifierValue(id));
3812 #if ENABLE(DASHBOARD_SUPPORT)
3814 static RefPtr<CSSValue> consumeWebkitDashboardRegion(CSSParserTokenRange& range, CSSParserMode mode)
3819 if (range.peek().id() == CSSValueNone)
3820 return consumeIdent(range);
3822 auto firstRegion = DashboardRegion::create();
3823 DashboardRegion* region = nullptr;
3825 bool requireCommas = false;
3827 while (!range.atEnd()) {
3829 region = firstRegion.ptr();
3831 auto nextRegion = DashboardRegion::create();
3832 region->m_next = nextRegion.copyRef();
3833 region = nextRegion.ptr();
3836 if (range.peek().functionId() != CSSValueDashboardRegion)
3839 CSSParserTokenRange rangeCopy = range;
3840 CSSParserTokenRange args = consumeFunction(rangeCopy);
3841 if (rangeCopy.end() == args.end())
3842 return nullptr; // No ) was found. Be strict about this, since tests are.
3844 // First arg is a label.
3845 if (args.peek().type() != IdentToken)
3847 region->m_label = args.consumeIncludingWhitespace().value().toString();
3849 // Comma is optional, so don't fail if we can't consume one.
3850 requireCommas = consumeCommaIncludingWhitespace(args);
3852 // Second arg is a type.
3853 if (args.peek().type() != IdentToken)
3855 region->m_geometryType = args.consumeIncludingWhitespace().value().toString();
3856 if (equalLettersIgnoringASCIICase(region->m_geometryType, "circle"))
3857 region->m_isCircle = true;
3858 else if (equalLettersIgnoringASCIICase(region->m_geometryType, "rectangle"))
3859 region->m_isRectangle = true;
3864 // This originally used CSSValueInvalid by accident. It might be more logical to use something else.
3865 RefPtr<CSSPrimitiveValue> amount = CSSValuePool::singleton().createIdentifierValue(CSSValueInvalid);
3866 region->setTop(amount.copyRef());
3867 region->setRight(amount.copyRef());
3868 region->setBottom(amount.copyRef());
3869 region->setLeft(WTFMove(amount));
3874 // Next four arguments must be offset numbers or auto.
3875 for (int i = 0; i < 4; ++i) {
3876 if (args.atEnd() || (requireCommas && !consumeCommaIncludingWhitespace(args)))
3882 RefPtr<CSSPrimitiveValue> amount;
3883 if (args.peek().id() == CSSValueAuto)
3884 amount = consumeIdent(args);
3886 amount = consumeLength(args, mode, ValueRangeAll);
3889 region->setTop(WTFMove(amount));
3891 region->setRight(WTFMove(amount));
3893 region->setBottom(WTFMove(amount));