1// Copyright 2015 The Chromium Authors. All rights reserved.
2// Copyright (C) 2016 Apple Inc. All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
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
13// distribution.
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.
17//
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.
29
30#include "config.h"
31#include "CSSPropertyParser.h"
32
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"
45#endif
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"
64#include "Counter.h"
65#if ENABLE(DASHBOARD_SUPPORT)
66#include "DashboardRegion.h"
67#endif
68#include "FontFace.h"
69#include "HashTools.h"
70// FIXME-NEWPARSER: Replace Pair and Rect with actual CSSValue subclasses (CSSValuePair and CSSQuadValue).
71#include "Pair.h"
72#include "Rect.h"
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"
81#include <bitset>
82#include <memory>
83#include <wtf/text/StringBuilder.h>
84
85namespace WebCore {
86
87bool isCustomPropertyName(const String& propertyName)
88{
89 return propertyName.length() > 2 && propertyName.characterAt(0) == '-' && propertyName.characterAt(1) == '-';
90}
91
92static bool hasPrefix(const char* string, unsigned length, const char* prefix)
93{
94 for (unsigned i = 0; i < length; ++i) {
95 if (!prefix[i])
96 return true;
97 if (string[i] != prefix[i])
98 return false;
99 }
100 return false;
101}
102
103#if PLATFORM(IOS_FAMILY)
104void cssPropertyNameIOSAliasing(const char* propertyName, const char*& propertyNameAlias, unsigned& newLength)
105{
106 if (!strcmp(propertyName, "-webkit-hyphenate-locale")) {
107 // Worked in iOS 4.2.
108 static const char webkitLocale[] = "-webkit-locale";
109 propertyNameAlias = webkitLocale;
110 newLength = strlen(webkitLocale);
111 }
112}
113#endif
114
115template <typename CharacterType>
116static CSSPropertyID cssPropertyID(const CharacterType* propertyName, unsigned length)
117{
118 char buffer[maxCSSPropertyNameLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
119
120 for (unsigned i = 0; i != length; ++i) {
121 CharacterType c = propertyName[i];
122 if (!c || c >= 0x7F)
123 return CSSPropertyInvalid; // illegal character
124 buffer[i] = toASCIILower(c);
125 }
126 buffer[length] = '\0';
127
128 const char* name = buffer;
129 if (buffer[0] == '-') {
130#if ENABLE(LEGACY_CSS_VENDOR_PREFIXES)
131 // If the prefix is -apple- or -khtml-, change it to -webkit-.
132 // This makes the string one character longer.
133 if (RuntimeEnabledFeatures::sharedFeatures().legacyCSSVendorPrefixesEnabled()
134 && (hasPrefix(buffer, length, "-apple-") || hasPrefix(buffer, length, "-khtml-"))) {
135 memmove(buffer + 7, buffer + 6, length + 1 - 6);
136 memcpy(buffer, "-webkit", 7);
137 ++length;
138 }
139#endif
140#if PLATFORM(IOS_FAMILY)
141 cssPropertyNameIOSAliasing(buffer, name, length);
142#endif
143 }
144
145 const Property* hashTableEntry = findProperty(name, length);
146 if (hashTableEntry) {
147 auto propertyID = static_cast<CSSPropertyID>(hashTableEntry->id);
148 if (isEnabledCSSProperty(propertyID))
149 return propertyID;
150 }
151 return CSSPropertyInvalid;
152}
153
154static bool isAppleLegacyCssValueKeyword(const char* valueKeyword, unsigned length)
155{
156 static const char applePrefix[] = "-apple-";
157 static const char appleSystemPrefix[] = "-apple-system";
158 static const char applePayPrefix[] = "-apple-pay";
159
160#if PLATFORM(MAC) || PLATFORM(IOS_FAMILY)
161 static const char* appleWirelessPlaybackTargetActive = getValueName(CSSValueAppleWirelessPlaybackTargetActive);
162#endif
163
164 return hasPrefix(valueKeyword, length, applePrefix)
165 && !hasPrefix(valueKeyword, length, appleSystemPrefix)
166 && !hasPrefix(valueKeyword, length, applePayPrefix)
167#if PLATFORM(MAC) || PLATFORM(IOS_FAMILY)
168 && !WTF::equal(reinterpret_cast<const LChar*>(valueKeyword), reinterpret_cast<const LChar*>(appleWirelessPlaybackTargetActive), length)
169#endif
170 ;
171}
172
173template <typename CharacterType>
174static CSSValueID cssValueKeywordID(const CharacterType* valueKeyword, unsigned length)
175{
176 char buffer[maxCSSValueKeywordLength + 1 + 1]; // 1 to turn "apple"/"khtml" into "webkit", 1 for null character
177
178 for (unsigned i = 0; i != length; ++i) {
179 CharacterType c = valueKeyword[i];
180 if (!c || c >= 0x7F)
181 return CSSValueInvalid; // illegal keyword.
182 buffer[i] = WTF::toASCIILower(c);
183 }
184 buffer[length] = '\0';
185
186 if (buffer[0] == '-') {
187 // If the prefix is -apple- or -khtml-, change it to -webkit-.
188 // This makes the string one character longer.
189 // On iOS we don't want to change values starting with -apple-system to -webkit-system.
190 // FIXME: Remove this mangling without breaking the web.
191 if (isAppleLegacyCssValueKeyword(buffer, length) || hasPrefix(buffer, length, "-khtml-")) {
192 memmove(buffer + 7, buffer + 6, length + 1 - 6);
193 memcpy(buffer, "-webkit", 7);
194 ++length;
195 }
196 }
197
198 const Value* hashTableEntry = findValue(buffer, length);
199 return hashTableEntry ? static_cast<CSSValueID>(hashTableEntry->id) : CSSValueInvalid;
200}
201
202CSSValueID cssValueKeywordID(StringView string)
203{
204 unsigned length = string.length();
205 if (!length)
206 return CSSValueInvalid;
207 if (length > maxCSSValueKeywordLength)
208 return CSSValueInvalid;
209
210 return string.is8Bit() ? cssValueKeywordID(string.characters8(), length) : cssValueKeywordID(string.characters16(), length);
211}
212
213CSSPropertyID cssPropertyID(StringView string)
214{
215 unsigned length = string.length();
216
217 if (!length)
218 return CSSPropertyInvalid;
219 if (length > maxCSSPropertyNameLength)
220 return CSSPropertyInvalid;
221
222 return string.is8Bit() ? cssPropertyID(string.characters8(), length) : cssPropertyID(string.characters16(), length);
223}
224
225using namespace CSSPropertyParserHelpers;
226
227CSSPropertyParser::CSSPropertyParser(const CSSParserTokenRange& range, const CSSParserContext& context, Vector<CSSProperty, 256>* parsedProperties, bool consumeWhitespace)
228 : m_range(range)
229 , m_context(context)
230 , m_parsedProperties(parsedProperties)
231{
232 if (consumeWhitespace)
233 m_range.consumeWhitespace();
234}
235
236void CSSPropertyParser::addProperty(CSSPropertyID property, CSSPropertyID currentShorthand, Ref<CSSValue>&& value, bool important, bool implicit)
237{
238 if (!isEnabledCSSProperty(property))
239 return;
240
241 int shorthandIndex = 0;
242 bool setFromShorthand = false;
243
244 if (currentShorthand) {
245 auto shorthands = matchingShorthandsForLonghand(property);
246 setFromShorthand = true;
247 if (shorthands.size() > 1)
248 shorthandIndex = indexOfShorthandForLonghand(currentShorthand, shorthands);
249 }
250
251 m_parsedProperties->append(CSSProperty(property, WTFMove(value), important, setFromShorthand, shorthandIndex, implicit));
252}
253
254void CSSPropertyParser::addExpandedPropertyForValue(CSSPropertyID property, Ref<CSSValue>&& value, bool important)
255{
256 const StylePropertyShorthand& shorthand = shorthandForProperty(property);
257 unsigned shorthandLength = shorthand.length();
258 ASSERT(shorthandLength);
259 const CSSPropertyID* longhands = shorthand.properties();
260 for (unsigned i = 0; i < shorthandLength; ++i)
261 addProperty(longhands[i], property, value.copyRef(), important);
262}
263
264bool CSSPropertyParser::parseValue(CSSPropertyID propertyID, bool important, const CSSParserTokenRange& range, const CSSParserContext& context, ParsedPropertyVector& parsedProperties, StyleRule::Type ruleType)
265{
266 int parsedPropertiesSize = parsedProperties.size();
267
268 CSSPropertyParser parser(range, context, &parsedProperties);
269 bool parseSuccess;
270
271#if ENABLE(CSS_DEVICE_ADAPTATION)
272 if (ruleType == StyleRule::Viewport)
273 parseSuccess = parser.parseViewportDescriptor(propertyID, important);
274 else
275#endif
276 if (ruleType == StyleRule::FontFace)
277 parseSuccess = parser.parseFontFaceDescriptor(propertyID);
278 else
279 parseSuccess = parser.parseValueStart(propertyID, important);
280
281 if (!parseSuccess)
282 parsedProperties.shrink(parsedPropertiesSize);
283
284 return parseSuccess;
285}
286
287RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, const CSSParserTokenRange& range, const CSSParserContext& context)
288{
289 CSSPropertyParser parser(range, context, nullptr);
290 RefPtr<CSSValue> value = parser.parseSingleValue(property);
291 if (!value || !parser.m_range.atEnd())
292 return nullptr;
293 return value;
294}
295
296bool CSSPropertyParser::canParseTypedCustomPropertyValue(const String& syntax, const CSSParserTokenRange& tokens, const CSSParserContext& context)
297{
298 CSSPropertyParser parser(tokens, context, nullptr);
299 return parser.canParseTypedCustomPropertyValue(syntax);
300}
301
302RefPtr<CSSCustomPropertyValue> CSSPropertyParser::parseTypedCustomPropertyValue(const String& name, const String& syntax, const CSSParserTokenRange& tokens, const StyleResolver& styleResolver, const CSSParserContext& context)
303{
304 CSSPropertyParser parser(tokens, context, nullptr, false);
305 RefPtr<CSSCustomPropertyValue> value = parser.parseTypedCustomPropertyValue(name, syntax, styleResolver);
306 if (!value || !parser.m_range.atEnd())
307 return nullptr;
308 return value;
309}
310
311void CSSPropertyParser::collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies, const CSSParserTokenRange& tokens, const CSSParserContext& context)
312{
313 CSSPropertyParser parser(tokens, context, nullptr);
314 parser.collectParsedCustomPropertyValueDependencies(syntax, isRoot, dependencies);
315}
316
317bool CSSPropertyParser::parseValueStart(CSSPropertyID propertyID, bool important)
318{
319 if (consumeCSSWideKeyword(propertyID, important))
320 return true;
321
322 CSSParserTokenRange originalRange = m_range;
323 bool isShorthand = isShorthandCSSProperty(propertyID);
324
325 if (isShorthand) {
326 // Variable references will fail to parse here and will fall out to the variable ref parser below.
327 if (parseShorthand(propertyID, important))
328 return true;
329 } else {
330 RefPtr<CSSValue> parsedValue = parseSingleValue(propertyID);
331 if (parsedValue && m_range.atEnd()) {
332 addProperty(propertyID, CSSPropertyInvalid, *parsedValue, important);
333 return true;
334 }
335 }
336
337 if (CSSVariableParser::containsValidVariableReferences(originalRange, m_context)) {
338 RefPtr<CSSVariableReferenceValue> variable = CSSVariableReferenceValue::create(originalRange);
339
340 if (isShorthand) {
341 RefPtr<CSSPendingSubstitutionValue> pendingValue = CSSPendingSubstitutionValue::create(propertyID, variable.releaseNonNull());
342 addExpandedPropertyForValue(propertyID, pendingValue.releaseNonNull(), important);
343 } else
344 addProperty(propertyID, CSSPropertyInvalid, variable.releaseNonNull(), important);
345 return true;
346 }
347
348 return false;
349}
350
351bool CSSPropertyParser::consumeCSSWideKeyword(CSSPropertyID propertyID, bool important)
352{
353 CSSParserTokenRange rangeCopy = m_range;
354 CSSValueID valueID = rangeCopy.consumeIncludingWhitespace().id();
355 if (!rangeCopy.atEnd())
356 return false;
357
358 RefPtr<CSSValue> value;
359 if (valueID == CSSValueInherit)
360 value = CSSValuePool::singleton().createInheritedValue();
361 else if (valueID == CSSValueInitial)
362 value = CSSValuePool::singleton().createExplicitInitialValue();
363 else if (valueID == CSSValueUnset)
364 value = CSSValuePool::singleton().createUnsetValue();
365 else if (valueID == CSSValueRevert)
366 value = CSSValuePool::singleton().createRevertValue();
367 else
368 return false;
369
370 const StylePropertyShorthand& shorthand = shorthandForProperty(propertyID);
371 if (!shorthand.length()) {
372 if (CSSProperty::isDescriptorOnly(propertyID))
373 return false;
374 addProperty(propertyID, CSSPropertyInvalid, value.releaseNonNull(), important);
375 } else
376 addExpandedPropertyForValue(propertyID, value.releaseNonNull(), important);
377 m_range = rangeCopy;
378 return true;
379}
380
381bool CSSPropertyParser::consumeTransformOrigin(bool important)
382{
383 RefPtr<CSSPrimitiveValue> resultX;
384 RefPtr<CSSPrimitiveValue> resultY;
385 if (consumeOneOrTwoValuedPosition(m_range, m_context.mode, UnitlessQuirk::Forbid, resultX, resultY)) {
386 m_range.consumeWhitespace();
387 bool atEnd = m_range.atEnd();
388 RefPtr<CSSPrimitiveValue> resultZ = consumeLength(m_range, m_context.mode, ValueRangeAll);
389 bool hasZ = resultZ;
390 if (!hasZ && !atEnd)
391 return false;
392 addProperty(CSSPropertyTransformOriginX, CSSPropertyTransformOrigin, resultX.releaseNonNull(), important);
393 addProperty(CSSPropertyTransformOriginY, CSSPropertyTransformOrigin, resultY.releaseNonNull(), important);
394 addProperty(CSSPropertyTransformOriginZ, CSSPropertyTransformOrigin, resultZ ? resultZ.releaseNonNull() : CSSValuePool::singleton().createValue(0, CSSPrimitiveValue::UnitType::CSS_PX), important, !hasZ);
395
396 return true;
397 }
398 return false;
399}
400
401bool CSSPropertyParser::consumePerspectiveOrigin(bool important)
402{
403 RefPtr<CSSPrimitiveValue> resultX;
404 RefPtr<CSSPrimitiveValue> resultY;
405 if (consumePosition(m_range, m_context.mode, UnitlessQuirk::Forbid, resultX, resultY)) {
406 addProperty(CSSPropertyPerspectiveOriginX, CSSPropertyPerspectiveOrigin, resultX.releaseNonNull(), important);
407 addProperty(CSSPropertyPerspectiveOriginY, CSSPropertyPerspectiveOrigin, resultY.releaseNonNull(), important);
408 return true;
409 }
410 return false;
411}
412
413// Methods for consuming non-shorthand properties starts here.
414static RefPtr<CSSValue> consumeWillChange(CSSParserTokenRange& range)
415{
416 if (range.peek().id() == CSSValueAuto)
417 return consumeIdent(range);
418
419 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
420 // Every comma-separated list of identifiers is a valid will-change value,
421 // unless the list includes an explicitly disallowed identifier.
422 while (true) {
423 if (range.peek().type() != IdentToken)
424 return nullptr;
425 CSSPropertyID propertyID = cssPropertyID(range.peek().value());
426 if (propertyID != CSSPropertyInvalid) {
427 // Now "all" is used by both CSSValue and CSSPropertyValue.
428 // Need to return nullptr when currentValue is CSSPropertyAll.
429 if (propertyID == CSSPropertyWillChange || propertyID == CSSPropertyAll)
430 return nullptr;
431 // FIXME-NEWPARSER: Use CSSCustomIdentValue someday.
432 values->append(CSSValuePool::singleton().createIdentifierValue(propertyID));
433 range.consumeIncludingWhitespace();
434 } else {
435 switch (range.peek().id()) {
436 case CSSValueNone:
437 case CSSValueAll:
438 case CSSValueAuto:
439 case CSSValueDefault:
440 case CSSValueInitial:
441 case CSSValueInherit:
442 return nullptr;
443 case CSSValueContents:
444 case CSSValueScrollPosition:
445 values->append(consumeIdent(range).releaseNonNull());
446 break;
447 default:
448 // Append properties we don't recognize, but that are legal, as strings.
449 values->append(consumeCustomIdent(range).releaseNonNull());
450 break;
451 }
452 }
453
454 if (range.atEnd())
455 break;
456 if (!consumeCommaIncludingWhitespace(range))
457 return nullptr;
458 }
459
460 return values;
461}
462
463static RefPtr<CSSFontFeatureValue> consumeFontFeatureTag(CSSParserTokenRange& range)
464{
465 // Feature tag name consists of 4-letter characters.
466 static const unsigned tagNameLength = 4;
467
468 const CSSParserToken& token = range.consumeIncludingWhitespace();
469 // Feature tag name comes first
470 if (token.type() != StringToken)
471 return nullptr;
472 if (token.value().length() != tagNameLength)
473 return nullptr;
474
475 FontTag tag;
476 for (unsigned i = 0; i < tag.size(); ++i) {
477 // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
478 UChar character = token.value()[i];
479 if (character < 0x20 || character > 0x7E)
480 return nullptr;
481 tag[i] = toASCIILower(character);
482 }
483
484 int tagValue = 1;
485 if (!range.atEnd() && range.peek().type() != CommaToken) {
486 // Feature tag values could follow: <integer> | on | off
487 if (auto primitiveValue = consumeInteger(range, 0))
488 tagValue = primitiveValue->intValue();
489 else if (range.peek().id() == CSSValueOn || range.peek().id() == CSSValueOff)
490 tagValue = range.consumeIncludingWhitespace().id() == CSSValueOn;
491 else
492 return nullptr;
493 }
494 return CSSFontFeatureValue::create(WTFMove(tag), tagValue);
495}
496
497static RefPtr<CSSValue> consumeFontFeatureSettings(CSSParserTokenRange& range)
498{
499 if (range.peek().id() == CSSValueNormal)
500 return consumeIdent(range);
501 RefPtr<CSSValueList> settings = CSSValueList::createCommaSeparated();
502 do {
503 RefPtr<CSSFontFeatureValue> fontFeatureValue = consumeFontFeatureTag(range);
504 if (!fontFeatureValue)
505 return nullptr;
506 settings->append(fontFeatureValue.releaseNonNull());
507 } while (consumeCommaIncludingWhitespace(range));
508 return settings;
509}
510
511#if ENABLE(VARIATION_FONTS)
512static RefPtr<CSSValue> consumeFontVariationTag(CSSParserTokenRange& range)
513{
514 if (range.peek().type() != StringToken)
515 return nullptr;
516
517 auto string = range.consumeIncludingWhitespace().value().toString();
518
519 FontTag tag;
520 if (string.length() != tag.size())
521 return nullptr;
522 for (unsigned i = 0; i < tag.size(); ++i) {
523 // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
524 UChar character = string[i];
525 if (character < 0x20 || character > 0x7E)
526 return nullptr;
527 tag[i] = character;
528 }
529
530 if (range.atEnd())
531 return nullptr;
532
533 double tagValue = 0;
534 auto success = consumeNumberRaw(range, tagValue);
535 if (!success)
536 return nullptr;
537
538 return CSSFontVariationValue::create(tag, tagValue);
539}
540
541static RefPtr<CSSValue> consumeFontVariationSettings(CSSParserTokenRange& range)
542{
543 if (range.peek().id() == CSSValueNormal)
544 return consumeIdent(range);
545
546 auto settings = CSSValueList::createCommaSeparated();
547 do {
548 RefPtr<CSSValue> variationValue = consumeFontVariationTag(range);
549 if (!variationValue)
550 return nullptr;
551 settings->append(variationValue.releaseNonNull());
552 } while (consumeCommaIncludingWhitespace(range));
553
554 if (!settings->length())
555 return nullptr;
556
557 return settings;
558}
559#endif // ENABLE(VARIATION_FONTS)
560
561static RefPtr<CSSValue> consumePage(CSSParserTokenRange& range)
562{
563 if (range.peek().id() == CSSValueAuto)
564 return consumeIdent(range);
565 return consumeCustomIdent(range);
566}
567
568static RefPtr<CSSValue> consumeQuotes(CSSParserTokenRange& range)
569{
570 if (range.peek().id() == CSSValueNone)
571 return consumeIdent(range);
572 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
573 while (!range.atEnd()) {
574 RefPtr<CSSPrimitiveValue> parsedValue = consumeString(range);
575 if (!parsedValue)
576 return nullptr;
577 values->append(parsedValue.releaseNonNull());
578 }
579 if (values->length() && values->length() % 2 == 0)
580 return values;
581 return nullptr;
582}
583
584class FontVariantLigaturesParser {
585public:
586 FontVariantLigaturesParser()
587 : m_sawCommonLigaturesValue(false)
588 , m_sawDiscretionaryLigaturesValue(false)
589 , m_sawHistoricalLigaturesValue(false)
590 , m_sawContextualLigaturesValue(false)
591 , m_result(CSSValueList::createSpaceSeparated())
592 {
593 }
594
595 enum class ParseResult {
596 ConsumedValue,
597 DisallowedValue,
598 UnknownValue
599 };
600
601 ParseResult consumeLigature(CSSParserTokenRange& range)
602 {
603 CSSValueID valueID = range.peek().id();
604 switch (valueID) {
605 case CSSValueNoCommonLigatures:
606 case CSSValueCommonLigatures:
607 if (m_sawCommonLigaturesValue)
608 return ParseResult::DisallowedValue;
609 m_sawCommonLigaturesValue = true;
610 break;
611 case CSSValueNoDiscretionaryLigatures:
612 case CSSValueDiscretionaryLigatures:
613 if (m_sawDiscretionaryLigaturesValue)
614 return ParseResult::DisallowedValue;
615 m_sawDiscretionaryLigaturesValue = true;
616 break;
617 case CSSValueNoHistoricalLigatures:
618 case CSSValueHistoricalLigatures:
619 if (m_sawHistoricalLigaturesValue)
620 return ParseResult::DisallowedValue;
621 m_sawHistoricalLigaturesValue = true;
622 break;
623 case CSSValueNoContextual:
624 case CSSValueContextual:
625 if (m_sawContextualLigaturesValue)
626 return ParseResult::DisallowedValue;
627 m_sawContextualLigaturesValue = true;
628 break;
629 default:
630 return ParseResult::UnknownValue;
631 }
632 m_result->append(consumeIdent(range).releaseNonNull());
633 return ParseResult::ConsumedValue;
634 }
635
636 RefPtr<CSSValue> finalizeValue()
637 {
638 if (!m_result->length())
639 return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
640 return WTFMove(m_result);
641 }
642
643private:
644 bool m_sawCommonLigaturesValue;
645 bool m_sawDiscretionaryLigaturesValue;
646 bool m_sawHistoricalLigaturesValue;
647 bool m_sawContextualLigaturesValue;
648 RefPtr<CSSValueList> m_result;
649};
650
651static RefPtr<CSSValue> consumeFontVariantLigatures(CSSParserTokenRange& range)
652{
653 if (range.peek().id() == CSSValueNormal || range.peek().id() == CSSValueNone)
654 return consumeIdent(range);
655
656 FontVariantLigaturesParser ligaturesParser;
657 do {
658 if (ligaturesParser.consumeLigature(range) !=
659 FontVariantLigaturesParser::ParseResult::ConsumedValue)
660 return nullptr;
661 } while (!range.atEnd());
662
663 return ligaturesParser.finalizeValue();
664}
665
666static RefPtr<CSSValue> consumeFontVariantEastAsian(CSSParserTokenRange& range)
667{
668 if (range.peek().id() == CSSValueNormal)
669 return consumeIdent(range);
670
671 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
672 FontVariantEastAsianVariant variant = FontVariantEastAsianVariant::Normal;
673 FontVariantEastAsianWidth width = FontVariantEastAsianWidth::Normal;
674 FontVariantEastAsianRuby ruby = FontVariantEastAsianRuby::Normal;
675
676 while (!range.atEnd()) {
677 if (range.peek().type() != IdentToken)
678 return nullptr;
679
680 auto id = range.peek().id();
681
682 switch (id) {
683 case CSSValueJis78:
684 variant = FontVariantEastAsianVariant::Jis78;
685 break;
686 case CSSValueJis83:
687 variant = FontVariantEastAsianVariant::Jis83;
688 break;
689 case CSSValueJis90:
690 variant = FontVariantEastAsianVariant::Jis90;
691 break;
692 case CSSValueJis04:
693 variant = FontVariantEastAsianVariant::Jis04;
694 break;
695 case CSSValueSimplified:
696 variant = FontVariantEastAsianVariant::Simplified;
697 break;
698 case CSSValueTraditional:
699 variant = FontVariantEastAsianVariant::Traditional;
700 break;
701 case CSSValueFullWidth:
702 width = FontVariantEastAsianWidth::Full;
703 break;
704 case CSSValueProportionalWidth:
705 width = FontVariantEastAsianWidth::Proportional;
706 break;
707 case CSSValueRuby:
708 ruby = FontVariantEastAsianRuby::Yes;
709 break;
710 default:
711 return nullptr;
712 }
713
714 range.consumeIncludingWhitespace();
715 }
716
717 switch (variant) {
718 case FontVariantEastAsianVariant::Normal:
719 break;
720 case FontVariantEastAsianVariant::Jis78:
721 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis78));
722 break;
723 case FontVariantEastAsianVariant::Jis83:
724 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis83));
725 break;
726 case FontVariantEastAsianVariant::Jis90:
727 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis90));
728 break;
729 case FontVariantEastAsianVariant::Jis04:
730 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueJis04));
731 break;
732 case FontVariantEastAsianVariant::Simplified:
733 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueSimplified));
734 break;
735 case FontVariantEastAsianVariant::Traditional:
736 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueTraditional));
737 break;
738 }
739
740 switch (width) {
741 case FontVariantEastAsianWidth::Normal:
742 break;
743 case FontVariantEastAsianWidth::Full:
744 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueFullWidth));
745 break;
746 case FontVariantEastAsianWidth::Proportional:
747 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueProportionalWidth));
748 break;
749 }
750
751 switch (ruby) {
752 case FontVariantEastAsianRuby::Normal:
753 break;
754 case FontVariantEastAsianRuby::Yes:
755 values->append(CSSValuePool::singleton().createIdentifierValue(CSSValueRuby));
756 }
757
758 if (!values->length())
759 return nullptr;
760
761 return values;
762}
763
764static RefPtr<CSSPrimitiveValue> consumeFontVariantCaps(CSSParserTokenRange& range)
765{
766 return consumeIdent<CSSValueNormal, CSSValueSmallCaps, CSSValueAllSmallCaps,
767 CSSValuePetiteCaps, CSSValueAllPetiteCaps,
768 CSSValueUnicase, CSSValueTitlingCaps>(range);
769}
770
771static RefPtr<CSSPrimitiveValue> consumeFontVariantAlternates(CSSParserTokenRange& range)
772{
773 return consumeIdent<CSSValueNormal, CSSValueHistoricalForms>(range);
774}
775
776static RefPtr<CSSPrimitiveValue> consumeFontVariantPosition(CSSParserTokenRange& range)
777{
778 return consumeIdent<CSSValueNormal, CSSValueSub, CSSValueSuper>(range);
779}
780
781class FontVariantNumericParser {
782public:
783 FontVariantNumericParser()
784 : m_sawNumericFigureValue(false)
785 , m_sawNumericSpacingValue(false)
786 , m_sawNumericFractionValue(false)
787 , m_sawOrdinalValue(false)
788 , m_sawSlashedZeroValue(false)
789 , m_result(CSSValueList::createSpaceSeparated())
790 {
791 }
792
793 enum class ParseResult {
794 ConsumedValue,
795 DisallowedValue,
796 UnknownValue
797 };
798
799 ParseResult consumeNumeric(CSSParserTokenRange& range)
800 {
801 CSSValueID valueID = range.peek().id();
802 switch (valueID) {
803 case CSSValueLiningNums:
804 case CSSValueOldstyleNums:
805 if (m_sawNumericFigureValue)
806 return ParseResult::DisallowedValue;
807 m_sawNumericFigureValue = true;
808 break;
809 case CSSValueProportionalNums:
810 case CSSValueTabularNums:
811 if (m_sawNumericSpacingValue)
812 return ParseResult::DisallowedValue;
813 m_sawNumericSpacingValue = true;
814 break;
815 case CSSValueDiagonalFractions:
816 case CSSValueStackedFractions:
817 if (m_sawNumericFractionValue)
818 return ParseResult::DisallowedValue;
819 m_sawNumericFractionValue = true;
820 break;
821 case CSSValueOrdinal:
822 if (m_sawOrdinalValue)
823 return ParseResult::DisallowedValue;
824 m_sawOrdinalValue = true;
825 break;
826 case CSSValueSlashedZero:
827 if (m_sawSlashedZeroValue)
828 return ParseResult::DisallowedValue;
829 m_sawSlashedZeroValue = true;
830 break;
831 default:
832 return ParseResult::UnknownValue;
833 }
834 m_result->append(consumeIdent(range).releaseNonNull());
835 return ParseResult::ConsumedValue;
836 }
837
838 RefPtr<CSSValue> finalizeValue()
839 {
840 if (!m_result->length())
841 return CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
842 return WTFMove(m_result);
843 }
844
845
846private:
847 bool m_sawNumericFigureValue;
848 bool m_sawNumericSpacingValue;
849 bool m_sawNumericFractionValue;
850 bool m_sawOrdinalValue;
851 bool m_sawSlashedZeroValue;
852 RefPtr<CSSValueList> m_result;
853};
854
855static RefPtr<CSSValue> consumeFontVariantNumeric(CSSParserTokenRange& range)
856{
857 if (range.peek().id() == CSSValueNormal)
858 return consumeIdent(range);
859
860 FontVariantNumericParser numericParser;
861 do {
862 if (numericParser.consumeNumeric(range) !=
863 FontVariantNumericParser::ParseResult::ConsumedValue)
864 return nullptr;
865 } while (!range.atEnd());
866
867 return numericParser.finalizeValue();
868}
869
870static RefPtr<CSSPrimitiveValue> consumeFontVariantCSS21(CSSParserTokenRange& range)
871{
872 return consumeIdent<CSSValueNormal, CSSValueSmallCaps>(range);
873}
874
875static RefPtr<CSSPrimitiveValue> consumeFontWeightKeywordValue(CSSParserTokenRange& range)
876{
877 return consumeIdent<CSSValueNormal, CSSValueBold, CSSValueBolder, CSSValueLighter>(range);
878}
879
880static RefPtr<CSSPrimitiveValue> consumeFontWeight(CSSParserTokenRange& range)
881{
882 if (auto result = consumeFontWeightKeywordValue(range))
883 return result;
884 return consumeFontWeightNumber(range);
885}
886
887#if ENABLE(VARIATION_FONTS)
888static RefPtr<CSSValue> consumeFontWeightRange(CSSParserTokenRange& range)
889{
890 if (auto result = consumeFontWeightKeywordValue(range))
891 return result;
892 auto firstNumber = consumeFontWeightNumber(range);
893 if (!firstNumber)
894 return nullptr;
895 if (range.atEnd())
896 return firstNumber;
897 auto secondNumber = consumeFontWeightNumber(range);
898 if (!secondNumber || firstNumber->floatValue() > secondNumber->floatValue())
899 return nullptr;
900 auto result = CSSValueList::createSpaceSeparated();
901 result->append(firstNumber.releaseNonNull());
902 result->append(secondNumber.releaseNonNull());
903 return RefPtr<CSSValue>(WTFMove(result));
904}
905#endif
906
907static RefPtr<CSSPrimitiveValue> consumeFontStretchKeywordValue(CSSParserTokenRange& range)
908{
909 return consumeIdent<CSSValueUltraCondensed, CSSValueExtraCondensed, CSSValueCondensed, CSSValueSemiCondensed, CSSValueNormal, CSSValueSemiExpanded, CSSValueExpanded, CSSValueExtraExpanded, CSSValueUltraExpanded>(range);
910}
911
912#if ENABLE(VARIATION_FONTS)
913static bool fontStretchIsWithinRange(float stretch)
914{
915 return stretch > 0;
916}
917#endif
918
919static RefPtr<CSSPrimitiveValue> consumeFontStretch(CSSParserTokenRange& range)
920{
921 if (auto result = consumeFontStretchKeywordValue(range))
922 return result;
923#if ENABLE(VARIATION_FONTS)
924 if (auto percent = consumePercent(range, ValueRangeNonNegative))
925 return fontStretchIsWithinRange(percent->value<float>()) ? percent : nullptr;
926#endif
927 return nullptr;
928}
929
930#if ENABLE(VARIATION_FONTS)
931static RefPtr<CSSValue> consumeFontStretchRange(CSSParserTokenRange& range)
932{
933 if (auto result = consumeFontStretchKeywordValue(range))
934 return result;
935 auto firstPercent = consumePercent(range, ValueRangeNonNegative);
936 if (!firstPercent || !fontStretchIsWithinRange(firstPercent->value<float>()))
937 return nullptr;
938 if (range.atEnd())
939 return firstPercent;
940 auto secondPercent = consumePercent(range, ValueRangeNonNegative);
941 if (!secondPercent || !fontStretchIsWithinRange(secondPercent->value<float>()) || firstPercent->floatValue() > secondPercent->floatValue())
942 return nullptr;
943 auto result = CSSValueList::createSpaceSeparated();
944 result->append(firstPercent.releaseNonNull());
945 result->append(secondPercent.releaseNonNull());
946 return RefPtr<CSSValue>(WTFMove(result));
947}
948#endif
949
950static RefPtr<CSSPrimitiveValue> consumeFontStyleKeywordValue(CSSParserTokenRange& range)
951{
952 return consumeIdent<CSSValueNormal, CSSValueItalic, CSSValueOblique>(range);
953}
954
955#if ENABLE(VARIATION_FONTS)
956static bool fontStyleIsWithinRange(float oblique)
957{
958 return oblique > -90 && oblique < 90;
959}
960#endif
961
962static RefPtr<CSSFontStyleValue> consumeFontStyle(CSSParserTokenRange& range, CSSParserMode cssParserMode)
963{
964 auto result = consumeFontStyleKeywordValue(range);
965 if (!result)
966 return nullptr;
967
968 auto valueID = result->valueID();
969 if (valueID == CSSValueNormal || valueID == CSSValueItalic)
970 return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(valueID));
971 ASSERT(result->valueID() == CSSValueOblique);
972#if ENABLE(VARIATION_FONTS)
973 if (!range.atEnd()) {
974 if (auto angle = consumeAngle(range, cssParserMode)) {
975 if (fontStyleIsWithinRange(angle->value<float>(CSSPrimitiveValue::CSS_DEG)))
976 return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueOblique), WTFMove(angle));
977 return nullptr;
978 }
979 }
980#else
981 UNUSED_PARAM(cssParserMode);
982#endif
983 return CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueOblique));
984}
985
986#if ENABLE(VARIATION_FONTS)
987static RefPtr<CSSFontStyleRangeValue> consumeFontStyleRange(CSSParserTokenRange& range, CSSParserMode cssParserMode)
988{
989 auto keyword = consumeFontStyleKeywordValue(range);
990 if (!keyword)
991 return nullptr;
992
993 if (keyword->valueID() != CSSValueOblique || range.atEnd())
994 return CSSFontStyleRangeValue::create(keyword.releaseNonNull());
995
996 if (auto firstAngle = consumeAngle(range, cssParserMode)) {
997 if (!fontStyleIsWithinRange(firstAngle->value<float>(CSSPrimitiveValue::CSS_DEG)))
998 return nullptr;
999 if (range.atEnd()) {
1000 auto result = CSSValueList::createSpaceSeparated();
1001 result->append(firstAngle.releaseNonNull());
1002 return CSSFontStyleRangeValue::create(keyword.releaseNonNull(), WTFMove(result));
1003 }
1004 auto secondAngle = consumeAngle(range, cssParserMode);
1005 if (!secondAngle || !fontStyleIsWithinRange(secondAngle->value<float>(CSSPrimitiveValue::CSS_DEG)) || firstAngle->floatValue(CSSPrimitiveValue::CSS_DEG) > secondAngle->floatValue(CSSPrimitiveValue::CSS_DEG))
1006 return nullptr;
1007 auto result = CSSValueList::createSpaceSeparated();
1008 result->append(firstAngle.releaseNonNull());
1009 result->append(secondAngle.releaseNonNull());
1010 return CSSFontStyleRangeValue::create(keyword.releaseNonNull(), WTFMove(result));
1011 }
1012
1013 return nullptr;
1014}
1015#endif
1016
1017static String concatenateFamilyName(CSSParserTokenRange& range)
1018{
1019 StringBuilder builder;
1020 bool addedSpace = false;
1021 const CSSParserToken& firstToken = range.peek();
1022 while (range.peek().type() == IdentToken) {
1023 if (!builder.isEmpty()) {
1024 builder.append(' ');
1025 addedSpace = true;
1026 }
1027 builder.append(range.consumeIncludingWhitespace().value());
1028 }
1029 if (!addedSpace && isCSSWideKeyword(firstToken.id()))
1030 return String();
1031 return builder.toString();
1032}
1033
1034static RefPtr<CSSValue> consumeFamilyName(CSSParserTokenRange& range)
1035{
1036 if (range.peek().type() == StringToken)
1037 return CSSValuePool::singleton().createFontFamilyValue(range.consumeIncludingWhitespace().value().toString());
1038 if (range.peek().type() != IdentToken)
1039 return nullptr;
1040 String familyName = concatenateFamilyName(range);
1041 if (familyName.isNull())
1042 return nullptr;
1043 return CSSValuePool::singleton().createFontFamilyValue(familyName);
1044}
1045
1046static RefPtr<CSSValue> consumeGenericFamily(CSSParserTokenRange& range)
1047{
1048 return consumeIdentRange(range, CSSValueSerif, CSSValueWebkitBody);
1049}
1050
1051static RefPtr<CSSValueList> consumeFontFamily(CSSParserTokenRange& range)
1052{
1053 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
1054 do {
1055 if (auto parsedValue = consumeGenericFamily(range))
1056 list->append(parsedValue.releaseNonNull());
1057 else {
1058 if (auto parsedValue = consumeFamilyName(range))
1059 list->append(parsedValue.releaseNonNull());
1060 else
1061 return nullptr;
1062 }
1063 } while (consumeCommaIncludingWhitespace(range));
1064 return list;
1065}
1066
1067static RefPtr<CSSValueList> consumeFontFamilyDescriptor(CSSParserTokenRange& range)
1068{
1069 // FIXME-NEWPARSER: https://bugs.webkit.org/show_bug.cgi?id=196381 For compatibility with the old parser, we have to make
1070 // a list here, even though the list always contains only a single family name.
1071 // Once the old parser is gone, we can delete this function, make the caller
1072 // use consumeFamilyName instead, and then patch the @font-face code to
1073 // not expect a list with a single name in it.
1074 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
1075 RefPtr<CSSValue> parsedValue = consumeFamilyName(range);
1076 if (parsedValue)
1077 list->append(parsedValue.releaseNonNull());
1078
1079 if (!range.atEnd() || !list->length())
1080 return nullptr;
1081
1082 return list;
1083}
1084
1085static RefPtr<CSSValue> consumeFontSynthesis(CSSParserTokenRange& range)
1086{
1087 // none | [ weight || style || small-caps ]
1088 CSSValueID id = range.peek().id();
1089 if (id == CSSValueNone)
1090 return consumeIdent(range);
1091
1092 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1093 while (true) {
1094 auto ident = consumeIdent<CSSValueWeight, CSSValueStyle, CSSValueSmallCaps>(range);
1095 if (!ident)
1096 break;
1097 if (list->hasValue(ident.get()))
1098 return nullptr;
1099 list->append(ident.releaseNonNull());
1100 }
1101
1102 if (!list->length())
1103 return nullptr;
1104 return list;
1105}
1106
1107static RefPtr<CSSValue> consumeLetterSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1108{
1109 if (range.peek().id() == CSSValueNormal)
1110 return consumeIdent(range);
1111
1112 return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1113}
1114
1115static RefPtr<CSSValue> consumeWordSpacing(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1116{
1117 if (range.peek().id() == CSSValueNormal)
1118 return consumeIdent(range);
1119
1120 return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1121}
1122
1123static RefPtr<CSSValue> consumeTabSize(CSSParserTokenRange& range, CSSParserMode)
1124{
1125 return consumeInteger(range, 0);
1126}
1127
1128#if ENABLE(TEXT_AUTOSIZING)
1129static RefPtr<CSSValue> consumeTextSizeAdjust(CSSParserTokenRange& range, CSSParserMode /* cssParserMode */)
1130{
1131 if (range.peek().id() == CSSValueAuto)
1132 return consumeIdent(range);
1133 if (range.peek().id() == CSSValueNone)
1134 return consumeIdent(range);
1135 return consumePercent(range, ValueRangeNonNegative);
1136}
1137#endif
1138
1139static RefPtr<CSSValue> consumeFontSize(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1140{
1141 if (range.peek().id() >= CSSValueXxSmall && range.peek().id() <= CSSValueLarger)
1142 return consumeIdent(range);
1143 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, unitless);
1144}
1145
1146static RefPtr<CSSPrimitiveValue> consumeLineHeight(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1147{
1148 if (range.peek().id() == CSSValueNormal)
1149 return consumeIdent(range);
1150
1151 RefPtr<CSSPrimitiveValue> lineHeight = consumeNumber(range, ValueRangeNonNegative);
1152 if (lineHeight)
1153 return lineHeight;
1154 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
1155}
1156
1157template<typename... Args>
1158static Ref<CSSPrimitiveValue> createPrimitiveValuePair(Args&&... args)
1159{
1160 return CSSValuePool::singleton().createValue(Pair::create(std::forward<Args>(args)...));
1161}
1162
1163
1164static RefPtr<CSSValue> consumeCounter(CSSParserTokenRange& range, int defaultValue)
1165{
1166 if (range.peek().id() == CSSValueNone)
1167 return consumeIdent(range);
1168
1169 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1170 do {
1171 RefPtr<CSSPrimitiveValue> counterName = consumeCustomIdent(range);
1172 if (!counterName)
1173 return nullptr;
1174 int i = defaultValue;
1175 if (RefPtr<CSSPrimitiveValue> counterValue = consumeInteger(range))
1176 i = counterValue->intValue();
1177 list->append(createPrimitiveValuePair(counterName.releaseNonNull(), CSSPrimitiveValue::create(i, CSSPrimitiveValue::UnitType::CSS_NUMBER), Pair::IdenticalValueEncoding::Coalesce));
1178 } while (!range.atEnd());
1179 return list;
1180}
1181
1182static RefPtr<CSSValue> consumePageSize(CSSParserTokenRange& range)
1183{
1184 return consumeIdent<CSSValueA3, CSSValueA4, CSSValueA5, CSSValueB4, CSSValueB5, CSSValueLedger, CSSValueLegal, CSSValueLetter>(range);
1185}
1186
1187static RefPtr<CSSValueList> consumeSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1188{
1189 RefPtr<CSSValueList> result = CSSValueList::createSpaceSeparated();
1190
1191 if (range.peek().id() == CSSValueAuto) {
1192 result->append(consumeIdent(range).releaseNonNull());
1193 return result;
1194 }
1195
1196 if (RefPtr<CSSValue> width = consumeLength(range, cssParserMode, ValueRangeNonNegative)) {
1197 RefPtr<CSSValue> height = consumeLength(range, cssParserMode, ValueRangeNonNegative);
1198 result->append(width.releaseNonNull());
1199 if (height)
1200 result->append(height.releaseNonNull());
1201 return result;
1202 }
1203
1204 RefPtr<CSSValue> pageSize = consumePageSize(range);
1205 RefPtr<CSSValue> orientation = consumeIdent<CSSValuePortrait, CSSValueLandscape>(range);
1206 if (!pageSize)
1207 pageSize = consumePageSize(range);
1208
1209 if (!orientation && !pageSize)
1210 return nullptr;
1211 if (pageSize)
1212 result->append(pageSize.releaseNonNull());
1213 if (orientation)
1214 result->append(orientation.releaseNonNull());
1215 return result;
1216}
1217
1218static RefPtr<CSSValue> consumeTextIndent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1219{
1220 // [ <length> | <percentage> ] && hanging? && each-line?
1221 // Keywords only allowed when css3Text is enabled.
1222 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1223
1224 bool hasLengthOrPercentage = false;
1225// bool hasEachLine = false;
1226 bool hasHanging = false;
1227
1228 do {
1229 if (!hasLengthOrPercentage) {
1230 if (RefPtr<CSSValue> textIndent = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow)) {
1231 list->append(*textIndent);
1232 hasLengthOrPercentage = true;
1233 continue;
1234 }
1235 }
1236
1237 CSSValueID id = range.peek().id();
1238 /* FIXME-NEWPARSER: We don't support this yet.
1239 if (!hasEachLine && id == CSSValueEachLine) {
1240 list->append(*consumeIdent(range));
1241 hasEachLine = true;
1242 continue;
1243 }
1244*/
1245
1246 if (!hasHanging && id == CSSValueHanging) {
1247 list->append(consumeIdent(range).releaseNonNull());
1248 hasHanging = true;
1249 continue;
1250 }
1251
1252 return nullptr;
1253 } while (!range.atEnd());
1254
1255 if (!hasLengthOrPercentage)
1256 return nullptr;
1257
1258 return list;
1259}
1260
1261static bool validWidthOrHeightKeyword(CSSValueID id, const CSSParserContext& /*context*/)
1262{
1263 if (id == CSSValueIntrinsic || id == CSSValueMinIntrinsic || id == CSSValueMinContent || id == CSSValueWebkitMinContent || id == CSSValueMaxContent || id == CSSValueWebkitMaxContent || id == CSSValueWebkitFillAvailable || id == CSSValueFitContent || id == CSSValueWebkitFitContent) {
1264 return true;
1265 }
1266 return false;
1267}
1268
1269static RefPtr<CSSValue> consumeMaxWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1270{
1271 if (range.peek().id() == CSSValueNone || validWidthOrHeightKeyword(range.peek().id(), context))
1272 return consumeIdent(range);
1273 return consumeLengthOrPercent(range, context.mode, ValueRangeNonNegative, unitless);
1274}
1275
1276static RefPtr<CSSValue> consumeWidthOrHeight(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
1277{
1278 if (range.peek().id() == CSSValueAuto || validWidthOrHeightKeyword(range.peek().id(), context))
1279 return consumeIdent(range);
1280 return consumeLengthOrPercent(range, context.mode, ValueRangeNonNegative, unitless);
1281}
1282
1283static RefPtr<CSSValue> consumeMarginOrOffset(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1284{
1285 if (range.peek().id() == CSSValueAuto)
1286 return consumeIdent(range);
1287 return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, unitless);
1288}
1289
1290static RefPtr<CSSPrimitiveValue> consumeClipComponent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1291{
1292 if (range.peek().id() == CSSValueAuto)
1293 return consumeIdent(range);
1294 return consumeLength(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
1295}
1296
1297static RefPtr<CSSValue> consumeClip(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1298{
1299 if (range.peek().id() == CSSValueAuto)
1300 return consumeIdent(range);
1301
1302 if (range.peek().functionId() != CSSValueRect)
1303 return nullptr;
1304
1305 CSSParserTokenRange args = consumeFunction(range);
1306 // rect(t, r, b, l) || rect(t r b l)
1307 RefPtr<CSSPrimitiveValue> top = consumeClipComponent(args, cssParserMode);
1308 if (!top)
1309 return nullptr;
1310 bool needsComma = consumeCommaIncludingWhitespace(args);
1311 RefPtr<CSSPrimitiveValue> right = consumeClipComponent(args, cssParserMode);
1312 if (!right || (needsComma && !consumeCommaIncludingWhitespace(args)))
1313 return nullptr;
1314 RefPtr<CSSPrimitiveValue> bottom = consumeClipComponent(args, cssParserMode);
1315 if (!bottom || (needsComma && !consumeCommaIncludingWhitespace(args)))
1316 return nullptr;
1317 RefPtr<CSSPrimitiveValue> left = consumeClipComponent(args, cssParserMode);
1318 if (!left || !args.atEnd())
1319 return nullptr;
1320
1321 auto rect = Rect::create();
1322 rect->setLeft(left.releaseNonNull());
1323 rect->setTop(top.releaseNonNull());
1324 rect->setRight(right.releaseNonNull());
1325 rect->setBottom(bottom.releaseNonNull());
1326 return CSSValuePool::singleton().createValue(WTFMove(rect));
1327}
1328
1329#if ENABLE(POINTER_EVENTS)
1330static RefPtr<CSSValue> consumeTouchAction(CSSParserTokenRange& range)
1331{
1332 CSSValueID id = range.peek().id();
1333 if (id == CSSValueNone || id == CSSValueAuto || id == CSSValueManipulation)
1334 return consumeIdent(range);
1335
1336 auto list = CSSValueList::createSpaceSeparated();
1337 while (true) {
1338 auto ident = consumeIdent<CSSValuePanX, CSSValuePanY, CSSValuePinchZoom>(range);
1339 if (!ident)
1340 break;
1341 if (list->hasValue(ident.get()))
1342 return nullptr;
1343 list->append(ident.releaseNonNull());
1344 }
1345
1346 if (!list->length())
1347 return nullptr;
1348 return list;
1349}
1350#endif
1351
1352static RefPtr<CSSPrimitiveValue> consumeLineClamp(CSSParserTokenRange& range)
1353{
1354 RefPtr<CSSPrimitiveValue> clampValue = consumePercent(range, ValueRangeNonNegative);
1355 if (clampValue)
1356 return clampValue;
1357 // When specifying number of lines, don't allow 0 as a valid value.
1358 return consumePositiveInteger(range);
1359}
1360
1361static RefPtr<CSSValue> consumeAutoOrString(CSSParserTokenRange& range)
1362{
1363 if (range.peek().id() == CSSValueAuto)
1364 return consumeIdent(range);
1365 return consumeString(range);
1366}
1367
1368static RefPtr<CSSValue> consumeHyphenateLimit(CSSParserTokenRange& range, CSSValueID valueID)
1369{
1370 if (range.peek().id() == valueID)
1371 return consumeIdent(range);
1372 return consumeNumber(range, ValueRangeNonNegative);
1373}
1374
1375static RefPtr<CSSValue> consumeColumnWidth(CSSParserTokenRange& range)
1376{
1377 if (range.peek().id() == CSSValueAuto)
1378 return consumeIdent(range);
1379 // Always parse lengths in strict mode here, since it would be ambiguous otherwise when used in
1380 // the 'columns' shorthand property.
1381 RefPtr<CSSPrimitiveValue> columnWidth = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
1382 if (!columnWidth || (!columnWidth->isCalculated() && !columnWidth->doubleValue()) || (columnWidth->cssCalcValue() && !columnWidth->cssCalcValue()->doubleValue()))
1383 return nullptr;
1384 return columnWidth;
1385}
1386
1387static RefPtr<CSSValue> consumeColumnCount(CSSParserTokenRange& range)
1388{
1389 if (range.peek().id() == CSSValueAuto)
1390 return consumeIdent(range);
1391 return consumePositiveInteger(range);
1392}
1393
1394static RefPtr<CSSValue> consumeGapLength(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1395{
1396 if (range.peek().id() == CSSValueNormal)
1397 return consumeIdent(range);
1398 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
1399}
1400
1401static RefPtr<CSSValue> consumeColumnSpan(CSSParserTokenRange& range)
1402{
1403 return consumeIdent<CSSValueAll, CSSValueNone>(range);
1404}
1405
1406static RefPtr<CSSValue> consumeZoom(CSSParserTokenRange& range, const CSSParserContext& /*context*/)
1407{
1408 const CSSParserToken& token = range.peek();
1409 RefPtr<CSSPrimitiveValue> zoom;
1410 if (token.type() == IdentToken)
1411 zoom = consumeIdent<CSSValueNormal, CSSValueReset, CSSValueDocument>(range);
1412 else {
1413 zoom = consumePercent(range, ValueRangeNonNegative);
1414 if (!zoom)
1415 zoom = consumeNumber(range, ValueRangeNonNegative);
1416 }
1417 return zoom;
1418}
1419
1420static RefPtr<CSSValue> consumeAnimationIterationCount(CSSParserTokenRange& range)
1421{
1422 if (range.peek().id() == CSSValueInfinite)
1423 return consumeIdent(range);
1424 return consumeNumber(range, ValueRangeNonNegative);
1425}
1426
1427static RefPtr<CSSValue> consumeAnimationName(CSSParserTokenRange& range)
1428{
1429 if (range.peek().id() == CSSValueNone)
1430 return consumeIdent(range);
1431
1432 if (range.peek().type() == StringToken) {
1433 const CSSParserToken& token = range.consumeIncludingWhitespace();
1434 if (equalIgnoringASCIICase(token.value(), "none"))
1435 return CSSValuePool::singleton().createIdentifierValue(CSSValueNone);
1436 // FIXME-NEWPARSER: Want to use a CSSCustomIdentValue here eventually.
1437 return CSSValuePool::singleton().createValue(token.value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
1438 }
1439
1440 return consumeCustomIdent(range);
1441}
1442
1443static RefPtr<CSSValue> consumeTransitionProperty(CSSParserTokenRange& range)
1444{
1445 const CSSParserToken& token = range.peek();
1446 if (token.type() != IdentToken)
1447 return nullptr;
1448 if (token.id() == CSSValueNone)
1449 return consumeIdent(range);
1450
1451 if (CSSPropertyID property = token.parseAsCSSPropertyID()) {
1452 range.consumeIncludingWhitespace();
1453
1454 // FIXME-NEWPARSER: No reason why we can't use the "all" property now that it exists.
1455 // The old parser used a value keyword for "all", though, since it predated support for
1456 // the property.
1457 if (property == CSSPropertyAll)
1458 return CSSValuePool::singleton().createIdentifierValue(CSSValueAll);
1459
1460 // FIXME-NEWPARSER: Want to use a CSSCustomIdentValue here eventually.
1461 return CSSValuePool::singleton().createIdentifierValue(property);
1462 }
1463 return consumeCustomIdent(range);
1464}
1465
1466
1467static RefPtr<CSSValue> consumeSteps(CSSParserTokenRange& range)
1468{
1469 ASSERT(range.peek().functionId() == CSSValueSteps);
1470 CSSParserTokenRange rangeCopy = range;
1471 CSSParserTokenRange args = consumeFunction(rangeCopy);
1472
1473 RefPtr<CSSPrimitiveValue> steps = consumePositiveInteger(args);
1474 if (!steps)
1475 return nullptr;
1476
1477 // FIXME-NEWPARSER: Support the middle value and change from a boolean to an enum.
1478 bool stepAtStart = false;
1479 if (consumeCommaIncludingWhitespace(args)) {
1480 switch (args.consumeIncludingWhitespace().id()) {
1481 case CSSValueStart:
1482 stepAtStart = true;
1483 break;
1484 case CSSValueEnd:
1485 stepAtStart = false;
1486 break;
1487 default:
1488 return nullptr;
1489 }
1490 }
1491
1492 if (!args.atEnd())
1493 return nullptr;
1494
1495 range = rangeCopy;
1496 return CSSStepsTimingFunctionValue::create(steps->intValue(), stepAtStart);
1497}
1498
1499static RefPtr<CSSValue> consumeCubicBezier(CSSParserTokenRange& range)
1500{
1501 ASSERT(range.peek().functionId() == CSSValueCubicBezier);
1502 CSSParserTokenRange rangeCopy = range;
1503 CSSParserTokenRange args = consumeFunction(rangeCopy);
1504
1505 double x1, y1, x2, y2;
1506 if (consumeNumberRaw(args, x1)
1507 && x1 >= 0 && x1 <= 1
1508 && consumeCommaIncludingWhitespace(args)
1509 && consumeNumberRaw(args, y1)
1510 && consumeCommaIncludingWhitespace(args)
1511 && consumeNumberRaw(args, x2)
1512 && x2 >= 0 && x2 <= 1
1513 && consumeCommaIncludingWhitespace(args)
1514 && consumeNumberRaw(args, y2)
1515 && args.atEnd()) {
1516 range = rangeCopy;
1517 return CSSCubicBezierTimingFunctionValue::create(x1, y1, x2, y2);
1518 }
1519
1520 return nullptr;
1521}
1522
1523static RefPtr<CSSValue> consumeSpringFunction(CSSParserTokenRange& range)
1524{
1525 ASSERT(range.peek().functionId() == CSSValueSpring);
1526 CSSParserTokenRange rangeCopy = range;
1527 CSSParserTokenRange args = consumeFunction(rangeCopy);
1528
1529 // Mass must be greater than 0.
1530 double mass;
1531 if (!consumeNumberRaw(args, mass) || mass <= 0)
1532 return nullptr;
1533
1534 // Stiffness must be greater than 0.
1535 double stiffness;
1536 if (!consumeNumberRaw(args, stiffness) || stiffness <= 0)
1537 return nullptr;
1538
1539 // Damping coefficient must be greater than or equal to 0.
1540 double damping;
1541 if (!consumeNumberRaw(args, damping) || damping < 0)
1542 return nullptr;
1543
1544 // Initial velocity may have any value.
1545 double initialVelocity;
1546 if (!consumeNumberRaw(args, initialVelocity))
1547 return nullptr;
1548
1549 if (!args.atEnd())
1550 return nullptr;
1551
1552 range = rangeCopy;
1553
1554 return CSSSpringTimingFunctionValue::create(mass, stiffness, damping, initialVelocity);
1555}
1556
1557static RefPtr<CSSValue> consumeAnimationTimingFunction(CSSParserTokenRange& range, const CSSParserContext& context)
1558{
1559 CSSValueID id = range.peek().id();
1560 if (id == CSSValueEase || id == CSSValueLinear || id == CSSValueEaseIn
1561 || id == CSSValueEaseOut || id == CSSValueEaseInOut || id == CSSValueStepStart || id == CSSValueStepEnd)
1562 return consumeIdent(range);
1563
1564 CSSValueID function = range.peek().functionId();
1565 if (function == CSSValueCubicBezier)
1566 return consumeCubicBezier(range);
1567 if (function == CSSValueSteps)
1568 return consumeSteps(range);
1569 if (context.springTimingFunctionEnabled && function == CSSValueSpring)
1570 return consumeSpringFunction(range);
1571 return nullptr;
1572}
1573
1574static RefPtr<CSSValue> consumeAnimationValue(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
1575{
1576 switch (property) {
1577 case CSSPropertyAnimationDelay:
1578 case CSSPropertyTransitionDelay:
1579 return consumeTime(range, context.mode, ValueRangeAll, UnitlessQuirk::Forbid);
1580 case CSSPropertyAnimationDirection:
1581 return consumeIdent<CSSValueNormal, CSSValueAlternate, CSSValueReverse, CSSValueAlternateReverse>(range);
1582 case CSSPropertyAnimationDuration:
1583 case CSSPropertyTransitionDuration:
1584 return consumeTime(range, context.mode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
1585 case CSSPropertyAnimationFillMode:
1586 return consumeIdent<CSSValueNone, CSSValueForwards, CSSValueBackwards, CSSValueBoth>(range);
1587 case CSSPropertyAnimationIterationCount:
1588 return consumeAnimationIterationCount(range);
1589 case CSSPropertyAnimationName:
1590 return consumeAnimationName(range);
1591 case CSSPropertyAnimationPlayState:
1592 return consumeIdent<CSSValueRunning, CSSValuePaused>(range);
1593 case CSSPropertyTransitionProperty:
1594 return consumeTransitionProperty(range);
1595 case CSSPropertyAnimationTimingFunction:
1596 case CSSPropertyTransitionTimingFunction:
1597 return consumeAnimationTimingFunction(range, context);
1598 default:
1599 ASSERT_NOT_REACHED();
1600 return nullptr;
1601 }
1602}
1603
1604static bool isValidAnimationPropertyList(CSSPropertyID property, const CSSValueList& valueList)
1605{
1606 if (property != CSSPropertyTransitionProperty || valueList.length() < 2)
1607 return true;
1608 for (auto& value : valueList) {
1609 if (value->isPrimitiveValue() && downcast<CSSPrimitiveValue>(value.get()).isValueID()
1610 && downcast<CSSPrimitiveValue>(value.get()).valueID() == CSSValueNone)
1611 return false;
1612 }
1613 return true;
1614}
1615
1616static RefPtr<CSSValue> consumeAnimationPropertyList(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
1617{
1618 RefPtr<CSSValueList> list;
1619 RefPtr<CSSValue> singleton;
1620 do {
1621 RefPtr<CSSValue> currentValue = consumeAnimationValue(property, range, context);
1622 if (!currentValue)
1623 return nullptr;
1624
1625 if (singleton && !list) {
1626 list = CSSValueList::createCommaSeparated();
1627 list->append(singleton.releaseNonNull());
1628 }
1629
1630 if (list)
1631 list->append(currentValue.releaseNonNull());
1632 else
1633 singleton = WTFMove(currentValue);
1634
1635 } while (consumeCommaIncludingWhitespace(range));
1636
1637 if (list) {
1638 if (!isValidAnimationPropertyList(property, *list))
1639 return nullptr;
1640
1641 ASSERT(list->length());
1642 return list;
1643 }
1644
1645 return singleton;
1646}
1647
1648bool CSSPropertyParser::consumeAnimationShorthand(const StylePropertyShorthand& shorthand, bool important)
1649{
1650 const unsigned longhandCount = shorthand.length();
1651 RefPtr<CSSValueList> longhands[8];
1652 ASSERT(longhandCount <= 8);
1653 for (size_t i = 0; i < longhandCount; ++i)
1654 longhands[i] = CSSValueList::createCommaSeparated();
1655
1656 do {
1657 bool parsedLonghand[8] = { false };
1658 do {
1659 bool foundProperty = false;
1660 for (size_t i = 0; i < longhandCount; ++i) {
1661 if (parsedLonghand[i])
1662 continue;
1663
1664 if (RefPtr<CSSValue> value = consumeAnimationValue(shorthand.properties()[i], m_range, m_context)) {
1665 parsedLonghand[i] = true;
1666 foundProperty = true;
1667 longhands[i]->append(*value);
1668 break;
1669 }
1670 }
1671 if (!foundProperty)
1672 return false;
1673 } while (!m_range.atEnd() && m_range.peek().type() != CommaToken);
1674
1675 // FIXME: This will make invalid longhands, see crbug.com/386459
1676 for (size_t i = 0; i < longhandCount; ++i) {
1677 if (!parsedLonghand[i])
1678 longhands[i]->append(CSSValuePool::singleton().createImplicitInitialValue());
1679 parsedLonghand[i] = false;
1680 }
1681 } while (consumeCommaIncludingWhitespace(m_range));
1682
1683 for (size_t i = 0; i < longhandCount; ++i) {
1684 if (!isValidAnimationPropertyList(shorthand.properties()[i], *longhands[i]))
1685 return false;
1686 }
1687
1688 for (size_t i = 0; i < longhandCount; ++i)
1689 addProperty(shorthand.properties()[i], shorthand.id(), *longhands[i], important);
1690
1691 return m_range.atEnd();
1692}
1693
1694static RefPtr<CSSValue> consumeZIndex(CSSParserTokenRange& range)
1695{
1696 if (range.peek().id() == CSSValueAuto)
1697 return consumeIdent(range);
1698 return consumeInteger(range);
1699}
1700
1701static RefPtr<CSSValue> consumeShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool isBoxShadowProperty)
1702{
1703 if (range.peek().id() == CSSValueNone)
1704 return consumeIdent(range);
1705
1706 RefPtr<CSSValueList> shadowValueList = CSSValueList::createCommaSeparated();
1707 do {
1708 if (RefPtr<CSSShadowValue> shadowValue = consumeSingleShadow(range, cssParserMode, isBoxShadowProperty, isBoxShadowProperty))
1709 shadowValueList->append(*shadowValue);
1710 else
1711 return nullptr;
1712 } while (consumeCommaIncludingWhitespace(range));
1713 return shadowValueList;
1714}
1715
1716static RefPtr<CSSValue> consumeTextDecorationLine(CSSParserTokenRange& range)
1717{
1718 CSSValueID id = range.peek().id();
1719 if (id == CSSValueNone)
1720 return consumeIdent(range);
1721
1722 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1723 while (true) {
1724#if ENABLE(LETTERPRESS)
1725 RefPtr<CSSPrimitiveValue> ident = consumeIdent<CSSValueBlink, CSSValueUnderline, CSSValueOverline, CSSValueLineThrough, CSSValueWebkitLetterpress>(range);
1726#else
1727 RefPtr<CSSPrimitiveValue> ident = consumeIdent<CSSValueBlink, CSSValueUnderline, CSSValueOverline, CSSValueLineThrough>(range);
1728#endif
1729 if (!ident)
1730 break;
1731 if (list->hasValue(ident.get()))
1732 return nullptr;
1733 list->append(ident.releaseNonNull());
1734 }
1735
1736 if (!list->length())
1737 return nullptr;
1738 return list;
1739}
1740
1741static RefPtr<CSSValue> consumeTextDecorationSkip(CSSParserTokenRange& range)
1742{
1743 CSSValueID id = range.peek().id();
1744 if (id == CSSValueNone)
1745 return consumeIdent(range);
1746
1747 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1748 while (true) {
1749 auto ident = consumeIdent<CSSValueAuto, CSSValueInk, CSSValueObjects>(range);
1750 if (!ident)
1751 break;
1752 if (list->hasValue(ident.get()))
1753 return nullptr;
1754 list->append(ident.releaseNonNull());
1755 }
1756
1757 if (!list->length())
1758 return nullptr;
1759 return list;
1760}
1761
1762static RefPtr<CSSValue> consumeTextEmphasisStyle(CSSParserTokenRange& range)
1763{
1764 CSSValueID id = range.peek().id();
1765 if (id == CSSValueNone)
1766 return consumeIdent(range);
1767
1768 if (RefPtr<CSSValue> textEmphasisStyle = consumeString(range))
1769 return textEmphasisStyle;
1770
1771 RefPtr<CSSPrimitiveValue> fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
1772 RefPtr<CSSPrimitiveValue> shape = consumeIdent<CSSValueDot, CSSValueCircle, CSSValueDoubleCircle, CSSValueTriangle, CSSValueSesame>(range);
1773 if (!fill)
1774 fill = consumeIdent<CSSValueFilled, CSSValueOpen>(range);
1775 if (fill && shape) {
1776 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
1777 parsedValues->append(fill.releaseNonNull());
1778 parsedValues->append(shape.releaseNonNull());
1779 return parsedValues;
1780 }
1781 if (fill)
1782 return fill;
1783 if (shape)
1784 return shape;
1785 return nullptr;
1786}
1787
1788static RefPtr<CSSPrimitiveValue> consumeCaretColor(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1789{
1790 if (range.peek().id() == CSSValueAuto)
1791 return consumeIdent(range);
1792 return consumeColor(range, cssParserMode);
1793}
1794
1795static RefPtr<CSSValue> consumeOutlineColor(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1796{
1797 // Allow the special focus color even in HTML Standard parsing mode.
1798 if (range.peek().id() == CSSValueWebkitFocusRingColor)
1799 return consumeIdent(range);
1800 return consumeColor(range, cssParserMode);
1801}
1802
1803static RefPtr<CSSPrimitiveValue> consumeLineWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1804{
1805 CSSValueID id = range.peek().id();
1806 if (id == CSSValueThin || id == CSSValueMedium || id == CSSValueThick)
1807 return consumeIdent(range);
1808 return consumeLength(range, cssParserMode, ValueRangeNonNegative, unitless);
1809}
1810
1811static RefPtr<CSSPrimitiveValue> consumeBorderWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
1812{
1813 return consumeLineWidth(range, cssParserMode, unitless);
1814}
1815
1816static RefPtr<CSSPrimitiveValue> consumeTextStrokeWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1817{
1818 return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
1819}
1820
1821static RefPtr<CSSPrimitiveValue> consumeColumnRuleWidth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1822{
1823 return consumeLineWidth(range, cssParserMode, UnitlessQuirk::Forbid);
1824}
1825
1826static bool consumeTranslate3d(CSSParserTokenRange& args, CSSParserMode cssParserMode, RefPtr<CSSFunctionValue>& transformValue)
1827{
1828 unsigned numberOfArguments = 2;
1829 RefPtr<CSSValue> parsedValue;
1830 do {
1831 parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1832 if (!parsedValue)
1833 return false;
1834 transformValue->append(*parsedValue);
1835 if (!consumeCommaIncludingWhitespace(args))
1836 return false;
1837 } while (--numberOfArguments);
1838 parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
1839 if (!parsedValue)
1840 return false;
1841 transformValue->append(*parsedValue);
1842 return true;
1843}
1844
1845static bool consumeNumbers(CSSParserTokenRange& args, RefPtr<CSSFunctionValue>& transformValue, unsigned numberOfArguments)
1846{
1847 do {
1848 RefPtr<CSSPrimitiveValue> parsedValue = consumeNumber(args, ValueRangeAll);
1849 if (!parsedValue)
1850 return false;
1851 transformValue->append(parsedValue.releaseNonNull());
1852 if (--numberOfArguments && !consumeCommaIncludingWhitespace(args))
1853 return false;
1854 } while (numberOfArguments);
1855 return true;
1856}
1857
1858static bool consumePerspective(CSSParserTokenRange& args, CSSParserMode cssParserMode, RefPtr<CSSFunctionValue>& transformValue)
1859{
1860 RefPtr<CSSPrimitiveValue> parsedValue = consumeLength(args, cssParserMode, ValueRangeNonNegative);
1861 if (!parsedValue) {
1862 double perspective;
1863 if (!consumeNumberRaw(args, perspective) || perspective < 0)
1864 return false;
1865 parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitType::CSS_PX);
1866 }
1867 if (!parsedValue)
1868 return false;
1869 transformValue->append(parsedValue.releaseNonNull());
1870 return true;
1871}
1872
1873static RefPtr<CSSValue> consumeTransformValue(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1874{
1875 CSSValueID functionId = range.peek().functionId();
1876 if (functionId == CSSValueInvalid)
1877 return nullptr;
1878 CSSParserTokenRange args = consumeFunction(range);
1879 if (args.atEnd())
1880 return nullptr;
1881
1882 RefPtr<CSSFunctionValue> transformValue = CSSFunctionValue::create(functionId);
1883 RefPtr<CSSValue> parsedValue;
1884 switch (functionId) {
1885 case CSSValueRotate:
1886 case CSSValueRotateX:
1887 case CSSValueRotateY:
1888 case CSSValueRotateZ:
1889 case CSSValueSkewX:
1890 case CSSValueSkewY:
1891 case CSSValueSkew:
1892 parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1893 if (!parsedValue)
1894 return nullptr;
1895 if (functionId == CSSValueSkew && consumeCommaIncludingWhitespace(args)) {
1896 transformValue->append(*parsedValue);
1897 parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1898 if (!parsedValue)
1899 return nullptr;
1900 }
1901 break;
1902 case CSSValueScaleX:
1903 case CSSValueScaleY:
1904 case CSSValueScaleZ:
1905 case CSSValueScale:
1906 parsedValue = consumeNumber(args, ValueRangeAll);
1907 if (!parsedValue)
1908 return nullptr;
1909 if (functionId == CSSValueScale && consumeCommaIncludingWhitespace(args)) {
1910 transformValue->append(*parsedValue);
1911 parsedValue = consumeNumber(args, ValueRangeAll);
1912 if (!parsedValue)
1913 return nullptr;
1914 }
1915 break;
1916 case CSSValuePerspective:
1917 if (!consumePerspective(args, cssParserMode, transformValue))
1918 return nullptr;
1919 break;
1920 case CSSValueTranslateX:
1921 case CSSValueTranslateY:
1922 case CSSValueTranslate:
1923 parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1924 if (!parsedValue)
1925 return nullptr;
1926 if (functionId == CSSValueTranslate && consumeCommaIncludingWhitespace(args)) {
1927 transformValue->append(*parsedValue);
1928 parsedValue = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
1929 if (!parsedValue)
1930 return nullptr;
1931 }
1932 break;
1933 case CSSValueTranslateZ:
1934 parsedValue = consumeLength(args, cssParserMode, ValueRangeAll);
1935 break;
1936 case CSSValueMatrix:
1937 case CSSValueMatrix3d:
1938 if (!consumeNumbers(args, transformValue, (functionId == CSSValueMatrix3d) ? 16 : 6))
1939 return nullptr;
1940 break;
1941 case CSSValueScale3d:
1942 if (!consumeNumbers(args, transformValue, 3))
1943 return nullptr;
1944 break;
1945 case CSSValueRotate3d:
1946 if (!consumeNumbers(args, transformValue, 3) || !consumeCommaIncludingWhitespace(args))
1947 return nullptr;
1948 parsedValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
1949 if (!parsedValue)
1950 return nullptr;
1951 break;
1952 case CSSValueTranslate3d:
1953 if (!consumeTranslate3d(args, cssParserMode, transformValue))
1954 return nullptr;
1955 break;
1956 default:
1957 return nullptr;
1958 }
1959 if (parsedValue)
1960 transformValue->append(*parsedValue);
1961 if (!args.atEnd())
1962 return nullptr;
1963 return transformValue;
1964}
1965
1966static RefPtr<CSSValue> consumeTransform(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1967{
1968 if (range.peek().id() == CSSValueNone)
1969 return consumeIdent(range);
1970
1971 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
1972 do {
1973 RefPtr<CSSValue> parsedTransformValue = consumeTransformValue(range, cssParserMode);
1974 if (!parsedTransformValue)
1975 return nullptr;
1976 list->append(parsedTransformValue.releaseNonNull());
1977 } while (!range.atEnd());
1978
1979 return list;
1980}
1981
1982template <CSSValueID start, CSSValueID end>
1983static RefPtr<CSSPrimitiveValue> consumePositionLonghand(CSSParserTokenRange& range, CSSParserMode cssParserMode)
1984{
1985 if (range.peek().type() == IdentToken) {
1986 CSSValueID id = range.peek().id();
1987 int percent;
1988 if (id == start)
1989 percent = 0;
1990 else if (id == CSSValueCenter)
1991 percent = 50;
1992 else if (id == end)
1993 percent = 100;
1994 else
1995 return nullptr;
1996 range.consumeIncludingWhitespace();
1997 return CSSPrimitiveValue::create(percent, CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
1998 }
1999 return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
2000}
2001
2002static RefPtr<CSSPrimitiveValue> consumePositionX(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2003{
2004 return consumePositionLonghand<CSSValueLeft, CSSValueRight>(range, cssParserMode);
2005}
2006
2007static RefPtr<CSSPrimitiveValue> consumePositionY(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2008{
2009 return consumePositionLonghand<CSSValueTop, CSSValueBottom>(range, cssParserMode);
2010}
2011
2012static RefPtr<CSSValue> consumePaintStroke(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2013{
2014 if (range.peek().id() == CSSValueNone)
2015 return consumeIdent(range);
2016 RefPtr<CSSPrimitiveValue> url = consumeUrl(range);
2017 if (url) {
2018 RefPtr<CSSValue> parsedValue;
2019 if (range.peek().id() == CSSValueNone)
2020 parsedValue = consumeIdent(range);
2021 else
2022 parsedValue = consumeColor(range, cssParserMode);
2023 if (parsedValue) {
2024 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
2025 values->append(url.releaseNonNull());
2026 values->append(parsedValue.releaseNonNull());
2027 return values;
2028 }
2029 return url;
2030 }
2031 return consumeColor(range, cssParserMode);
2032}
2033
2034static RefPtr<CSSValue> consumeGlyphOrientation(CSSParserTokenRange& range, CSSParserMode mode, CSSPropertyID property)
2035{
2036 if (range.peek().id() == CSSValueAuto) {
2037 if (property == CSSPropertyGlyphOrientationVertical)
2038 return consumeIdent(range);
2039 return nullptr;
2040 }
2041
2042 return consumeAngle(range, mode, UnitlessQuirk::Allow);
2043}
2044
2045static RefPtr<CSSValue> consumePaintOrder(CSSParserTokenRange& range)
2046{
2047 if (range.peek().id() == CSSValueNormal)
2048 return consumeIdent(range);
2049
2050 Vector<CSSValueID, 3> paintTypeList;
2051 RefPtr<CSSPrimitiveValue> fill;
2052 RefPtr<CSSPrimitiveValue> stroke;
2053 RefPtr<CSSPrimitiveValue> markers;
2054 do {
2055 CSSValueID id = range.peek().id();
2056 if (id == CSSValueFill && !fill)
2057 fill = consumeIdent(range);
2058 else if (id == CSSValueStroke && !stroke)
2059 stroke = consumeIdent(range);
2060 else if (id == CSSValueMarkers && !markers)
2061 markers = consumeIdent(range);
2062 else
2063 return nullptr;
2064 paintTypeList.append(id);
2065 } while (!range.atEnd());
2066
2067 // After parsing we serialize the paint-order list. Since it is not possible to
2068 // pop a last list items from CSSValueList without bigger cost, we create the
2069 // list after parsing.
2070 CSSValueID firstPaintOrderType = paintTypeList.at(0);
2071 RefPtr<CSSValueList> paintOrderList = CSSValueList::createSpaceSeparated();
2072 switch (firstPaintOrderType) {
2073 case CSSValueFill:
2074 case CSSValueStroke:
2075 paintOrderList->append(firstPaintOrderType == CSSValueFill ? fill.releaseNonNull() : stroke.releaseNonNull());
2076 if (paintTypeList.size() > 1) {
2077 if (paintTypeList.at(1) == CSSValueMarkers)
2078 paintOrderList->append(markers.releaseNonNull());
2079 }
2080 break;
2081 case CSSValueMarkers:
2082 paintOrderList->append(markers.releaseNonNull());
2083 if (paintTypeList.size() > 1) {
2084 if (paintTypeList.at(1) == CSSValueStroke)
2085 paintOrderList->append(stroke.releaseNonNull());
2086 }
2087 break;
2088 default:
2089 ASSERT_NOT_REACHED();
2090 }
2091
2092 return paintOrderList;
2093}
2094
2095static RefPtr<CSSValue> consumeNoneOrURI(CSSParserTokenRange& range)
2096{
2097 if (range.peek().id() == CSSValueNone)
2098 return consumeIdent(range);
2099 return consumeUrl(range);
2100}
2101
2102static RefPtr<CSSValue> consumeFlexBasis(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2103{
2104 // FIXME: Support intrinsic dimensions too.
2105 if (range.peek().id() == CSSValueAuto)
2106 return consumeIdent(range);
2107 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2108}
2109
2110static RefPtr<CSSValue> consumeKerning(CSSParserTokenRange& range, CSSParserMode mode)
2111{
2112 RefPtr<CSSValue> result = consumeIdent<CSSValueAuto, CSSValueNormal>(range);
2113 if (result)
2114 return result;
2115 return consumeLength(range, mode, ValueRangeAll, UnitlessQuirk::Allow);
2116}
2117
2118static RefPtr<CSSValue> consumeStrokeDasharray(CSSParserTokenRange& range)
2119{
2120 CSSValueID id = range.peek().id();
2121 if (id == CSSValueNone)
2122 return consumeIdent(range);
2123
2124 RefPtr<CSSValueList> dashes = CSSValueList::createCommaSeparated();
2125 do {
2126 RefPtr<CSSPrimitiveValue> dash = consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeNonNegative);
2127 if (!dash || (consumeCommaIncludingWhitespace(range) && range.atEnd()))
2128 return nullptr;
2129 dashes->append(dash.releaseNonNull());
2130 } while (!range.atEnd());
2131 return dashes;
2132}
2133
2134static RefPtr<CSSPrimitiveValue> consumeBaselineShift(CSSParserTokenRange& range)
2135{
2136 CSSValueID id = range.peek().id();
2137 if (id == CSSValueBaseline || id == CSSValueSub || id == CSSValueSuper)
2138 return consumeIdent(range);
2139 return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll);
2140}
2141
2142static RefPtr<CSSPrimitiveValue> consumeRxOrRy(CSSParserTokenRange& range)
2143{
2144 // FIXME-NEWPARSER: We don't support auto values when mapping, so for now turn this
2145 // off until we can figure out if we're even supposed to support it.
2146 // if (range.peek().id() == CSSValueAuto)
2147 // return consumeIdent(range);
2148 return consumeLengthOrPercent(range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
2149}
2150
2151static RefPtr<CSSValue> consumeCursor(CSSParserTokenRange& range, const CSSParserContext& context, bool inQuirksMode)
2152{
2153 RefPtr<CSSValueList> list;
2154 while (RefPtr<CSSValue> image = consumeImage(range, context, ConsumeGeneratedImage::Forbid)) {
2155 double num;
2156 IntPoint hotSpot(-1, -1);
2157 bool hotSpotSpecified = false;
2158 if (consumeNumberRaw(range, num)) {
2159 hotSpot.setX(int(num));
2160 if (!consumeNumberRaw(range, num))
2161 return nullptr;
2162 hotSpot.setY(int(num));
2163 hotSpotSpecified = true;
2164 }
2165
2166 if (!list)
2167 list = CSSValueList::createCommaSeparated();
2168
2169 list->append(CSSCursorImageValue::create(image.releaseNonNull(), hotSpotSpecified, hotSpot, context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No));
2170 if (!consumeCommaIncludingWhitespace(range))
2171 return nullptr;
2172 }
2173
2174 CSSValueID id = range.peek().id();
2175 RefPtr<CSSValue> cursorType;
2176 if (id == CSSValueHand) {
2177 if (!inQuirksMode) // Non-standard behavior
2178 return nullptr;
2179 cursorType = CSSValuePool::singleton().createIdentifierValue(CSSValuePointer);
2180 range.consumeIncludingWhitespace();
2181 } else if ((id >= CSSValueAuto && id <= CSSValueWebkitZoomOut) || id == CSSValueCopy || id == CSSValueNone) {
2182 cursorType = consumeIdent(range);
2183 } else {
2184 return nullptr;
2185 }
2186
2187 if (!list)
2188 return cursorType;
2189 list->append(cursorType.releaseNonNull());
2190 return list;
2191}
2192
2193static RefPtr<CSSValue> consumeAttr(CSSParserTokenRange args, CSSParserContext context)
2194{
2195 if (args.peek().type() != IdentToken)
2196 return nullptr;
2197
2198 CSSParserToken token = args.consumeIncludingWhitespace();
2199 auto attrName = token.value().toAtomicString();
2200 if (context.isHTMLDocument)
2201 attrName = attrName.convertToASCIILowercase();
2202
2203 if (!args.atEnd())
2204 return nullptr;
2205
2206 // FIXME-NEWPARSER: We want to use a CSSCustomIdentValue here eventually for the attrName.
2207 // FIXME-NEWPARSER: We want to use a CSSFunctionValue rather than relying on a custom
2208 // attr() primitive value.
2209 return CSSValuePool::singleton().createValue(attrName, CSSPrimitiveValue::CSS_ATTR);
2210}
2211
2212static RefPtr<CSSValue> consumeCounterContent(CSSParserTokenRange args, bool counters)
2213{
2214 RefPtr<CSSPrimitiveValue> identifier = consumeCustomIdent(args);
2215 if (!identifier)
2216 return nullptr;
2217
2218 RefPtr<CSSPrimitiveValue> separator;
2219 if (!counters)
2220 separator = CSSPrimitiveValue::create(String(), CSSPrimitiveValue::UnitType::CSS_STRING);
2221 else {
2222 if (!consumeCommaIncludingWhitespace(args) || args.peek().type() != StringToken)
2223 return nullptr;
2224 separator = CSSPrimitiveValue::create(args.consumeIncludingWhitespace().value().toString(), CSSPrimitiveValue::UnitType::CSS_STRING);
2225 }
2226
2227 RefPtr<CSSPrimitiveValue> listStyle;
2228 if (consumeCommaIncludingWhitespace(args)) {
2229 CSSValueID id = args.peek().id();
2230 if ((id != CSSValueNone && (id < CSSValueDisc || id > CSSValueKatakanaIroha)))
2231 return nullptr;
2232 listStyle = consumeIdent(args);
2233 } else
2234 listStyle = CSSValuePool::singleton().createIdentifierValue(CSSValueDecimal);
2235
2236 if (!args.atEnd())
2237 return nullptr;
2238
2239 // FIXME-NEWPARSER: Should just have a CSSCounterValue.
2240 return CSSValuePool::singleton().createValue(Counter::create(identifier.releaseNonNull(), listStyle.releaseNonNull(), separator.releaseNonNull()));
2241}
2242
2243static RefPtr<CSSValue> consumeContent(CSSParserTokenRange& range, CSSParserContext context)
2244{
2245 if (identMatches<CSSValueNone, CSSValueNormal>(range.peek().id()))
2246 return consumeIdent(range);
2247
2248 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
2249
2250 do {
2251 RefPtr<CSSValue> parsedValue = consumeImage(range, context);
2252 if (!parsedValue)
2253 parsedValue = consumeIdent<CSSValueOpenQuote, CSSValueCloseQuote, CSSValueNoOpenQuote, CSSValueNoCloseQuote>(range);
2254 if (!parsedValue)
2255 parsedValue = consumeString(range);
2256 if (!parsedValue) {
2257 if (range.peek().functionId() == CSSValueAttr)
2258 parsedValue = consumeAttr(consumeFunction(range), context);
2259 else if (range.peek().functionId() == CSSValueCounter)
2260 parsedValue = consumeCounterContent(consumeFunction(range), false);
2261 else if (range.peek().functionId() == CSSValueCounters)
2262 parsedValue = consumeCounterContent(consumeFunction(range), true);
2263 if (!parsedValue)
2264 return nullptr;
2265 }
2266 values->append(parsedValue.releaseNonNull());
2267 } while (!range.atEnd());
2268
2269 return values;
2270}
2271
2272static RefPtr<CSSPrimitiveValue> consumePerspective(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2273{
2274 if (range.peek().id() == CSSValueNone)
2275 return consumeIdent(range);
2276 RefPtr<CSSPrimitiveValue> parsedValue = consumeLength(range, cssParserMode, ValueRangeAll);
2277 if (!parsedValue) {
2278 // FIXME: Make this quirk only apply to the webkit prefixed version of the property.
2279 double perspective;
2280 if (!consumeNumberRaw(range, perspective))
2281 return nullptr;
2282 parsedValue = CSSPrimitiveValue::create(perspective, CSSPrimitiveValue::UnitType::CSS_PX);
2283 }
2284 if (parsedValue && (parsedValue->isCalculated() || parsedValue->doubleValue() > 0))
2285 return parsedValue;
2286 return nullptr;
2287}
2288
2289#if ENABLE(CSS_SCROLL_SNAP)
2290
2291static RefPtr<CSSValueList> consumeScrollSnapAlign(CSSParserTokenRange& range)
2292{
2293 RefPtr<CSSValueList> alignmentValue = CSSValueList::createSpaceSeparated();
2294 if (RefPtr<CSSPrimitiveValue> firstValue = consumeIdent<CSSValueNone, CSSValueStart, CSSValueCenter, CSSValueEnd>(range)) {
2295 alignmentValue->append(firstValue.releaseNonNull());
2296 if (auto secondValue = consumeIdent<CSSValueNone, CSSValueStart, CSSValueCenter, CSSValueEnd>(range))
2297 alignmentValue->append(secondValue.releaseNonNull());
2298 }
2299 return alignmentValue->length() ? alignmentValue : nullptr;
2300}
2301
2302static RefPtr<CSSValueList> consumeScrollSnapType(CSSParserTokenRange& range)
2303{
2304 RefPtr<CSSValueList> typeValue = CSSValueList::createSpaceSeparated();
2305 RefPtr<CSSPrimitiveValue> secondValue;
2306
2307 auto firstValue = consumeIdent<CSSValueX, CSSValueY, CSSValueBlock, CSSValueInline, CSSValueBoth>(range);
2308 if (firstValue)
2309 secondValue = consumeIdent<CSSValueProximity, CSSValueMandatory>(range);
2310 else
2311 firstValue = consumeIdent<CSSValueNone, CSSValueProximity, CSSValueMandatory>(range);
2312
2313 if (!firstValue)
2314 return nullptr;
2315
2316 typeValue->append(firstValue.releaseNonNull());
2317 if (secondValue)
2318 typeValue->append(secondValue.releaseNonNull());
2319
2320 return typeValue;
2321}
2322
2323#endif
2324
2325static RefPtr<CSSValue> consumeBorderRadiusCorner(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2326{
2327 RefPtr<CSSPrimitiveValue> parsedValue1 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2328 if (!parsedValue1)
2329 return nullptr;
2330 RefPtr<CSSPrimitiveValue> parsedValue2 = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2331 if (!parsedValue2)
2332 parsedValue2 = parsedValue1;
2333 return createPrimitiveValuePair(parsedValue1.releaseNonNull(), parsedValue2.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2334}
2335
2336static RefPtr<CSSValue> consumeTextUnderlineOffset(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2337{
2338 if (auto value = consumeIdent<CSSValueAuto>(range))
2339 return value;
2340 return consumeLength(range, cssParserMode, ValueRangeAll);
2341}
2342
2343static RefPtr<CSSValue> consumeTextDecorationThickness(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2344{
2345 if (auto value = consumeIdent<CSSValueAuto, CSSValueFromFont>(range))
2346 return value;
2347 return consumeLength(range, cssParserMode, ValueRangeAll);
2348}
2349
2350static RefPtr<CSSPrimitiveValue> consumeVerticalAlign(CSSParserTokenRange& range, CSSParserMode cssParserMode)
2351{
2352 RefPtr<CSSPrimitiveValue> parsedValue = consumeIdentRange(range, CSSValueBaseline, CSSValueWebkitBaselineMiddle);
2353 if (!parsedValue)
2354 parsedValue = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2355 return parsedValue;
2356}
2357
2358static RefPtr<CSSPrimitiveValue> consumeShapeRadius(CSSParserTokenRange& args, CSSParserMode cssParserMode)
2359{
2360 if (identMatches<CSSValueClosestSide, CSSValueFarthestSide>(args.peek().id()))
2361 return consumeIdent(args);
2362 return consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
2363}
2364
2365static RefPtr<CSSBasicShapeCircle> consumeBasicShapeCircle(CSSParserTokenRange& args, const CSSParserContext& context)
2366{
2367 // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
2368 // circle( [<shape-radius>]? [at <position>]? )
2369 RefPtr<CSSBasicShapeCircle> shape = CSSBasicShapeCircle::create();
2370 if (RefPtr<CSSPrimitiveValue> radius = consumeShapeRadius(args, context.mode))
2371 shape->setRadius(radius.releaseNonNull());
2372 if (consumeIdent<CSSValueAt>(args)) {
2373 RefPtr<CSSPrimitiveValue> centerX;
2374 RefPtr<CSSPrimitiveValue> centerY;
2375 if (!consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY))
2376 return nullptr;
2377 shape->setCenterX(centerX.releaseNonNull());
2378 shape->setCenterY(centerY.releaseNonNull());
2379 }
2380 return shape;
2381}
2382
2383static RefPtr<CSSBasicShapeEllipse> consumeBasicShapeEllipse(CSSParserTokenRange& args, const CSSParserContext& context)
2384{
2385 // spec: https://drafts.csswg.org/css-shapes/#supported-basic-shapes
2386 // ellipse( [<shape-radius>{2}]? [at <position>]? )
2387 RefPtr<CSSBasicShapeEllipse> shape = CSSBasicShapeEllipse::create();
2388 if (RefPtr<CSSPrimitiveValue> radiusX = consumeShapeRadius(args, context.mode)) {
2389 shape->setRadiusX(radiusX.releaseNonNull());
2390 if (RefPtr<CSSPrimitiveValue> radiusY = consumeShapeRadius(args, context.mode))
2391 shape->setRadiusY(radiusY.releaseNonNull());
2392 }
2393 if (consumeIdent<CSSValueAt>(args)) {
2394 RefPtr<CSSPrimitiveValue> centerX;
2395 RefPtr<CSSPrimitiveValue> centerY;
2396 if (!consumePosition(args, context.mode, UnitlessQuirk::Forbid, centerX, centerY))
2397 return nullptr;
2398 shape->setCenterX(centerX.releaseNonNull());
2399 shape->setCenterY(centerY.releaseNonNull());
2400 }
2401 return shape;
2402}
2403
2404static RefPtr<CSSBasicShapePolygon> consumeBasicShapePolygon(CSSParserTokenRange& args, const CSSParserContext& context)
2405{
2406 RefPtr<CSSBasicShapePolygon> shape = CSSBasicShapePolygon::create();
2407 if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
2408 shape->setWindRule(args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? WindRule::EvenOdd : WindRule::NonZero);
2409 if (!consumeCommaIncludingWhitespace(args))
2410 return nullptr;
2411 }
2412
2413 do {
2414 RefPtr<CSSPrimitiveValue> xLength = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2415 if (!xLength)
2416 return nullptr;
2417 RefPtr<CSSPrimitiveValue> yLength = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2418 if (!yLength)
2419 return nullptr;
2420 shape->appendPoint(xLength.releaseNonNull(), yLength.releaseNonNull());
2421 } while (consumeCommaIncludingWhitespace(args));
2422 return shape;
2423}
2424
2425static RefPtr<CSSBasicShapePath> consumeBasicShapePath(CSSParserTokenRange& args)
2426{
2427 WindRule windRule = WindRule::NonZero;
2428 if (identMatches<CSSValueEvenodd, CSSValueNonzero>(args.peek().id())) {
2429 windRule = args.consumeIncludingWhitespace().id() == CSSValueEvenodd ? WindRule::EvenOdd : WindRule::NonZero;
2430 if (!consumeCommaIncludingWhitespace(args))
2431 return nullptr;
2432 }
2433
2434 if (args.peek().type() != StringToken)
2435 return nullptr;
2436
2437 auto byteStream = std::make_unique<SVGPathByteStream>();
2438 if (!buildSVGPathByteStreamFromString(args.consumeIncludingWhitespace().value().toString(), *byteStream, UnalteredParsing))
2439 return nullptr;
2440
2441 auto shape = CSSBasicShapePath::create(WTFMove(byteStream));
2442 shape->setWindRule(windRule);
2443
2444 return shape;
2445}
2446
2447static void complete4Sides(RefPtr<CSSPrimitiveValue> side[4])
2448{
2449 if (side[3])
2450 return;
2451 if (!side[2]) {
2452 if (!side[1])
2453 side[1] = side[0];
2454 side[2] = side[0];
2455 }
2456 side[3] = side[1];
2457}
2458
2459static bool consumeRadii(RefPtr<CSSPrimitiveValue> horizontalRadii[4], RefPtr<CSSPrimitiveValue> verticalRadii[4], CSSParserTokenRange& range, CSSParserMode cssParserMode, bool useLegacyParsing)
2460{
2461 unsigned i = 0;
2462 for (; i < 4 && !range.atEnd() && range.peek().type() != DelimiterToken; ++i) {
2463 horizontalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2464 if (!horizontalRadii[i])
2465 return false;
2466 }
2467 if (!horizontalRadii[0])
2468 return false;
2469 if (range.atEnd()) {
2470 // Legacy syntax: -webkit-border-radius: l1 l2; is equivalent to border-radius: l1 / l2;
2471 if (useLegacyParsing && i == 2) {
2472 verticalRadii[0] = horizontalRadii[1];
2473 horizontalRadii[1] = nullptr;
2474 } else {
2475 complete4Sides(horizontalRadii);
2476 for (unsigned i = 0; i < 4; ++i)
2477 verticalRadii[i] = horizontalRadii[i];
2478 return true;
2479 }
2480 } else {
2481 if (!consumeSlashIncludingWhitespace(range))
2482 return false;
2483 for (i = 0; i < 4 && !range.atEnd(); ++i) {
2484 verticalRadii[i] = consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
2485 if (!verticalRadii[i])
2486 return false;
2487 }
2488 if (!verticalRadii[0] || !range.atEnd())
2489 return false;
2490 }
2491 complete4Sides(horizontalRadii);
2492 complete4Sides(verticalRadii);
2493 return true;
2494}
2495
2496static RefPtr<CSSBasicShapeInset> consumeBasicShapeInset(CSSParserTokenRange& args, const CSSParserContext& context)
2497{
2498 RefPtr<CSSBasicShapeInset> shape = CSSBasicShapeInset::create();
2499 RefPtr<CSSPrimitiveValue> top = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2500 if (!top)
2501 return nullptr;
2502 RefPtr<CSSPrimitiveValue> right = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2503 RefPtr<CSSPrimitiveValue> bottom;
2504 RefPtr<CSSPrimitiveValue> left;
2505 if (right) {
2506 bottom = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2507 if (bottom)
2508 left = consumeLengthOrPercent(args, context.mode, ValueRangeAll);
2509 }
2510 if (left)
2511 shape->updateShapeSize4Values(top.releaseNonNull(), right.releaseNonNull(), bottom.releaseNonNull(), left.releaseNonNull());
2512 else if (bottom)
2513 shape->updateShapeSize3Values(top.releaseNonNull(), right.releaseNonNull(), bottom.releaseNonNull());
2514 else if (right)
2515 shape->updateShapeSize2Values(top.releaseNonNull(), right.releaseNonNull());
2516 else
2517 shape->updateShapeSize1Value(top.releaseNonNull());
2518
2519 if (consumeIdent<CSSValueRound>(args)) {
2520 RefPtr<CSSPrimitiveValue> horizontalRadii[4] = { 0 };
2521 RefPtr<CSSPrimitiveValue> verticalRadii[4] = { 0 };
2522 if (!consumeRadii(horizontalRadii, verticalRadii, args, context.mode, false))
2523 return nullptr;
2524 shape->setTopLeftRadius(createPrimitiveValuePair(horizontalRadii[0].releaseNonNull(), verticalRadii[0].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2525 shape->setTopRightRadius(createPrimitiveValuePair(horizontalRadii[1].releaseNonNull(), verticalRadii[1].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2526 shape->setBottomRightRadius(createPrimitiveValuePair(horizontalRadii[2].releaseNonNull(), verticalRadii[2].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2527 shape->setBottomLeftRadius(createPrimitiveValuePair(horizontalRadii[3].releaseNonNull(), verticalRadii[3].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce));
2528 }
2529 return shape;
2530}
2531
2532static RefPtr<CSSValue> consumeBasicShape(CSSParserTokenRange& range, const CSSParserContext& context)
2533{
2534 RefPtr<CSSValue> result;
2535 if (range.peek().type() != FunctionToken)
2536 return nullptr;
2537 CSSValueID id = range.peek().functionId();
2538 CSSParserTokenRange rangeCopy = range;
2539 CSSParserTokenRange args = consumeFunction(rangeCopy);
2540
2541 // FIXME-NEWPARSER: CSSBasicShape should be a CSSValue, and shapes should not be primitive values.
2542 RefPtr<CSSBasicShape> shape;
2543 if (id == CSSValueCircle)
2544 shape = consumeBasicShapeCircle(args, context);
2545 else if (id == CSSValueEllipse)
2546 shape = consumeBasicShapeEllipse(args, context);
2547 else if (id == CSSValuePolygon)
2548 shape = consumeBasicShapePolygon(args, context);
2549 else if (id == CSSValueInset)
2550 shape = consumeBasicShapeInset(args, context);
2551 else if (id == CSSValuePath)
2552 shape = consumeBasicShapePath(args);
2553 if (!shape)
2554 return nullptr;
2555 range = rangeCopy;
2556
2557 if (!args.atEnd())
2558 return nullptr;
2559
2560 return CSSValuePool::singleton().createValue(shape.releaseNonNull());
2561}
2562
2563static RefPtr<CSSValue> consumeBasicShapeOrBox(CSSParserTokenRange& range, const CSSParserContext& context)
2564{
2565 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
2566 bool shapeFound = false;
2567 bool boxFound = false;
2568 while (!range.atEnd() && !(shapeFound && boxFound)) {
2569 RefPtr<CSSValue> componentValue;
2570 if (range.peek().type() == FunctionToken && !shapeFound) {
2571 componentValue = consumeBasicShape(range, context);
2572 shapeFound = true;
2573 } else if (range.peek().type() == IdentToken && !boxFound) {
2574 componentValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox, CSSValueFillBox, CSSValueStrokeBox, CSSValueViewBox>(range);
2575 boxFound = true;
2576 }
2577 if (!componentValue)
2578 return nullptr;
2579 list->append(componentValue.releaseNonNull());
2580 }
2581
2582 if (!range.atEnd() || !list->length())
2583 return nullptr;
2584
2585 return list;
2586}
2587
2588static RefPtr<CSSValue> consumeWebkitClipPath(CSSParserTokenRange& range, const CSSParserContext& context)
2589{
2590 if (range.peek().id() == CSSValueNone)
2591 return consumeIdent(range);
2592 if (RefPtr<CSSPrimitiveValue> url = consumeUrl(range))
2593 return url;
2594 return consumeBasicShapeOrBox(range, context);
2595}
2596
2597static RefPtr<CSSValue> consumeShapeOutside(CSSParserTokenRange& range, const CSSParserContext& context)
2598{
2599 if (RefPtr<CSSValue> imageValue = consumeImageOrNone(range, context))
2600 return imageValue;
2601 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
2602 if (RefPtr<CSSValue> boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
2603 list->append(boxValue.releaseNonNull());
2604 if (RefPtr<CSSValue> shapeValue = consumeBasicShape(range, context)) {
2605 list->append(shapeValue.releaseNonNull());
2606 if (list->length() < 2) {
2607 if (RefPtr<CSSValue> boxValue = consumeIdent<CSSValueContentBox, CSSValuePaddingBox, CSSValueBorderBox, CSSValueMarginBox>(range))
2608 list->append(boxValue.releaseNonNull());
2609 }
2610 }
2611 if (!list->length())
2612 return nullptr;
2613 return list;
2614}
2615
2616static bool isAuto(CSSValueID id)
2617{
2618 return identMatches<CSSValueAuto>(id);
2619}
2620
2621static bool isNormalOrStretch(CSSValueID id)
2622{
2623 return identMatches<CSSValueNormal, CSSValueStretch>(id);
2624}
2625
2626static bool isLeftOrRightKeyword(CSSValueID id)
2627{
2628 return identMatches<CSSValueLeft, CSSValueRight>(id);
2629}
2630
2631static bool isContentDistributionKeyword(CSSValueID id)
2632{
2633 return identMatches<CSSValueSpaceBetween, CSSValueSpaceAround, CSSValueSpaceEvenly, CSSValueStretch>(id);
2634}
2635
2636static bool isContentPositionKeyword(CSSValueID id)
2637{
2638 return identMatches<CSSValueStart, CSSValueEnd, CSSValueCenter, CSSValueFlexStart, CSSValueFlexEnd>(id);
2639}
2640
2641static bool isContentPositionOrLeftOrRightKeyword(CSSValueID id)
2642{
2643 return isContentPositionKeyword(id) || isLeftOrRightKeyword(id);
2644}
2645
2646static bool isOverflowKeyword(CSSValueID id)
2647{
2648 return CSSPropertyParserHelpers::identMatches<CSSValueUnsafe, CSSValueSafe>(id);
2649}
2650
2651static bool isBaselineKeyword(CSSValueID id)
2652{
2653 return identMatches<CSSValueFirst, CSSValueLast, CSSValueBaseline>(id);
2654}
2655
2656static RefPtr<CSSPrimitiveValue> consumeOverflowPositionKeyword(CSSParserTokenRange& range)
2657{
2658 return isOverflowKeyword(range.peek().id()) ? consumeIdent(range) : nullptr;
2659}
2660
2661static CSSValueID getBaselineKeyword(RefPtr<CSSValue> value)
2662{
2663 auto& primitiveValue = downcast<CSSPrimitiveValue>(*value);
2664 if (primitiveValue.pairValue()) {
2665 ASSERT(primitiveValue.pairValue()->first()->valueID() == CSSValueLast);
2666 ASSERT(primitiveValue.pairValue()->second()->valueID() == CSSValueBaseline);
2667 return CSSValueLastBaseline;
2668 }
2669 ASSERT(primitiveValue.valueID() == CSSValueBaseline);
2670 return CSSValueBaseline;
2671}
2672
2673static RefPtr<CSSValue> consumeBaselineKeyword(CSSParserTokenRange& range)
2674{
2675 RefPtr<CSSPrimitiveValue> preference = consumeIdent<CSSValueFirst, CSSValueLast>(range);
2676 RefPtr<CSSPrimitiveValue> baseline = consumeIdent<CSSValueBaseline>(range);
2677 if (!baseline)
2678 return nullptr;
2679 if (preference && preference->valueID() == CSSValueLast)
2680 return createPrimitiveValuePair(preference.releaseNonNull(), baseline.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2681 return baseline;
2682}
2683
2684using IsPositionKeyword = bool (*)(CSSValueID);
2685
2686static RefPtr<CSSValue> consumeContentDistributionOverflowPosition(CSSParserTokenRange& range, IsPositionKeyword isPositionKeyword)
2687{
2688 ASSERT(isPositionKeyword);
2689 CSSValueID id = range.peek().id();
2690 if (identMatches<CSSValueNormal>(id))
2691 return CSSContentDistributionValue::create(CSSValueInvalid, range.consumeIncludingWhitespace().id(), CSSValueInvalid);
2692
2693 if (isBaselineKeyword(id)) {
2694 RefPtr<CSSValue> baseline = consumeBaselineKeyword(range);
2695 if (!baseline)
2696 return nullptr;
2697 return CSSContentDistributionValue::create(CSSValueInvalid, getBaselineKeyword(baseline), CSSValueInvalid);
2698 }
2699
2700 if (isContentDistributionKeyword(id))
2701 return CSSContentDistributionValue::create(range.consumeIncludingWhitespace().id(), CSSValueInvalid, CSSValueInvalid);
2702
2703 CSSValueID overflow = isOverflowKeyword(id) ? range.consumeIncludingWhitespace().id() : CSSValueInvalid;
2704 if (isPositionKeyword(range.peek().id()))
2705 return CSSContentDistributionValue::create(CSSValueInvalid, range.consumeIncludingWhitespace().id(), overflow);
2706
2707 return nullptr;
2708}
2709
2710static RefPtr<CSSPrimitiveValue> consumeBorderImageRepeatKeyword(CSSParserTokenRange& range)
2711{
2712 return consumeIdent<CSSValueStretch, CSSValueRepeat, CSSValueSpace, CSSValueRound>(range);
2713}
2714
2715static RefPtr<CSSValue> consumeBorderImageRepeat(CSSParserTokenRange& range)
2716{
2717 RefPtr<CSSPrimitiveValue> horizontal = consumeBorderImageRepeatKeyword(range);
2718 if (!horizontal)
2719 return nullptr;
2720 RefPtr<CSSPrimitiveValue> vertical = consumeBorderImageRepeatKeyword(range);
2721 if (!vertical)
2722 vertical = horizontal;
2723 return createPrimitiveValuePair(horizontal.releaseNonNull(), vertical.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
2724}
2725
2726static RefPtr<CSSValue> consumeBorderImageSlice(CSSPropertyID property, CSSParserTokenRange& range)
2727{
2728 bool fill = consumeIdent<CSSValueFill>(range);
2729 RefPtr<CSSPrimitiveValue> slices[4] = { 0 };
2730
2731 for (size_t index = 0; index < 4; ++index) {
2732 RefPtr<CSSPrimitiveValue> value = consumePercent(range, ValueRangeNonNegative);
2733 if (!value)
2734 value = consumeNumber(range, ValueRangeNonNegative);
2735 if (!value)
2736 break;
2737 slices[index] = value;
2738 }
2739 if (!slices[0])
2740 return nullptr;
2741 if (consumeIdent<CSSValueFill>(range)) {
2742 if (fill)
2743 return nullptr;
2744 fill = true;
2745 }
2746 complete4Sides(slices);
2747 // FIXME: For backwards compatibility, -webkit-border-image, -webkit-mask-box-image and -webkit-box-reflect have to do a fill by default.
2748 // FIXME: What do we do with -webkit-box-reflect and -webkit-mask-box-image? Probably just have to leave them filling...
2749 if (property == CSSPropertyWebkitBorderImage || property == CSSPropertyWebkitMaskBoxImage || property == CSSPropertyWebkitBoxReflect)
2750 fill = true;
2751
2752 // Now build a rect value to hold all four of our primitive values.
2753 // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2754 auto quad = Quad::create();
2755 quad->setTop(slices[0].releaseNonNull());
2756 quad->setRight(slices[1].releaseNonNull());
2757 quad->setBottom(slices[2].releaseNonNull());
2758 quad->setLeft(slices[3].releaseNonNull());
2759
2760 // Make our new border image value now.
2761 return CSSBorderImageSliceValue::create(CSSValuePool::singleton().createValue(WTFMove(quad)), fill);
2762}
2763
2764static RefPtr<CSSValue> consumeBorderImageOutset(CSSParserTokenRange& range)
2765{
2766 RefPtr<CSSPrimitiveValue> outsets[4] = { 0 };
2767
2768 RefPtr<CSSPrimitiveValue> value;
2769 for (size_t index = 0; index < 4; ++index) {
2770 value = consumeNumber(range, ValueRangeNonNegative);
2771 if (!value)
2772 value = consumeLength(range, HTMLStandardMode, ValueRangeNonNegative);
2773 if (!value)
2774 break;
2775 outsets[index] = value;
2776 }
2777 if (!outsets[0])
2778 return nullptr;
2779 complete4Sides(outsets);
2780
2781 // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2782 auto quad = Quad::create();
2783 quad->setTop(outsets[0].releaseNonNull());
2784 quad->setRight(outsets[1].releaseNonNull());
2785 quad->setBottom(outsets[2].releaseNonNull());
2786 quad->setLeft(outsets[3].releaseNonNull());
2787
2788 return CSSValuePool::singleton().createValue(WTFMove(quad));
2789}
2790
2791static RefPtr<CSSValue> consumeBorderImageWidth(CSSParserTokenRange& range)
2792{
2793 RefPtr<CSSPrimitiveValue> widths[4];
2794
2795 RefPtr<CSSPrimitiveValue> value;
2796 for (size_t index = 0; index < 4; ++index) {
2797 value = consumeNumber(range, ValueRangeNonNegative);
2798 if (!value)
2799 value = consumeLengthOrPercent(range, HTMLStandardMode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
2800 if (!value)
2801 value = consumeIdent<CSSValueAuto>(range);
2802 if (!value)
2803 break;
2804 widths[index] = value;
2805 }
2806 if (!widths[0])
2807 return nullptr;
2808 complete4Sides(widths);
2809
2810 // FIXME-NEWPARSER: Should just have a CSSQuadValue.
2811 auto quad = Quad::create();
2812 quad->setTop(widths[0].releaseNonNull());
2813 quad->setRight(widths[1].releaseNonNull());
2814 quad->setBottom(widths[2].releaseNonNull());
2815 quad->setLeft(widths[3].releaseNonNull());
2816
2817 return CSSValuePool::singleton().createValue(WTFMove(quad));
2818}
2819
2820static bool consumeBorderImageComponents(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context, RefPtr<CSSValue>& source,
2821 RefPtr<CSSValue>& slice, RefPtr<CSSValue>& width, RefPtr<CSSValue>& outset, RefPtr<CSSValue>& repeat)
2822{
2823 do {
2824 if (!source) {
2825 source = consumeImageOrNone(range, context);
2826 if (source)
2827 continue;
2828 }
2829 if (!repeat) {
2830 repeat = consumeBorderImageRepeat(range);
2831 if (repeat)
2832 continue;
2833 }
2834 if (!slice) {
2835 slice = consumeBorderImageSlice(property, range);
2836 if (slice) {
2837 ASSERT(!width && !outset);
2838 if (consumeSlashIncludingWhitespace(range)) {
2839 width = consumeBorderImageWidth(range);
2840 if (consumeSlashIncludingWhitespace(range)) {
2841 outset = consumeBorderImageOutset(range);
2842 if (!outset)
2843 return false;
2844 } else if (!width) {
2845 return false;
2846 }
2847 }
2848 } else {
2849 return false;
2850 }
2851 } else {
2852 return false;
2853 }
2854 } while (!range.atEnd());
2855 return true;
2856}
2857
2858static RefPtr<CSSValue> consumeWebkitBorderImage(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2859{
2860 RefPtr<CSSValue> source;
2861 RefPtr<CSSValue> slice;
2862 RefPtr<CSSValue> width;
2863 RefPtr<CSSValue> outset;
2864 RefPtr<CSSValue> repeat;
2865 if (consumeBorderImageComponents(property, range, context, source, slice, width, outset, repeat))
2866 return createBorderImageValue(WTFMove(source), WTFMove(slice), WTFMove(width), WTFMove(outset), WTFMove(repeat));
2867 return nullptr;
2868}
2869
2870static RefPtr<CSSValue> consumeReflect(CSSParserTokenRange& range, const CSSParserContext& context)
2871{
2872 if (range.peek().id() == CSSValueNone)
2873 return consumeIdent(range);
2874
2875 RefPtr<CSSPrimitiveValue> direction = consumeIdent<CSSValueAbove, CSSValueBelow, CSSValueLeft, CSSValueRight>(range);
2876 if (!direction)
2877 return nullptr;
2878
2879 RefPtr<CSSPrimitiveValue> offset;
2880 if (range.atEnd())
2881 offset = CSSValuePool::singleton().createValue(0, CSSPrimitiveValue::UnitType::CSS_PX);
2882 else {
2883 offset = consumeLengthOrPercent(range, context.mode, ValueRangeAll, UnitlessQuirk::Forbid);
2884 if (!offset)
2885 return nullptr;
2886 }
2887
2888 RefPtr<CSSValue> mask;
2889 if (!range.atEnd()) {
2890 mask = consumeWebkitBorderImage(CSSPropertyWebkitBoxReflect, range, context);
2891 if (!mask)
2892 return nullptr;
2893 }
2894 return CSSReflectValue::create(direction.releaseNonNull(), offset.releaseNonNull(), WTFMove(mask));
2895}
2896
2897#if ENABLE(CSS_IMAGE_ORIENTATION)
2898static RefPtr<CSSValue> consumeImageOrientation(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless = UnitlessQuirk::Forbid)
2899{
2900 if (range.peek().type() != NumberToken) {
2901 RefPtr<CSSPrimitiveValue> angle = consumeAngle(range, cssParserMode, unitless);
2902 if (angle && angle->doubleValue() == 0)
2903 return angle;
2904 }
2905 return nullptr;
2906}
2907#endif
2908
2909static RefPtr<CSSPrimitiveValue> consumeBackgroundBlendMode(CSSParserTokenRange& range)
2910{
2911 CSSValueID id = range.peek().id();
2912 if (id == CSSValueNormal || id == CSSValueOverlay || (id >= CSSValueMultiply && id <= CSSValueLuminosity))
2913 return consumeIdent(range);
2914 return nullptr;
2915}
2916
2917static RefPtr<CSSPrimitiveValue> consumeBackgroundAttachment(CSSParserTokenRange& range)
2918{
2919 return consumeIdent<CSSValueScroll, CSSValueFixed, CSSValueLocal>(range);
2920}
2921
2922static RefPtr<CSSPrimitiveValue> consumeBackgroundBox(CSSParserTokenRange& range)
2923{
2924 return consumeIdent<CSSValueBorderBox, CSSValuePaddingBox, CSSValueContentBox, CSSValueWebkitText>(range);
2925}
2926
2927static RefPtr<CSSPrimitiveValue> consumeBackgroundComposite(CSSParserTokenRange& range)
2928{
2929 return consumeIdentRange(range, CSSValueClear, CSSValuePlusLighter);
2930}
2931
2932static RefPtr<CSSPrimitiveValue> consumeWebkitMaskSourceType(CSSParserTokenRange& range)
2933{
2934 return consumeIdent<CSSValueAuto, CSSValueAlpha, CSSValueLuminance>(range);
2935}
2936
2937static RefPtr<CSSPrimitiveValue> consumePrefixedBackgroundBox(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& /*context*/)
2938{
2939 // The values 'border', 'padding' and 'content' are deprecated and do not apply to the version of the property that has the -webkit- prefix removed.
2940 if (RefPtr<CSSPrimitiveValue> value = consumeIdentRange(range, CSSValueBorder, CSSValuePaddingBox))
2941 return value;
2942 if (range.peek().id() == CSSValueWebkitText || ((property == CSSPropertyWebkitBackgroundClip || property == CSSPropertyWebkitMaskClip) && range.peek().id() == CSSValueText))
2943 return consumeIdent(range);
2944 return nullptr;
2945}
2946
2947static RefPtr<CSSPrimitiveValue> consumeBackgroundSize(CSSPropertyID property, CSSParserTokenRange& range, CSSParserMode cssParserMode)
2948{
2949 if (identMatches<CSSValueContain, CSSValueCover>(range.peek().id()))
2950 return consumeIdent(range);
2951
2952 // FIXME: We're allowing the unitless quirk on this property because our
2953 // tests assume that. Other browser engines don't allow it though.
2954 RefPtr<CSSPrimitiveValue> horizontal = consumeIdent<CSSValueAuto>(range);
2955 if (!horizontal)
2956 horizontal = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2957
2958 RefPtr<CSSPrimitiveValue> vertical;
2959 if (!range.atEnd()) {
2960 if (range.peek().id() == CSSValueAuto) // `auto' is the default
2961 range.consumeIncludingWhitespace();
2962 else
2963 vertical = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
2964 } else if (!vertical && property == CSSPropertyWebkitBackgroundSize) {
2965 // Legacy syntax: "-webkit-background-size: 10px" is equivalent to "background-size: 10px 10px".
2966 vertical = horizontal;
2967 }
2968 if (!vertical)
2969 return horizontal;
2970 return createPrimitiveValuePair(horizontal.releaseNonNull(), vertical.releaseNonNull(), property == CSSPropertyWebkitBackgroundSize ? Pair::IdenticalValueEncoding::Coalesce : Pair::IdenticalValueEncoding::DoNotCoalesce);
2971}
2972
2973static RefPtr<CSSValueList> consumeGridAutoFlow(CSSParserTokenRange& range)
2974{
2975 RefPtr<CSSPrimitiveValue> rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
2976 RefPtr<CSSPrimitiveValue> denseAlgorithm = consumeIdent<CSSValueDense>(range);
2977 if (!rowOrColumnValue) {
2978 rowOrColumnValue = consumeIdent<CSSValueRow, CSSValueColumn>(range);
2979 if (!rowOrColumnValue && !denseAlgorithm)
2980 return nullptr;
2981 }
2982 RefPtr<CSSValueList> parsedValues = CSSValueList::createSpaceSeparated();
2983 if (rowOrColumnValue)
2984 parsedValues->append(rowOrColumnValue.releaseNonNull());
2985 if (denseAlgorithm)
2986 parsedValues->append(denseAlgorithm.releaseNonNull());
2987 return parsedValues;
2988}
2989
2990static RefPtr<CSSValue> consumeBackgroundComponent(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
2991{
2992 switch (property) {
2993 case CSSPropertyBackgroundClip:
2994 return consumeBackgroundBox(range);
2995 case CSSPropertyBackgroundBlendMode:
2996 return consumeBackgroundBlendMode(range);
2997 case CSSPropertyBackgroundAttachment:
2998 return consumeBackgroundAttachment(range);
2999 case CSSPropertyBackgroundOrigin:
3000 return consumeBackgroundBox(range);
3001 case CSSPropertyWebkitMaskComposite:
3002 case CSSPropertyWebkitBackgroundComposite:
3003 return consumeBackgroundComposite(range);
3004 case CSSPropertyWebkitBackgroundClip:
3005 case CSSPropertyWebkitBackgroundOrigin:
3006 case CSSPropertyWebkitMaskClip:
3007 case CSSPropertyWebkitMaskOrigin:
3008 return consumePrefixedBackgroundBox(property, range, context);
3009 case CSSPropertyBackgroundImage:
3010 case CSSPropertyWebkitMaskImage:
3011 return consumeImageOrNone(range, context);
3012 case CSSPropertyWebkitMaskSourceType:
3013 return consumeWebkitMaskSourceType(range);
3014 case CSSPropertyBackgroundPositionX:
3015 case CSSPropertyWebkitMaskPositionX:
3016 return consumePositionX(range, context.mode);
3017 case CSSPropertyBackgroundPositionY:
3018 case CSSPropertyWebkitMaskPositionY:
3019 return consumePositionY(range, context.mode);
3020 case CSSPropertyBackgroundSize:
3021 case CSSPropertyWebkitBackgroundSize:
3022 case CSSPropertyWebkitMaskSize:
3023 return consumeBackgroundSize(property, range, context.mode);
3024 case CSSPropertyBackgroundColor:
3025 return consumeColor(range, context.mode);
3026 default:
3027 break;
3028 };
3029 return nullptr;
3030}
3031
3032static void addBackgroundValue(RefPtr<CSSValue>& list, Ref<CSSValue>&& value)
3033{
3034 if (list) {
3035 if (!list->isBaseValueList()) {
3036 RefPtr<CSSValue> firstValue = list;
3037 list = CSSValueList::createCommaSeparated();
3038 downcast<CSSValueList>(*list).append(firstValue.releaseNonNull());
3039 }
3040 downcast<CSSValueList>(*list).append(WTFMove(value));
3041 } else {
3042 // To conserve memory we don't actually wrap a single value in a list.
3043 list = WTFMove(value);
3044 }
3045}
3046
3047static RefPtr<CSSValue> consumeCommaSeparatedBackgroundComponent(CSSPropertyID property, CSSParserTokenRange& range, const CSSParserContext& context)
3048{
3049 RefPtr<CSSValue> result;
3050 do {
3051 RefPtr<CSSValue> value = consumeBackgroundComponent(property, range, context);
3052 if (!value)
3053 return nullptr;
3054 addBackgroundValue(result, value.releaseNonNull());
3055 } while (consumeCommaIncludingWhitespace(range));
3056 return result;
3057}
3058
3059static bool isSelfPositionKeyword(CSSValueID id)
3060{
3061 return identMatches<CSSValueStart, CSSValueEnd, CSSValueCenter, CSSValueSelfStart, CSSValueSelfEnd, CSSValueFlexStart, CSSValueFlexEnd>(id);
3062}
3063
3064static bool isSelfPositionOrLeftOrRightKeyword(CSSValueID id)
3065{
3066 return isSelfPositionKeyword(id) || isLeftOrRightKeyword(id);
3067}
3068
3069static RefPtr<CSSValue> consumeSelfPositionOverflowPosition(CSSParserTokenRange& range, IsPositionKeyword isPositionKeyword)
3070{
3071 ASSERT(isPositionKeyword);
3072 CSSValueID id = range.peek().id();
3073 if (isAuto(id) || isNormalOrStretch(id))
3074 return consumeIdent(range);
3075
3076 if (isBaselineKeyword(id))
3077 return consumeBaselineKeyword(range);
3078
3079 RefPtr<CSSPrimitiveValue> overflowPosition = consumeOverflowPositionKeyword(range);
3080 if (!isPositionKeyword(range.peek().id()))
3081 return nullptr;
3082 RefPtr<CSSPrimitiveValue> selfPosition = consumeIdent(range);
3083 if (overflowPosition)
3084 return createPrimitiveValuePair(overflowPosition.releaseNonNull(), selfPosition.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
3085 return selfPosition;
3086}
3087
3088static RefPtr<CSSValue> consumeAlignItems(CSSParserTokenRange& range)
3089{
3090 // align-items property does not allow the 'auto' value.
3091 if (identMatches<CSSValueAuto>(range.peek().id()))
3092 return nullptr;
3093 return consumeSelfPositionOverflowPosition(range, isSelfPositionKeyword);
3094}
3095
3096static RefPtr<CSSValue> consumeJustifyItems(CSSParserTokenRange& range)
3097{
3098 // justify-items property does not allow the 'auto' value.
3099 if (identMatches<CSSValueAuto>(range.peek().id()))
3100 return nullptr;
3101 CSSParserTokenRange rangeCopy = range;
3102 RefPtr<CSSPrimitiveValue> legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
3103 RefPtr<CSSPrimitiveValue> positionKeyword = consumeIdent<CSSValueCenter, CSSValueLeft, CSSValueRight>(rangeCopy);
3104 if (!legacy)
3105 legacy = consumeIdent<CSSValueLegacy>(rangeCopy);
3106 if (legacy) {
3107 range = rangeCopy;
3108 if (positionKeyword)
3109 return createPrimitiveValuePair(legacy.releaseNonNull(), positionKeyword.releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce);
3110 return legacy;
3111 }
3112 return consumeSelfPositionOverflowPosition(range, isSelfPositionOrLeftOrRightKeyword);
3113}
3114
3115static RefPtr<CSSValue> consumeFitContent(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3116{
3117 CSSParserTokenRange rangeCopy = range;
3118 CSSParserTokenRange args = consumeFunction(rangeCopy);
3119 RefPtr<CSSPrimitiveValue> length = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3120 if (!length || !args.atEnd())
3121 return nullptr;
3122 range = rangeCopy;
3123 RefPtr<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueFitContent);
3124 result->append(length.releaseNonNull());
3125 return result;
3126}
3127
3128static RefPtr<CSSPrimitiveValue> consumeCustomIdentForGridLine(CSSParserTokenRange& range)
3129{
3130 if (range.peek().id() == CSSValueAuto || range.peek().id() == CSSValueSpan)
3131 return nullptr;
3132 return consumeCustomIdent(range);
3133}
3134
3135static RefPtr<CSSValue> consumeGridLine(CSSParserTokenRange& range)
3136{
3137 if (range.peek().id() == CSSValueAuto)
3138 return consumeIdent(range);
3139
3140 RefPtr<CSSPrimitiveValue> spanValue;
3141 RefPtr<CSSPrimitiveValue> gridLineName;
3142 RefPtr<CSSPrimitiveValue> numericValue = consumeInteger(range);
3143 if (numericValue) {
3144 gridLineName = consumeCustomIdentForGridLine(range);
3145 spanValue = consumeIdent<CSSValueSpan>(range);
3146 } else {
3147 spanValue = consumeIdent<CSSValueSpan>(range);
3148 if (spanValue) {
3149 numericValue = consumeInteger(range);
3150 gridLineName = consumeCustomIdentForGridLine(range);
3151 if (!numericValue)
3152 numericValue = consumeInteger(range);
3153 } else {
3154 gridLineName = consumeCustomIdentForGridLine(range);
3155 if (gridLineName) {
3156 numericValue = consumeInteger(range);
3157 spanValue = consumeIdent<CSSValueSpan>(range);
3158 if (!spanValue && !numericValue)
3159 return gridLineName;
3160 } else {
3161 return nullptr;
3162 }
3163 }
3164 }
3165
3166 if (spanValue && !numericValue && !gridLineName)
3167 return nullptr; // "span" keyword alone is invalid.
3168 if (spanValue && numericValue && numericValue->intValue() < 0)
3169 return nullptr; // Negative numbers are not allowed for span.
3170 if (numericValue && numericValue->intValue() == 0)
3171 return nullptr; // An <integer> value of zero makes the declaration invalid.
3172
3173 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3174 if (spanValue)
3175 values->append(spanValue.releaseNonNull());
3176 if (numericValue)
3177 values->append(numericValue.releaseNonNull());
3178 if (gridLineName)
3179 values->append(gridLineName.releaseNonNull());
3180 ASSERT(values->length());
3181 return values;
3182}
3183
3184static bool isGridTrackFixedSized(const CSSPrimitiveValue& primitiveValue)
3185{
3186 CSSValueID valueID = primitiveValue.valueID();
3187 if (valueID == CSSValueMinContent || valueID == CSSValueWebkitMinContent || valueID == CSSValueMaxContent || valueID == CSSValueWebkitMaxContent || valueID == CSSValueAuto || primitiveValue.isFlex())
3188 return false;
3189
3190 return true;
3191}
3192
3193static bool isGridTrackFixedSized(const CSSValue& value)
3194{
3195 if (value.isPrimitiveValue())
3196 return isGridTrackFixedSized(downcast<CSSPrimitiveValue>(value));
3197
3198 ASSERT(value.isFunctionValue());
3199 auto& function = downcast<CSSFunctionValue>(value);
3200 if (function.name() == CSSValueFitContent || function.length() < 2)
3201 return false;
3202
3203 const CSSValue* minPrimitiveValue = downcast<CSSPrimitiveValue>(function.item(0));
3204 const CSSValue* maxPrimitiveValue = downcast<CSSPrimitiveValue>(function.item(1));
3205 return isGridTrackFixedSized(*minPrimitiveValue) || isGridTrackFixedSized(*maxPrimitiveValue);
3206}
3207
3208static Vector<String> parseGridTemplateAreasColumnNames(const String& gridRowNames)
3209{
3210 ASSERT(!gridRowNames.isEmpty());
3211 Vector<String> columnNames;
3212 // Using StringImpl to avoid checks and indirection in every call to String::operator[].
3213 StringImpl& text = *gridRowNames.impl();
3214
3215 StringBuilder areaName;
3216 for (unsigned i = 0; i < text.length(); ++i) {
3217 if (isCSSSpace(text[i])) {
3218 if (!areaName.isEmpty()) {
3219 columnNames.append(areaName.toString());
3220 areaName.clear();
3221 }
3222 continue;
3223 }
3224 if (text[i] == '.') {
3225 if (areaName == ".")
3226 continue;
3227 if (!areaName.isEmpty()) {
3228 columnNames.append(areaName.toString());
3229 areaName.clear();
3230 }
3231 } else {
3232 if (!isNameCodePoint(text[i]))
3233 return Vector<String>();
3234 if (areaName == ".") {
3235 columnNames.append(areaName.toString());
3236 areaName.clear();
3237 }
3238 }
3239
3240 areaName.append(text[i]);
3241 }
3242
3243 if (!areaName.isEmpty())
3244 columnNames.append(areaName.toString());
3245
3246 return columnNames;
3247}
3248
3249static bool parseGridTemplateAreasRow(const String& gridRowNames, NamedGridAreaMap& gridAreaMap, const size_t rowCount, size_t& columnCount)
3250{
3251 if (gridRowNames.isAllSpecialCharacters<isCSSSpace>())
3252 return false;
3253
3254 Vector<String> columnNames = parseGridTemplateAreasColumnNames(gridRowNames);
3255 if (rowCount == 0) {
3256 columnCount = columnNames.size();
3257 if (columnCount == 0)
3258 return false;
3259 } else if (columnCount != columnNames.size()) {
3260 // The declaration is invalid if all the rows don't have the number of columns.
3261 return false;
3262 }
3263
3264 for (size_t currentColumn = 0; currentColumn < columnCount; ++currentColumn) {
3265 const String& gridAreaName = columnNames[currentColumn];
3266
3267 // Unamed areas are always valid (we consider them to be 1x1).
3268 if (gridAreaName == ".")
3269 continue;
3270
3271 size_t lookAheadColumn = currentColumn + 1;
3272 while (lookAheadColumn < columnCount && columnNames[lookAheadColumn] == gridAreaName)
3273 lookAheadColumn++;
3274
3275 NamedGridAreaMap::iterator gridAreaIt = gridAreaMap.find(gridAreaName);
3276 if (gridAreaIt == gridAreaMap.end()) {
3277 gridAreaMap.add(gridAreaName, GridArea(GridSpan::translatedDefiniteGridSpan(rowCount, rowCount + 1), GridSpan::translatedDefiniteGridSpan(currentColumn, lookAheadColumn)));
3278 } else {
3279 GridArea& gridArea = gridAreaIt->value;
3280
3281 // The following checks test that the grid area is a single filled-in rectangle.
3282 // 1. The new row is adjacent to the previously parsed row.
3283 if (rowCount != gridArea.rows.endLine())
3284 return false;
3285
3286 // 2. The new area starts at the same position as the previously parsed area.
3287 if (currentColumn != gridArea.columns.startLine())
3288 return false;
3289
3290 // 3. The new area ends at the same position as the previously parsed area.
3291 if (lookAheadColumn != gridArea.columns.endLine())
3292 return false;
3293
3294 gridArea.rows = GridSpan::translatedDefiniteGridSpan(gridArea.rows.startLine(), gridArea.rows.endLine() + 1);
3295 }
3296 currentColumn = lookAheadColumn - 1;
3297 }
3298
3299 return true;
3300}
3301
3302static RefPtr<CSSPrimitiveValue> consumeGridBreadth(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3303{
3304 const CSSParserToken& token = range.peek();
3305 if (identMatches<CSSValueMinContent, CSSValueWebkitMinContent, CSSValueMaxContent, CSSValueWebkitMaxContent, CSSValueAuto>(token.id()))
3306 return consumeIdent(range);
3307 if (token.type() == DimensionToken && token.unitType() == CSSPrimitiveValue::UnitType::CSS_FR) {
3308 if (range.peek().numericValue() < 0)
3309 return nullptr;
3310 return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::CSS_FR);
3311 }
3312 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3313}
3314
3315static RefPtr<CSSValue> consumeGridTrackSize(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3316{
3317 const CSSParserToken& token = range.peek();
3318 if (identMatches<CSSValueAuto>(token.id()))
3319 return consumeIdent(range);
3320
3321 if (token.functionId() == CSSValueMinmax) {
3322 CSSParserTokenRange rangeCopy = range;
3323 CSSParserTokenRange args = consumeFunction(rangeCopy);
3324 RefPtr<CSSPrimitiveValue> minTrackBreadth = consumeGridBreadth(args, cssParserMode);
3325 if (!minTrackBreadth || minTrackBreadth->isFlex() || !consumeCommaIncludingWhitespace(args))
3326 return nullptr;
3327 RefPtr<CSSPrimitiveValue> maxTrackBreadth = consumeGridBreadth(args, cssParserMode);
3328 if (!maxTrackBreadth || !args.atEnd())
3329 return nullptr;
3330 range = rangeCopy;
3331 RefPtr<CSSFunctionValue> result = CSSFunctionValue::create(CSSValueMinmax);
3332 result->append(minTrackBreadth.releaseNonNull());
3333 result->append(maxTrackBreadth.releaseNonNull());
3334 return result;
3335 }
3336
3337 if (token.functionId() == CSSValueFitContent)
3338 return consumeFitContent(range, cssParserMode);
3339
3340 return consumeGridBreadth(range, cssParserMode);
3341}
3342
3343// Appends to the passed in CSSGridLineNamesValue if any, otherwise creates a new one.
3344static RefPtr<CSSGridLineNamesValue> consumeGridLineNames(CSSParserTokenRange& range, CSSGridLineNamesValue* lineNames = nullptr)
3345{
3346 CSSParserTokenRange rangeCopy = range;
3347 if (rangeCopy.consumeIncludingWhitespace().type() != LeftBracketToken)
3348 return nullptr;
3349
3350 RefPtr<CSSGridLineNamesValue> result = lineNames;
3351 if (!result)
3352 result = CSSGridLineNamesValue::create();
3353 while (RefPtr<CSSPrimitiveValue> lineName = consumeCustomIdentForGridLine(rangeCopy))
3354 result->append(lineName.releaseNonNull());
3355 if (rangeCopy.consumeIncludingWhitespace().type() != RightBracketToken)
3356 return nullptr;
3357 range = rangeCopy;
3358 return result;
3359}
3360
3361static bool consumeGridTrackRepeatFunction(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSValueList& list, bool& isAutoRepeat, bool& allTracksAreFixedSized)
3362{
3363 CSSParserTokenRange args = consumeFunction(range);
3364 // The number of repetitions for <auto-repeat> is not important at parsing level
3365 // because it will be computed later, let's set it to 1.
3366 size_t repetitions = 1;
3367 isAutoRepeat = identMatches<CSSValueAutoFill, CSSValueAutoFit>(args.peek().id());
3368 RefPtr<CSSValueList> repeatedValues;
3369 if (isAutoRepeat)
3370 repeatedValues = CSSGridAutoRepeatValue::create(args.consumeIncludingWhitespace().id());
3371 else {
3372 // FIXME: a consumeIntegerRaw would be more efficient here.
3373 RefPtr<CSSPrimitiveValue> repetition = consumePositiveInteger(args);
3374 if (!repetition)
3375 return false;
3376 repetitions = clampTo<size_t>(repetition->doubleValue(), 0, GridPosition::max());
3377 repeatedValues = CSSValueList::createSpaceSeparated();
3378 }
3379 if (!consumeCommaIncludingWhitespace(args))
3380 return false;
3381 RefPtr<CSSGridLineNamesValue> lineNames = consumeGridLineNames(args);
3382 if (lineNames)
3383 repeatedValues->append(lineNames.releaseNonNull());
3384
3385 size_t numberOfTracks = 0;
3386 while (!args.atEnd()) {
3387 RefPtr<CSSValue> trackSize = consumeGridTrackSize(args, cssParserMode);
3388 if (!trackSize)
3389 return false;
3390 if (allTracksAreFixedSized)
3391 allTracksAreFixedSized = isGridTrackFixedSized(*trackSize);
3392 repeatedValues->append(trackSize.releaseNonNull());
3393 ++numberOfTracks;
3394 lineNames = consumeGridLineNames(args);
3395 if (lineNames)
3396 repeatedValues->append(lineNames.releaseNonNull());
3397 }
3398 // We should have found at least one <track-size> or else it is not a valid <track-list>.
3399 if (!numberOfTracks)
3400 return false;
3401
3402 if (isAutoRepeat)
3403 list.append(repeatedValues.releaseNonNull());
3404 else {
3405 // We clamp the repetitions to a multiple of the repeat() track list's size, while staying below the max grid size.
3406 repetitions = std::min(repetitions, GridPosition::max() / numberOfTracks);
3407 for (size_t i = 0; i < repetitions; ++i) {
3408 for (size_t j = 0; j < repeatedValues->length(); ++j)
3409 list.append(*repeatedValues->itemWithoutBoundsCheck(j));
3410 }
3411 }
3412 return true;
3413}
3414
3415enum TrackListType { GridTemplate, GridTemplateNoRepeat, GridAuto };
3416
3417static RefPtr<CSSValue> consumeGridTrackList(CSSParserTokenRange& range, CSSParserMode cssParserMode, TrackListType trackListType)
3418{
3419 bool allowGridLineNames = trackListType != GridAuto;
3420 RefPtr<CSSValueList> values = CSSValueList::createSpaceSeparated();
3421 RefPtr<CSSGridLineNamesValue> lineNames = consumeGridLineNames(range);
3422 if (lineNames) {
3423 if (!allowGridLineNames)
3424 return nullptr;
3425 values->append(lineNames.releaseNonNull());
3426 }
3427
3428 bool allowRepeat = trackListType == GridTemplate;
3429 bool seenAutoRepeat = false;
3430 bool allTracksAreFixedSized = true;
3431 do {
3432 bool isAutoRepeat;
3433 if (range.peek().functionId() == CSSValueRepeat) {
3434 if (!allowRepeat)
3435 return nullptr;
3436 if (!consumeGridTrackRepeatFunction(range, cssParserMode, *values, isAutoRepeat, allTracksAreFixedSized))
3437 return nullptr;
3438 if (isAutoRepeat && seenAutoRepeat)
3439 return nullptr;
3440 seenAutoRepeat = seenAutoRepeat || isAutoRepeat;
3441 } else if (RefPtr<CSSValue> value = consumeGridTrackSize(range, cssParserMode)) {
3442 if (allTracksAreFixedSized)
3443 allTracksAreFixedSized = isGridTrackFixedSized(*value);
3444 values->append(value.releaseNonNull());
3445 } else {
3446 return nullptr;
3447 }
3448 if (seenAutoRepeat && !allTracksAreFixedSized)
3449 return nullptr;
3450 lineNames = consumeGridLineNames(range);
3451 if (lineNames) {
3452 if (!allowGridLineNames)
3453 return nullptr;
3454 values->append(lineNames.releaseNonNull());
3455 }
3456 } while (!range.atEnd() && range.peek().type() != DelimiterToken);
3457 return values;
3458}
3459
3460static RefPtr<CSSValue> consumeGridTemplatesRowsOrColumns(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3461{
3462 if (range.peek().id() == CSSValueNone)
3463 return consumeIdent(range);
3464 return consumeGridTrackList(range, cssParserMode, GridTemplate);
3465}
3466
3467static RefPtr<CSSValue> consumeGridTemplateAreas(CSSParserTokenRange& range)
3468{
3469 if (range.peek().id() == CSSValueNone)
3470 return consumeIdent(range);
3471
3472 NamedGridAreaMap gridAreaMap;
3473 size_t rowCount = 0;
3474 size_t columnCount = 0;
3475
3476 while (range.peek().type() == StringToken) {
3477 if (!parseGridTemplateAreasRow(range.consumeIncludingWhitespace().value().toString(), gridAreaMap, rowCount, columnCount))
3478 return nullptr;
3479 ++rowCount;
3480 }
3481
3482 if (rowCount == 0)
3483 return nullptr;
3484 ASSERT(columnCount);
3485 return CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount);
3486}
3487
3488static RefPtr<CSSValue> consumeLineBoxContain(CSSParserTokenRange& range)
3489{
3490 if (range.peek().id() == CSSValueNone)
3491 return consumeIdent(range);
3492
3493 LineBoxContain lineBoxContain = LineBoxContainNone;
3494
3495 while (range.peek().type() == IdentToken) {
3496 auto id = range.peek().id();
3497 if (id == CSSValueBlock) {
3498 if (lineBoxContain & LineBoxContainBlock)
3499 return nullptr;
3500 lineBoxContain |= LineBoxContainBlock;
3501 } else if (id == CSSValueInline) {
3502 if (lineBoxContain & LineBoxContainInline)
3503 return nullptr;
3504 lineBoxContain |= LineBoxContainInline;
3505 } else if (id == CSSValueFont) {
3506 if (lineBoxContain & LineBoxContainFont)
3507 return nullptr;
3508 lineBoxContain |= LineBoxContainFont;
3509 } else if (id == CSSValueGlyphs) {
3510 if (lineBoxContain & LineBoxContainGlyphs)
3511 return nullptr;
3512 lineBoxContain |= LineBoxContainGlyphs;
3513 } else if (id == CSSValueReplaced) {
3514 if (lineBoxContain & LineBoxContainReplaced)
3515 return nullptr;
3516 lineBoxContain |= LineBoxContainReplaced;
3517 } else if (id == CSSValueInlineBox) {
3518 if (lineBoxContain & LineBoxContainInlineBox)
3519 return nullptr;
3520 lineBoxContain |= LineBoxContainInlineBox;
3521 } else if (id == CSSValueInitialLetter) {
3522 if (lineBoxContain & LineBoxContainInitialLetter)
3523 return nullptr;
3524 lineBoxContain |= LineBoxContainInitialLetter;
3525 } else
3526 return nullptr;
3527 range.consumeIncludingWhitespace();
3528 }
3529
3530 if (!lineBoxContain)
3531 return nullptr;
3532
3533 return CSSLineBoxContainValue::create(lineBoxContain);
3534}
3535
3536static RefPtr<CSSValue> consumeLineGrid(CSSParserTokenRange& range)
3537{
3538 if (range.peek().id() == CSSValueNone)
3539 return consumeIdent(range);
3540 return consumeCustomIdent(range);
3541}
3542
3543static RefPtr<CSSValue> consumeInitialLetter(CSSParserTokenRange& range)
3544{
3545 RefPtr<CSSValue> ident = consumeIdent<CSSValueNormal>(range);
3546 if (ident)
3547 return ident;
3548
3549 RefPtr<CSSPrimitiveValue> height = consumeNumber(range, ValueRangeNonNegative);
3550 if (!height)
3551 return nullptr;
3552
3553 RefPtr<CSSPrimitiveValue> position;
3554 if (!range.atEnd()) {
3555 position = consumeNumber(range, ValueRangeNonNegative);
3556 if (!position || !range.atEnd())
3557 return nullptr;
3558 } else
3559 position = height.copyRef();
3560
3561 return createPrimitiveValuePair(position.releaseNonNull(), WTFMove(height));
3562}
3563
3564static RefPtr<CSSValue> consumeSpeakAs(CSSParserTokenRange& range)
3565{
3566 if (range.peek().id() == CSSValueNone)
3567 return consumeIdent(range);
3568
3569 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3570
3571 bool seenNormal = false;
3572 bool seenSpellOut = false;
3573 bool seenLiteralPunctuation = false;
3574 bool seenNoPunctuation = false;
3575
3576 // normal | spell-out || digits || [ literal-punctuation | no-punctuation ]
3577 while (!range.atEnd()) {
3578 CSSValueID valueID = range.peek().id();
3579 if ((valueID == CSSValueNormal && seenSpellOut)
3580 || (valueID == CSSValueSpellOut && seenNormal)
3581 || (valueID == CSSValueLiteralPunctuation && seenNoPunctuation)
3582 || (valueID == CSSValueNoPunctuation && seenLiteralPunctuation))
3583 return nullptr;
3584 RefPtr<CSSValue> ident = consumeIdent<CSSValueNormal, CSSValueSpellOut, CSSValueDigits, CSSValueLiteralPunctuation, CSSValueNoPunctuation>(range);
3585 if (!ident)
3586 return nullptr;
3587 switch (valueID) {
3588 case CSSValueNormal:
3589 seenNormal = true;
3590 break;
3591 case CSSValueSpellOut:
3592 seenSpellOut = true;
3593 break;
3594 case CSSValueLiteralPunctuation:
3595 seenLiteralPunctuation = true;
3596 break;
3597 case CSSValueNoPunctuation:
3598 seenNoPunctuation = true;
3599 break;
3600 default:
3601 break;
3602 }
3603 list->append(ident.releaseNonNull());
3604 }
3605
3606 return list->length() ? list : nullptr;
3607}
3608
3609static RefPtr<CSSValue> consumeHangingPunctuation(CSSParserTokenRange& range)
3610{
3611 if (range.peek().id() == CSSValueNone)
3612 return consumeIdent(range);
3613
3614 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3615
3616 bool seenForceEnd = false;
3617 bool seenAllowEnd = false;
3618 bool seenFirst = false;
3619 bool seenLast = false;
3620
3621 while (!range.atEnd()) {
3622 CSSValueID valueID = range.peek().id();
3623 if ((valueID == CSSValueFirst && seenFirst)
3624 || (valueID == CSSValueLast && seenLast)
3625 || (valueID == CSSValueAllowEnd && (seenAllowEnd || seenForceEnd))
3626 || (valueID == CSSValueForceEnd && (seenAllowEnd || seenForceEnd)))
3627 return nullptr;
3628 RefPtr<CSSValue> ident = consumeIdent<CSSValueAllowEnd, CSSValueForceEnd, CSSValueFirst, CSSValueLast>(range);
3629 if (!ident)
3630 return nullptr;
3631 switch (valueID) {
3632 case CSSValueAllowEnd:
3633 seenAllowEnd = true;
3634 break;
3635 case CSSValueForceEnd:
3636 seenForceEnd = true;
3637 break;
3638 case CSSValueFirst:
3639 seenFirst = true;
3640 break;
3641 case CSSValueLast:
3642 seenLast = true;
3643 break;
3644 default:
3645 break;
3646 }
3647 list->append(ident.releaseNonNull());
3648 }
3649
3650 return list->length() ? list : nullptr;
3651}
3652
3653static RefPtr<CSSValue> consumeWebkitMarqueeIncrement(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3654{
3655 if (range.peek().type() == IdentToken)
3656 return consumeIdent<CSSValueSmall, CSSValueMedium, CSSValueLarge>(range);
3657 return consumeLengthOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Allow);
3658}
3659
3660static RefPtr<CSSValue> consumeWebkitMarqueeRepetition(CSSParserTokenRange& range)
3661{
3662 if (range.peek().type() == IdentToken)
3663 return consumeIdent<CSSValueInfinite>(range);
3664 return consumeNumber(range, ValueRangeNonNegative);
3665}
3666
3667static RefPtr<CSSValue> consumeWebkitMarqueeSpeed(CSSParserTokenRange& range, CSSParserMode cssParserMode)
3668{
3669 if (range.peek().type() == IdentToken)
3670 return consumeIdent<CSSValueSlow, CSSValueNormal, CSSValueFast>(range);
3671 return consumeTime(range, cssParserMode, ValueRangeNonNegative, UnitlessQuirk::Allow);
3672}
3673
3674static RefPtr<CSSValue> consumeAlt(CSSParserTokenRange& range, const CSSParserContext& context)
3675{
3676 if (range.peek().type() == StringToken)
3677 return consumeString(range);
3678
3679 if (range.peek().functionId() != CSSValueAttr)
3680 return nullptr;
3681
3682 return consumeAttr(consumeFunction(range), context);
3683}
3684
3685static RefPtr<CSSValue> consumeWebkitAspectRatio(CSSParserTokenRange& range)
3686{
3687 if (range.peek().type() == IdentToken)
3688 return consumeIdent<CSSValueAuto, CSSValueFromDimensions, CSSValueFromIntrinsic>(range);
3689
3690 RefPtr<CSSPrimitiveValue> leftValue = consumeNumber(range, ValueRangeNonNegative);
3691 if (!leftValue || !leftValue->floatValue() || range.atEnd() || !consumeSlashIncludingWhitespace(range))
3692 return nullptr;
3693 RefPtr<CSSPrimitiveValue> rightValue = consumeNumber(range, ValueRangeNonNegative);
3694 if (!rightValue || !rightValue->floatValue())
3695 return nullptr;
3696
3697 return CSSAspectRatioValue::create(leftValue->floatValue(), rightValue->floatValue());
3698}
3699
3700static RefPtr<CSSValue> consumeTextEmphasisPosition(CSSParserTokenRange& range)
3701{
3702 bool foundOverOrUnder = false;
3703 CSSValueID overUnderValueID = CSSValueOver;
3704 bool foundLeftOrRight = false;
3705 CSSValueID leftRightValueID = CSSValueRight;
3706 while (!range.atEnd()) {
3707 switch (range.peek().id()) {
3708 case CSSValueOver:
3709 if (foundOverOrUnder)
3710 return nullptr;
3711 foundOverOrUnder = true;
3712 overUnderValueID = CSSValueOver;
3713 break;
3714 case CSSValueUnder:
3715 if (foundOverOrUnder)
3716 return nullptr;
3717 foundOverOrUnder = true;
3718 overUnderValueID = CSSValueUnder;
3719 break;
3720 case CSSValueLeft:
3721 if (foundLeftOrRight)
3722 return nullptr;
3723 foundLeftOrRight = true;
3724 leftRightValueID = CSSValueLeft;
3725 break;
3726 case CSSValueRight:
3727 if (foundLeftOrRight)
3728 return nullptr;
3729 foundLeftOrRight = true;
3730 leftRightValueID = CSSValueRight;
3731 break;
3732 default:
3733 return nullptr;
3734 }
3735
3736 range.consumeIncludingWhitespace();
3737 }
3738 if (!foundOverOrUnder)
3739 return nullptr;
3740 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3741 list->append(CSSValuePool::singleton().createIdentifierValue(overUnderValueID));
3742 if (foundLeftOrRight)
3743 list->append(CSSValuePool::singleton().createIdentifierValue(leftRightValueID));
3744 return list;
3745}
3746
3747#if ENABLE(DARK_MODE_CSS)
3748
3749static RefPtr<CSSValue> consumeColorScheme(CSSParserTokenRange& range)
3750{
3751 if (isAuto(range.peek().id()))
3752 return consumeIdent(range);
3753
3754 Vector<CSSValueID, 3> identifiers;
3755
3756 while (!range.atEnd()) {
3757 if (range.peek().type() != IdentToken)
3758 return nullptr;
3759
3760 CSSValueID id = range.peek().id();
3761
3762 switch (id) {
3763 case CSSValueAuto:
3764 // Auto is only allowed as a single value, and was handled earlier.
3765 // Don't allow it in the list.
3766 return nullptr;
3767
3768 case CSSValueOnly:
3769 case CSSValueLight:
3770 case CSSValueDark:
3771 if (!identifiers.appendIfNotContains(id))
3772 return nullptr;
3773 break;
3774
3775 default:
3776 // Unknown identifiers are allowed and ignored.
3777 break;
3778 }
3779
3780 range.consumeIncludingWhitespace();
3781 }
3782
3783 RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
3784 for (auto id : identifiers)
3785 list->append(CSSValuePool::singleton().createIdentifierValue(id));
3786 return list;
3787}
3788
3789#endif
3790
3791#if ENABLE(DASHBOARD_SUPPORT)
3792
3793static RefPtr<CSSValue> consumeWebkitDashboardRegion(CSSParserTokenRange& range, CSSParserMode mode)
3794{
3795 if (range.atEnd())
3796 return nullptr;
3797
3798 if (range.peek().id() == CSSValueNone)
3799 return consumeIdent(range);
3800
3801 auto firstRegion = DashboardRegion::create();
3802 DashboardRegion* region = nullptr;
3803
3804 bool requireCommas = false;
3805
3806 while (!range.atEnd()) {
3807 if (!region)
3808 region = firstRegion.ptr();
3809 else {
3810 auto nextRegion = DashboardRegion::create();
3811 region->m_next = nextRegion.copyRef();
3812 region = nextRegion.ptr();
3813 }
3814
3815 if (range.peek().functionId() != CSSValueDashboardRegion)
3816 return nullptr;
3817
3818 CSSParserTokenRange rangeCopy = range;
3819 CSSParserTokenRange args = consumeFunction(rangeCopy);
3820 if (rangeCopy.end() == args.end())
3821 return nullptr; // No ) was found. Be strict about this, since tests are.
3822
3823 // First arg is a label.
3824 if (args.peek().type() != IdentToken)
3825 return nullptr;
3826 region->m_label = args.consumeIncludingWhitespace().value().toString();
3827
3828 // Comma is optional, so don't fail if we can't consume one.
3829 requireCommas = consumeCommaIncludingWhitespace(args);
3830
3831 // Second arg is a type.
3832 if (args.peek().type() != IdentToken)
3833 return nullptr;
3834 region->m_geometryType = args.consumeIncludingWhitespace().value().toString();
3835 if (equalLettersIgnoringASCIICase(region->m_geometryType, "circle"))
3836 region->m_isCircle = true;
3837 else if (equalLettersIgnoringASCIICase(region->m_geometryType, "rectangle"))
3838 region->m_isRectangle = true;
3839 else
3840 return nullptr;
3841
3842 if (args.atEnd()) {
3843 // This originally used CSSValueInvalid by accident. It might be more logical to use something else.
3844 RefPtr<CSSPrimitiveValue> amount = CSSValuePool::singleton().createIdentifierValue(CSSValueInvalid);
3845 region->setTop(amount.copyRef());
3846 region->setRight(amount.copyRef());
3847 region->setBottom(amount.copyRef());
3848 region->setLeft(WTFMove(amount));
3849 range = rangeCopy;
3850 continue;
3851 }
3852
3853 // Next four arguments must be offset numbers or auto.
3854 for (int i = 0; i < 4; ++i) {
3855 if (args.atEnd() || (requireCommas && !consumeCommaIncludingWhitespace(args)))
3856 return nullptr;
3857
3858 if (args.atEnd())
3859 return nullptr;
3860
3861 RefPtr<CSSPrimitiveValue> amount;
3862 if (args.peek().id() == CSSValueAuto)
3863 amount = consumeIdent(args);
3864 else
3865 amount = consumeLength(args, mode, ValueRangeAll);
3866
3867 if (!i)
3868 region->setTop(WTFMove(amount));
3869 else if (i == 1)
3870 region->setRight(WTFMove(amount));
3871 else if (i == 2)
3872 region->setBottom(WTFMove(amount));
3873 else
3874 region->setLeft(WTFMove(amount));
3875 }
3876
3877 if (!args.atEnd())
3878 return nullptr;
3879
3880 range = rangeCopy;
3881 }
3882
3883 return CSSValuePool::singleton().createValue(RefPtr<DashboardRegion>(WTFMove(firstRegion)));
3884}
3885
3886#endif
3887
3888RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, CSSPropertyID currentShorthand)
3889{
3890 if (CSSParserFastPaths::isKeywordPropertyID(property)) {
3891 if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(property, m_range.peek().id(), m_context))
3892 return nullptr;
3893
3894 return consumeIdent(m_range);
3895 }
3896 switch (property) {
3897 case CSSPropertyWillChange:
3898 return consumeWillChange(m_range);
3899 case CSSPropertyPage:
3900 return consumePage(m_range);
3901 case CSSPropertyQuotes:
3902 return consumeQuotes(m_range);
3903 case CSSPropertyFontVariantCaps:
3904 return consumeFontVariantCaps(m_range);
3905 case CSSPropertyFontVariantLigatures:
3906 return consumeFontVariantLigatures(m_range);
3907 case CSSPropertyFontVariantNumeric:
3908 return consumeFontVariantNumeric(m_range);
3909 case CSSPropertyFontVariantEastAsian:
3910 return consumeFontVariantEastAsian(m_range);
3911 case CSSPropertyFontFeatureSettings:
3912 return consumeFontFeatureSettings(m_range);
3913 case CSSPropertyFontFamily:
3914 return consumeFontFamily(m_range);
3915 case CSSPropertyFontWeight:
3916 return consumeFontWeight(m_range);
3917 case CSSPropertyFontStretch:
3918 return consumeFontStretch(m_range);
3919 case CSSPropertyFontStyle:
3920 return consumeFontStyle(m_range, m_context.mode);
3921 case CSSPropertyFontSynthesis:
3922 return consumeFontSynthesis(m_range);
3923#if ENABLE(VARIATION_FONTS)
3924 case CSSPropertyFontVariationSettings:
3925 return consumeFontVariationSettings(m_range);
3926#endif
3927 case CSSPropertyLetterSpacing:
3928 return consumeLetterSpacing(m_range, m_context.mode);
3929 case CSSPropertyWordSpacing:
3930 return consumeWordSpacing(m_range, m_context.mode);
3931 case CSSPropertyTabSize:
3932 return consumeTabSize(m_range, m_context.mode);
3933#if ENABLE(TEXT_AUTOSIZING)
3934 case CSSPropertyWebkitTextSizeAdjust:
3935 // FIXME: Support toggling the validation of this property via a runtime setting that is independent of
3936 // whether isTextAutosizingEnabled() is true. We want to enable this property on iOS, when simulating
3937 // a iOS device in Safari's responsive design mode and when optionally enabled in DRT/WTR. Otherwise,
3938 // this property should be disabled by default.
3939#if !PLATFORM(IOS_FAMILY)
3940 if (!m_context.textAutosizingEnabled)
3941 return nullptr;
3942#endif
3943 return consumeTextSizeAdjust(m_range, m_context.mode);
3944#endif
3945 case CSSPropertyFontSize:
3946 return consumeFontSize(m_range, m_context.mode, UnitlessQuirk::Allow);
3947 case CSSPropertyLineHeight:
3948 return consumeLineHeight(m_range, m_context.mode);
3949 case CSSPropertyWebkitBorderHorizontalSpacing:
3950 case CSSPropertyWebkitBorderVerticalSpacing:
3951 return consumeLength(m_range, m_context.mode, ValueRangeNonNegative);
3952 case CSSPropertyCounterIncrement:
3953 case CSSPropertyCounterReset:
3954 return consumeCounter(m_range, property == CSSPropertyCounterIncrement ? 1 : 0);
3955 case CSSPropertySize:
3956 return consumeSize(m_range, m_context.mode);
3957 case CSSPropertyTextIndent:
3958 return consumeTextIndent(m_range, m_context.mode);
3959 case CSSPropertyMaxWidth:
3960 case CSSPropertyMaxHeight:
3961 return consumeMaxWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
3962 case CSSPropertyMaxInlineSize:
3963 case CSSPropertyMaxBlockSize:
3964 return consumeMaxWidthOrHeight(m_range, m_context);
3965 case CSSPropertyMinWidth:
3966 case CSSPropertyMinHeight:
3967 case CSSPropertyWidth:
3968 case CSSPropertyHeight:
3969 return consumeWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
3970 case CSSPropertyMinInlineSize:
3971 case CSSPropertyMinBlockSize:
3972 case CSSPropertyInlineSize:
3973 case CSSPropertyBlockSize:
3974 return consumeWidthOrHeight(m_range, m_context);
3975 case CSSPropertyMarginTop:
3976 case CSSPropertyMarginRight:
3977 case CSSPropertyMarginBottom:
3978 case CSSPropertyMarginLeft:
3979 case CSSPropertyBottom:
3980 case CSSPropertyLeft:
3981 case CSSPropertyRight:
3982 case CSSPropertyTop: {
3983 UnitlessQuirk unitless = currentShorthand != CSSPropertyInset ? UnitlessQuirk::Allow : UnitlessQuirk::Forbid;
3984 return consumeMarginOrOffset(m_range, m_context.mode, unitless);
3985 }
3986 case CSSPropertyInsetInlineStart:
3987 case CSSPropertyInsetInlineEnd:
3988 case CSSPropertyInsetBlockStart:
3989 case CSSPropertyInsetBlockEnd:
3990 case CSSPropertyMarginInlineStart:
3991 case CSSPropertyMarginInlineEnd:
3992 case CSSPropertyMarginBlockStart:
3993 case CSSPropertyMarginBlockEnd:
3994 return consumeMarginOrOffset(m_range, m_context.mode, UnitlessQuirk::Forbid);
3995 case CSSPropertyPaddingTop:
3996 case CSSPropertyPaddingRight:
3997 case CSSPropertyPaddingBottom:
3998 case CSSPropertyPaddingLeft:
3999 return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative, UnitlessQuirk::Allow);
4000 case CSSPropertyPaddingInlineStart:
4001 case CSSPropertyPaddingInlineEnd:
4002 case CSSPropertyPaddingBlockStart:
4003 case CSSPropertyPaddingBlockEnd:
4004 return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative, UnitlessQuirk::Forbid);
4005#if ENABLE(CSS_SCROLL_SNAP)
4006 case CSSPropertyScrollSnapMarginBottom:
4007 case CSSPropertyScrollSnapMarginLeft:
4008 case CSSPropertyScrollSnapMarginRight:
4009 case CSSPropertyScrollSnapMarginTop:
4010 return consumeLength(m_range, m_context.mode, ValueRangeAll);
4011 case CSSPropertyScrollPaddingBottom:
4012 case CSSPropertyScrollPaddingLeft:
4013 case CSSPropertyScrollPaddingRight:
4014 case CSSPropertyScrollPaddingTop:
4015 return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeAll);
4016 case CSSPropertyScrollSnapAlign:
4017 return consumeScrollSnapAlign(m_range);
4018 case CSSPropertyScrollSnapType:
4019 return consumeScrollSnapType(m_range);
4020#endif
4021 case CSSPropertyClip:
4022 return consumeClip(m_range, m_context.mode);
4023#if ENABLE(POINTER_EVENTS)
4024 case CSSPropertyTouchAction:
4025 return consumeTouchAction(m_range);
4026#endif
4027 case CSSPropertyObjectPosition:
4028 return consumePosition(m_range, m_context.mode, UnitlessQuirk::Forbid);
4029 case CSSPropertyWebkitLineClamp:
4030 return consumeLineClamp(m_range);
4031 case CSSPropertyWebkitFontSizeDelta:
4032 return consumeLength(m_range, m_context.mode, ValueRangeAll, UnitlessQuirk::Allow);
4033 case CSSPropertyWebkitHyphenateCharacter:
4034 case CSSPropertyWebkitLocale:
4035 return consumeAutoOrString(m_range);
4036 case CSSPropertyWebkitHyphenateLimitBefore:
4037 case CSSPropertyWebkitHyphenateLimitAfter:
4038 return consumeHyphenateLimit(m_range, CSSValueAuto);
4039 case CSSPropertyWebkitHyphenateLimitLines:
4040 return consumeHyphenateLimit(m_range, CSSValueNoLimit);
4041 case CSSPropertyColumnWidth:
4042 return consumeColumnWidth(m_range);
4043 case CSSPropertyColumnCount:
4044 return consumeColumnCount(m_range);
4045 case CSSPropertyColumnGap:
4046 return consumeGapLength(m_range, m_context.mode);
4047 case CSSPropertyRowGap:
4048 return consumeGapLength(m_range, m_context.mode);
4049 case CSSPropertyColumnSpan:
4050 return consumeColumnSpan(m_range);
4051 case CSSPropertyZoom:
4052 return consumeZoom(m_range, m_context);
4053 case CSSPropertyAnimationDelay:
4054 case CSSPropertyTransitionDelay:
4055 case CSSPropertyAnimationDirection:
4056 case CSSPropertyAnimationDuration:
4057 case CSSPropertyTransitionDuration:
4058 case CSSPropertyAnimationFillMode:
4059 case CSSPropertyAnimationIterationCount:
4060 case CSSPropertyAnimationName:
4061 case CSSPropertyAnimationPlayState:
4062 case CSSPropertyTransitionProperty:
4063 case CSSPropertyAnimationTimingFunction:
4064 case CSSPropertyTransitionTimingFunction:
4065 return consumeAnimationPropertyList(property, m_range, m_context);
4066 case CSSPropertyShapeMargin:
4067 return consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative);
4068 case CSSPropertyShapeImageThreshold:
4069 return consumeNumber(m_range, ValueRangeAll);
4070 case CSSPropertyWebkitBoxOrdinalGroup:
4071 case CSSPropertyOrphans:
4072 case CSSPropertyWidows:
4073 return consumePositiveInteger(m_range);
4074 case CSSPropertyTextDecorationColor:
4075 return consumeColor(m_range, m_context.mode);
4076 case CSSPropertyTextDecorationSkip:
4077 return consumeTextDecorationSkip(m_range);
4078 case CSSPropertyWebkitTextStrokeWidth:
4079 return consumeTextStrokeWidth(m_range, m_context.mode);
4080 case CSSPropertyWebkitTextFillColor:
4081#if ENABLE(TOUCH_EVENTS)
4082 case CSSPropertyWebkitTapHighlightColor:
4083#endif
4084 case CSSPropertyWebkitTextEmphasisColor:
4085 case CSSPropertyBorderInlineStartColor:
4086 case CSSPropertyBorderInlineEndColor:
4087 case CSSPropertyBorderBlockStartColor:
4088 case CSSPropertyBorderBlockEndColor:
4089 case CSSPropertyWebkitTextStrokeColor:
4090 case CSSPropertyStrokeColor:
4091 case CSSPropertyStopColor:
4092 case CSSPropertyFloodColor:
4093 case CSSPropertyLightingColor:
4094 case CSSPropertyColumnRuleColor:
4095 return consumeColor(m_range, m_context.mode);
4096 case CSSPropertyCaretColor:
4097 return consumeCaretColor(m_range, m_context.mode);
4098 case CSSPropertyColor:
4099 case CSSPropertyBackgroundColor:
4100 return consumeColor(m_range, m_context.mode, inQuirksMode());
4101 case CSSPropertyBorderInlineStartWidth:
4102 case CSSPropertyBorderInlineEndWidth:
4103 case CSSPropertyBorderBlockStartWidth:
4104 case CSSPropertyBorderBlockEndWidth:
4105 return consumeBorderWidth(m_range, m_context.mode, UnitlessQuirk::Forbid);
4106 case CSSPropertyBorderBottomColor:
4107 case CSSPropertyBorderLeftColor:
4108 case CSSPropertyBorderRightColor:
4109 case CSSPropertyBorderTopColor: {
4110 bool allowQuirkyColors = inQuirksMode()
4111 && (currentShorthand == CSSPropertyInvalid || currentShorthand == CSSPropertyBorderColor);
4112 return consumeColor(m_range, m_context.mode, allowQuirkyColors);
4113 }
4114 case CSSPropertyBorderBottomWidth:
4115 case CSSPropertyBorderLeftWidth:
4116 case CSSPropertyBorderRightWidth:
4117 case CSSPropertyBorderTopWidth: {
4118 bool allowQuirkyLengths = inQuirksMode()
4119 && (currentShorthand == CSSPropertyInvalid || currentShorthand == CSSPropertyBorderWidth);
4120 UnitlessQuirk unitless = allowQuirkyLengths ? UnitlessQuirk::Allow : UnitlessQuirk::Forbid;
4121 return consumeBorderWidth(m_range, m_context.mode, unitless);
4122 }
4123 case CSSPropertyZIndex:
4124 return consumeZIndex(m_range);
4125 case CSSPropertyTextShadow: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3
4126 case CSSPropertyBoxShadow:
4127 case CSSPropertyWebkitBoxShadow:
4128 return consumeShadow(m_range, m_context.mode, property == CSSPropertyBoxShadow || property == CSSPropertyWebkitBoxShadow);
4129 case CSSPropertyFilter:
4130#if ENABLE(FILTERS_LEVEL_2)
4131 case CSSPropertyWebkitBackdropFilter:
4132#endif
4133 return consumeFilter(m_range, m_context, AllowedFilterFunctions::PixelFilters);
4134 case CSSPropertyAppleColorFilter:
4135 if (!m_context.colorFilterEnabled)
4136 return nullptr;
4137 return consumeFilter(m_range, m_context, AllowedFilterFunctions::ColorFilters);
4138 case CSSPropertyTextDecoration:
4139 case CSSPropertyWebkitTextDecorationsInEffect:
4140 case CSSPropertyTextDecorationLine:
4141 return consumeTextDecorationLine(m_range);
4142 case CSSPropertyWebkitTextEmphasisStyle:
4143 return consumeTextEmphasisStyle(m_range);
4144 case CSSPropertyOutlineColor:
4145 return consumeOutlineColor(m_range, m_context.mode);
4146 case CSSPropertyOutlineOffset:
4147 return consumeLength(m_range, m_context.mode, ValueRangeAll);
4148 case CSSPropertyOutlineWidth:
4149 return consumeLineWidth(m_range, m_context.mode, UnitlessQuirk::Forbid);
4150 case CSSPropertyTransform:
4151 return consumeTransform(m_range, m_context.mode);
4152 case CSSPropertyTransformBox:
4153 return consumeIdent<CSSValueBorderBox, CSSValueViewBox, CSSValueFillBox>(m_range);
4154 case CSSPropertyTransformOriginX:
4155 case CSSPropertyPerspectiveOriginX:
4156 return consumePositionX(m_range, m_context.mode);
4157 case CSSPropertyTransformOriginY:
4158 case CSSPropertyPerspectiveOriginY:
4159 return consumePositionY(m_range, m_context.mode);
4160 case CSSPropertyTransformOriginZ:
4161 return consumeLength(m_range, m_context.mode, ValueRangeAll);
4162 case CSSPropertyFill:
4163 case CSSPropertyStroke:
4164 return consumePaintStroke(m_range, m_context.mode);
4165 case CSSPropertyGlyphOrientationVertical:
4166 case CSSPropertyGlyphOrientationHorizontal:
4167 return consumeGlyphOrientation(m_range, m_context.mode, property);
4168 case CSSPropertyPaintOrder:
4169 return consumePaintOrder(m_range);
4170 case CSSPropertyMarkerStart:
4171 case CSSPropertyMarkerMid:
4172 case CSSPropertyMarkerEnd:
4173 case CSSPropertyClipPath:
4174 case CSSPropertyMask:
4175 return consumeNoneOrURI(m_range);
4176 case CSSPropertyFlexBasis:
4177 return consumeFlexBasis(m_range, m_context.mode);
4178 case CSSPropertyFlexGrow:
4179 case CSSPropertyFlexShrink:
4180 return consumeNumber(m_range, ValueRangeNonNegative);
4181 case CSSPropertyStrokeDasharray:
4182 return consumeStrokeDasharray(m_range);
4183 case CSSPropertyColumnRuleWidth:
4184 return consumeColumnRuleWidth(m_range, m_context.mode);
4185 case CSSPropertyStrokeOpacity:
4186 case CSSPropertyFillOpacity:
4187 case CSSPropertyStopOpacity:
4188 case CSSPropertyFloodOpacity:
4189 case CSSPropertyOpacity:
4190 case CSSPropertyWebkitBoxFlex:
4191 return consumeNumber(m_range, ValueRangeAll);
4192 case CSSPropertyBaselineShift:
4193 return consumeBaselineShift(m_range);
4194 case CSSPropertyKerning:
4195 return consumeKerning(m_range, m_context.mode);
4196 case CSSPropertyStrokeMiterlimit:
4197 return consumeNumber(m_range, ValueRangeNonNegative);
4198 case CSSPropertyStrokeWidth:
4199 case CSSPropertyStrokeDashoffset:
4200 case CSSPropertyCx:
4201 case CSSPropertyCy:
4202 case CSSPropertyX:
4203 case CSSPropertyY:
4204 case CSSPropertyR:
4205 return consumeLengthOrPercent(m_range, SVGAttributeMode, ValueRangeAll, UnitlessQuirk::Forbid);
4206 case CSSPropertyRx:
4207 case CSSPropertyRy:
4208 return consumeRxOrRy(m_range);
4209 case CSSPropertyCursor:
4210 return consumeCursor(m_range, m_context, inQuirksMode());
4211 case CSSPropertyContent:
4212 return consumeContent(m_range, m_context);
4213 case CSSPropertyListStyleImage:
4214 case CSSPropertyBorderImageSource:
4215 case CSSPropertyWebkitMaskBoxImageSource:
4216 return consumeImageOrNone(m_range, m_context);
4217 case CSSPropertyPerspective:
4218 return consumePerspective(m_range, m_context.mode);
4219 case CSSPropertyBorderTopRightRadius:
4220 case CSSPropertyBorderTopLeftRadius:
4221 case CSSPropertyBorderBottomLeftRadius:
4222 case CSSPropertyBorderBottomRightRadius:
4223 return consumeBorderRadiusCorner(m_range, m_context.mode);
4224 case CSSPropertyWebkitBoxFlexGroup:
4225 return consumeInteger(m_range, 0);
4226 case CSSPropertyOrder:
4227 return consumeInteger(m_range);
4228 case CSSPropertyTextUnderlinePosition:
4229 // auto | [ [ under | from-font ] || [ left | right ] ], but we only support auto | under | from-font for now
4230 return consumeIdent<CSSValueAuto, CSSValueUnder, CSSValueFromFont>(m_range);
4231 case CSSPropertyTextUnderlineOffset:
4232 return consumeTextUnderlineOffset(m_range, m_context.mode);
4233 case CSSPropertyTextDecorationThickness:
4234 return consumeTextDecorationThickness(m_range, m_context.mode);
4235 case CSSPropertyVerticalAlign:
4236 return consumeVerticalAlign(m_range, m_context.mode);
4237 case CSSPropertyShapeOutside:
4238 return consumeShapeOutside(m_range, m_context);
4239 case CSSPropertyWebkitClipPath:
4240 return consumeWebkitClipPath(m_range, m_context);
4241 case CSSPropertyJustifyContent:
4242 // justify-content property does not allow the <baseline-position> values.
4243 if (isBaselineKeyword(m_range.peek().id()))
4244 return nullptr;
4245 return consumeContentDistributionOverflowPosition(m_range, isContentPositionOrLeftOrRightKeyword);
4246 case CSSPropertyAlignContent:
4247 return consumeContentDistributionOverflowPosition(m_range, isContentPositionKeyword);
4248 case CSSPropertyBorderImageRepeat:
4249 case CSSPropertyWebkitMaskBoxImageRepeat:
4250 return consumeBorderImageRepeat(m_range);
4251 case CSSPropertyBorderImageSlice:
4252 case CSSPropertyWebkitMaskBoxImageSlice:
4253 return consumeBorderImageSlice(property, m_range);
4254 case CSSPropertyBorderImageOutset:
4255 case CSSPropertyWebkitMaskBoxImageOutset:
4256 return consumeBorderImageOutset(m_range);
4257 case CSSPropertyBorderImageWidth:
4258 case CSSPropertyWebkitMaskBoxImageWidth:
4259 return consumeBorderImageWidth(m_range);
4260 case CSSPropertyWebkitBorderImage:
4261 case CSSPropertyWebkitMaskBoxImage:
4262 return consumeWebkitBorderImage(property, m_range, m_context);
4263 case CSSPropertyWebkitBoxReflect:
4264 return consumeReflect(m_range, m_context);
4265 case CSSPropertyWebkitLineBoxContain:
4266 return consumeLineBoxContain(m_range);
4267#if ENABLE(CSS_IMAGE_ORIENTATION)
4268 case CSSPropertyImageOrientation:
4269 return consumeImageOrientation(m_range, m_context.mode);
4270#endif
4271 case CSSPropertyBackgroundAttachment:
4272 case CSSPropertyBackgroundBlendMode:
4273 case CSSPropertyBackgroundClip:
4274 case CSSPropertyBackgroundImage:
4275 case CSSPropertyBackgroundOrigin:
4276 case CSSPropertyBackgroundPositionX:
4277 case CSSPropertyBackgroundPositionY:
4278 case CSSPropertyBackgroundSize:
4279 case CSSPropertyWebkitBackgroundClip:
4280 case CSSPropertyWebkitBackgroundOrigin:
4281 case CSSPropertyWebkitBackgroundComposite:
4282 case CSSPropertyWebkitBackgroundSize:
4283 case CSSPropertyWebkitMaskClip:
4284 case CSSPropertyWebkitMaskComposite:
4285 case CSSPropertyWebkitMaskImage:
4286 case CSSPropertyWebkitMaskOrigin:
4287 case CSSPropertyWebkitMaskPositionX:
4288 case CSSPropertyWebkitMaskPositionY:
4289 case CSSPropertyWebkitMaskSize:
4290 case CSSPropertyWebkitMaskSourceType:
4291 return consumeCommaSeparatedBackgroundComponent(property, m_range, m_context);
4292 case CSSPropertyWebkitMaskRepeatX:
4293 case CSSPropertyWebkitMaskRepeatY:
4294 return nullptr;
4295 case CSSPropertyAlignItems:
4296 return consumeAlignItems(m_range);
4297 case CSSPropertyJustifySelf:
4298 return consumeSelfPositionOverflowPosition(m_range, isSelfPositionOrLeftOrRightKeyword);
4299 case CSSPropertyAlignSelf:
4300 return consumeSelfPositionOverflowPosition(m_range, isSelfPositionKeyword);
4301 case CSSPropertyJustifyItems:
4302 return consumeJustifyItems(m_range);
4303 case CSSPropertyGridColumnEnd:
4304 case CSSPropertyGridColumnStart:
4305 case CSSPropertyGridRowEnd:
4306 case CSSPropertyGridRowStart:
4307 return consumeGridLine(m_range);
4308 case CSSPropertyGridAutoColumns:
4309 case CSSPropertyGridAutoRows:
4310 return consumeGridTrackList(m_range, m_context.mode, GridAuto);
4311 case CSSPropertyGridTemplateColumns:
4312 case CSSPropertyGridTemplateRows:
4313 return consumeGridTemplatesRowsOrColumns(m_range, m_context.mode);
4314 case CSSPropertyGridTemplateAreas:
4315 return consumeGridTemplateAreas(m_range);
4316 case CSSPropertyGridAutoFlow:
4317 return consumeGridAutoFlow(m_range);
4318 case CSSPropertyWebkitLineGrid:
4319 return consumeLineGrid(m_range);
4320 case CSSPropertyWebkitInitialLetter:
4321 return consumeInitialLetter(m_range);
4322 case CSSPropertySpeakAs:
4323 return consumeSpeakAs(m_range);
4324 case CSSPropertyHangingPunctuation:
4325 return consumeHangingPunctuation(m_range);
4326 case CSSPropertyWebkitMarqueeIncrement:
4327 return consumeWebkitMarqueeIncrement(m_range, m_context.mode);
4328 case CSSPropertyWebkitMarqueeRepetition:
4329 return consumeWebkitMarqueeRepetition(m_range);
4330 case CSSPropertyWebkitMarqueeSpeed:
4331 return consumeWebkitMarqueeSpeed(m_range, m_context.mode);
4332 case CSSPropertyAlt:
4333 return consumeAlt(m_range, m_context);
4334 case CSSPropertyWebkitAspectRatio:
4335 return consumeWebkitAspectRatio(m_range);
4336 case CSSPropertyWebkitTextEmphasisPosition:
4337 return consumeTextEmphasisPosition(m_range);
4338#if ENABLE(DARK_MODE_CSS)
4339 case CSSPropertyColorScheme:
4340 if (!RuntimeEnabledFeatures::sharedFeatures().darkModeCSSEnabled())
4341 return nullptr;
4342 return consumeColorScheme(m_range);
4343#endif
4344#if ENABLE(DASHBOARD_SUPPORT)
4345 case CSSPropertyWebkitDashboardRegion:
4346 return consumeWebkitDashboardRegion(m_range, m_context.mode);
4347#endif
4348 default:
4349 return nullptr;
4350 }
4351}
4352
4353bool CSSPropertyParser::canParseTypedCustomPropertyValue(const String& syntax)
4354{
4355 if (syntax != "*") {
4356 m_range.consumeWhitespace();
4357
4358 // First check for keywords
4359 CSSValueID id = m_range.peek().id();
4360 if (id == CSSValueInherit || id == CSSValueInitial || id == CSSValueRevert)
4361 return true;
4362
4363 auto localRange = m_range;
4364 while (!localRange.atEnd()) {
4365 auto id = localRange.consume().functionId();
4366 if (id == CSSValueVar || id == CSSValueEnv)
4367 return true; // For variables, we just permit everything
4368 }
4369
4370 auto primitiveVal = consumeWidthOrHeight(m_range, m_context);
4371 if (primitiveVal && primitiveVal->isPrimitiveValue() && m_range.atEnd())
4372 return true;
4373 return false;
4374 }
4375
4376 return true;
4377}
4378
4379void CSSPropertyParser::collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies)
4380{
4381 if (syntax != "*") {
4382 m_range.consumeWhitespace();
4383 auto primitiveVal = consumeWidthOrHeight(m_range, m_context);
4384 if (!m_range.atEnd())
4385 return;
4386 if (primitiveVal && primitiveVal->isPrimitiveValue()) {
4387 primitiveVal->collectDirectComputationalDependencies(dependencies);
4388 if (isRoot)
4389 primitiveVal->collectDirectRootComputationalDependencies(dependencies);
4390 }
4391 }
4392}
4393
4394RefPtr<CSSCustomPropertyValue> CSSPropertyParser::parseTypedCustomPropertyValue(const String& name, const String& syntax, const StyleResolver& styleResolver)
4395{
4396 if (syntax != "*") {
4397 m_range.consumeWhitespace();
4398 auto primitiveVal = consumeWidthOrHeight(m_range, m_context);
4399 if (primitiveVal && primitiveVal->isPrimitiveValue() && downcast<CSSPrimitiveValue>(*primitiveVal).isLength()) {
4400 auto length = StyleBuilderConverter::convertLength(styleResolver, *primitiveVal);
4401 if (!length.isCalculated() && !length.isUndefined())
4402 return CSSCustomPropertyValue::createSyntaxLength(name, WTFMove(length));
4403 }
4404 } else {
4405 auto propertyValue = CSSCustomPropertyValue::createSyntaxAll(name, CSSVariableData::create(m_range));
4406 while (!m_range.atEnd())
4407 m_range.consume();
4408 return { WTFMove(propertyValue) };
4409 }
4410
4411 return nullptr;
4412}
4413
4414static RefPtr<CSSValueList> consumeFontFaceUnicodeRange(CSSParserTokenRange& range)
4415{
4416 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
4417
4418 do {
4419 const CSSParserToken& token = range.consumeIncludingWhitespace();
4420 if (token.type() != UnicodeRangeToken)
4421 return nullptr;
4422
4423 UChar32 start = token.unicodeRangeStart();
4424 UChar32 end = token.unicodeRangeEnd();
4425 if (start > end)
4426 return nullptr;
4427 values->append(CSSUnicodeRangeValue::create(start, end));
4428 } while (consumeCommaIncludingWhitespace(range));
4429
4430 return values;
4431}
4432
4433static RefPtr<CSSPrimitiveValue> consumeFontFaceFontDisplay(CSSParserTokenRange& range)
4434{
4435 return consumeIdent<CSSValueAuto, CSSValueBlock, CSSValueSwap, CSSValueFallback, CSSValueOptional>(range);
4436}
4437
4438static RefPtr<CSSValue> consumeFontFaceSrcURI(CSSParserTokenRange& range, const CSSParserContext& context)
4439{
4440 String url = consumeUrlAsStringView(range).toString();
4441 if (url.isNull())
4442 return nullptr;
4443
4444 RefPtr<CSSFontFaceSrcValue> uriValue = CSSFontFaceSrcValue::create(context.completeURL(url), context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No);
4445
4446 if (range.peek().functionId() != CSSValueFormat)
4447 return uriValue;
4448
4449 // FIXME: https://drafts.csswg.org/css-fonts says that format() contains a comma-separated list of strings,
4450 // but CSSFontFaceSrcValue stores only one format. Allowing one format for now.
4451 // FIXME: We're allowing the format to be an identifier as well as a string, because the old
4452 // parser did. It's not clear if we need to continue to support this behavior, but we have lots of
4453 // layout tests that rely on it.
4454 CSSParserTokenRange args = consumeFunction(range);
4455 const CSSParserToken& arg = args.consumeIncludingWhitespace();
4456 if ((arg.type() != StringToken && arg.type() != IdentToken) || !args.atEnd())
4457 return nullptr;
4458 uriValue->setFormat(arg.value().toString());
4459 return uriValue;
4460}
4461
4462static RefPtr<CSSValue> consumeFontFaceSrcLocal(CSSParserTokenRange& range)
4463{
4464 CSSParserTokenRange args = consumeFunction(range);
4465 if (args.peek().type() == StringToken) {
4466 const CSSParserToken& arg = args.consumeIncludingWhitespace();
4467 if (!args.atEnd())
4468 return nullptr;
4469 return CSSFontFaceSrcValue::createLocal(arg.value().toString());
4470 }
4471 if (args.peek().type() == IdentToken) {
4472 String familyName = concatenateFamilyName(args);
4473 if (!args.atEnd())
4474 return nullptr;
4475 return CSSFontFaceSrcValue::createLocal(familyName);
4476 }
4477 return nullptr;
4478}
4479
4480static RefPtr<CSSValueList> consumeFontFaceSrc(CSSParserTokenRange& range, const CSSParserContext& context)
4481{
4482 RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
4483
4484 do {
4485 const CSSParserToken& token = range.peek();
4486 RefPtr<CSSValue> parsedValue;
4487 if (token.functionId() == CSSValueLocal)
4488 parsedValue = consumeFontFaceSrcLocal(range);
4489 else
4490 parsedValue = consumeFontFaceSrcURI(range, context);
4491 if (!parsedValue)
4492 return nullptr;
4493 values->append(parsedValue.releaseNonNull());
4494 } while (consumeCommaIncludingWhitespace(range));
4495 return values;
4496}
4497
4498bool CSSPropertyParser::parseFontFaceDescriptor(CSSPropertyID propId)
4499{
4500 RefPtr<CSSValue> parsedValue;
4501 switch (propId) {
4502 case CSSPropertyFontFamily:
4503 parsedValue = consumeFontFamilyDescriptor(m_range);
4504 break;
4505 case CSSPropertySrc: // This is a list of urls or local references.
4506 parsedValue = consumeFontFaceSrc(m_range, m_context);
4507 break;
4508 case CSSPropertyUnicodeRange:
4509 parsedValue = consumeFontFaceUnicodeRange(m_range);
4510 break;
4511 case CSSPropertyFontDisplay:
4512 parsedValue = consumeFontFaceFontDisplay(m_range);
4513 break;
4514 case CSSPropertyFontWeight:
4515#if ENABLE(VARIATION_FONTS)
4516 parsedValue = consumeFontWeightRange(m_range);
4517#else
4518 parsedValue = consumeFontWeight(m_range);
4519#endif
4520 break;
4521 case CSSPropertyFontStretch:
4522#if ENABLE(VARIATION_FONTS)
4523 parsedValue = consumeFontStretchRange(m_range);
4524#else
4525 parsedValue = consumeFontStretch(m_range);
4526#endif
4527 break;
4528 case CSSPropertyFontStyle:
4529#if ENABLE(VARIATION_FONTS)
4530 parsedValue = consumeFontStyleRange(m_range, m_context.mode);
4531#else
4532 parsedValue = consumeFontStyle(m_range, m_context.mode);
4533#endif
4534 break;
4535 case CSSPropertyFontVariantCaps:
4536 parsedValue = consumeFontVariantCaps(m_range);
4537 break;
4538 case CSSPropertyFontVariantLigatures:
4539 parsedValue = consumeFontVariantLigatures(m_range);
4540 break;
4541 case CSSPropertyFontVariantNumeric:
4542 parsedValue = consumeFontVariantNumeric(m_range);
4543 break;
4544 case CSSPropertyFontVariantEastAsian:
4545 parsedValue = consumeFontVariantEastAsian(m_range);
4546 break;
4547 case CSSPropertyFontVariantAlternates:
4548 parsedValue = consumeFontVariantAlternates(m_range);
4549 break;
4550 case CSSPropertyFontVariantPosition:
4551 parsedValue = consumeFontVariantPosition(m_range);
4552 break;
4553 case CSSPropertyFontVariant:
4554 return consumeFontVariantShorthand(false);
4555 case CSSPropertyFontFeatureSettings:
4556 parsedValue = consumeFontFeatureSettings(m_range);
4557 break;
4558 default:
4559 break;
4560 }
4561
4562 if (!parsedValue || !m_range.atEnd())
4563 return false;
4564
4565 addProperty(propId, CSSPropertyInvalid, *parsedValue, false);
4566 return true;
4567}
4568
4569bool CSSPropertyParser::consumeSystemFont(bool important)
4570{
4571 CSSValueID systemFontID = m_range.consumeIncludingWhitespace().id();
4572 ASSERT(systemFontID >= CSSValueCaption && systemFontID <= CSSValueStatusBar);
4573 if (!m_range.atEnd())
4574 return false;
4575
4576 FontCascadeDescription fontDescription;
4577 RenderTheme::singleton().systemFont(systemFontID, fontDescription);
4578 if (!fontDescription.isAbsoluteSize())
4579 return false;
4580
4581 addProperty(CSSPropertyFontStyle, CSSPropertyFont, CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(isItalic(fontDescription.italic()) ? CSSValueItalic : CSSValueNormal)), important);
4582 addProperty(CSSPropertyFontWeight, CSSPropertyFont, CSSValuePool::singleton().createValue(static_cast<float>(fontDescription.weight())), important);
4583 addProperty(CSSPropertyFontSize, CSSPropertyFont, CSSValuePool::singleton().createValue(fontDescription.specifiedSize(), CSSPrimitiveValue::CSS_PX), important);
4584 Ref<CSSValueList> fontFamilyList = CSSValueList::createCommaSeparated();
4585 fontFamilyList->append(CSSValuePool::singleton().createFontFamilyValue(fontDescription.familyAt(0), FromSystemFontID::Yes));
4586 addProperty(CSSPropertyFontFamily, CSSPropertyFont, WTFMove(fontFamilyList), important);
4587 addProperty(CSSPropertyFontVariantCaps, CSSPropertyFont, CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important);
4588 addProperty(CSSPropertyLineHeight, CSSPropertyFont, CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important);
4589
4590 // FIXME_NEWPARSER: What about FontVariantNumeric and FontVariantLigatures?
4591
4592 return true;
4593}
4594
4595bool CSSPropertyParser::consumeFont(bool important)
4596{
4597 // Let's check if there is an inherit or initial somewhere in the shorthand.
4598 CSSParserTokenRange range = m_range;
4599 while (!range.atEnd()) {
4600 CSSValueID id = range.consumeIncludingWhitespace().id();
4601 if (id == CSSValueInherit || id == CSSValueInitial)
4602 return false;
4603 }
4604 // Optional font-style, font-variant, font-stretch and font-weight.
4605 RefPtr<CSSFontStyleValue> fontStyle;
4606 RefPtr<CSSPrimitiveValue> fontVariantCaps;
4607 RefPtr<CSSPrimitiveValue> fontWeight;
4608 RefPtr<CSSPrimitiveValue> fontStretch;
4609
4610 while (!m_range.atEnd()) {
4611 CSSValueID id = m_range.peek().id();
4612 if (!fontStyle) {
4613 fontStyle = consumeFontStyle(m_range, m_context.mode);
4614 if (fontStyle)
4615 continue;
4616 }
4617 if (!fontVariantCaps && (id == CSSValueNormal || id == CSSValueSmallCaps)) {
4618 // Font variant in the shorthand is particular, it only accepts normal or small-caps.
4619 // See https://drafts.csswg.org/css-fonts/#propdef-font
4620 fontVariantCaps = consumeFontVariantCSS21(m_range);
4621 if (fontVariantCaps)
4622 continue;
4623 }
4624 if (!fontWeight) {
4625 fontWeight = consumeFontWeight(m_range);
4626 if (fontWeight)
4627 continue;
4628 }
4629 if (!fontStretch) {
4630 fontStretch = consumeFontStretchKeywordValue(m_range);
4631 if (fontStretch)
4632 continue;
4633 }
4634 break;
4635 }
4636
4637 if (m_range.atEnd())
4638 return false;
4639
4640 bool hasStyle = fontStyle;
4641 bool hasVariant = fontVariantCaps;
4642 bool hasWeight = fontWeight;
4643 bool hasStretch = fontStretch;
4644
4645 if (!fontStyle)
4646 fontStyle = CSSFontStyleValue::create(CSSValuePool::singleton().createIdentifierValue(CSSValueNormal));
4647
4648 addProperty(CSSPropertyFontStyle, CSSPropertyFont, fontStyle.releaseNonNull(), important, !hasStyle);
4649 addProperty(CSSPropertyFontVariantCaps, CSSPropertyFont, fontVariantCaps ? fontVariantCaps.releaseNonNull() : CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important, !hasVariant);
4650/*
4651 // FIXME-NEWPARSER: What do we do with these? They aren't part of our fontShorthand().
4652 addProperty(CSSPropertyFontVariantLigatures, CSSPropertyFont, CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important, true);
4653 addProperty(CSSPropertyFontVariantNumeric, CSSPropertyFont, CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important, true);
4654*/
4655
4656 addProperty(CSSPropertyFontWeight, CSSPropertyFont, fontWeight ? fontWeight.releaseNonNull() : CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important, !hasWeight);
4657 addProperty(CSSPropertyFontStretch, CSSPropertyFont, fontStretch ? fontStretch.releaseNonNull() : CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important, !hasStretch);
4658
4659 // Now a font size _must_ come.
4660 RefPtr<CSSValue> fontSize = consumeFontSize(m_range, m_context.mode);
4661 if (!fontSize || m_range.atEnd())
4662 return false;
4663
4664 addProperty(CSSPropertyFontSize, CSSPropertyFont, *fontSize, important);
4665
4666 if (consumeSlashIncludingWhitespace(m_range)) {
4667 RefPtr<CSSPrimitiveValue> lineHeight = consumeLineHeight(m_range, m_context.mode);
4668 if (!lineHeight)
4669 return false;
4670 addProperty(CSSPropertyLineHeight, CSSPropertyFont, lineHeight.releaseNonNull(), important);
4671 } else
4672 addProperty(CSSPropertyLineHeight, CSSPropertyFont, CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important, true);
4673
4674 // Font family must come now.
4675 RefPtr<CSSValue> parsedFamilyValue = consumeFontFamily(m_range);
4676 if (!parsedFamilyValue)
4677 return false;
4678
4679 addProperty(CSSPropertyFontFamily, CSSPropertyFont, parsedFamilyValue.releaseNonNull(), important);
4680
4681 return m_range.atEnd();
4682}
4683
4684bool CSSPropertyParser::consumeFontVariantShorthand(bool important)
4685{
4686 if (identMatches<CSSValueNormal, CSSValueNone>(m_range.peek().id())) {
4687 addProperty(CSSPropertyFontVariantLigatures, CSSPropertyFontVariant, consumeIdent(m_range).releaseNonNull(), important);
4688 addProperty(CSSPropertyFontVariantCaps, CSSPropertyFontVariant, CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important);
4689 addProperty(CSSPropertyFontVariantEastAsian, CSSPropertyFontVariant, CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important);
4690 addProperty(CSSPropertyFontVariantPosition, CSSPropertyFontVariant, CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important);
4691 return m_range.atEnd();
4692 }
4693
4694 RefPtr<CSSPrimitiveValue> capsValue;
4695 RefPtr<CSSPrimitiveValue> alternatesValue;
4696 RefPtr<CSSPrimitiveValue> positionValue;
4697
4698 RefPtr<CSSValue> eastAsianValue;
4699 FontVariantLigaturesParser ligaturesParser;
4700 FontVariantNumericParser numericParser;
4701 do {
4702 if (!capsValue) {
4703 capsValue = consumeFontVariantCaps(m_range);
4704 if (capsValue)
4705 continue;
4706 }
4707
4708 if (!positionValue) {
4709 positionValue = consumeFontVariantPosition(m_range);
4710 if (positionValue)
4711 continue;
4712 }
4713
4714 if (!alternatesValue) {
4715 alternatesValue = consumeFontVariantAlternates(m_range);
4716 if (alternatesValue)
4717 continue;
4718 }
4719
4720 FontVariantLigaturesParser::ParseResult ligaturesParseResult = ligaturesParser.consumeLigature(m_range);
4721 FontVariantNumericParser::ParseResult numericParseResult = numericParser.consumeNumeric(m_range);
4722 if (ligaturesParseResult == FontVariantLigaturesParser::ParseResult::ConsumedValue
4723 || numericParseResult == FontVariantNumericParser::ParseResult::ConsumedValue)
4724 continue;
4725
4726 if (ligaturesParseResult == FontVariantLigaturesParser::ParseResult::DisallowedValue
4727 || numericParseResult == FontVariantNumericParser::ParseResult::DisallowedValue)
4728 return false;
4729
4730 if (!eastAsianValue) {
4731 eastAsianValue = consumeFontVariantEastAsian(m_range);
4732 if (eastAsianValue)
4733 continue;
4734 }
4735
4736 // Saw some value that didn't match anything else.
4737 return false;
4738
4739 } while (!m_range.atEnd());
4740
4741 addProperty(CSSPropertyFontVariantLigatures, CSSPropertyFontVariant, ligaturesParser.finalizeValue().releaseNonNull(), important);
4742 addProperty(CSSPropertyFontVariantNumeric, CSSPropertyFontVariant, numericParser.finalizeValue().releaseNonNull(), important);
4743 addProperty(CSSPropertyFontVariantCaps, CSSPropertyFontVariant, capsValue ? capsValue.releaseNonNull() : CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important);
4744 addProperty(CSSPropertyFontVariantAlternates, CSSPropertyFontVariant, alternatesValue ? alternatesValue.releaseNonNull() : CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important);
4745 addProperty(CSSPropertyFontVariantPosition, CSSPropertyFontVariant, positionValue ? positionValue.releaseNonNull() : CSSValuePool::singleton().createIdentifierValue(CSSValueNormal), important);
4746
4747 if (!eastAsianValue)
4748 eastAsianValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
4749 addProperty(CSSPropertyFontVariantEastAsian, CSSPropertyFontVariant, eastAsianValue.releaseNonNull(), important);
4750
4751 return true;
4752}
4753
4754bool CSSPropertyParser::consumeBorderSpacing(bool important)
4755{
4756 RefPtr<CSSValue> horizontalSpacing = consumeLength(m_range, m_context.mode, ValueRangeNonNegative, UnitlessQuirk::Allow);
4757 if (!horizontalSpacing)
4758 return false;
4759 RefPtr<CSSValue> verticalSpacing = horizontalSpacing;
4760 if (!m_range.atEnd())
4761 verticalSpacing = consumeLength(m_range, m_context.mode, ValueRangeNonNegative, UnitlessQuirk::Allow);
4762 if (!verticalSpacing || !m_range.atEnd())
4763 return false;
4764 addProperty(CSSPropertyWebkitBorderHorizontalSpacing, CSSPropertyBorderSpacing, horizontalSpacing.releaseNonNull(), important);
4765 addProperty(CSSPropertyWebkitBorderVerticalSpacing, CSSPropertyBorderSpacing, verticalSpacing.releaseNonNull(), important);
4766 return true;
4767}
4768
4769#if ENABLE(CSS_DEVICE_ADAPTATION)
4770
4771static RefPtr<CSSValue> consumeSingleViewportDescriptor(CSSParserTokenRange& range, CSSPropertyID propId, CSSParserMode cssParserMode)
4772{
4773 CSSValueID id = range.peek().id();
4774 switch (propId) {
4775 case CSSPropertyMinWidth:
4776 case CSSPropertyMaxWidth:
4777 case CSSPropertyMinHeight:
4778 case CSSPropertyMaxHeight:
4779 if (id == CSSValueAuto)
4780 return consumeIdent(range);
4781 return consumeLengthOrPercent(range, cssParserMode, ValueRangeNonNegative);
4782 case CSSPropertyMinZoom:
4783 case CSSPropertyMaxZoom:
4784 case CSSPropertyZoom: {
4785 if (id == CSSValueAuto)
4786 return consumeIdent(range);
4787 RefPtr<CSSValue> parsedValue = consumeNumber(range, ValueRangeNonNegative);
4788 if (parsedValue)
4789 return parsedValue;
4790 return consumePercent(range, ValueRangeNonNegative);
4791 }
4792 case CSSPropertyUserZoom:
4793 return consumeIdent<CSSValueZoom, CSSValueFixed>(range);
4794 case CSSPropertyOrientation:
4795 return consumeIdent<CSSValueAuto, CSSValuePortrait, CSSValueLandscape>(range);
4796 default:
4797 ASSERT_NOT_REACHED();
4798 break;
4799 }
4800
4801 ASSERT_NOT_REACHED();
4802 return nullptr;
4803}
4804
4805bool CSSPropertyParser::parseViewportDescriptor(CSSPropertyID propId, bool important)
4806{
4807 switch (propId) {
4808 case CSSPropertyWidth: {
4809 RefPtr<CSSValue> minWidth = consumeSingleViewportDescriptor(m_range, CSSPropertyMinWidth, m_context.mode);
4810 if (!minWidth)
4811 return false;
4812 RefPtr<CSSValue> maxWidth = minWidth;
4813 if (!m_range.atEnd())
4814 maxWidth = consumeSingleViewportDescriptor(m_range, CSSPropertyMaxWidth, m_context.mode);
4815 if (!maxWidth || !m_range.atEnd())
4816 return false;
4817 addProperty(CSSPropertyMinWidth, CSSPropertyInvalid, *minWidth, important);
4818 addProperty(CSSPropertyMaxWidth, CSSPropertyInvalid, *maxWidth, important);
4819 return true;
4820 }
4821 case CSSPropertyHeight: {
4822 RefPtr<CSSValue> minHeight = consumeSingleViewportDescriptor(m_range, CSSPropertyMinHeight, m_context.mode);
4823 if (!minHeight)
4824 return false;
4825 RefPtr<CSSValue> maxHeight = minHeight;
4826 if (!m_range.atEnd())
4827 maxHeight = consumeSingleViewportDescriptor(m_range, CSSPropertyMaxHeight, m_context.mode);
4828 if (!maxHeight || !m_range.atEnd())
4829 return false;
4830 addProperty(CSSPropertyMinHeight, CSSPropertyInvalid, *minHeight, important);
4831 addProperty(CSSPropertyMaxHeight, CSSPropertyInvalid, *maxHeight, important);
4832 return true;
4833 }
4834 case CSSPropertyMinWidth:
4835 case CSSPropertyMaxWidth:
4836 case CSSPropertyMinHeight:
4837 case CSSPropertyMaxHeight:
4838 case CSSPropertyMinZoom:
4839 case CSSPropertyMaxZoom:
4840 case CSSPropertyZoom:
4841 case CSSPropertyUserZoom:
4842 case CSSPropertyOrientation: {
4843 RefPtr<CSSValue> parsedValue = consumeSingleViewportDescriptor(m_range, propId, m_context.mode);
4844 if (!parsedValue || !m_range.atEnd())
4845 return false;
4846 addProperty(propId, CSSPropertyInvalid, parsedValue.releaseNonNull(), important);
4847 return true;
4848 }
4849 default:
4850 return false;
4851 }
4852}
4853
4854#endif
4855
4856bool CSSPropertyParser::consumeColumns(bool important)
4857{
4858 RefPtr<CSSValue> columnWidth;
4859 RefPtr<CSSValue> columnCount;
4860 bool hasPendingExplicitAuto = false;
4861
4862 for (unsigned propertiesParsed = 0; propertiesParsed < 2 && !m_range.atEnd(); ++propertiesParsed) {
4863 if (!propertiesParsed && m_range.peek().id() == CSSValueAuto) {
4864 // 'auto' is a valid value for any of the two longhands, and at this point
4865 // we don't know which one(s) it is meant for. We need to see if there are other values first.
4866 consumeIdent(m_range);
4867 hasPendingExplicitAuto = true;
4868 } else {
4869 if (!columnWidth) {
4870 if ((columnWidth = consumeColumnWidth(m_range)))
4871 continue;
4872 }
4873 if (!columnCount) {
4874 if ((columnCount = consumeColumnCount(m_range)))
4875 continue;
4876 }
4877 // If we didn't find at least one match, this is an invalid shorthand and we have to ignore it.
4878 return false;
4879 }
4880 }
4881
4882 if (!m_range.atEnd())
4883 return false;
4884
4885 // Any unassigned property at this point will become implicit 'auto'.
4886 if (columnWidth)
4887 addProperty(CSSPropertyColumnWidth, CSSPropertyInvalid, columnWidth.releaseNonNull(), important);
4888 else {
4889 addProperty(CSSPropertyColumnWidth, CSSPropertyInvalid, CSSValuePool::singleton().createIdentifierValue(CSSValueAuto), important, !hasPendingExplicitAuto /* implicit */);
4890 hasPendingExplicitAuto = false;
4891 }
4892
4893 if (columnCount)
4894 addProperty(CSSPropertyColumnCount, CSSPropertyInvalid, columnCount.releaseNonNull(), important);
4895 else
4896 addProperty(CSSPropertyColumnCount, CSSPropertyInvalid, CSSValuePool::singleton().createIdentifierValue(CSSValueAuto), important, !hasPendingExplicitAuto /* implicit */);
4897
4898 return true;
4899}
4900
4901bool CSSPropertyParser::consumeShorthandGreedily(const StylePropertyShorthand& shorthand, bool important)
4902{
4903 ASSERT(shorthand.length() <= 6); // Existing shorthands have at most 6 longhands.
4904 RefPtr<CSSValue> longhands[6];
4905 const CSSPropertyID* shorthandProperties = shorthand.properties();
4906 do {
4907 bool foundLonghand = false;
4908 for (size_t i = 0; !foundLonghand && i < shorthand.length(); ++i) {
4909 if (longhands[i])
4910 continue;
4911 longhands[i] = parseSingleValue(shorthandProperties[i], shorthand.id());
4912 if (longhands[i])
4913 foundLonghand = true;
4914 }
4915 if (!foundLonghand)
4916 return false;
4917 } while (!m_range.atEnd());
4918
4919 for (size_t i = 0; i < shorthand.length(); ++i) {
4920 if (longhands[i])
4921 addProperty(shorthandProperties[i], shorthand.id(), longhands[i].releaseNonNull(), important);
4922 else
4923 addProperty(shorthandProperties[i], shorthand.id(), CSSValuePool::singleton().createImplicitInitialValue(), important);
4924 }
4925 return true;
4926}
4927
4928bool CSSPropertyParser::consumeFlex(bool important)
4929{
4930 static const double unsetValue = -1;
4931 double flexGrow = unsetValue;
4932 double flexShrink = unsetValue;
4933 RefPtr<CSSPrimitiveValue> flexBasis;
4934
4935 if (m_range.peek().id() == CSSValueNone) {
4936 flexGrow = 0;
4937 flexShrink = 0;
4938 flexBasis = CSSValuePool::singleton().createIdentifierValue(CSSValueAuto);
4939 m_range.consumeIncludingWhitespace();
4940 } else {
4941 unsigned index = 0;
4942 while (!m_range.atEnd() && index++ < 3) {
4943 double num;
4944 if (consumeNumberRaw(m_range, num)) {
4945 if (num < 0)
4946 return false;
4947 if (flexGrow == unsetValue)
4948 flexGrow = num;
4949 else if (flexShrink == unsetValue)
4950 flexShrink = num;
4951 else if (!num) // flex only allows a basis of 0 (sans units) if flex-grow and flex-shrink values have already been set.
4952 flexBasis = CSSPrimitiveValue::create(0, CSSPrimitiveValue::UnitType::CSS_PX);
4953 else
4954 return false;
4955 } else if (!flexBasis) {
4956 if (m_range.peek().id() == CSSValueAuto)
4957 flexBasis = consumeIdent(m_range);
4958 if (!flexBasis)
4959 flexBasis = consumeLengthOrPercent(m_range, m_context.mode, ValueRangeNonNegative);
4960 if (index == 2 && !m_range.atEnd())
4961 return false;
4962 }
4963 }
4964 if (index == 0)
4965 return false;
4966 if (flexGrow == unsetValue)
4967 flexGrow = 1;
4968 if (flexShrink == unsetValue)
4969 flexShrink = 1;
4970
4971 // FIXME: Using % here is a hack to work around intrinsic sizing implementation being
4972 // a mess (e.g., turned off for nested column flexboxes, failing to relayout properly even
4973 // if turned back on for nested columns, etc.). We have layout test coverage of both
4974 // scenarios.
4975 if (!flexBasis)
4976 flexBasis = CSSPrimitiveValue::create(0, CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
4977 }
4978
4979 if (!m_range.atEnd())
4980 return false;
4981 addProperty(CSSPropertyFlexGrow, CSSPropertyFlex, CSSPrimitiveValue::create(clampTo<float>(flexGrow), CSSPrimitiveValue::UnitType::CSS_NUMBER), important);
4982 addProperty(CSSPropertyFlexShrink, CSSPropertyFlex, CSSPrimitiveValue::create(clampTo<float>(flexShrink), CSSPrimitiveValue::UnitType::CSS_NUMBER), important);
4983 addProperty(CSSPropertyFlexBasis, CSSPropertyFlex, flexBasis.releaseNonNull(), important);
4984 return true;
4985}
4986
4987bool CSSPropertyParser::consumeBorder(RefPtr<CSSValue>& width, RefPtr<CSSValue>& style, RefPtr<CSSValue>& color)
4988{
4989 while (!width || !style || !color) {
4990 if (!width) {
4991 width = consumeLineWidth(m_range, m_context.mode, UnitlessQuirk::Forbid);
4992 if (width)
4993 continue;
4994 }
4995 if (!style) {
4996 style = parseSingleValue(CSSPropertyBorderLeftStyle, CSSPropertyBorder);
4997 if (style)
4998 continue;
4999 }
5000 if (!color) {
5001 color = consumeColor(m_range, m_context.mode);
5002 if (color)
5003 continue;
5004 }
5005 break;
5006 }
5007
5008 if (!width && !style && !color)
5009 return false;
5010
5011 if (!width)
5012 width = CSSValuePool::singleton().createImplicitInitialValue();
5013 if (!style)
5014 style = CSSValuePool::singleton().createImplicitInitialValue();
5015 if (!color)
5016 color = CSSValuePool::singleton().createImplicitInitialValue();
5017
5018 return m_range.atEnd();
5019}
5020
5021bool CSSPropertyParser::consume2ValueShorthand(const StylePropertyShorthand& shorthand, bool important)
5022{
5023 ASSERT(shorthand.length() == 2);
5024 const CSSPropertyID* longhands = shorthand.properties();
5025 RefPtr<CSSValue> start = parseSingleValue(longhands[0], shorthand.id());
5026 if (!start)
5027 return false;
5028
5029 RefPtr<CSSValue> end = parseSingleValue(longhands[1], shorthand.id());
5030 bool endImplicit = !end;
5031 if (endImplicit)
5032 end = start;
5033 addProperty(longhands[0], shorthand.id(), start.releaseNonNull(), important);
5034 addProperty(longhands[1], shorthand.id(), end.releaseNonNull(), important, endImplicit);
5035
5036 return m_range.atEnd();
5037}
5038
5039bool CSSPropertyParser::consume4ValueShorthand(const StylePropertyShorthand& shorthand, bool important)
5040{
5041 ASSERT(shorthand.length() == 4);
5042 const CSSPropertyID* longhands = shorthand.properties();
5043 RefPtr<CSSValue> top = parseSingleValue(longhands[0], shorthand.id());
5044 if (!top)
5045 return false;
5046
5047 RefPtr<CSSValue> right = parseSingleValue(longhands[1], shorthand.id());
5048 RefPtr<CSSValue> bottom;
5049 RefPtr<CSSValue> left;
5050 if (right) {
5051 bottom = parseSingleValue(longhands[2], shorthand.id());
5052 if (bottom)
5053 left = parseSingleValue(longhands[3], shorthand.id());
5054 }
5055
5056 bool rightImplicit = !right;
5057 bool bottomImplicit = !bottom;
5058 bool leftImplicit = !left;
5059
5060 if (!right)
5061 right = top;
5062 if (!bottom)
5063 bottom = top;
5064 if (!left)
5065 left = right;
5066
5067 addProperty(longhands[0], shorthand.id(), top.releaseNonNull(), important);
5068 addProperty(longhands[1], shorthand.id(), right.releaseNonNull(), important, rightImplicit);
5069 addProperty(longhands[2], shorthand.id(), bottom.releaseNonNull(), important, bottomImplicit);
5070 addProperty(longhands[3], shorthand.id(), left.releaseNonNull(), important, leftImplicit);
5071
5072 return m_range.atEnd();
5073}
5074
5075bool CSSPropertyParser::consumeBorderImage(CSSPropertyID property, bool important)
5076{
5077 RefPtr<CSSValue> source;
5078 RefPtr<CSSValue> slice;
5079 RefPtr<CSSValue> width;
5080 RefPtr<CSSValue> outset;
5081 RefPtr<CSSValue> repeat;
5082
5083 if (consumeBorderImageComponents(property, m_range, m_context, source, slice, width, outset, repeat)) {
5084 if (!source)
5085 source = CSSValuePool::singleton().createImplicitInitialValue();
5086 if (!slice)
5087 slice = CSSValuePool::singleton().createImplicitInitialValue();
5088 if (!width)
5089 width = CSSValuePool::singleton().createImplicitInitialValue();
5090 if (!outset)
5091 outset = CSSValuePool::singleton().createImplicitInitialValue();
5092 if (!repeat)
5093 repeat = CSSValuePool::singleton().createImplicitInitialValue();
5094 switch (property) {
5095 case CSSPropertyWebkitMaskBoxImage:
5096 addProperty(CSSPropertyWebkitMaskBoxImageSource, CSSPropertyWebkitMaskBoxImage, source.releaseNonNull(), important);
5097 addProperty(CSSPropertyWebkitMaskBoxImageSlice, CSSPropertyWebkitMaskBoxImage, slice.releaseNonNull(), important);
5098 addProperty(CSSPropertyWebkitMaskBoxImageWidth, CSSPropertyWebkitMaskBoxImage, width.releaseNonNull(), important);
5099 addProperty(CSSPropertyWebkitMaskBoxImageOutset, CSSPropertyWebkitMaskBoxImage, outset.releaseNonNull(), important);
5100 addProperty(CSSPropertyWebkitMaskBoxImageRepeat, CSSPropertyWebkitMaskBoxImage, repeat.releaseNonNull(), important);
5101 return true;
5102 case CSSPropertyBorderImage:
5103 addProperty(CSSPropertyBorderImageSource, CSSPropertyBorderImage, source.releaseNonNull(), important);
5104 addProperty(CSSPropertyBorderImageSlice, CSSPropertyBorderImage, slice.releaseNonNull(), important);
5105 addProperty(CSSPropertyBorderImageWidth, CSSPropertyBorderImage, width.releaseNonNull() , important);
5106 addProperty(CSSPropertyBorderImageOutset, CSSPropertyBorderImage, outset.releaseNonNull(), important);
5107 addProperty(CSSPropertyBorderImageRepeat, CSSPropertyBorderImage, repeat.releaseNonNull(), important);
5108 return true;
5109 default:
5110 ASSERT_NOT_REACHED();
5111 return false;
5112 }
5113 }
5114 return false;
5115}
5116
5117static inline CSSValueID mapFromPageBreakBetween(CSSValueID value)
5118{
5119 if (value == CSSValueAlways)
5120 return CSSValuePage;
5121 if (value == CSSValueAuto || value == CSSValueAvoid || value == CSSValueLeft || value == CSSValueRight)
5122 return value;
5123 return CSSValueInvalid;
5124}
5125
5126static inline CSSValueID mapFromColumnBreakBetween(CSSValueID value)
5127{
5128 if (value == CSSValueAlways)
5129 return CSSValueColumn;
5130 if (value == CSSValueAuto)
5131 return value;
5132 if (value == CSSValueAvoid)
5133 return CSSValueAvoidColumn;
5134 return CSSValueInvalid;
5135}
5136
5137static inline CSSValueID mapFromColumnRegionOrPageBreakInside(CSSValueID value)
5138{
5139 if (value == CSSValueAuto || value == CSSValueAvoid)
5140 return value;
5141 return CSSValueInvalid;
5142}
5143
5144static inline CSSPropertyID mapFromLegacyBreakProperty(CSSPropertyID property)
5145{
5146 if (property == CSSPropertyPageBreakAfter || property == CSSPropertyWebkitColumnBreakAfter)
5147 return CSSPropertyBreakAfter;
5148 if (property == CSSPropertyPageBreakBefore || property == CSSPropertyWebkitColumnBreakBefore)
5149 return CSSPropertyBreakBefore;
5150 ASSERT(property == CSSPropertyPageBreakInside || property == CSSPropertyWebkitColumnBreakInside);
5151 return CSSPropertyBreakInside;
5152}
5153
5154bool CSSPropertyParser::consumeLegacyBreakProperty(CSSPropertyID property, bool important)
5155{
5156 // The fragmentation spec says that page-break-(after|before|inside) are to be treated as
5157 // shorthands for their break-(after|before|inside) counterparts. We'll do the same for the
5158 // non-standard properties -webkit-column-break-(after|before|inside).
5159 RefPtr<CSSPrimitiveValue> keyword = consumeIdent(m_range);
5160 if (!keyword)
5161 return false;
5162 if (!m_range.atEnd())
5163 return false;
5164 CSSValueID value = keyword->valueID();
5165 switch (property) {
5166 case CSSPropertyPageBreakAfter:
5167 case CSSPropertyPageBreakBefore:
5168 value = mapFromPageBreakBetween(value);
5169 break;
5170 case CSSPropertyWebkitColumnBreakAfter:
5171 case CSSPropertyWebkitColumnBreakBefore:
5172 value = mapFromColumnBreakBetween(value);
5173 break;
5174 case CSSPropertyPageBreakInside:
5175 case CSSPropertyWebkitColumnBreakInside:
5176 value = mapFromColumnRegionOrPageBreakInside(value);
5177 break;
5178 default:
5179 ASSERT_NOT_REACHED();
5180 }
5181 if (value == CSSValueInvalid)
5182 return false;
5183
5184 CSSPropertyID genericBreakProperty = mapFromLegacyBreakProperty(property);
5185 addProperty(genericBreakProperty, property, CSSValuePool::singleton().createIdentifierValue(value), important);
5186 return true;
5187}
5188
5189static bool consumeBackgroundPosition(CSSParserTokenRange& range, const CSSParserContext& context, UnitlessQuirk unitless, RefPtr<CSSValue>& resultX, RefPtr<CSSValue>& resultY)
5190{
5191 do {
5192 RefPtr<CSSPrimitiveValue> positionX;
5193 RefPtr<CSSPrimitiveValue> positionY;
5194 if (!consumePosition(range, context.mode, unitless, positionX, positionY))
5195 return false;
5196 addBackgroundValue(resultX, positionX.releaseNonNull());
5197 addBackgroundValue(resultY, positionY.releaseNonNull());
5198 } while (consumeCommaIncludingWhitespace(range));
5199 return true;
5200}
5201
5202static bool consumeRepeatStyleComponent(CSSParserTokenRange& range, RefPtr<CSSPrimitiveValue>& value1, RefPtr<CSSPrimitiveValue>& value2, bool& implicit)
5203{
5204 if (consumeIdent<CSSValueRepeatX>(range)) {
5205 value1 = CSSValuePool::singleton().createIdentifierValue(CSSValueRepeat);
5206 value2 = CSSValuePool::singleton().createIdentifierValue(CSSValueNoRepeat);
5207 implicit = true;
5208 return true;
5209 }
5210 if (consumeIdent<CSSValueRepeatY>(range)) {
5211 value1 = CSSValuePool::singleton().createIdentifierValue(CSSValueNoRepeat);
5212 value2 = CSSValuePool::singleton().createIdentifierValue(CSSValueRepeat);
5213 implicit = true;
5214 return true;
5215 }
5216 value1 = consumeIdent<CSSValueRepeat, CSSValueNoRepeat, CSSValueRound, CSSValueSpace>(range);
5217 if (!value1)
5218 return false;
5219
5220 value2 = consumeIdent<CSSValueRepeat, CSSValueNoRepeat, CSSValueRound, CSSValueSpace>(range);
5221 if (!value2) {
5222 value2 = value1;
5223 implicit = true;
5224 }
5225 return true;
5226}
5227
5228static bool consumeRepeatStyle(CSSParserTokenRange& range, RefPtr<CSSValue>& resultX, RefPtr<CSSValue>& resultY, bool& implicit)
5229{
5230 do {
5231 RefPtr<CSSPrimitiveValue> repeatX;
5232 RefPtr<CSSPrimitiveValue> repeatY;
5233 if (!consumeRepeatStyleComponent(range, repeatX, repeatY, implicit))
5234 return false;
5235 addBackgroundValue(resultX, repeatX.releaseNonNull());
5236 addBackgroundValue(resultY, repeatY.releaseNonNull());
5237 } while (consumeCommaIncludingWhitespace(range));
5238 return true;
5239}
5240
5241// Note: consumeBackgroundShorthand assumes y properties (for example background-position-y) follow
5242// the x properties in the shorthand array.
5243bool CSSPropertyParser::consumeBackgroundShorthand(const StylePropertyShorthand& shorthand, bool important)
5244{
5245 const unsigned longhandCount = shorthand.length();
5246 RefPtr<CSSValue> longhands[10];
5247 ASSERT(longhandCount <= 10);
5248
5249 bool implicit = false;
5250 do {
5251 bool parsedLonghand[10] = { false };
5252 RefPtr<CSSValue> originValue;
5253 do {
5254 bool foundProperty = false;
5255 for (size_t i = 0; i < longhandCount; ++i) {
5256 if (parsedLonghand[i])
5257 continue;
5258
5259 RefPtr<CSSValue> value;
5260 RefPtr<CSSValue> valueY;
5261 CSSPropertyID property = shorthand.properties()[i];
5262 if (property == CSSPropertyBackgroundRepeatX || property == CSSPropertyWebkitMaskRepeatX) {
5263 RefPtr<CSSPrimitiveValue> primitiveValue;
5264 RefPtr<CSSPrimitiveValue> primitiveValueY;
5265 consumeRepeatStyleComponent(m_range, primitiveValue, primitiveValueY, implicit);
5266 value = primitiveValue;
5267 valueY = primitiveValueY;
5268 } else if (property == CSSPropertyBackgroundPositionX || property == CSSPropertyWebkitMaskPositionX) {
5269 CSSParserTokenRange rangeCopy = m_range;
5270 RefPtr<CSSPrimitiveValue> primitiveValue;
5271 RefPtr<CSSPrimitiveValue> primitiveValueY;
5272 if (!consumePosition(rangeCopy, m_context.mode, UnitlessQuirk::Forbid, primitiveValue, primitiveValueY))
5273 continue;
5274 value = primitiveValue;
5275 valueY = primitiveValueY;
5276 m_range = rangeCopy;
5277 } else if (property == CSSPropertyBackgroundSize || property == CSSPropertyWebkitMaskSize) {
5278 if (!consumeSlashIncludingWhitespace(m_range))
5279 continue;
5280 value = consumeBackgroundSize(property, m_range, m_context.mode);
5281 if (!value || !parsedLonghand[i - 1]) // Position must have been parsed in the current layer.
5282 return false;
5283 } else if (property == CSSPropertyBackgroundPositionY || property == CSSPropertyBackgroundRepeatY
5284 || property == CSSPropertyWebkitMaskPositionY || property == CSSPropertyWebkitMaskRepeatY) {
5285 continue;
5286 } else {
5287 value = consumeBackgroundComponent(property, m_range, m_context);
5288 }
5289 if (value) {
5290 if (property == CSSPropertyBackgroundOrigin || property == CSSPropertyWebkitMaskOrigin)
5291 originValue = value;
5292 parsedLonghand[i] = true;
5293 foundProperty = true;
5294 addBackgroundValue(longhands[i], value.releaseNonNull());
5295 if (valueY) {
5296 parsedLonghand[i + 1] = true;
5297 addBackgroundValue(longhands[i + 1], valueY.releaseNonNull());
5298 }
5299 }
5300 }
5301 if (!foundProperty)
5302 return false;
5303 } while (!m_range.atEnd() && m_range.peek().type() != CommaToken);
5304
5305 // FIXME: This will make invalid longhands, see crbug.com/386459
5306 for (size_t i = 0; i < longhandCount; ++i) {
5307 CSSPropertyID property = shorthand.properties()[i];
5308 if (property == CSSPropertyBackgroundColor && !m_range.atEnd()) {
5309 if (parsedLonghand[i])
5310 return false; // Colors are only allowed in the last layer.
5311 continue;
5312 }
5313 if ((property == CSSPropertyBackgroundClip || property == CSSPropertyWebkitMaskClip) && !parsedLonghand[i] && originValue) {
5314 addBackgroundValue(longhands[i], originValue.releaseNonNull());
5315 continue;
5316 }
5317 if (!parsedLonghand[i])
5318 addBackgroundValue(longhands[i], CSSValuePool::singleton().createImplicitInitialValue());
5319 }
5320 } while (consumeCommaIncludingWhitespace(m_range));
5321 if (!m_range.atEnd())
5322 return false;
5323
5324 for (size_t i = 0; i < longhandCount; ++i) {
5325 CSSPropertyID property = shorthand.properties()[i];
5326 if (property == CSSPropertyBackgroundSize && longhands[i] && m_context.useLegacyBackgroundSizeShorthandBehavior)
5327 continue;
5328 addProperty(property, shorthand.id(), *longhands[i], important, implicit);
5329 }
5330 return true;
5331}
5332
5333// FIXME-NEWPARSER: Hack to work around the fact that we aren't using CSSCustomIdentValue
5334// for stuff yet. This can be replaced by CSSValue::isCustomIdentValue() once we switch
5335// to using CSSCustomIdentValue everywhere.
5336static bool isCustomIdentValue(const CSSValue& value)
5337{
5338 return is<CSSPrimitiveValue>(value) && downcast<CSSPrimitiveValue>(value).isString();
5339}
5340
5341bool CSSPropertyParser::consumeGridItemPositionShorthand(CSSPropertyID shorthandId, bool important)
5342{
5343 const StylePropertyShorthand& shorthand = shorthandForProperty(shorthandId);
5344 ASSERT(shorthand.length() == 2);
5345 RefPtr<CSSValue> startValue = consumeGridLine(m_range);
5346 if (!startValue)
5347 return false;
5348
5349 RefPtr<CSSValue> endValue;
5350 if (consumeSlashIncludingWhitespace(m_range)) {
5351 endValue = consumeGridLine(m_range);
5352 if (!endValue)
5353 return false;
5354 } else {
5355 endValue = isCustomIdentValue(*startValue) ? startValue : CSSValuePool::singleton().createIdentifierValue(CSSValueAuto);
5356 }
5357 if (!m_range.atEnd())
5358 return false;
5359 addProperty(shorthand.properties()[0], shorthandId, startValue.releaseNonNull(), important);
5360 addProperty(shorthand.properties()[1], shorthandId, endValue.releaseNonNull(), important);
5361 return true;
5362}
5363
5364bool CSSPropertyParser::consumeGridAreaShorthand(bool important)
5365{
5366 RefPtr<CSSValue> rowStartValue = consumeGridLine(m_range);
5367 if (!rowStartValue)
5368 return false;
5369 RefPtr<CSSValue> columnStartValue;
5370 RefPtr<CSSValue> rowEndValue;
5371 RefPtr<CSSValue> columnEndValue;
5372 if (consumeSlashIncludingWhitespace(m_range)) {
5373 columnStartValue = consumeGridLine(m_range);
5374 if (!columnStartValue)
5375 return false;
5376 if (consumeSlashIncludingWhitespace(m_range)) {
5377 rowEndValue = consumeGridLine(m_range);
5378 if (!rowEndValue)
5379 return false;
5380 if (consumeSlashIncludingWhitespace(m_range)) {
5381 columnEndValue = consumeGridLine(m_range);
5382 if (!columnEndValue)
5383 return false;
5384 }
5385 }
5386 }
5387 if (!m_range.atEnd())
5388 return false;
5389 if (!columnStartValue)
5390 columnStartValue = isCustomIdentValue(*rowStartValue) ? rowStartValue : CSSValuePool::singleton().createIdentifierValue(CSSValueAuto);
5391 if (!rowEndValue)
5392 rowEndValue = isCustomIdentValue(*rowStartValue) ? rowStartValue : CSSValuePool::singleton().createIdentifierValue(CSSValueAuto);
5393 if (!columnEndValue)
5394 columnEndValue = isCustomIdentValue(*columnStartValue) ? columnStartValue : CSSValuePool::singleton().createIdentifierValue(CSSValueAuto);
5395
5396 addProperty(CSSPropertyGridRowStart, CSSPropertyGridArea, rowStartValue.releaseNonNull(), important);
5397 addProperty(CSSPropertyGridColumnStart, CSSPropertyGridArea, columnStartValue.releaseNonNull(), important);
5398 addProperty(CSSPropertyGridRowEnd, CSSPropertyGridArea, rowEndValue.releaseNonNull(), important);
5399 addProperty(CSSPropertyGridColumnEnd, CSSPropertyGridArea, columnEndValue.releaseNonNull(), important);
5400 return true;
5401}
5402
5403bool CSSPropertyParser::consumeGridTemplateRowsAndAreasAndColumns(CSSPropertyID shorthandId, bool important)
5404{
5405 NamedGridAreaMap gridAreaMap;
5406 size_t rowCount = 0;
5407 size_t columnCount = 0;
5408 RefPtr<CSSValueList> templateRows = CSSValueList::createSpaceSeparated();
5409
5410 // Persists between loop iterations so we can use the same value for
5411 // consecutive <line-names> values
5412 RefPtr<CSSGridLineNamesValue> lineNames;
5413
5414 do {
5415 // Handle leading <custom-ident>*.
5416 bool hasPreviousLineNames = lineNames;
5417 lineNames = consumeGridLineNames(m_range, lineNames.get());
5418 if (lineNames && !hasPreviousLineNames)
5419 templateRows->append(*lineNames);
5420
5421 // Handle a template-area's row.
5422 if (m_range.peek().type() != StringToken || !parseGridTemplateAreasRow(m_range.consumeIncludingWhitespace().value().toString(), gridAreaMap, rowCount, columnCount))
5423 return false;
5424 ++rowCount;
5425
5426 // Handle template-rows's track-size.
5427 RefPtr<CSSValue> value = consumeGridTrackSize(m_range, m_context.mode);
5428 if (!value)
5429 value = CSSValuePool::singleton().createIdentifierValue(CSSValueAuto);
5430 templateRows->append(*value);
5431
5432 // This will handle the trailing/leading <custom-ident>* in the grammar.
5433 lineNames = consumeGridLineNames(m_range);
5434 if (lineNames)
5435 templateRows->append(lineNames.releaseNonNull());
5436 } while (!m_range.atEnd() && !(m_range.peek().type() == DelimiterToken && m_range.peek().delimiter() == '/'));
5437
5438 RefPtr<CSSValue> columnsValue;
5439 if (!m_range.atEnd()) {
5440 if (!consumeSlashIncludingWhitespace(m_range))
5441 return false;
5442 columnsValue = consumeGridTrackList(m_range, m_context.mode, GridTemplateNoRepeat);
5443 if (!columnsValue || !m_range.atEnd())
5444 return false;
5445 } else {
5446 columnsValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNone);
5447 }
5448 addProperty(CSSPropertyGridTemplateRows, shorthandId, templateRows.releaseNonNull(), important);
5449 addProperty(CSSPropertyGridTemplateColumns, shorthandId, columnsValue.releaseNonNull(), important);
5450 addProperty(CSSPropertyGridTemplateAreas, shorthandId, CSSGridTemplateAreasValue::create(gridAreaMap, rowCount, columnCount), important);
5451 return true;
5452}
5453
5454bool CSSPropertyParser::consumeGridTemplateShorthand(CSSPropertyID shorthandId, bool important)
5455{
5456 CSSParserTokenRange rangeCopy = m_range;
5457 RefPtr<CSSValue> rowsValue = consumeIdent<CSSValueNone>(m_range);
5458
5459 // 1- 'none' case.
5460 if (rowsValue && m_range.atEnd()) {
5461 addProperty(CSSPropertyGridTemplateRows, shorthandId, CSSValuePool::singleton().createIdentifierValue(CSSValueNone), important);
5462 addProperty(CSSPropertyGridTemplateColumns, shorthandId, CSSValuePool::singleton().createIdentifierValue(CSSValueNone), important);
5463 addProperty(CSSPropertyGridTemplateAreas, shorthandId, CSSValuePool::singleton().createIdentifierValue(CSSValueNone), important);
5464 return true;
5465 }
5466
5467 // 2- <grid-template-rows> / <grid-template-columns>
5468 if (!rowsValue)
5469 rowsValue = consumeGridTrackList(m_range, m_context.mode, GridTemplate);
5470
5471 if (rowsValue) {
5472 if (!consumeSlashIncludingWhitespace(m_range))
5473 return false;
5474 RefPtr<CSSValue> columnsValue = consumeGridTemplatesRowsOrColumns(m_range, m_context.mode);
5475 if (!columnsValue || !m_range.atEnd())
5476 return false;
5477
5478 addProperty(CSSPropertyGridTemplateRows, shorthandId, rowsValue.releaseNonNull(), important);
5479 addProperty(CSSPropertyGridTemplateColumns, shorthandId, columnsValue.releaseNonNull(), important);
5480 addProperty(CSSPropertyGridTemplateAreas, shorthandId, CSSValuePool::singleton().createIdentifierValue(CSSValueNone), important);
5481 return true;
5482 }
5483
5484 // 3- [ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <track-list> ]?
5485 m_range = rangeCopy;
5486 return consumeGridTemplateRowsAndAreasAndColumns(shorthandId, important);
5487}
5488
5489static RefPtr<CSSValue> consumeImplicitGridAutoFlow(CSSParserTokenRange& range, Ref<CSSPrimitiveValue>&& flowDirection)
5490{
5491 // [ auto-flow && dense? ]
5492 if (range.atEnd())
5493 return nullptr;
5494 auto list = CSSValueList::createSpaceSeparated();
5495 list->append(WTFMove(flowDirection));
5496 if (range.peek().id() == CSSValueAutoFlow) {
5497 range.consumeIncludingWhitespace();
5498 RefPtr<CSSValue> denseIdent = consumeIdent<CSSValueDense>(range);
5499 if (denseIdent)
5500 list->append(denseIdent.releaseNonNull());
5501 } else {
5502 // Dense case
5503 if (range.peek().id() != CSSValueDense)
5504 return nullptr;
5505 range.consumeIncludingWhitespace();
5506 if (range.atEnd() || range.peek().id() != CSSValueAutoFlow)
5507 return nullptr;
5508 range.consumeIncludingWhitespace();
5509 list->append(CSSValuePool::singleton().createIdentifierValue(CSSValueDense));
5510 }
5511
5512 return list;
5513}
5514
5515bool CSSPropertyParser::consumeGridShorthand(bool important)
5516{
5517 ASSERT(shorthandForProperty(CSSPropertyGrid).length() == 6);
5518
5519 CSSParserTokenRange rangeCopy = m_range;
5520
5521 // 1- <grid-template>
5522 if (consumeGridTemplateShorthand(CSSPropertyGrid, important)) {
5523 // It can only be specified the explicit or the implicit grid properties in a single grid declaration.
5524 // The sub-properties not specified are set to their initial value, as normal for shorthands.
5525 addProperty(CSSPropertyGridAutoFlow, CSSPropertyGrid, CSSValuePool::singleton().createImplicitInitialValue(), important);
5526 addProperty(CSSPropertyGridAutoColumns, CSSPropertyGrid, CSSValuePool::singleton().createImplicitInitialValue(), important);
5527 addProperty(CSSPropertyGridAutoRows, CSSPropertyGrid, CSSValuePool::singleton().createImplicitInitialValue(), important);
5528 return true;
5529 }
5530
5531 m_range = rangeCopy;
5532
5533 RefPtr<CSSValue> autoColumnsValue;
5534 RefPtr<CSSValue> autoRowsValue;
5535 RefPtr<CSSValue> templateRows;
5536 RefPtr<CSSValue> templateColumns;
5537 RefPtr<CSSValue> gridAutoFlow;
5538
5539 if (m_range.peek().id() == CSSValueAutoFlow || m_range.peek().id() == CSSValueDense) {
5540 // 2- [ auto-flow && dense? ] <grid-auto-rows>? / <grid-template-columns>
5541 gridAutoFlow = consumeImplicitGridAutoFlow(m_range, CSSValuePool::singleton().createIdentifierValue(CSSValueRow));
5542 if (!gridAutoFlow || m_range.atEnd())
5543 return false;
5544 if (consumeSlashIncludingWhitespace(m_range))
5545 autoRowsValue = CSSValuePool::singleton().createImplicitInitialValue();
5546 else {
5547 autoRowsValue = consumeGridTrackList(m_range, m_context.mode, GridAuto);
5548 if (!autoRowsValue)
5549 return false;
5550 if (!consumeSlashIncludingWhitespace(m_range))
5551 return false;
5552 }
5553 if (m_range.atEnd())
5554 return false;
5555 templateColumns = consumeGridTemplatesRowsOrColumns(m_range, m_context.mode);
5556 if (!templateColumns)
5557 return false;
5558 templateRows = CSSValuePool::singleton().createImplicitInitialValue();
5559 autoColumnsValue = CSSValuePool::singleton().createImplicitInitialValue();
5560 } else {
5561 // 3- <grid-template-rows> / [ auto-flow && dense? ] <grid-auto-columns>?
5562 templateRows = consumeGridTemplatesRowsOrColumns(m_range, m_context.mode);
5563 if (!templateRows)
5564 return false;
5565 if (!consumeSlashIncludingWhitespace(m_range) || m_range.atEnd())
5566 return false;
5567 gridAutoFlow = consumeImplicitGridAutoFlow(m_range, CSSValuePool::singleton().createIdentifierValue(CSSValueColumn));
5568 if (!gridAutoFlow)
5569 return false;
5570 if (m_range.atEnd())
5571 autoColumnsValue = CSSValuePool::singleton().createImplicitInitialValue();
5572 else {
5573 autoColumnsValue = consumeGridTrackList(m_range, m_context.mode, GridAuto);
5574 if (!autoColumnsValue)
5575 return false;
5576 }
5577 templateColumns = CSSValuePool::singleton().createImplicitInitialValue();
5578 autoRowsValue = CSSValuePool::singleton().createImplicitInitialValue();
5579 }
5580
5581 if (!m_range.atEnd())
5582 return false;
5583
5584 // It can only be specified the explicit or the implicit grid properties in a single grid declaration.
5585 // The sub-properties not specified are set to their initial value, as normal for shorthands.
5586 addProperty(CSSPropertyGridTemplateColumns, CSSPropertyGrid, templateColumns.releaseNonNull(), important);
5587 addProperty(CSSPropertyGridTemplateRows, CSSPropertyGrid, templateRows.releaseNonNull(), important);
5588 addProperty(CSSPropertyGridTemplateAreas, CSSPropertyGrid, CSSValuePool::singleton().createImplicitInitialValue(), important);
5589 addProperty(CSSPropertyGridAutoFlow, CSSPropertyGrid, gridAutoFlow.releaseNonNull(), important);
5590 addProperty(CSSPropertyGridAutoColumns, CSSPropertyGrid, autoColumnsValue.releaseNonNull(), important);
5591 addProperty(CSSPropertyGridAutoRows, CSSPropertyGrid, autoRowsValue.releaseNonNull(), important);
5592
5593 return true;
5594}
5595
5596bool CSSPropertyParser::consumePlaceContentShorthand(bool important)
5597{
5598 ASSERT(shorthandForProperty(CSSPropertyPlaceContent).length() == 2);
5599
5600 if (m_range.atEnd())
5601 return false;
5602
5603 CSSParserTokenRange rangeCopy = m_range;
5604 bool isBaseline = isBaselineKeyword(m_range.peek().id());
5605 RefPtr<CSSValue> alignContentValue = consumeContentDistributionOverflowPosition(m_range, isContentPositionKeyword);
5606 if (!alignContentValue)
5607 return false;
5608
5609 // justify-content property does not allow the <baseline-position> values.
5610 if (m_range.atEnd() && isBaseline)
5611 return false;
5612 if (isBaselineKeyword(m_range.peek().id()))
5613 return false;
5614
5615 if (m_range.atEnd())
5616 m_range = rangeCopy;
5617 RefPtr<CSSValue> justifyContentValue = consumeContentDistributionOverflowPosition(m_range, isContentPositionOrLeftOrRightKeyword);
5618 if (!justifyContentValue)
5619 return false;
5620 if (!m_range.atEnd())
5621 return false;
5622
5623 addProperty(CSSPropertyAlignContent, CSSPropertyPlaceContent, alignContentValue.releaseNonNull(), important);
5624 addProperty(CSSPropertyJustifyContent, CSSPropertyPlaceContent, justifyContentValue.releaseNonNull(), important);
5625 return true;
5626}
5627
5628bool CSSPropertyParser::consumePlaceItemsShorthand(bool important)
5629{
5630 ASSERT(shorthandForProperty(CSSPropertyPlaceItems).length() == 2);
5631
5632 CSSParserTokenRange rangeCopy = m_range;
5633 RefPtr<CSSValue> alignItemsValue = consumeAlignItems(m_range);
5634 if (!alignItemsValue)
5635 return false;
5636
5637 if (m_range.atEnd())
5638 m_range = rangeCopy;
5639 RefPtr<CSSValue> justifyItemsValue = consumeJustifyItems(m_range);
5640 if (!justifyItemsValue)
5641 return false;
5642
5643 if (!m_range.atEnd())
5644 return false;
5645
5646 addProperty(CSSPropertyAlignItems, CSSPropertyPlaceItems, alignItemsValue.releaseNonNull(), important);
5647 addProperty(CSSPropertyJustifyItems, CSSPropertyPlaceItems, justifyItemsValue.releaseNonNull(), important);
5648 return true;
5649}
5650
5651bool CSSPropertyParser::consumePlaceSelfShorthand(bool important)
5652{
5653 ASSERT(shorthandForProperty(CSSPropertyPlaceSelf).length() == 2);
5654
5655 CSSParserTokenRange rangeCopy = m_range;
5656 RefPtr<CSSValue> alignSelfValue = consumeSelfPositionOverflowPosition(m_range, isSelfPositionKeyword);
5657 if (!alignSelfValue)
5658 return false;
5659
5660 if (m_range.atEnd())
5661 m_range = rangeCopy;
5662 RefPtr<CSSValue> justifySelfValue = consumeSelfPositionOverflowPosition(m_range, isSelfPositionOrLeftOrRightKeyword);
5663 if (!justifySelfValue)
5664 return false;
5665
5666 if (!m_range.atEnd())
5667 return false;
5668
5669 addProperty(CSSPropertyAlignSelf, CSSPropertyPlaceSelf, alignSelfValue.releaseNonNull(), important);
5670 addProperty(CSSPropertyJustifySelf, CSSPropertyPlaceSelf, justifySelfValue.releaseNonNull(), important);
5671 return true;
5672}
5673
5674bool CSSPropertyParser::parseShorthand(CSSPropertyID property, bool important)
5675{
5676 switch (property) {
5677 case CSSPropertyWebkitMarginCollapse: {
5678 CSSValueID id = m_range.consumeIncludingWhitespace().id();
5679 if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyWebkitMarginBeforeCollapse, id, m_context))
5680 return false;
5681 addProperty(CSSPropertyWebkitMarginBeforeCollapse, CSSPropertyWebkitMarginCollapse, CSSValuePool::singleton().createIdentifierValue(id), important);
5682 if (m_range.atEnd()) {
5683 addProperty(CSSPropertyWebkitMarginAfterCollapse, CSSPropertyWebkitMarginCollapse, CSSValuePool::singleton().createIdentifierValue(id), important);
5684 return true;
5685 }
5686 id = m_range.consumeIncludingWhitespace().id();
5687 if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyWebkitMarginAfterCollapse, id, m_context))
5688 return false;
5689 addProperty(CSSPropertyWebkitMarginAfterCollapse, CSSPropertyWebkitMarginCollapse, CSSValuePool::singleton().createIdentifierValue(id), important);
5690 return true;
5691 }
5692 case CSSPropertyOverflow: {
5693 CSSValueID id = m_range.consumeIncludingWhitespace().id();
5694 if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(CSSPropertyOverflowY, id, m_context))
5695 return false;
5696 if (!m_range.atEnd())
5697 return false;
5698 RefPtr<CSSValue> overflowYValue = CSSValuePool::singleton().createIdentifierValue(id);
5699 RefPtr<CSSValue> overflowXValue;
5700
5701 // FIXME: -webkit-paged-x or -webkit-paged-y only apply to overflow-y. If this value has been
5702 // set using the shorthand, then for now overflow-x will default to auto, but once we implement
5703 // pagination controls, it should default to hidden. If the overflow-y value is anything but
5704 // paged-x or paged-y, then overflow-x and overflow-y should have the same value.
5705 if (id == CSSValueWebkitPagedX || id == CSSValueWebkitPagedY)
5706 overflowXValue = CSSValuePool::singleton().createIdentifierValue(CSSValueAuto);
5707 else
5708 overflowXValue = overflowYValue;
5709 addProperty(CSSPropertyOverflowX, CSSPropertyOverflow, *overflowXValue, important);
5710 addProperty(CSSPropertyOverflowY, CSSPropertyOverflow, *overflowYValue, important);
5711 return true;
5712 }
5713 case CSSPropertyFont: {
5714 const CSSParserToken& token = m_range.peek();
5715 if (token.id() >= CSSValueCaption && token.id() <= CSSValueStatusBar)
5716 return consumeSystemFont(important);
5717 return consumeFont(important);
5718 }
5719 case CSSPropertyFontVariant:
5720 return consumeFontVariantShorthand(important);
5721 case CSSPropertyBorderSpacing:
5722 return consumeBorderSpacing(important);
5723 case CSSPropertyColumns:
5724 return consumeColumns(important);
5725 case CSSPropertyAnimation:
5726 return consumeAnimationShorthand(animationShorthandForParsing(), important);
5727 case CSSPropertyTransition:
5728 return consumeAnimationShorthand(transitionShorthandForParsing(), important);
5729 case CSSPropertyTextDecoration:
5730 case CSSPropertyWebkitTextDecoration:
5731 // FIXME-NEWPARSER: We need to unprefix -line/-style/-color ASAP and get rid
5732 // of -webkit-text-decoration completely.
5733 return consumeShorthandGreedily(webkitTextDecorationShorthand(), important);
5734 case CSSPropertyInset:
5735 return consume4ValueShorthand(insetShorthand(), important);
5736 case CSSPropertyInsetBlock:
5737 return consume2ValueShorthand(insetBlockShorthand(), important);
5738 case CSSPropertyInsetInline:
5739 return consume2ValueShorthand(insetInlineShorthand(), important);
5740 case CSSPropertyMargin:
5741 return consume4ValueShorthand(marginShorthand(), important);
5742 case CSSPropertyMarginBlock:
5743 return consume2ValueShorthand(marginBlockShorthand(), important);
5744 case CSSPropertyMarginInline:
5745 return consume2ValueShorthand(marginInlineShorthand(), important);
5746 case CSSPropertyPadding:
5747 return consume4ValueShorthand(paddingShorthand(), important);
5748 case CSSPropertyPaddingBlock:
5749 return consume2ValueShorthand(paddingBlockShorthand(), important);
5750 case CSSPropertyPaddingInline:
5751 return consume2ValueShorthand(paddingInlineShorthand(), important);
5752#if ENABLE(CSS_SCROLL_SNAP)
5753 case CSSPropertyScrollSnapMargin:
5754 return consume4ValueShorthand(scrollSnapMarginShorthand(), important);
5755 case CSSPropertyScrollPadding:
5756 return consume4ValueShorthand(scrollPaddingShorthand(), important);
5757#endif
5758 case CSSPropertyWebkitTextEmphasis:
5759 return consumeShorthandGreedily(webkitTextEmphasisShorthand(), important);
5760 case CSSPropertyOutline:
5761 return consumeShorthandGreedily(outlineShorthand(), important);
5762 case CSSPropertyBorderInline: {
5763 RefPtr<CSSValue> width;
5764 RefPtr<CSSValue> style;
5765 RefPtr<CSSValue> color;
5766 if (!consumeBorder(width, style, color))
5767 return false;
5768
5769 addExpandedPropertyForValue(CSSPropertyBorderInlineWidth, width.releaseNonNull(), important);
5770 addExpandedPropertyForValue(CSSPropertyBorderInlineStyle, style.releaseNonNull(), important);
5771 addExpandedPropertyForValue(CSSPropertyBorderInlineColor, color.releaseNonNull(), important);
5772 return true;
5773 }
5774 case CSSPropertyBorderInlineColor:
5775 return consume2ValueShorthand(borderInlineColorShorthand(), important);
5776 case CSSPropertyBorderInlineStyle:
5777 return consume2ValueShorthand(borderInlineStyleShorthand(), important);
5778 case CSSPropertyBorderInlineWidth:
5779 return consume2ValueShorthand(borderInlineWidthShorthand(), important);
5780 case CSSPropertyBorderInlineStart:
5781 return consumeShorthandGreedily(borderInlineStartShorthand(), important);
5782 case CSSPropertyBorderInlineEnd:
5783 return consumeShorthandGreedily(borderInlineEndShorthand(), important);
5784 case CSSPropertyBorderBlock: {
5785 RefPtr<CSSValue> width;
5786 RefPtr<CSSValue> style;
5787 RefPtr<CSSValue> color;
5788 if (!consumeBorder(width, style, color))
5789 return false;
5790
5791 addExpandedPropertyForValue(CSSPropertyBorderBlockWidth, width.releaseNonNull(), important);
5792 addExpandedPropertyForValue(CSSPropertyBorderBlockStyle, style.releaseNonNull(), important);
5793 addExpandedPropertyForValue(CSSPropertyBorderBlockColor, color.releaseNonNull(), important);
5794 return true;
5795 }
5796 case CSSPropertyBorderBlockColor:
5797 return consume2ValueShorthand(borderBlockColorShorthand(), important);
5798 case CSSPropertyBorderBlockStyle:
5799 return consume2ValueShorthand(borderBlockStyleShorthand(), important);
5800 case CSSPropertyBorderBlockWidth:
5801 return consume2ValueShorthand(borderBlockWidthShorthand(), important);
5802 case CSSPropertyBorderBlockStart:
5803 return consumeShorthandGreedily(borderBlockStartShorthand(), important);
5804 case CSSPropertyBorderBlockEnd:
5805 return consumeShorthandGreedily(borderBlockEndShorthand(), important);
5806 case CSSPropertyWebkitTextStroke:
5807 return consumeShorthandGreedily(webkitTextStrokeShorthand(), important);
5808 case CSSPropertyMarker: {
5809 RefPtr<CSSValue> marker = parseSingleValue(CSSPropertyMarkerStart);
5810 if (!marker || !m_range.atEnd())
5811 return false;
5812 auto markerRef = marker.releaseNonNull();
5813 addProperty(CSSPropertyMarkerStart, CSSPropertyMarker, markerRef.copyRef(), important);
5814 addProperty(CSSPropertyMarkerMid, CSSPropertyMarker, markerRef.copyRef(), important);
5815 addProperty(CSSPropertyMarkerEnd, CSSPropertyMarker, markerRef.copyRef(), important);
5816 return true;
5817 }
5818 case CSSPropertyFlex:
5819 return consumeFlex(important);
5820 case CSSPropertyFlexFlow:
5821 return consumeShorthandGreedily(flexFlowShorthand(), important);
5822 case CSSPropertyColumnRule:
5823 return consumeShorthandGreedily(columnRuleShorthand(), important);
5824 case CSSPropertyListStyle:
5825 return consumeShorthandGreedily(listStyleShorthand(), important);
5826 case CSSPropertyBorderRadius:
5827 case CSSPropertyWebkitBorderRadius: {
5828 RefPtr<CSSPrimitiveValue> horizontalRadii[4];
5829 RefPtr<CSSPrimitiveValue> verticalRadii[4];
5830 if (!consumeRadii(horizontalRadii, verticalRadii, m_range, m_context.mode, property == CSSPropertyWebkitBorderRadius))
5831 return false;
5832 addProperty(CSSPropertyBorderTopLeftRadius, CSSPropertyBorderRadius, createPrimitiveValuePair(horizontalRadii[0].releaseNonNull(), verticalRadii[0].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce), important);
5833 addProperty(CSSPropertyBorderTopRightRadius, CSSPropertyBorderRadius, createPrimitiveValuePair(horizontalRadii[1].releaseNonNull(), verticalRadii[1].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce), important);
5834 addProperty(CSSPropertyBorderBottomRightRadius, CSSPropertyBorderRadius, createPrimitiveValuePair(horizontalRadii[2].releaseNonNull(), verticalRadii[2].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce), important);
5835 addProperty(CSSPropertyBorderBottomLeftRadius, CSSPropertyBorderRadius, createPrimitiveValuePair(horizontalRadii[3].releaseNonNull(), verticalRadii[3].releaseNonNull(), Pair::IdenticalValueEncoding::Coalesce), important);
5836 return true;
5837 }
5838 case CSSPropertyBorderColor:
5839 return consume4ValueShorthand(borderColorShorthand(), important);
5840 case CSSPropertyBorderStyle:
5841 return consume4ValueShorthand(borderStyleShorthand(), important);
5842 case CSSPropertyBorderWidth:
5843 return consume4ValueShorthand(borderWidthShorthand(), important);
5844 case CSSPropertyBorderTop:
5845 return consumeShorthandGreedily(borderTopShorthand(), important);
5846 case CSSPropertyBorderRight:
5847 return consumeShorthandGreedily(borderRightShorthand(), important);
5848 case CSSPropertyBorderBottom:
5849 return consumeShorthandGreedily(borderBottomShorthand(), important);
5850 case CSSPropertyBorderLeft:
5851 return consumeShorthandGreedily(borderLeftShorthand(), important);
5852 case CSSPropertyBorder: {
5853 RefPtr<CSSValue> width;
5854 RefPtr<CSSValue> style;
5855 RefPtr<CSSValue> color;
5856 if (!consumeBorder(width, style, color))
5857 return false;
5858
5859 addExpandedPropertyForValue(CSSPropertyBorderWidth, width.releaseNonNull(), important);
5860 addExpandedPropertyForValue(CSSPropertyBorderStyle, style.releaseNonNull(), important);
5861 addExpandedPropertyForValue(CSSPropertyBorderColor, color.releaseNonNull(), important);
5862 addExpandedPropertyForValue(CSSPropertyBorderImage, CSSValuePool::singleton().createImplicitInitialValue(), important);
5863 return true;
5864 }
5865 case CSSPropertyBorderImage:
5866 return consumeBorderImage(property, important);
5867 case CSSPropertyPageBreakAfter:
5868 case CSSPropertyPageBreakBefore:
5869 case CSSPropertyPageBreakInside:
5870 case CSSPropertyWebkitColumnBreakAfter:
5871 case CSSPropertyWebkitColumnBreakBefore:
5872 case CSSPropertyWebkitColumnBreakInside:
5873 return consumeLegacyBreakProperty(property, important);
5874 case CSSPropertyWebkitMaskPosition:
5875 case CSSPropertyBackgroundPosition: {
5876 RefPtr<CSSValue> resultX;
5877 RefPtr<CSSValue> resultY;
5878 if (!consumeBackgroundPosition(m_range, m_context, UnitlessQuirk::Allow, resultX, resultY) || !m_range.atEnd())
5879 return false;
5880 addProperty(property == CSSPropertyBackgroundPosition ? CSSPropertyBackgroundPositionX : CSSPropertyWebkitMaskPositionX, property, resultX.releaseNonNull(), important);
5881 addProperty(property == CSSPropertyBackgroundPosition ? CSSPropertyBackgroundPositionY : CSSPropertyWebkitMaskPositionY, property, resultY.releaseNonNull(), important);
5882 return true;
5883 }
5884 case CSSPropertyBackgroundRepeat:
5885 case CSSPropertyWebkitMaskRepeat: {
5886 RefPtr<CSSValue> resultX;
5887 RefPtr<CSSValue> resultY;
5888 bool implicit = false;
5889 if (!consumeRepeatStyle(m_range, resultX, resultY, implicit) || !m_range.atEnd())
5890 return false;
5891 addProperty(property == CSSPropertyBackgroundRepeat ? CSSPropertyBackgroundRepeatX : CSSPropertyWebkitMaskRepeatX, property, resultX.releaseNonNull(), important, implicit);
5892 addProperty(property == CSSPropertyBackgroundRepeat ? CSSPropertyBackgroundRepeatY : CSSPropertyWebkitMaskRepeatY, property, resultY.releaseNonNull(), important, implicit);
5893 return true;
5894 }
5895 case CSSPropertyBackground:
5896 return consumeBackgroundShorthand(backgroundShorthand(), important);
5897 case CSSPropertyWebkitMask:
5898 return consumeBackgroundShorthand(webkitMaskShorthand(), important);
5899 case CSSPropertyTransformOrigin:
5900 return consumeTransformOrigin(important);
5901 case CSSPropertyPerspectiveOrigin:
5902 return consumePerspectiveOrigin(important);
5903 case CSSPropertyGap: {
5904 RefPtr<CSSValue> rowGap = consumeGapLength(m_range, m_context.mode);
5905 RefPtr<CSSValue> columnGap = consumeGapLength(m_range, m_context.mode);
5906 if (!rowGap || !m_range.atEnd())
5907 return false;
5908 if (!columnGap)
5909 columnGap = rowGap;
5910 addProperty(CSSPropertyRowGap, CSSPropertyGap, rowGap.releaseNonNull(), important);
5911 addProperty(CSSPropertyColumnGap, CSSPropertyGap, columnGap.releaseNonNull(), important);
5912 return true;
5913 }
5914 case CSSPropertyGridColumn:
5915 case CSSPropertyGridRow:
5916 return consumeGridItemPositionShorthand(property, important);
5917 case CSSPropertyGridArea:
5918 return consumeGridAreaShorthand(important);
5919 case CSSPropertyGridTemplate:
5920 return consumeGridTemplateShorthand(CSSPropertyGridTemplate, important);
5921 case CSSPropertyGrid:
5922 return consumeGridShorthand(important);
5923 case CSSPropertyPlaceContent:
5924 return consumePlaceContentShorthand(important);
5925 case CSSPropertyPlaceItems:
5926 return consumePlaceItemsShorthand(important);
5927 case CSSPropertyPlaceSelf:
5928 return consumePlaceSelfShorthand(important);
5929 case CSSPropertyWebkitMarquee:
5930 return consumeShorthandGreedily(webkitMarqueeShorthand(), important);
5931 default:
5932 return false;
5933 }
5934}
5935
5936} // namespace WebCore
5937