Bumping manifests a=b2g-bump
[gecko.git] / layout / style / nsCSSParser.cpp
blob5f1be7ff3fd3506c30fa476176d4ae948da1b77f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=78: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/Move.h"
12 #include "mozilla/MathAlgorithms.h"
14 #include "nsCSSParser.h"
15 #include "nsCSSProps.h"
16 #include "nsCSSKeywords.h"
17 #include "nsCSSScanner.h"
18 #include "mozilla/css/ErrorReporter.h"
19 #include "mozilla/css/Loader.h"
20 #include "mozilla/css/StyleRule.h"
21 #include "mozilla/css/ImportRule.h"
22 #include "nsCSSRules.h"
23 #include "mozilla/css/NameSpaceRule.h"
24 #include "nsTArray.h"
25 #include "mozilla/CSSStyleSheet.h"
26 #include "mozilla/css/Declaration.h"
27 #include "nsStyleConsts.h"
28 #include "nsNetUtil.h"
29 #include "nsCOMPtr.h"
30 #include "nsString.h"
31 #include "nsIAtom.h"
32 #include "nsColor.h"
33 #include "nsCSSPseudoClasses.h"
34 #include "nsCSSPseudoElements.h"
35 #include "nsNameSpaceManager.h"
36 #include "nsXMLNameSpaceMap.h"
37 #include "nsError.h"
38 #include "nsIMediaList.h"
39 #include "nsStyleUtil.h"
40 #include "nsIPrincipal.h"
41 #include "prprf.h"
42 #include "nsContentUtils.h"
43 #include "nsAutoPtr.h"
44 #include "CSSCalc.h"
45 #include "nsMediaFeatures.h"
46 #include "nsLayoutUtils.h"
47 #include "mozilla/Preferences.h"
48 #include "nsRuleData.h"
49 #include "mozilla/CSSVariableValues.h"
50 #include "mozilla/dom/URL.h"
51 #include "gfxFontFamilyList.h"
53 using namespace mozilla;
55 typedef nsCSSProps::KTableValue KTableValue;
57 const uint32_t
58 nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
59 #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \
60 stylestruct_, stylestructoffset_, animtype_) \
61 parsevariant_,
62 #include "nsCSSPropList.h"
63 #undef CSS_PROP
66 // Maximum number of repetitions for the repeat() function
67 // in the grid-template-columns and grid-template-rows properties,
68 // to limit high memory usage from small stylesheets.
69 // Must be a positive integer. Should be large-ish.
70 #define GRID_TEMPLATE_MAX_REPETITIONS 10000
72 // End-of-array marker for mask arguments to ParseBitmaskValues
73 #define MASK_END_VALUE (-1)
75 MOZ_BEGIN_ENUM_CLASS(CSSParseResult, int32_t)
76 // Parsed something successfully:
77 Ok,
78 // Did not find what we were looking for, but did not consume any token:
79 NotFound,
80 // Unexpected token or token value, too late for UngetToken() to be enough:
81 Error
82 MOZ_END_ENUM_CLASS(CSSParseResult)
84 namespace {
86 // Rule processing function
87 typedef void (* RuleAppendFunc) (css::Rule* aRule, void* aData);
88 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer);
89 static void AppendRuleToSheet(css::Rule* aRule, void* aParser);
91 struct CSSParserInputState {
92 nsCSSScannerPosition mPosition;
93 nsCSSToken mToken;
94 bool mHavePushBack;
97 // Your basic top-down recursive descent style parser
98 // The exposed methods and members of this class are precisely those
99 // needed by nsCSSParser, far below.
100 class CSSParserImpl {
101 public:
102 CSSParserImpl();
103 ~CSSParserImpl();
105 nsresult SetStyleSheet(CSSStyleSheet* aSheet);
107 nsresult SetQuirkMode(bool aQuirkMode);
109 nsresult SetChildLoader(mozilla::css::Loader* aChildLoader);
111 // Clears everything set by the above Set*() functions.
112 void Reset();
114 nsresult ParseSheet(const nsAString& aInput,
115 nsIURI* aSheetURI,
116 nsIURI* aBaseURI,
117 nsIPrincipal* aSheetPrincipal,
118 uint32_t aLineNumber,
119 bool aAllowUnsafeRules);
121 nsresult ParseStyleAttribute(const nsAString& aAttributeValue,
122 nsIURI* aDocURL,
123 nsIURI* aBaseURL,
124 nsIPrincipal* aNodePrincipal,
125 css::StyleRule** aResult);
127 nsresult ParseDeclarations(const nsAString& aBuffer,
128 nsIURI* aSheetURL,
129 nsIURI* aBaseURL,
130 nsIPrincipal* aSheetPrincipal,
131 css::Declaration* aDeclaration,
132 bool* aChanged);
134 nsresult ParseRule(const nsAString& aRule,
135 nsIURI* aSheetURL,
136 nsIURI* aBaseURL,
137 nsIPrincipal* aSheetPrincipal,
138 css::Rule** aResult);
140 nsresult ParseProperty(const nsCSSProperty aPropID,
141 const nsAString& aPropValue,
142 nsIURI* aSheetURL,
143 nsIURI* aBaseURL,
144 nsIPrincipal* aSheetPrincipal,
145 css::Declaration* aDeclaration,
146 bool* aChanged,
147 bool aIsImportant,
148 bool aIsSVGMode);
150 void ParseMediaList(const nsSubstring& aBuffer,
151 nsIURI* aURL, // for error reporting
152 uint32_t aLineNumber, // for error reporting
153 nsMediaList* aMediaList,
154 bool aHTMLMode);
156 bool ParseSourceSizeList(const nsAString& aBuffer,
157 nsIURI* aURI, // for error reporting
158 uint32_t aLineNumber, // for error reporting
159 InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
160 InfallibleTArray<nsCSSValue>& aValues,
161 bool aHTMLMode);
163 nsresult ParseVariable(const nsAString& aVariableName,
164 const nsAString& aPropValue,
165 nsIURI* aSheetURL,
166 nsIURI* aBaseURL,
167 nsIPrincipal* aSheetPrincipal,
168 css::Declaration* aDeclaration,
169 bool* aChanged,
170 bool aIsImportant);
172 bool ParseFontFamilyListString(const nsSubstring& aBuffer,
173 nsIURI* aURL, // for error reporting
174 uint32_t aLineNumber, // for error reporting
175 nsCSSValue& aValue);
177 bool ParseColorString(const nsSubstring& aBuffer,
178 nsIURI* aURL, // for error reporting
179 uint32_t aLineNumber, // for error reporting
180 nsCSSValue& aValue,
181 bool aSuppressErrors /* false */);
183 nsresult ParseSelectorString(const nsSubstring& aSelectorString,
184 nsIURI* aURL, // for error reporting
185 uint32_t aLineNumber, // for error reporting
186 nsCSSSelectorList **aSelectorList);
188 already_AddRefed<nsCSSKeyframeRule>
189 ParseKeyframeRule(const nsSubstring& aBuffer,
190 nsIURI* aURL,
191 uint32_t aLineNumber);
193 bool ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
194 nsIURI* aURL, // for error reporting
195 uint32_t aLineNumber, // for error reporting
196 InfallibleTArray<float>& aSelectorList);
198 bool EvaluateSupportsDeclaration(const nsAString& aProperty,
199 const nsAString& aValue,
200 nsIURI* aDocURL,
201 nsIURI* aBaseURL,
202 nsIPrincipal* aDocPrincipal);
204 bool EvaluateSupportsCondition(const nsAString& aCondition,
205 nsIURI* aDocURL,
206 nsIURI* aBaseURL,
207 nsIPrincipal* aDocPrincipal);
209 bool ParseCounterStyleName(const nsAString& aBuffer,
210 nsIURI* aURL,
211 nsAString& aName);
213 bool ParseCounterDescriptor(nsCSSCounterDesc aDescID,
214 const nsAString& aBuffer,
215 nsIURI* aSheetURL,
216 nsIURI* aBaseURL,
217 nsIPrincipal* aSheetPrincipal,
218 nsCSSValue& aValue);
220 bool ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
221 const nsAString& aBuffer,
222 nsIURI* aSheetURL,
223 nsIURI* aBaseURL,
224 nsIPrincipal* aSheetPrincipal,
225 nsCSSValue& aValue);
227 bool IsValueValidForProperty(const nsCSSProperty aPropID,
228 const nsAString& aPropValue);
230 typedef nsCSSParser::VariableEnumFunc VariableEnumFunc;
233 * Parses a CSS token stream value and invokes a callback function for each
234 * variable reference that is encountered.
236 * @param aPropertyValue The CSS token stream value.
237 * @param aFunc The callback function to invoke; its parameters are the
238 * variable name found and the aData argument passed in to this function.
239 * @param aData User data to pass in to the callback.
240 * @return Whether aPropertyValue could be parsed as a valid CSS token stream
241 * value (e.g., without syntactic errors in variable references).
243 bool EnumerateVariableReferences(const nsAString& aPropertyValue,
244 VariableEnumFunc aFunc,
245 void* aData);
248 * Parses aPropertyValue as a CSS token stream value and resolves any
249 * variable references using the variables in aVariables.
251 * @param aPropertyValue The CSS token stream value.
252 * @param aVariables The set of variable values to use when resolving variable
253 * references.
254 * @param aResult Out parameter that gets the resolved value.
255 * @param aFirstToken Out parameter that gets the type of the first token in
256 * aResult.
257 * @param aLastToken Out parameter that gets the type of the last token in
258 * aResult.
259 * @return Whether aResult could be parsed successfully and variable reference
260 * substitution succeeded.
262 bool ResolveVariableValue(const nsAString& aPropertyValue,
263 const CSSVariableValues* aVariables,
264 nsString& aResult,
265 nsCSSTokenSerializationType& aFirstToken,
266 nsCSSTokenSerializationType& aLastToken);
269 * Parses a string as a CSS token stream value for particular property,
270 * resolving any variable references. The parsed property value is stored
271 * in the specified nsRuleData object. If aShorthandPropertyID has a value
272 * other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
273 * otherwise, aPropertyID will be parsed. Either way, only aPropertyID,
274 * a longhand property, will be copied over to the rule data.
276 * If the property cannot be parsed, it will be treated as if 'initial' or
277 * 'inherit' were specified, for non-inherited and inherited properties
278 * respectively.
280 * @param aPropertyID The ID of the longhand property whose value is to be
281 * copied to the rule data.
282 * @param aShorthandPropertyID The ID of the shorthand property to be parsed.
283 * If a longhand property is to be parsed, aPropertyID is that property,
284 * and aShorthandPropertyID must be eCSSProperty_UNKNOWN.
285 * @param aValue The CSS token stream value.
286 * @param aVariables The set of variable values to use when resolving variable
287 * references.
288 * @param aRuleData The rule data object into which parsed property value for
289 * aPropertyID will be stored.
291 void ParsePropertyWithVariableReferences(nsCSSProperty aPropertyID,
292 nsCSSProperty aShorthandPropertyID,
293 const nsAString& aValue,
294 const CSSVariableValues* aVariables,
295 nsRuleData* aRuleData,
296 nsIURI* aDocURL,
297 nsIURI* aBaseURL,
298 nsIPrincipal* aDocPrincipal,
299 CSSStyleSheet* aSheet,
300 uint32_t aLineNumber,
301 uint32_t aLineOffset);
303 nsCSSProperty LookupEnabledProperty(const nsAString& aProperty) {
304 static_assert(nsCSSProps::eEnabledForAllContent == 0,
305 "nsCSSProps::eEnabledForAllContent should be zero for "
306 "this bitfield to work");
307 nsCSSProps::EnabledState enabledState = nsCSSProps::eEnabledForAllContent;
308 if (mUnsafeRulesEnabled) {
309 enabledState |= nsCSSProps::eEnabledInUASheets;
311 if (mIsChromeOrCertifiedApp) {
312 enabledState |= nsCSSProps::eEnabledInChromeOrCertifiedApp;
314 return nsCSSProps::LookupProperty(aProperty, enabledState);
317 protected:
318 class nsAutoParseCompoundProperty;
319 friend class nsAutoParseCompoundProperty;
321 class nsAutoFailingSupportsRule;
322 friend class nsAutoFailingSupportsRule;
324 class nsAutoSuppressErrors;
325 friend class nsAutoSuppressErrors;
327 void AppendRule(css::Rule* aRule);
328 friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule
331 * This helper class automatically calls SetParsingCompoundProperty in its
332 * constructor and takes care of resetting it to false in its destructor.
334 class nsAutoParseCompoundProperty {
335 public:
336 explicit nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
338 NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
339 "already parsing compound property");
340 NS_ASSERTION(aParser, "Null parser?");
341 aParser->SetParsingCompoundProperty(true);
344 ~nsAutoParseCompoundProperty()
346 mParser->SetParsingCompoundProperty(false);
348 private:
349 CSSParserImpl* mParser;
353 * This helper class conditionally sets mInFailingSupportsRule to
354 * true if aCondition = false, and resets it to its original value in its
355 * destructor. If we are already somewhere within a failing @supports
356 * rule, passing in aCondition = true does not change mInFailingSupportsRule.
358 class nsAutoFailingSupportsRule {
359 public:
360 nsAutoFailingSupportsRule(CSSParserImpl* aParser,
361 bool aCondition)
362 : mParser(aParser),
363 mOriginalValue(aParser->mInFailingSupportsRule)
365 if (!aCondition) {
366 mParser->mInFailingSupportsRule = true;
370 ~nsAutoFailingSupportsRule()
372 mParser->mInFailingSupportsRule = mOriginalValue;
375 private:
376 CSSParserImpl* mParser;
377 bool mOriginalValue;
381 * Auto class to set aParser->mSuppressErrors to the specified value
382 * and restore it to its original value later.
384 class nsAutoSuppressErrors {
385 public:
386 explicit nsAutoSuppressErrors(CSSParserImpl* aParser,
387 bool aSuppressErrors = true)
388 : mParser(aParser),
389 mOriginalValue(aParser->mSuppressErrors)
391 mParser->mSuppressErrors = aSuppressErrors;
394 ~nsAutoSuppressErrors()
396 mParser->mSuppressErrors = mOriginalValue;
399 private:
400 CSSParserImpl* mParser;
401 bool mOriginalValue;
404 // the caller must hold on to aString until parsing is done
405 void InitScanner(nsCSSScanner& aScanner,
406 css::ErrorReporter& aReporter,
407 nsIURI* aSheetURI, nsIURI* aBaseURI,
408 nsIPrincipal* aSheetPrincipal);
409 void ReleaseScanner(void);
410 bool IsSVGMode() const {
411 return mScanner->IsSVGMode();
415 * Saves the current input state, which includes any currently pushed
416 * back token, and the current position of the scanner.
418 void SaveInputState(CSSParserInputState& aState);
421 * Restores the saved input state by pushing back any saved pushback
422 * token and calling RestoreSavedPosition on the scanner.
424 void RestoreSavedInputState(const CSSParserInputState& aState);
426 bool GetToken(bool aSkipWS);
427 void UngetToken();
428 bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum);
430 bool ExpectSymbol(char16_t aSymbol, bool aSkipWS);
431 bool ExpectEndProperty();
432 bool CheckEndProperty();
433 nsSubstring* NextIdent();
435 // returns true when the stop symbol is found, and false for EOF
436 bool SkipUntil(char16_t aStopSymbol);
437 void SkipUntilOneOf(const char16_t* aStopSymbolChars);
438 // For each character in aStopSymbolChars from the end of the array
439 // to the start, calls SkipUntil with that character.
440 typedef nsAutoTArray<char16_t, 16> StopSymbolCharStack;
441 void SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars);
442 // returns true if the stop symbol or EOF is found, and false for an
443 // unexpected ')', ']' or '}'; this not safe to call outside variable
444 // resolution, as it doesn't handle mismatched content
445 bool SkipBalancedContentUntil(char16_t aStopSymbol);
447 void SkipRuleSet(bool aInsideBraces);
448 bool SkipAtRule(bool aInsideBlock);
449 bool SkipDeclaration(bool aCheckForBraces);
451 void PushGroup(css::GroupRule* aRule);
452 void PopGroup();
454 bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData,
455 bool aInsideBraces = false);
456 bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData,
457 bool aInAtRule);
458 bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
459 bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
460 bool ParseURLOrString(nsString& aURL);
461 bool GatherMedia(nsMediaList* aMedia, bool aInAtRule);
463 enum eMediaQueryType { eMediaQueryNormal,
464 // Parsing an at rule
465 eMediaQueryAtRule,
466 // Attempt to consume a single media-condition and
467 // stop. Note that the spec defines "expression and/or
468 // expression" as one condition but "expression,
469 // expression" as two.
470 eMediaQuerySingleCondition };
471 bool ParseMediaQuery(eMediaQueryType aMode, nsMediaQuery **aQuery,
472 bool *aHitStop);
473 bool ParseMediaQueryExpression(nsMediaQuery* aQuery);
474 void ProcessImport(const nsString& aURLSpec,
475 nsMediaList* aMedia,
476 RuleAppendFunc aAppendFunc,
477 void* aProcessData,
478 uint32_t aLineNumber,
479 uint32_t aColumnNumber);
480 bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc,
481 void* aProcessData);
482 bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
483 bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
484 bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
485 void ProcessNameSpace(const nsString& aPrefix,
486 const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
487 void* aProcessData,
488 uint32_t aLineNumber, uint32_t aColumnNumber);
490 bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
491 bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
492 void* aProcessData);
493 bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule);
494 bool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
495 bool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
496 nsCSSValue& aValue);
498 bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
499 bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData);
500 already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule();
501 bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList);
503 bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData);
504 bool ParseSupportsCondition(bool& aConditionMet);
505 bool ParseSupportsConditionNegation(bool& aConditionMet);
506 bool ParseSupportsConditionInParens(bool& aConditionMet);
507 bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet);
508 bool ParseSupportsConditionTerms(bool& aConditionMet);
509 enum SupportsConditionTermOperator { eAnd, eOr };
510 bool ParseSupportsConditionTermsAfterOperator(
511 bool& aConditionMet,
512 SupportsConditionTermOperator aOperator);
514 bool ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aProcessData);
515 bool ParseCounterStyleName(nsAString& aName, bool aForDefinition);
516 bool ParseCounterStyleNameValue(nsCSSValue& aValue);
517 bool ParseCounterDescriptor(nsCSSCounterStyleRule *aRule);
518 bool ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
519 nsCSSValue& aValue);
520 bool ParseCounterRange(nsCSSValuePair& aPair);
523 * Parses the current input stream for a CSS token stream value and resolves
524 * any variable references using the variables in aVariables.
526 * @param aVariables The set of variable values to use when resolving variable
527 * references.
528 * @param aResult Out parameter that, if the function returns true, will be
529 * replaced with the resolved value.
530 * @return Whether aResult could be parsed successfully and variable reference
531 * substitution succeeded.
533 bool ResolveValueWithVariableReferences(
534 const CSSVariableValues* aVariables,
535 nsString& aResult,
536 nsCSSTokenSerializationType& aResultFirstToken,
537 nsCSSTokenSerializationType& aResultLastToken);
538 // Helper function for ResolveValueWithVariableReferences.
539 bool ResolveValueWithVariableReferencesRec(
540 nsString& aResult,
541 nsCSSTokenSerializationType& aResultFirstToken,
542 nsCSSTokenSerializationType& aResultLastToken,
543 const CSSVariableValues* aVariables);
545 enum nsSelectorParsingStatus {
546 // we have parsed a selector and we saw a token that cannot be
547 // part of a selector:
548 eSelectorParsingStatus_Done,
549 // we should continue parsing the selector:
550 eSelectorParsingStatus_Continue,
551 // we saw an unexpected token or token value,
552 // or we saw end-of-file with an unfinished selector:
553 eSelectorParsingStatus_Error
555 nsSelectorParsingStatus ParseIDSelector(int32_t& aDataMask,
556 nsCSSSelector& aSelector);
558 nsSelectorParsingStatus ParseClassSelector(int32_t& aDataMask,
559 nsCSSSelector& aSelector);
561 // aPseudoElement and aPseudoElementArgs are the location where
562 // pseudo-elements (as opposed to pseudo-classes) are stored;
563 // pseudo-classes are stored on aSelector. aPseudoElement and
564 // aPseudoElementArgs must be non-null iff !aIsNegated.
565 nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask,
566 nsCSSSelector& aSelector,
567 bool aIsNegated,
568 nsIAtom** aPseudoElement,
569 nsAtomList** aPseudoElementArgs,
570 nsCSSPseudoElements::Type* aPseudoElementType);
572 nsSelectorParsingStatus ParseAttributeSelector(int32_t& aDataMask,
573 nsCSSSelector& aSelector);
575 nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask,
576 nsCSSSelector& aSelector,
577 bool aIsNegated);
579 nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
580 nsCSSPseudoClasses::Type aType);
582 nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
583 nsCSSPseudoClasses::Type aType);
585 nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
586 nsCSSPseudoClasses::Type aType);
588 nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask,
589 nsCSSSelector& aSelector);
591 // If aStopChar is non-zero, the selector list is done when we hit
592 // aStopChar. Otherwise, it's done when we hit EOF.
593 bool ParseSelectorList(nsCSSSelectorList*& aListHead,
594 char16_t aStopChar);
595 bool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
596 bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator);
598 enum {
599 eParseDeclaration_InBraces = 1 << 0,
600 eParseDeclaration_AllowImportant = 1 << 1
602 enum nsCSSContextType {
603 eCSSContext_General,
604 eCSSContext_Page
607 css::Declaration* ParseDeclarationBlock(uint32_t aFlags,
608 nsCSSContextType aContext = eCSSContext_General);
609 bool ParseDeclaration(css::Declaration* aDeclaration,
610 uint32_t aFlags,
611 bool aMustCallValueAppended,
612 bool* aChanged,
613 nsCSSContextType aContext = eCSSContext_General);
615 bool ParseProperty(nsCSSProperty aPropID);
616 bool ParsePropertyByFunction(nsCSSProperty aPropID);
617 bool ParseSingleValueProperty(nsCSSValue& aValue,
618 nsCSSProperty aPropID);
620 enum PriorityParsingStatus {
621 ePriority_None,
622 ePriority_Important,
623 ePriority_Error
625 PriorityParsingStatus ParsePriority();
627 #ifdef MOZ_XUL
628 bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs);
629 #endif
631 void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties);
633 // Property specific parsing routines
634 bool ParseBackground();
636 struct BackgroundParseState {
637 nsCSSValue& mColor;
638 nsCSSValueList* mImage;
639 nsCSSValuePairList* mRepeat;
640 nsCSSValueList* mAttachment;
641 nsCSSValueList* mClip;
642 nsCSSValueList* mOrigin;
643 nsCSSValueList* mPosition;
644 nsCSSValuePairList* mSize;
645 BackgroundParseState(
646 nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
647 nsCSSValueList* aAttachment, nsCSSValueList* aClip,
648 nsCSSValueList* aOrigin, nsCSSValueList* aPosition,
649 nsCSSValuePairList* aSize) :
650 mColor(aColor), mImage(aImage), mRepeat(aRepeat),
651 mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
652 mPosition(aPosition), mSize(aSize) {};
655 bool ParseBackgroundItem(BackgroundParseState& aState);
657 bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id
658 bool ParseBackgroundRepeat();
659 bool ParseBackgroundRepeatValues(nsCSSValuePair& aValue);
660 bool ParseBackgroundPosition();
662 // ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
663 // which is still used by some properties. See ParsePositionValue
664 // for the css3-background syntax.
665 bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit,
666 bool aAllowExplicitCenter = true); // deprecated
668 // ParsePositionValue parses a CSS <position> value, which is used by
669 // the 'background-position' property.
670 bool ParsePositionValue(nsCSSValue& aOut);
672 bool ParseBackgroundSize();
673 bool ParseBackgroundSizeValues(nsCSSValuePair& aOut);
674 bool ParseBorderColor();
675 bool ParseBorderColors(nsCSSProperty aProperty);
676 void SetBorderImageInitialValues();
677 bool ParseBorderImageRepeat(bool aAcceptsInherit);
678 // If ParseBorderImageSlice returns false, aConsumedTokens indicates
679 // whether or not any tokens were consumed (in other words, was the property
680 // in error or just not present). If ParseBorderImageSlice returns true
681 // aConsumedTokens is always true.
682 bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens);
683 bool ParseBorderImageWidth(bool aAcceptsInherit);
684 bool ParseBorderImageOutset(bool aAcceptsInherit);
685 bool ParseBorderImage();
686 bool ParseBorderSpacing();
687 bool ParseBorderSide(const nsCSSProperty aPropIDs[],
688 bool aSetAllSides);
689 bool ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
690 int32_t aSourceType);
691 bool ParseBorderStyle();
692 bool ParseBorderWidth();
694 bool ParseCalc(nsCSSValue &aValue, int32_t aVariantMask);
695 bool ParseCalcAdditiveExpression(nsCSSValue& aValue,
696 int32_t& aVariantMask);
697 bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
698 int32_t& aVariantMask,
699 bool *aHadFinalWS);
700 bool ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask);
701 bool RequireWhitespace();
703 // For "flex" shorthand property, defined in CSS Flexbox spec
704 bool ParseFlex();
705 // For "flex-flow" shorthand property, defined in CSS Flexbox spec
706 bool ParseFlexFlow();
708 // CSS Grid
709 bool ParseGridAutoFlow();
711 // Parse a <line-names> expression.
712 // If successful, either leave aValue untouched,
713 // to indicate that we parsed the empty list,
714 // or set it to a eCSSUnit_List of eCSSUnit_Ident.
716 // To parse an optional <line-names> (ie. if not finding an open paren
717 // is considered the same as an empty list),
718 // treat CSSParseResult::NotFound the same as CSSParseResult::Ok.
720 // If aValue is already a eCSSUnit_List, append to that list.
721 CSSParseResult ParseGridLineNames(nsCSSValue& aValue);
722 bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr);
723 bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue);
724 bool ParseGridTrackBreadth(nsCSSValue& aValue);
725 CSSParseResult ParseGridTrackSize(nsCSSValue& aValue);
726 bool ParseGridAutoColumnsRows(nsCSSProperty aPropID);
727 bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr);
729 // Assuming a [ <line-names>? ] has already been parsed,
730 // parse the rest of a <track-list>.
732 // This exists because [ <line-names>? ] is ambiguous in the
733 // 'grid-template' shorthand: it can be either the start of a <track-list>,
734 // or of the intertwined syntax that sets both
735 // grid-template-rows and grid-template-areas.
737 // On success, |aValue| will be a list of odd length >= 3,
738 // starting with a <line-names> (which is itself a list)
739 // and alternating between that and <track-size>.
740 bool ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
741 const nsCSSValue& aFirstLineNames);
742 bool ParseGridTemplateColumnsRows(nsCSSProperty aPropID);
744 // |aAreaIndices| is a lookup table to help us parse faster,
745 // mapping area names to indices in |aResult.mNamedAreas|.
746 bool ParseGridTemplateAreasLine(const nsAutoString& aInput,
747 css::GridTemplateAreasValue* aResult,
748 nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices);
749 bool ParseGridTemplateAreas();
750 bool ParseGridTemplate();
751 bool ParseGridTemplateAfterSlash(bool aColumnsIsTrackList);
752 bool ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames);
753 bool ParseGrid();
754 bool ParseGridShorthandAutoProps();
755 bool ParseGridLine(nsCSSValue& aValue);
756 bool ParseGridColumnRowStartEnd(nsCSSProperty aPropID);
757 bool ParseGridColumnRow(nsCSSProperty aStartPropID,
758 nsCSSProperty aEndPropID);
759 bool ParseGridArea();
761 // for 'clip' and '-moz-image-region'
762 bool ParseRect(nsCSSProperty aPropID);
763 bool ParseColumns();
764 bool ParseContent();
765 bool ParseCounterData(nsCSSProperty aPropID);
766 bool ParseCursor();
767 bool ParseFont();
768 bool ParseFontSynthesis(nsCSSValue& aValue);
769 bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue);
770 bool ParseFontVariantAlternates(nsCSSValue& aValue);
771 bool MergeBitmaskValue(int32_t aNewValue, const int32_t aMasks[],
772 int32_t& aMergedValue);
773 bool ParseBitmaskValues(nsCSSValue& aValue,
774 const KTableValue aKeywordTable[],
775 const int32_t aMasks[]);
776 bool ParseFontVariantEastAsian(nsCSSValue& aValue);
777 bool ParseFontVariantLigatures(nsCSSValue& aValue);
778 bool ParseFontVariantNumeric(nsCSSValue& aValue);
779 bool ParseFontVariant();
780 bool ParseFontWeight(nsCSSValue& aValue);
781 bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword, bool& aQuoted);
782 bool ParseFamily(nsCSSValue& aValue);
783 bool ParseFontFeatureSettings(nsCSSValue& aValue);
784 bool ParseFontSrc(nsCSSValue& aValue);
785 bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
786 bool ParseFontRanges(nsCSSValue& aValue);
787 bool ParseListStyle();
788 bool ParseListStyleType(nsCSSValue& aValue);
789 bool ParseMargin();
790 bool ParseMarks(nsCSSValue& aValue);
791 bool ParseClipPath();
792 bool ParseTransform(bool aIsPrefixed);
793 bool ParseObjectPosition();
794 bool ParseOutline();
795 bool ParseOverflow();
796 bool ParsePadding();
797 bool ParseQuotes();
798 bool ParseRubyPosition(nsCSSValue& aValue);
799 bool ParseSize();
800 bool ParseTextAlign(nsCSSValue& aValue,
801 const KTableValue aTable[]);
802 bool ParseTextAlign(nsCSSValue& aValue);
803 bool ParseTextAlignLast(nsCSSValue& aValue);
804 bool ParseTextDecoration();
805 bool ParseTextDecorationLine(nsCSSValue& aValue);
806 bool ParseTextCombineUpright(nsCSSValue& aValue);
807 bool ParseTextOverflow(nsCSSValue& aValue);
808 bool ParseTouchAction(nsCSSValue& aValue);
810 bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow);
811 bool ParseShadowList(nsCSSProperty aProperty);
812 bool ParseTransitionProperty();
813 bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
814 bool ParseTransitionTimingFunctionValueComponent(float& aComponent,
815 char aStop,
816 bool aCheckRange);
817 bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
818 enum ParseAnimationOrTransitionShorthandResult {
819 eParseAnimationOrTransitionShorthand_Values,
820 eParseAnimationOrTransitionShorthand_Inherit,
821 eParseAnimationOrTransitionShorthand_Error
823 ParseAnimationOrTransitionShorthandResult
824 ParseAnimationOrTransitionShorthand(const nsCSSProperty* aProperties,
825 const nsCSSValue* aInitialValues,
826 nsCSSValue* aValues,
827 size_t aNumProperties);
828 bool ParseTransition();
829 bool ParseAnimation();
830 bool ParseWillChange();
832 bool ParsePaint(nsCSSProperty aPropID);
833 bool ParseDasharray();
834 bool ParseMarker();
835 bool ParsePaintOrder();
836 bool ParseAll();
839 * Parses a variable value from a custom property declaration.
841 * @param aType Out parameter into which will be stored the type of variable
842 * value, indicating whether the parsed value was a token stream or one of
843 * the CSS-wide keywords.
844 * @param aValue Out parameter into which will be stored the token stream
845 * as a string, if the parsed custom property did take a token stream.
846 * @return Whether parsing succeeded.
848 bool ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
849 nsString& aValue);
852 * Parses a CSS variable value. This could be 'initial', 'inherit', 'unset'
853 * or a token stream, which may or may not include variable references.
855 * @param aType Out parameter into which the type of the variable value
856 * will be stored.
857 * @param aDropBackslash Out parameter indicating whether during variable
858 * value parsing there was a trailing backslash before EOF that must
859 * be dropped when serializing the variable value.
860 * @param aImpliedCharacters Out parameter appended to which will be any
861 * characters that were implied when encountering EOF and which
862 * must be included at the end of the serialized variable value.
863 * @param aFunc A callback function to invoke when a variable reference
864 * is encountered. May be null. Arguments are the variable name
865 * and the aData argument passed in to this function.
866 * @param User data to pass in to the callback.
867 * @return Whether parsing succeeded.
869 bool ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
870 bool* aDropBackslash,
871 nsString& aImpliedCharacters,
872 void (*aFunc)(const nsAString&, void*),
873 void* aData);
876 * Returns whether the scanner dropped a backslash just before EOF.
878 bool BackslashDropped();
881 * Calls AppendImpliedEOFCharacters on mScanner.
883 void AppendImpliedEOFCharacters(nsAString& aResult);
885 // Reused utility parsing routines
886 void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
887 bool ParseBoxProperties(const nsCSSProperty aPropIDs[]);
888 bool ParseGroupedBoxProperty(int32_t aVariantMask,
889 nsCSSValue& aValue);
890 bool ParseDirectionalBoxProperty(nsCSSProperty aProperty,
891 int32_t aSourceType);
892 bool ParseBoxCornerRadius(const nsCSSProperty aPropID);
893 bool ParseBoxCornerRadiiInternals(nsCSSValue array[]);
894 bool ParseBoxCornerRadii(const nsCSSProperty aPropIDs[]);
896 int32_t ParseChoice(nsCSSValue aValues[],
897 const nsCSSProperty aPropIDs[], int32_t aNumIDs);
898 bool ParseColor(nsCSSValue& aValue);
899 bool ParseNumberColorComponent(uint8_t& aComponent, char aStop);
900 bool ParsePercentageColorComponent(float& aComponent, char aStop);
901 // ParseHSLColor parses everything starting with the opening '('
902 // up through and including the aStop char.
903 bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
904 char aStop);
905 // ParseColorOpacity will enforce that the color ends with a ')'
906 // after the opacity
907 bool ParseColorOpacity(uint8_t& aOpacity);
908 bool ParseColorOpacity(float& aOpacity);
909 bool ParseEnum(nsCSSValue& aValue,
910 const KTableValue aKeywordTable[]);
911 bool ParseVariant(nsCSSValue& aValue,
912 int32_t aVariantMask,
913 const KTableValue aKeywordTable[]);
914 bool ParseNonNegativeVariant(nsCSSValue& aValue,
915 int32_t aVariantMask,
916 const KTableValue aKeywordTable[]);
917 bool ParseOneOrLargerVariant(nsCSSValue& aValue,
918 int32_t aVariantMask,
919 const KTableValue aKeywordTable[]);
920 bool ParseNonNegativeInteger(nsCSSValue& aValue)
922 return ParseNonNegativeVariant(aValue, VARIANT_INTEGER, nullptr);
925 // http://dev.w3.org/csswg/css-values/#custom-idents
926 // Parse an identifier that is none of:
927 // * a CSS-wide keyword
928 // * "default"
929 // * a keyword in |aExcludedKeywords|
930 // * a keyword in |aPropertyKTable|
932 // |aExcludedKeywords| is an array of nsCSSKeyword
933 // that ends with a eCSSKeyword_UNKNOWN marker.
935 // |aPropertyKTable| can be used if some of the keywords to exclude
936 // also appear in an existing nsCSSProps::KTableValue,
937 // to avoid duplicating them.
938 bool ParseCustomIdent(nsCSSValue& aValue,
939 const nsAutoString& aIdentValue,
940 const nsCSSKeyword aExcludedKeywords[] = nullptr,
941 const nsCSSProps::KTableValue aPropertyKTable[] = nullptr);
942 bool ParseCounter(nsCSSValue& aValue);
943 bool ParseAttr(nsCSSValue& aValue);
944 bool ParseSymbols(nsCSSValue& aValue);
945 bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL);
946 bool TranslateDimension(nsCSSValue& aValue, int32_t aVariantMask,
947 float aNumber, const nsString& aUnit);
948 bool ParseImageOrientation(nsCSSValue& aAngle);
949 bool ParseImageRect(nsCSSValue& aImage);
950 bool ParseElement(nsCSSValue& aValue);
951 bool ParseColorStop(nsCSSValueGradient* aGradient);
952 bool ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating,
953 bool aIsLegacy);
954 bool ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating,
955 bool aIsLegacy);
956 bool IsLegacyGradientLine(const nsCSSTokenType& aType,
957 const nsString& aId);
958 bool ParseGradientColorStops(nsCSSValueGradient* aGradient,
959 nsCSSValue& aValue);
961 void SetParsingCompoundProperty(bool aBool) {
962 mParsingCompoundProperty = aBool;
964 bool IsParsingCompoundProperty(void) const {
965 return mParsingCompoundProperty;
968 /* Functions for basic shapes */
969 bool ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens);
970 bool ParsePolygonFunction(nsCSSValue& aValue);
971 bool ParseCircleOrEllipseFunction(nsCSSKeyword, nsCSSValue& aValue);
972 bool ParseInsetFunction(nsCSSValue& aValue);
974 /* Functions for transform Parsing */
975 bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue);
976 bool ParseFunction(nsCSSKeyword aFunction, const int32_t aAllowedTypes[],
977 int32_t aVariantMaskAll, uint16_t aMinElems,
978 uint16_t aMaxElems, nsCSSValue &aValue);
979 bool ParseFunctionInternals(const int32_t aVariantMask[],
980 int32_t aVariantMaskAll,
981 uint16_t aMinElems,
982 uint16_t aMaxElems,
983 InfallibleTArray<nsCSSValue>& aOutput);
985 /* Functions for transform-origin/perspective-origin Parsing */
986 bool ParseTransformOrigin(bool aPerspective);
988 /* Functions for filter parsing */
989 bool ParseFilter();
990 bool ParseSingleFilter(nsCSSValue* aValue);
991 bool ParseDropShadow(nsCSSValue* aValue);
993 /* Find and return the namespace ID associated with aPrefix.
994 If aPrefix has not been declared in an @namespace rule, returns
995 kNameSpaceID_Unknown. */
996 int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
998 /* Find the correct default namespace, and set it on aSelector. */
999 void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
1001 // Current token. The value is valid after calling GetToken and invalidated
1002 // by UngetToken.
1003 nsCSSToken mToken;
1005 // Our scanner.
1006 nsCSSScanner* mScanner;
1008 // Our error reporter.
1009 css::ErrorReporter* mReporter;
1011 // The URI to be used as a base for relative URIs.
1012 nsCOMPtr<nsIURI> mBaseURI;
1014 // The URI to be used as an HTTP "Referer" and for error reporting.
1015 nsCOMPtr<nsIURI> mSheetURI;
1017 // The principal of the sheet involved
1018 nsCOMPtr<nsIPrincipal> mSheetPrincipal;
1020 // The sheet we're parsing into
1021 nsRefPtr<CSSStyleSheet> mSheet;
1023 // Used for @import rules
1024 mozilla::css::Loader* mChildLoader; // not ref counted, it owns us
1026 // Sheet section we're in. This is used to enforce correct ordering of the
1027 // various rule types (eg the fact that a @charset rule must come before
1028 // anything else). Note that there are checks of similar things in various
1029 // places in CSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
1030 enum nsCSSSection {
1031 eCSSSection_Charset,
1032 eCSSSection_Import,
1033 eCSSSection_NameSpace,
1034 eCSSSection_General
1036 nsCSSSection mSection;
1038 nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it
1040 // After an UngetToken is done this flag is true. The next call to
1041 // GetToken clears the flag.
1042 bool mHavePushBack : 1;
1044 // True if we are in quirks mode; false in standards or almost standards mode
1045 bool mNavQuirkMode : 1;
1047 // True when the hashless color quirk applies.
1048 bool mHashlessColorQuirk : 1;
1050 // True when the unitless length quirk applies.
1051 bool mUnitlessLengthQuirk : 1;
1053 // True if unsafe rules should be allowed
1054 bool mUnsafeRulesEnabled : 1;
1056 // True if we are in parsing rules for Chrome or Certified App content,
1057 // in which case CSS properties with the
1058 // CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP
1059 // flag should be allowed.
1060 bool mIsChromeOrCertifiedApp : 1;
1062 // True if viewport units should be allowed.
1063 bool mViewportUnitsEnabled : 1;
1065 // True for parsing media lists for HTML attributes, where we have to
1066 // ignore CSS comments.
1067 bool mHTMLMediaMode : 1;
1069 // This flag is set when parsing a non-box shorthand; it's used to not apply
1070 // some quirks during shorthand parsing
1071 bool mParsingCompoundProperty : 1;
1073 // True if we are in the middle of parsing an @supports condition.
1074 // This is used to avoid recording the input stream when variable references
1075 // are encountered in a property declaration in the @supports condition.
1076 bool mInSupportsCondition : 1;
1078 // True if we are somewhere within a @supports rule whose condition is
1079 // false.
1080 bool mInFailingSupportsRule : 1;
1082 // True if we will suppress all parse errors (except unexpected EOFs).
1083 // This is used to prevent errors for declarations inside a failing
1084 // @supports rule.
1085 bool mSuppressErrors : 1;
1087 // Stack of rule groups; used for @media and such.
1088 InfallibleTArray<nsRefPtr<css::GroupRule> > mGroupStack;
1090 // During the parsing of a property (which may be a shorthand), the data
1091 // are stored in |mTempData|. (It is needed to ensure that parser
1092 // errors cause the data to be ignored, and to ensure that a
1093 // non-'!important' declaration does not override an '!important'
1094 // one.)
1095 nsCSSExpandedDataBlock mTempData;
1097 // All data from successfully parsed properties are placed into |mData|.
1098 nsCSSExpandedDataBlock mData;
1100 public:
1101 // Used from nsCSSParser constructors and destructors
1102 CSSParserImpl* mNextFree;
1105 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer)
1107 css::Rule **pointer = static_cast<css::Rule**>(aPointer);
1108 NS_ADDREF(*pointer = aRule);
1111 static void AppendRuleToSheet(css::Rule* aRule, void* aParser)
1113 CSSParserImpl* parser = (CSSParserImpl*) aParser;
1114 parser->AppendRule(aRule);
1117 #define REPORT_UNEXPECTED(msg_) \
1118 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_); }
1120 #define REPORT_UNEXPECTED_P(msg_, param_) \
1121 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_); }
1123 #define REPORT_UNEXPECTED_TOKEN(msg_) \
1124 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken); }
1126 #define REPORT_UNEXPECTED_TOKEN_CHAR(msg_, ch_) \
1127 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken, ch_); }
1129 #define REPORT_UNEXPECTED_EOF(lf_) \
1130 mReporter->ReportUnexpectedEOF(#lf_)
1132 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
1133 mReporter->ReportUnexpectedEOF(ch_)
1135 #define OUTPUT_ERROR() \
1136 mReporter->OutputError()
1138 #define OUTPUT_ERROR_WITH_POSITION(linenum_, lineoff_) \
1139 mReporter->OutputError(linenum_, lineoff_)
1141 #define CLEAR_ERROR() \
1142 mReporter->ClearError()
1144 CSSParserImpl::CSSParserImpl()
1145 : mToken(),
1146 mScanner(nullptr),
1147 mReporter(nullptr),
1148 mChildLoader(nullptr),
1149 mSection(eCSSSection_Charset),
1150 mNameSpaceMap(nullptr),
1151 mHavePushBack(false),
1152 mNavQuirkMode(false),
1153 mHashlessColorQuirk(false),
1154 mUnitlessLengthQuirk(false),
1155 mUnsafeRulesEnabled(false),
1156 mIsChromeOrCertifiedApp(false),
1157 mViewportUnitsEnabled(true),
1158 mHTMLMediaMode(false),
1159 mParsingCompoundProperty(false),
1160 mInSupportsCondition(false),
1161 mInFailingSupportsRule(false),
1162 mSuppressErrors(false),
1163 mNextFree(nullptr)
1167 CSSParserImpl::~CSSParserImpl()
1169 mData.AssertInitialState();
1170 mTempData.AssertInitialState();
1173 nsresult
1174 CSSParserImpl::SetStyleSheet(CSSStyleSheet* aSheet)
1176 if (aSheet != mSheet) {
1177 // Switch to using the new sheet, if any
1178 mGroupStack.Clear();
1179 mSheet = aSheet;
1180 if (mSheet) {
1181 mNameSpaceMap = mSheet->GetNameSpaceMap();
1182 } else {
1183 mNameSpaceMap = nullptr;
1185 } else if (mSheet) {
1186 mNameSpaceMap = mSheet->GetNameSpaceMap();
1189 return NS_OK;
1192 nsresult
1193 CSSParserImpl::SetQuirkMode(bool aQuirkMode)
1195 mNavQuirkMode = aQuirkMode;
1196 return NS_OK;
1199 nsresult
1200 CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader)
1202 mChildLoader = aChildLoader; // not ref counted, it owns us
1203 return NS_OK;
1206 void
1207 CSSParserImpl::Reset()
1209 NS_ASSERTION(!mScanner, "resetting with scanner active");
1210 SetStyleSheet(nullptr);
1211 SetQuirkMode(false);
1212 SetChildLoader(nullptr);
1215 void
1216 CSSParserImpl::InitScanner(nsCSSScanner& aScanner,
1217 css::ErrorReporter& aReporter,
1218 nsIURI* aSheetURI, nsIURI* aBaseURI,
1219 nsIPrincipal* aSheetPrincipal)
1221 NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
1222 NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
1223 NS_PRECONDITION(!mScanner, "already have scanner");
1225 mScanner = &aScanner;
1226 mReporter = &aReporter;
1227 mScanner->SetErrorReporter(mReporter);
1229 mBaseURI = aBaseURI;
1230 mSheetURI = aSheetURI;
1231 mSheetPrincipal = aSheetPrincipal;
1232 mHavePushBack = false;
1235 void
1236 CSSParserImpl::ReleaseScanner()
1238 mScanner = nullptr;
1239 mReporter = nullptr;
1240 mBaseURI = nullptr;
1241 mSheetURI = nullptr;
1242 mSheetPrincipal = nullptr;
1245 nsresult
1246 CSSParserImpl::ParseSheet(const nsAString& aInput,
1247 nsIURI* aSheetURI,
1248 nsIURI* aBaseURI,
1249 nsIPrincipal* aSheetPrincipal,
1250 uint32_t aLineNumber,
1251 bool aAllowUnsafeRules)
1253 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1254 NS_PRECONDITION(aBaseURI, "need base URI");
1255 NS_PRECONDITION(aSheetURI, "need sheet URI");
1256 NS_PRECONDITION(mSheet, "Must have sheet to parse into");
1257 NS_ENSURE_STATE(mSheet);
1259 #ifdef DEBUG
1260 nsIURI* uri = mSheet->GetSheetURI();
1261 bool equal;
1262 NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
1263 "Sheet URI does not match passed URI");
1264 NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
1265 &equal)) &&
1266 equal,
1267 "Sheet principal does not match passed principal");
1268 #endif
1270 nsCSSScanner scanner(aInput, aLineNumber);
1271 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1272 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1274 int32_t ruleCount = mSheet->StyleRuleCount();
1275 if (0 < ruleCount) {
1276 const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1);
1277 if (lastRule) {
1278 switch (lastRule->GetType()) {
1279 case css::Rule::CHARSET_RULE:
1280 case css::Rule::IMPORT_RULE:
1281 mSection = eCSSSection_Import;
1282 break;
1283 case css::Rule::NAMESPACE_RULE:
1284 mSection = eCSSSection_NameSpace;
1285 break;
1286 default:
1287 mSection = eCSSSection_General;
1288 break;
1292 else {
1293 mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
1296 mUnsafeRulesEnabled = aAllowUnsafeRules;
1297 mIsChromeOrCertifiedApp =
1298 dom::IsChromeURI(aSheetURI) ||
1299 aSheetPrincipal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
1301 nsCSSToken* tk = &mToken;
1302 for (;;) {
1303 // Get next non-whitespace token
1304 if (!GetToken(true)) {
1305 OUTPUT_ERROR();
1306 break;
1308 if (eCSSToken_HTMLComment == tk->mType) {
1309 continue; // legal here only
1311 if (eCSSToken_AtKeyword == tk->mType) {
1312 ParseAtRule(AppendRuleToSheet, this, false);
1313 continue;
1315 UngetToken();
1316 if (ParseRuleSet(AppendRuleToSheet, this)) {
1317 mSection = eCSSSection_General;
1320 ReleaseScanner();
1322 mUnsafeRulesEnabled = false;
1323 mIsChromeOrCertifiedApp = false;
1325 // XXX check for low level errors
1326 return NS_OK;
1330 * Determines whether the identifier contained in the given string is a
1331 * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
1333 static bool
1334 NonMozillaVendorIdentifier(const nsAString& ident)
1336 return (ident.First() == char16_t('-') &&
1337 !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
1338 ident.First() == char16_t('_');
1342 nsresult
1343 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
1344 nsIURI* aDocURI,
1345 nsIURI* aBaseURI,
1346 nsIPrincipal* aNodePrincipal,
1347 css::StyleRule** aResult)
1349 NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
1350 NS_PRECONDITION(aBaseURI, "need base URI");
1352 // XXX line number?
1353 nsCSSScanner scanner(aAttributeValue, 0);
1354 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI);
1355 InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal);
1357 mSection = eCSSSection_General;
1359 uint32_t parseFlags = eParseDeclaration_AllowImportant;
1361 css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
1362 if (declaration) {
1363 // Create a style rule for the declaration
1364 NS_ADDREF(*aResult = new css::StyleRule(nullptr, declaration, 0, 0));
1365 } else {
1366 *aResult = nullptr;
1369 ReleaseScanner();
1371 // XXX check for low level errors
1372 return NS_OK;
1375 nsresult
1376 CSSParserImpl::ParseDeclarations(const nsAString& aBuffer,
1377 nsIURI* aSheetURI,
1378 nsIURI* aBaseURI,
1379 nsIPrincipal* aSheetPrincipal,
1380 css::Declaration* aDeclaration,
1381 bool* aChanged)
1383 *aChanged = false;
1385 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1387 nsCSSScanner scanner(aBuffer, 0);
1388 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1389 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1391 mSection = eCSSSection_General;
1393 mData.AssertInitialState();
1394 aDeclaration->ClearData();
1395 // We could check if it was already empty, but...
1396 *aChanged = true;
1398 for (;;) {
1399 // If we cleared the old decl, then we want to be calling
1400 // ValueAppended as we parse.
1401 if (!ParseDeclaration(aDeclaration, eParseDeclaration_AllowImportant,
1402 true, aChanged)) {
1403 if (!SkipDeclaration(false)) {
1404 break;
1409 aDeclaration->CompressFrom(&mData);
1410 ReleaseScanner();
1411 return NS_OK;
1414 nsresult
1415 CSSParserImpl::ParseRule(const nsAString& aRule,
1416 nsIURI* aSheetURI,
1417 nsIURI* aBaseURI,
1418 nsIPrincipal* aSheetPrincipal,
1419 css::Rule** aResult)
1421 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1422 NS_PRECONDITION(aBaseURI, "need base URI");
1424 *aResult = nullptr;
1426 nsCSSScanner scanner(aRule, 0);
1427 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1428 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1430 mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
1432 nsCSSToken* tk = &mToken;
1433 // Get first non-whitespace token
1434 nsresult rv = NS_OK;
1435 if (!GetToken(true)) {
1436 REPORT_UNEXPECTED(PEParseRuleWSOnly);
1437 OUTPUT_ERROR();
1438 rv = NS_ERROR_DOM_SYNTAX_ERR;
1439 } else {
1440 if (eCSSToken_AtKeyword == tk->mType) {
1441 // FIXME: perhaps aInsideBlock should be true when we are?
1442 ParseAtRule(AssignRuleToPointer, aResult, false);
1443 } else {
1444 UngetToken();
1445 ParseRuleSet(AssignRuleToPointer, aResult);
1448 if (*aResult && GetToken(true)) {
1449 // garbage after rule
1450 REPORT_UNEXPECTED_TOKEN(PERuleTrailing);
1451 NS_RELEASE(*aResult);
1454 if (!*aResult) {
1455 rv = NS_ERROR_DOM_SYNTAX_ERR;
1456 OUTPUT_ERROR();
1460 ReleaseScanner();
1461 return rv;
1464 // See Bug 723197
1465 #ifdef _MSC_VER
1466 #pragma optimize( "", off )
1467 #pragma warning( push )
1468 #pragma warning( disable : 4748 )
1469 #endif
1471 nsresult
1472 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
1473 const nsAString& aPropValue,
1474 nsIURI* aSheetURI,
1475 nsIURI* aBaseURI,
1476 nsIPrincipal* aSheetPrincipal,
1477 css::Declaration* aDeclaration,
1478 bool* aChanged,
1479 bool aIsImportant,
1480 bool aIsSVGMode)
1482 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1483 NS_PRECONDITION(aBaseURI, "need base URI");
1484 NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
1485 MOZ_ASSERT(aPropID != eCSSPropertyExtra_variable);
1487 mData.AssertInitialState();
1488 mTempData.AssertInitialState();
1489 aDeclaration->AssertMutable();
1491 nsCSSScanner scanner(aPropValue, 0);
1492 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1493 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1494 mSection = eCSSSection_General;
1495 scanner.SetSVGMode(aIsSVGMode);
1497 *aChanged = false;
1499 // Check for unknown or preffed off properties
1500 if (eCSSProperty_UNKNOWN == aPropID ||
1501 !(nsCSSProps::IsEnabled(aPropID) ||
1502 (mUnsafeRulesEnabled &&
1503 nsCSSProps::PropHasFlags(aPropID,
1504 CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS)))) {
1505 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1506 REPORT_UNEXPECTED_P(PEUnknownProperty, propName);
1507 REPORT_UNEXPECTED(PEDeclDropped);
1508 OUTPUT_ERROR();
1509 ReleaseScanner();
1510 return NS_OK;
1513 bool parsedOK = ParseProperty(aPropID);
1514 // We should now be at EOF
1515 if (parsedOK && GetToken(true)) {
1516 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1517 parsedOK = false;
1520 if (!parsedOK) {
1521 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1522 REPORT_UNEXPECTED_P(PEValueParsingError, propName);
1523 REPORT_UNEXPECTED(PEDeclDropped);
1524 OUTPUT_ERROR();
1525 mTempData.ClearProperty(aPropID);
1526 } else {
1528 // We know we don't need to force a ValueAppended call for the new
1529 // value. So if we are not processing a shorthand, and there's
1530 // already a value for this property in the declaration at the
1531 // same importance level, then we can just copy our parsed value
1532 // directly into the declaration without going through the whole
1533 // expand/compress thing.
1534 if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData,
1535 aChanged)) {
1536 // Do it the slow way
1537 aDeclaration->ExpandTo(&mData);
1538 *aChanged = mData.TransferFromBlock(mTempData, aPropID, aIsImportant,
1539 true, false, aDeclaration);
1540 aDeclaration->CompressFrom(&mData);
1542 CLEAR_ERROR();
1545 mTempData.AssertInitialState();
1547 ReleaseScanner();
1548 return NS_OK;
1551 nsresult
1552 CSSParserImpl::ParseVariable(const nsAString& aVariableName,
1553 const nsAString& aPropValue,
1554 nsIURI* aSheetURI,
1555 nsIURI* aBaseURI,
1556 nsIPrincipal* aSheetPrincipal,
1557 css::Declaration* aDeclaration,
1558 bool* aChanged,
1559 bool aIsImportant)
1561 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1562 NS_PRECONDITION(aBaseURI, "need base URI");
1563 NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
1564 NS_PRECONDITION(nsLayoutUtils::CSSVariablesEnabled(),
1565 "expected Variables to be enabled");
1567 mData.AssertInitialState();
1568 mTempData.AssertInitialState();
1569 aDeclaration->AssertMutable();
1571 nsCSSScanner scanner(aPropValue, 0);
1572 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1573 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1574 mSection = eCSSSection_General;
1576 *aChanged = false;
1578 CSSVariableDeclarations::Type variableType;
1579 nsString variableValue;
1581 bool parsedOK = ParseVariableDeclaration(&variableType, variableValue);
1583 // We should now be at EOF
1584 if (parsedOK && GetToken(true)) {
1585 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1586 parsedOK = false;
1589 if (!parsedOK) {
1590 REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("--") +
1591 aVariableName);
1592 REPORT_UNEXPECTED(PEDeclDropped);
1593 OUTPUT_ERROR();
1594 } else {
1595 CLEAR_ERROR();
1596 aDeclaration->AddVariableDeclaration(aVariableName, variableType,
1597 variableValue, aIsImportant, true);
1598 *aChanged = true;
1601 mTempData.AssertInitialState();
1603 ReleaseScanner();
1604 return NS_OK;
1607 #ifdef _MSC_VER
1608 #pragma warning( pop )
1609 #pragma optimize( "", on )
1610 #endif
1612 void
1613 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
1614 nsIURI* aURI, // for error reporting
1615 uint32_t aLineNumber, // for error reporting
1616 nsMediaList* aMediaList,
1617 bool aHTMLMode)
1619 // XXX Are there cases where the caller wants to keep what it already
1620 // has in case of parser error? If GatherMedia ever changes to return
1621 // a value other than true, we probably should avoid modifying aMediaList.
1622 aMediaList->Clear();
1624 // fake base URI since media lists don't have URIs in them
1625 nsCSSScanner scanner(aBuffer, aLineNumber);
1626 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1627 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1629 mHTMLMediaMode = aHTMLMode;
1631 // XXXldb We need to make the scanner not skip CSS comments! (Or
1632 // should we?)
1634 // For aHTMLMode, we used to follow the parsing rules in
1635 // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
1636 // which wouldn't work for media queries since they remove all but the
1637 // first word. However, they're changed in
1638 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
1639 // (as of 2008-05-29) which says that the media attribute just points
1640 // to a media query. (The main substative difference is the relative
1641 // precedence of commas and paretheses.)
1643 DebugOnly<bool> parsedOK = GatherMedia(aMediaList, false);
1644 NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid "
1645 "trashing aMediaList");
1647 CLEAR_ERROR();
1648 ReleaseScanner();
1649 mHTMLMediaMode = false;
1652 // <source-size-list> = <source-size>#?
1653 // <source-size> = <media-condition>? <length>
1654 bool
1655 CSSParserImpl::ParseSourceSizeList(const nsAString& aBuffer,
1656 nsIURI* aURI, // for error reporting
1657 uint32_t aLineNumber, // for error reporting
1658 InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
1659 InfallibleTArray<nsCSSValue>& aValues,
1660 bool aHTMLMode)
1662 aQueries.Clear();
1663 aValues.Clear();
1665 // fake base URI since media value lists don't have URIs in them
1666 nsCSSScanner scanner(aBuffer, aLineNumber);
1667 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1668 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1670 // See ParseMediaList comment about HTML mode
1671 mHTMLMediaMode = aHTMLMode;
1673 bool hitError = false;
1674 for (;;) {
1675 nsAutoPtr<nsMediaQuery> query;
1676 nsCSSValue value;
1678 bool hitStop;
1679 if (!ParseMediaQuery(eMediaQuerySingleCondition, getter_Transfers(query),
1680 &hitStop)) {
1681 NS_ASSERTION(!hitStop, "should return true when hit stop");
1682 hitError = true;
1683 break;
1686 if (!query) {
1687 REPORT_UNEXPECTED_EOF(PEParseSourceSizeListEOF);
1688 NS_ASSERTION(hitStop,
1689 "should return hitStop or an error if returning no query");
1690 hitError = true;
1691 break;
1694 if (hitStop) {
1695 // Empty conditions (e.g. just a bare value) should be treated as always
1696 // matching (a query with no expressions fails to match, so a negated one
1697 // always matches.)
1698 query->SetNegated();
1701 if (!ParseNonNegativeVariant(value, VARIANT_LPCALC, nullptr)) {
1702 hitError = true;
1703 break;
1706 aQueries.AppendElement(query.forget());
1707 aValues.AppendElement(value);
1709 if (!GetToken(true)) {
1710 // Expected EOF
1711 break;
1714 if (eCSSToken_Symbol != mToken.mType || mToken.mSymbol != ',') {
1715 REPORT_UNEXPECTED_TOKEN(PEParseSourceSizeListNotComma);
1716 hitError = true;
1717 break;
1721 if (hitError) {
1722 // Per spec, a parse failure in this list invalidates it
1723 // entirely. Currently, this grammar is specified standalone and not part of
1724 // any larger grammar, so it doesn't make sense to try to advance the token
1725 // beyond it.
1726 OUTPUT_ERROR();
1729 CLEAR_ERROR();
1730 ReleaseScanner();
1731 mHTMLMediaMode = false;
1733 return !hitError;
1736 bool
1737 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
1738 nsIURI* aURI, // for error reporting
1739 uint32_t aLineNumber, // for error reporting
1740 nsCSSValue& aValue,
1741 bool aSuppressErrors /* false */)
1743 nsCSSScanner scanner(aBuffer, aLineNumber);
1744 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1745 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1747 nsAutoSuppressErrors suppressErrors(this, aSuppressErrors);
1749 // Parse a color, and check that there's nothing else after it.
1750 bool colorParsed = ParseColor(aValue) && !GetToken(true);
1752 if (aSuppressErrors) {
1753 CLEAR_ERROR();
1754 } else {
1755 OUTPUT_ERROR();
1758 ReleaseScanner();
1759 return colorParsed;
1762 bool
1763 CSSParserImpl::ParseFontFamilyListString(const nsSubstring& aBuffer,
1764 nsIURI* aURI, // for error reporting
1765 uint32_t aLineNumber, // for error reporting
1766 nsCSSValue& aValue)
1768 nsCSSScanner scanner(aBuffer, aLineNumber);
1769 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1770 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1772 // Parse a font family list, and check that there's nothing else after it.
1773 bool familyParsed = ParseFamily(aValue) && !GetToken(true);
1774 OUTPUT_ERROR();
1775 ReleaseScanner();
1776 return familyParsed;
1779 nsresult
1780 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
1781 nsIURI* aURI, // for error reporting
1782 uint32_t aLineNumber, // for error reporting
1783 nsCSSSelectorList **aSelectorList)
1785 nsCSSScanner scanner(aSelectorString, aLineNumber);
1786 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1787 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1789 bool success = ParseSelectorList(*aSelectorList, char16_t(0));
1791 // We deliberately do not call OUTPUT_ERROR here, because all our
1792 // callers map a failure return to a JS exception, and if that JS
1793 // exception is caught, people don't want to see parser diagnostics;
1794 // see e.g. http://bugs.jquery.com/ticket/7535
1795 // It would be nice to be able to save the parser diagnostics into
1796 // the exception, so that if it _isn't_ caught we can report them
1797 // along with the usual uncaught-exception message, but we don't
1798 // have any way to do that at present; see bug 631621.
1799 CLEAR_ERROR();
1800 ReleaseScanner();
1802 if (success) {
1803 NS_ASSERTION(*aSelectorList, "Should have list!");
1804 return NS_OK;
1807 NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
1809 return NS_ERROR_DOM_SYNTAX_ERR;
1813 already_AddRefed<nsCSSKeyframeRule>
1814 CSSParserImpl::ParseKeyframeRule(const nsSubstring& aBuffer,
1815 nsIURI* aURI,
1816 uint32_t aLineNumber)
1818 nsCSSScanner scanner(aBuffer, aLineNumber);
1819 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1820 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1822 nsRefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule();
1823 if (GetToken(true)) {
1824 // extra garbage at the end
1825 result = nullptr;
1828 OUTPUT_ERROR();
1829 ReleaseScanner();
1831 return result.forget();
1834 bool
1835 CSSParserImpl::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
1836 nsIURI* aURI, // for error reporting
1837 uint32_t aLineNumber, // for error reporting
1838 InfallibleTArray<float>& aSelectorList)
1840 NS_ABORT_IF_FALSE(aSelectorList.IsEmpty(), "given list should start empty");
1842 nsCSSScanner scanner(aSelectorString, aLineNumber);
1843 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1844 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1846 bool success = ParseKeyframeSelectorList(aSelectorList) &&
1847 // must consume entire input string
1848 !GetToken(true);
1850 OUTPUT_ERROR();
1851 ReleaseScanner();
1853 if (success) {
1854 NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty");
1855 } else {
1856 aSelectorList.Clear();
1859 return success;
1862 bool
1863 CSSParserImpl::EvaluateSupportsDeclaration(const nsAString& aProperty,
1864 const nsAString& aValue,
1865 nsIURI* aDocURL,
1866 nsIURI* aBaseURL,
1867 nsIPrincipal* aDocPrincipal)
1869 nsCSSProperty propID = LookupEnabledProperty(aProperty);
1870 if (propID == eCSSProperty_UNKNOWN) {
1871 return false;
1874 nsCSSScanner scanner(aValue, 0);
1875 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
1876 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
1877 nsAutoSuppressErrors suppressErrors(this);
1879 bool parsedOK;
1881 if (propID == eCSSPropertyExtra_variable) {
1882 MOZ_ASSERT(Substring(aProperty, 0,
1883 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
1884 const nsDependentSubstring varName =
1885 Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH); // remove '--'
1886 CSSVariableDeclarations::Type variableType;
1887 nsString variableValue;
1888 parsedOK = ParseVariableDeclaration(&variableType, variableValue) &&
1889 !GetToken(true);
1890 } else {
1891 parsedOK = ParseProperty(propID) && !GetToken(true);
1893 mTempData.ClearProperty(propID);
1894 mTempData.AssertInitialState();
1897 CLEAR_ERROR();
1898 ReleaseScanner();
1900 return parsedOK;
1903 bool
1904 CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration,
1905 nsIURI* aDocURL,
1906 nsIURI* aBaseURL,
1907 nsIPrincipal* aDocPrincipal)
1909 nsCSSScanner scanner(aDeclaration, 0);
1910 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
1911 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
1912 nsAutoSuppressErrors suppressErrors(this);
1914 bool conditionMet;
1915 bool parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true);
1917 CLEAR_ERROR();
1918 ReleaseScanner();
1920 return parsedOK && conditionMet;
1923 bool
1924 CSSParserImpl::EnumerateVariableReferences(const nsAString& aPropertyValue,
1925 VariableEnumFunc aFunc,
1926 void* aData)
1928 nsCSSScanner scanner(aPropertyValue, 0);
1929 css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
1930 InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
1931 nsAutoSuppressErrors suppressErrors(this);
1933 CSSVariableDeclarations::Type type;
1934 bool dropBackslash;
1935 nsString impliedCharacters;
1936 bool result = ParseValueWithVariables(&type, &dropBackslash,
1937 impliedCharacters, aFunc, aData) &&
1938 !GetToken(true);
1940 ReleaseScanner();
1942 return result;
1945 static bool
1946 SeparatorRequiredBetweenTokens(nsCSSTokenSerializationType aToken1,
1947 nsCSSTokenSerializationType aToken2)
1949 // The two lines marked with (*) do not correspond to entries in
1950 // the table in the css-syntax spec but which we need to handle,
1951 // as we treat them as whole tokens.
1952 switch (aToken1) {
1953 case eCSSTokenSerialization_Ident:
1954 return aToken2 == eCSSTokenSerialization_Ident ||
1955 aToken2 == eCSSTokenSerialization_Function ||
1956 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1957 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1958 aToken2 == eCSSTokenSerialization_Number ||
1959 aToken2 == eCSSTokenSerialization_Percentage ||
1960 aToken2 == eCSSTokenSerialization_Dimension ||
1961 aToken2 == eCSSTokenSerialization_URange ||
1962 aToken2 == eCSSTokenSerialization_CDC ||
1963 aToken2 == eCSSTokenSerialization_Symbol_OpenParen;
1964 case eCSSTokenSerialization_AtKeyword_or_Hash:
1965 case eCSSTokenSerialization_Dimension:
1966 return aToken2 == eCSSTokenSerialization_Ident ||
1967 aToken2 == eCSSTokenSerialization_Function ||
1968 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1969 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1970 aToken2 == eCSSTokenSerialization_Number ||
1971 aToken2 == eCSSTokenSerialization_Percentage ||
1972 aToken2 == eCSSTokenSerialization_Dimension ||
1973 aToken2 == eCSSTokenSerialization_URange ||
1974 aToken2 == eCSSTokenSerialization_CDC;
1975 case eCSSTokenSerialization_Symbol_Hash:
1976 return aToken2 == eCSSTokenSerialization_Ident ||
1977 aToken2 == eCSSTokenSerialization_Function ||
1978 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1979 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1980 aToken2 == eCSSTokenSerialization_Number ||
1981 aToken2 == eCSSTokenSerialization_Percentage ||
1982 aToken2 == eCSSTokenSerialization_Dimension ||
1983 aToken2 == eCSSTokenSerialization_URange;
1984 case eCSSTokenSerialization_Symbol_Minus:
1985 case eCSSTokenSerialization_Number:
1986 return aToken2 == eCSSTokenSerialization_Ident ||
1987 aToken2 == eCSSTokenSerialization_Function ||
1988 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1989 aToken2 == eCSSTokenSerialization_Number ||
1990 aToken2 == eCSSTokenSerialization_Percentage ||
1991 aToken2 == eCSSTokenSerialization_Dimension ||
1992 aToken2 == eCSSTokenSerialization_URange;
1993 case eCSSTokenSerialization_Symbol_At:
1994 return aToken2 == eCSSTokenSerialization_Ident ||
1995 aToken2 == eCSSTokenSerialization_Function ||
1996 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1997 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1998 aToken2 == eCSSTokenSerialization_URange;
1999 case eCSSTokenSerialization_URange:
2000 return aToken2 == eCSSTokenSerialization_Ident ||
2001 aToken2 == eCSSTokenSerialization_Function ||
2002 aToken2 == eCSSTokenSerialization_Number ||
2003 aToken2 == eCSSTokenSerialization_Percentage ||
2004 aToken2 == eCSSTokenSerialization_Dimension ||
2005 aToken2 == eCSSTokenSerialization_Symbol_Question;
2006 case eCSSTokenSerialization_Symbol_Dot_or_Plus:
2007 return aToken2 == eCSSTokenSerialization_Number ||
2008 aToken2 == eCSSTokenSerialization_Percentage ||
2009 aToken2 == eCSSTokenSerialization_Dimension;
2010 case eCSSTokenSerialization_Symbol_Assorted:
2011 case eCSSTokenSerialization_Symbol_Asterisk:
2012 return aToken2 == eCSSTokenSerialization_Symbol_Equals;
2013 case eCSSTokenSerialization_Symbol_Bar:
2014 return aToken2 == eCSSTokenSerialization_Symbol_Equals ||
2015 aToken2 == eCSSTokenSerialization_Symbol_Bar ||
2016 aToken2 == eCSSTokenSerialization_DashMatch; // (*)
2017 case eCSSTokenSerialization_Symbol_Slash:
2018 return aToken2 == eCSSTokenSerialization_Symbol_Asterisk ||
2019 aToken2 == eCSSTokenSerialization_ContainsMatch; // (*)
2020 default:
2021 MOZ_ASSERT(aToken1 == eCSSTokenSerialization_Nothing ||
2022 aToken1 == eCSSTokenSerialization_Whitespace ||
2023 aToken1 == eCSSTokenSerialization_Percentage ||
2024 aToken1 == eCSSTokenSerialization_URL_or_BadURL ||
2025 aToken1 == eCSSTokenSerialization_Function ||
2026 aToken1 == eCSSTokenSerialization_CDC ||
2027 aToken1 == eCSSTokenSerialization_Symbol_OpenParen ||
2028 aToken1 == eCSSTokenSerialization_Symbol_Question ||
2029 aToken1 == eCSSTokenSerialization_Symbol_Assorted ||
2030 aToken1 == eCSSTokenSerialization_Symbol_Asterisk ||
2031 aToken1 == eCSSTokenSerialization_Symbol_Equals ||
2032 aToken1 == eCSSTokenSerialization_Symbol_Bar ||
2033 aToken1 == eCSSTokenSerialization_Symbol_Slash ||
2034 aToken1 == eCSSTokenSerialization_Other ||
2035 "unexpected nsCSSTokenSerializationType value");
2036 return false;
2041 * Appends aValue to aResult, possibly inserting an empty CSS
2042 * comment between the two to ensure that tokens from both strings
2043 * remain separated.
2045 static void
2046 AppendTokens(nsAString& aResult,
2047 nsCSSTokenSerializationType& aResultFirstToken,
2048 nsCSSTokenSerializationType& aResultLastToken,
2049 nsCSSTokenSerializationType aValueFirstToken,
2050 nsCSSTokenSerializationType aValueLastToken,
2051 const nsAString& aValue)
2053 if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
2054 aResult.AppendLiteral("/**/");
2056 aResult.Append(aValue);
2057 if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
2058 aResultFirstToken = aValueFirstToken;
2060 if (aValueLastToken != eCSSTokenSerialization_Nothing) {
2061 aResultLastToken = aValueLastToken;
2066 * Stops the given scanner recording, and appends the recorded result
2067 * to aResult, possibly inserting an empty CSS comment between the two to
2068 * ensure that tokens from both strings remain separated.
2070 static void
2071 StopRecordingAndAppendTokens(nsString& aResult,
2072 nsCSSTokenSerializationType& aResultFirstToken,
2073 nsCSSTokenSerializationType& aResultLastToken,
2074 nsCSSTokenSerializationType aValueFirstToken,
2075 nsCSSTokenSerializationType aValueLastToken,
2076 nsCSSScanner* aScanner)
2078 if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
2079 aResult.AppendLiteral("/**/");
2081 aScanner->StopRecording(aResult);
2082 if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
2083 aResultFirstToken = aValueFirstToken;
2085 if (aValueLastToken != eCSSTokenSerialization_Nothing) {
2086 aResultLastToken = aValueLastToken;
2090 bool
2091 CSSParserImpl::ResolveValueWithVariableReferencesRec(
2092 nsString& aResult,
2093 nsCSSTokenSerializationType& aResultFirstToken,
2094 nsCSSTokenSerializationType& aResultLastToken,
2095 const CSSVariableValues* aVariables)
2097 // This function assumes we are already recording, and will leave the scanner
2098 // recording when it returns.
2099 MOZ_ASSERT(mScanner->IsRecording());
2100 MOZ_ASSERT(aResult.IsEmpty());
2102 // Stack of closing characters for currently open constructs.
2103 nsAutoTArray<char16_t, 16> stack;
2105 // The resolved value for this ResolveValueWithVariableReferencesRec call.
2106 nsString value;
2108 // The length of the scanner's recording before the currently parsed token.
2109 // This is used so that when we encounter a "var(" token, we can strip
2110 // it off the end of the recording, regardless of how long the token was.
2111 // (With escapes, it could be longer than four characters.)
2112 uint32_t lengthBeforeVar = 0;
2114 // Tracking the type of token that appears at the start and end of |value|
2115 // and that appears at the start and end of the scanner recording. These are
2116 // used to determine whether we need to insert "/**/" when pasting token
2117 // streams together.
2118 nsCSSTokenSerializationType valueFirstToken = eCSSTokenSerialization_Nothing,
2119 valueLastToken = eCSSTokenSerialization_Nothing,
2120 recFirstToken = eCSSTokenSerialization_Nothing,
2121 recLastToken = eCSSTokenSerialization_Nothing;
2123 #define UPDATE_RECORDING_TOKENS(type) \
2124 if (recFirstToken == eCSSTokenSerialization_Nothing) { \
2125 recFirstToken = type; \
2127 recLastToken = type;
2129 while (GetToken(false)) {
2130 switch (mToken.mType) {
2131 case eCSSToken_Symbol: {
2132 nsCSSTokenSerializationType type = eCSSTokenSerialization_Other;
2133 if (mToken.mSymbol == '(') {
2134 stack.AppendElement(')');
2135 type = eCSSTokenSerialization_Symbol_OpenParen;
2136 } else if (mToken.mSymbol == '[') {
2137 stack.AppendElement(']');
2138 } else if (mToken.mSymbol == '{') {
2139 stack.AppendElement('}');
2140 } else if (mToken.mSymbol == ';') {
2141 if (stack.IsEmpty()) {
2142 // A ';' that is at the top level of the value or at the top level
2143 // of a variable reference's fallback is invalid.
2144 return false;
2146 } else if (mToken.mSymbol == '!') {
2147 if (stack.IsEmpty()) {
2148 // An '!' that is at the top level of the value or at the top level
2149 // of a variable reference's fallback is invalid.
2150 return false;
2152 } else if (mToken.mSymbol == ')' &&
2153 stack.IsEmpty()) {
2154 // We're closing a "var(".
2155 nsString finalTokens;
2156 mScanner->StopRecording(finalTokens);
2157 MOZ_ASSERT(finalTokens[finalTokens.Length() - 1] == ')');
2158 finalTokens.Truncate(finalTokens.Length() - 1);
2159 aResult.Append(value);
2161 AppendTokens(aResult, valueFirstToken, valueLastToken,
2162 recFirstToken, recLastToken, finalTokens);
2164 mScanner->StartRecording();
2165 UngetToken();
2166 aResultFirstToken = valueFirstToken;
2167 aResultLastToken = valueLastToken;
2168 return true;
2169 } else if (mToken.mSymbol == ')' ||
2170 mToken.mSymbol == ']' ||
2171 mToken.mSymbol == '}') {
2172 if (stack.IsEmpty() ||
2173 stack.LastElement() != mToken.mSymbol) {
2174 // A mismatched closing bracket is invalid.
2175 return false;
2177 stack.TruncateLength(stack.Length() - 1);
2178 } else if (mToken.mSymbol == '#') {
2179 type = eCSSTokenSerialization_Symbol_Hash;
2180 } else if (mToken.mSymbol == '@') {
2181 type = eCSSTokenSerialization_Symbol_At;
2182 } else if (mToken.mSymbol == '.' ||
2183 mToken.mSymbol == '+') {
2184 type = eCSSTokenSerialization_Symbol_Dot_or_Plus;
2185 } else if (mToken.mSymbol == '-') {
2186 type = eCSSTokenSerialization_Symbol_Minus;
2187 } else if (mToken.mSymbol == '?') {
2188 type = eCSSTokenSerialization_Symbol_Question;
2189 } else if (mToken.mSymbol == '$' ||
2190 mToken.mSymbol == '^' ||
2191 mToken.mSymbol == '~') {
2192 type = eCSSTokenSerialization_Symbol_Assorted;
2193 } else if (mToken.mSymbol == '=') {
2194 type = eCSSTokenSerialization_Symbol_Equals;
2195 } else if (mToken.mSymbol == '|') {
2196 type = eCSSTokenSerialization_Symbol_Bar;
2197 } else if (mToken.mSymbol == '/') {
2198 type = eCSSTokenSerialization_Symbol_Slash;
2199 } else if (mToken.mSymbol == '*') {
2200 type = eCSSTokenSerialization_Symbol_Asterisk;
2202 UPDATE_RECORDING_TOKENS(type);
2203 break;
2206 case eCSSToken_Function:
2207 if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
2208 // Save the tokens before the "var(" to our resolved value.
2209 nsString recording;
2210 mScanner->StopRecording(recording);
2211 recording.Truncate(lengthBeforeVar);
2212 AppendTokens(value, valueFirstToken, valueLastToken,
2213 recFirstToken, recLastToken, recording);
2214 recFirstToken = eCSSTokenSerialization_Nothing;
2215 recLastToken = eCSSTokenSerialization_Nothing;
2217 if (!GetToken(true) ||
2218 mToken.mType != eCSSToken_Ident ||
2219 !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
2220 // "var(" must be followed by an identifier, and it must be a
2221 // custom property name.
2222 return false;
2225 // Turn the custom property name into a variable name by removing the
2226 // '--' prefix.
2227 MOZ_ASSERT(Substring(mToken.mIdent, 0,
2228 CSS_CUSTOM_NAME_PREFIX_LENGTH).
2229 EqualsLiteral("--"));
2230 nsDependentString variableName(mToken.mIdent,
2231 CSS_CUSTOM_NAME_PREFIX_LENGTH);
2233 // Get the value of the identified variable. Note that we
2234 // check if the variable value is the empty string, as that means
2235 // that the variable was invalid at computed value time due to
2236 // unresolveable variable references or cycles.
2237 nsString variableValue;
2238 nsCSSTokenSerializationType varFirstToken, varLastToken;
2239 bool valid = aVariables->Get(variableName, variableValue,
2240 varFirstToken, varLastToken) &&
2241 !variableValue.IsEmpty();
2243 if (!GetToken(true) ||
2244 mToken.IsSymbol(')')) {
2245 mScanner->StartRecording();
2246 if (!valid) {
2247 // Invalid variable with no fallback.
2248 return false;
2250 // Valid variable with no fallback.
2251 AppendTokens(value, valueFirstToken, valueLastToken,
2252 varFirstToken, varLastToken, variableValue);
2253 } else if (mToken.IsSymbol(',')) {
2254 mScanner->StartRecording();
2255 if (!GetToken(false) ||
2256 mToken.IsSymbol(')')) {
2257 // Comma must be followed by at least one fallback token.
2258 return false;
2260 UngetToken();
2261 if (valid) {
2262 // Valid variable with ignored fallback.
2263 mScanner->StopRecording();
2264 AppendTokens(value, valueFirstToken, valueLastToken,
2265 varFirstToken, varLastToken, variableValue);
2266 bool ok = SkipBalancedContentUntil(')');
2267 mScanner->StartRecording();
2268 if (!ok) {
2269 return false;
2271 } else {
2272 nsString fallback;
2273 if (!ResolveValueWithVariableReferencesRec(fallback,
2274 varFirstToken,
2275 varLastToken,
2276 aVariables)) {
2277 // Fallback value had invalid tokens or an invalid variable reference
2278 // that itself had no fallback.
2279 return false;
2281 AppendTokens(value, valueFirstToken, valueLastToken,
2282 varFirstToken, varLastToken, fallback);
2283 // Now we're either at the pushed back ')' that finished the
2284 // fallback or at EOF.
2285 DebugOnly<bool> gotToken = GetToken(false);
2286 MOZ_ASSERT(!gotToken || mToken.IsSymbol(')'));
2288 } else {
2289 // Expected ',' or ')' after the variable name.
2290 mScanner->StartRecording();
2291 return false;
2293 } else {
2294 stack.AppendElement(')');
2295 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function);
2297 break;
2299 case eCSSToken_Bad_String:
2300 case eCSSToken_Bad_URL:
2301 return false;
2303 case eCSSToken_Whitespace:
2304 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace);
2305 break;
2307 case eCSSToken_AtKeyword:
2308 case eCSSToken_Hash:
2309 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash);
2310 break;
2312 case eCSSToken_Number:
2313 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number);
2314 break;
2316 case eCSSToken_Dimension:
2317 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension);
2318 break;
2320 case eCSSToken_Ident:
2321 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident);
2322 break;
2324 case eCSSToken_Percentage:
2325 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage);
2326 break;
2328 case eCSSToken_URange:
2329 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange);
2330 break;
2332 case eCSSToken_URL:
2333 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL);
2334 break;
2336 case eCSSToken_HTMLComment:
2337 if (mToken.mIdent[0] == '-') {
2338 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC);
2339 } else {
2340 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
2342 break;
2344 case eCSSToken_Dashmatch:
2345 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch);
2346 break;
2348 case eCSSToken_Containsmatch:
2349 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch);
2350 break;
2352 default:
2353 NS_NOTREACHED("unexpected token type");
2354 // fall through
2355 case eCSSToken_ID:
2356 case eCSSToken_String:
2357 case eCSSToken_Includes:
2358 case eCSSToken_Beginsmatch:
2359 case eCSSToken_Endsmatch:
2360 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
2361 break;
2364 lengthBeforeVar = mScanner->RecordingLength();
2367 #undef UPDATE_RECORDING_TOKENS
2369 aResult.Append(value);
2370 StopRecordingAndAppendTokens(aResult, valueFirstToken, valueLastToken,
2371 recFirstToken, recLastToken, mScanner);
2373 // Append any implicitly closed brackets.
2374 if (!stack.IsEmpty()) {
2375 do {
2376 aResult.Append(stack.LastElement());
2377 stack.TruncateLength(stack.Length() - 1);
2378 } while (!stack.IsEmpty());
2379 valueLastToken = eCSSTokenSerialization_Other;
2382 mScanner->StartRecording();
2383 aResultFirstToken = valueFirstToken;
2384 aResultLastToken = valueLastToken;
2385 return true;
2388 bool
2389 CSSParserImpl::ResolveValueWithVariableReferences(
2390 const CSSVariableValues* aVariables,
2391 nsString& aResult,
2392 nsCSSTokenSerializationType& aFirstToken,
2393 nsCSSTokenSerializationType& aLastToken)
2395 aResult.Truncate(0);
2397 // Start recording before we read the first token.
2398 mScanner->StartRecording();
2400 if (!GetToken(false)) {
2401 // Value was empty since we reached EOF.
2402 mScanner->StopRecording();
2403 return false;
2406 UngetToken();
2408 nsString value;
2409 nsCSSTokenSerializationType firstToken, lastToken;
2410 bool ok = ResolveValueWithVariableReferencesRec(value, firstToken, lastToken, aVariables) &&
2411 !GetToken(true);
2413 mScanner->StopRecording();
2415 if (ok) {
2416 aResult = value;
2417 aFirstToken = firstToken;
2418 aLastToken = lastToken;
2420 return ok;
2423 bool
2424 CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue,
2425 const CSSVariableValues* aVariables,
2426 nsString& aResult,
2427 nsCSSTokenSerializationType& aFirstToken,
2428 nsCSSTokenSerializationType& aLastToken)
2430 nsCSSScanner scanner(aPropertyValue, 0);
2432 // At this point, we know that aPropertyValue is syntactically correct
2433 // for a token stream that has variable references. We also won't be
2434 // interpreting any of the stream as we parse it, apart from expanding
2435 // var() references, so we don't need a base URL etc. or any useful
2436 // error reporting.
2437 css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
2438 InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
2440 bool valid = ResolveValueWithVariableReferences(aVariables, aResult,
2441 aFirstToken, aLastToken);
2443 ReleaseScanner();
2444 return valid;
2447 void
2448 CSSParserImpl::ParsePropertyWithVariableReferences(
2449 nsCSSProperty aPropertyID,
2450 nsCSSProperty aShorthandPropertyID,
2451 const nsAString& aValue,
2452 const CSSVariableValues* aVariables,
2453 nsRuleData* aRuleData,
2454 nsIURI* aDocURL,
2455 nsIURI* aBaseURL,
2456 nsIPrincipal* aDocPrincipal,
2457 CSSStyleSheet* aSheet,
2458 uint32_t aLineNumber,
2459 uint32_t aLineOffset)
2461 mTempData.AssertInitialState();
2463 bool valid;
2464 nsString expandedValue;
2466 // Resolve any variable references in the property value.
2468 nsCSSScanner scanner(aValue, 0);
2469 css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
2470 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2472 nsCSSTokenSerializationType firstToken, lastToken;
2473 valid = ResolveValueWithVariableReferences(aVariables, expandedValue,
2474 firstToken, lastToken);
2475 if (!valid) {
2476 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropertyID));
2477 REPORT_UNEXPECTED(PEInvalidVariableReference);
2478 REPORT_UNEXPECTED_P(PEValueParsingError, propName);
2479 if (nsCSSProps::IsInherited(aPropertyID)) {
2480 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
2481 } else {
2482 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
2484 OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
2486 ReleaseScanner();
2489 nsCSSProperty propertyToParse =
2490 aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID :
2491 aPropertyID;
2493 // Parse the property with that resolved value.
2494 if (valid) {
2495 nsCSSScanner scanner(expandedValue, 0);
2496 css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
2497 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2498 valid = ParseProperty(propertyToParse);
2499 if (valid && GetToken(true)) {
2500 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
2501 valid = false;
2503 if (!valid) {
2504 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(
2505 propertyToParse));
2506 REPORT_UNEXPECTED_P(PEValueWithVariablesParsingError, propName);
2507 if (nsCSSProps::IsInherited(aPropertyID)) {
2508 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
2509 } else {
2510 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
2512 OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
2514 ReleaseScanner();
2517 // If the property could not be parsed with the resolved value, then we
2518 // treat it as if the value were 'initial' or 'inherit', depending on whether
2519 // the property is an inherited property.
2520 if (!valid) {
2521 nsCSSValue defaultValue;
2522 if (nsCSSProps::IsInherited(aPropertyID)) {
2523 defaultValue.SetInheritValue();
2524 } else {
2525 defaultValue.SetInitialValue();
2527 mTempData.AddLonghandProperty(aPropertyID, defaultValue);
2530 // Copy the property value into the rule data.
2531 mTempData.MapRuleInfoInto(aPropertyID, aRuleData);
2533 mTempData.ClearProperty(propertyToParse);
2534 mTempData.AssertInitialState();
2537 bool
2538 CSSParserImpl::ParseCounterStyleName(const nsAString& aBuffer,
2539 nsIURI* aURL,
2540 nsAString& aName)
2542 nsCSSScanner scanner(aBuffer, 0);
2543 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURL);
2544 InitScanner(scanner, reporter, aURL, aURL, nullptr);
2546 bool success = ParseCounterStyleName(aName, true) && !GetToken(true);
2548 OUTPUT_ERROR();
2549 ReleaseScanner();
2551 return success;
2554 bool
2555 CSSParserImpl::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
2556 const nsAString& aBuffer,
2557 nsIURI* aSheetURL,
2558 nsIURI* aBaseURL,
2559 nsIPrincipal* aSheetPrincipal,
2560 nsCSSValue& aValue)
2562 nsCSSScanner scanner(aBuffer, 0);
2563 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
2564 InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
2566 bool success = ParseCounterDescriptorValue(aDescID, aValue) &&
2567 !GetToken(true);
2569 OUTPUT_ERROR();
2570 ReleaseScanner();
2572 return success;
2575 bool
2576 CSSParserImpl::ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
2577 const nsAString& aBuffer,
2578 nsIURI* aSheetURL,
2579 nsIURI* aBaseURL,
2580 nsIPrincipal* aSheetPrincipal,
2581 nsCSSValue& aValue)
2583 nsCSSScanner scanner(aBuffer, 0);
2584 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
2585 InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
2587 bool success = ParseFontDescriptorValue(aDescID, aValue) &&
2588 !GetToken(true);
2590 OUTPUT_ERROR();
2591 ReleaseScanner();
2593 return success;
2596 //----------------------------------------------------------------------
2598 bool
2599 CSSParserImpl::GetToken(bool aSkipWS)
2601 if (mHavePushBack) {
2602 mHavePushBack = false;
2603 if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) {
2604 return true;
2607 return mScanner->Next(mToken, aSkipWS);
2610 void
2611 CSSParserImpl::UngetToken()
2613 NS_PRECONDITION(!mHavePushBack, "double pushback");
2614 mHavePushBack = true;
2617 bool
2618 CSSParserImpl::GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum)
2620 // Peek at next token so that mScanner updates line and column vals
2621 if (!GetToken(aSkipWS)) {
2622 return false;
2624 UngetToken();
2625 // The scanner uses one-indexing for line numbers but zero-indexing
2626 // for column numbers.
2627 *linenum = mScanner->GetLineNumber();
2628 *colnum = 1 + mScanner->GetColumnNumber();
2629 return true;
2632 bool
2633 CSSParserImpl::ExpectSymbol(char16_t aSymbol,
2634 bool aSkipWS)
2636 if (!GetToken(aSkipWS)) {
2637 // CSS2.1 specifies that all "open constructs" are to be closed at
2638 // EOF. It simplifies higher layers if we claim to have found an
2639 // ), ], }, or ; if we encounter EOF while looking for one of them.
2640 // Do still issue a diagnostic, to aid debugging.
2641 if (aSymbol == ')' || aSymbol == ']' ||
2642 aSymbol == '}' || aSymbol == ';') {
2643 REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
2644 return true;
2646 else
2647 return false;
2649 if (mToken.IsSymbol(aSymbol)) {
2650 return true;
2652 UngetToken();
2653 return false;
2656 // Checks to see if we're at the end of a property. If an error occurs during
2657 // the check, does not signal a parse error.
2658 bool
2659 CSSParserImpl::CheckEndProperty()
2661 if (!GetToken(true)) {
2662 return true; // properties may end with eof
2664 if ((eCSSToken_Symbol == mToken.mType) &&
2665 ((';' == mToken.mSymbol) ||
2666 ('!' == mToken.mSymbol) ||
2667 ('}' == mToken.mSymbol) ||
2668 (')' == mToken.mSymbol))) {
2669 // XXX need to verify that ! is only followed by "important [;|}]
2670 // XXX this requires a multi-token pushback buffer
2671 UngetToken();
2672 return true;
2674 UngetToken();
2675 return false;
2678 // Checks if we're at the end of a property, raising an error if we're not.
2679 bool
2680 CSSParserImpl::ExpectEndProperty()
2682 if (CheckEndProperty())
2683 return true;
2685 // If we're here, we read something incorrect, so we should report it.
2686 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
2687 return false;
2690 // Parses the priority suffix on a property, which at present may be
2691 // either '!important' or nothing.
2692 CSSParserImpl::PriorityParsingStatus
2693 CSSParserImpl::ParsePriority()
2695 if (!GetToken(true)) {
2696 return ePriority_None; // properties may end with EOF
2698 if (!mToken.IsSymbol('!')) {
2699 UngetToken();
2700 return ePriority_None; // dunno what it is, but it's not a priority
2703 if (!GetToken(true)) {
2704 // EOF is not ok after !
2705 REPORT_UNEXPECTED_EOF(PEImportantEOF);
2706 return ePriority_Error;
2709 if (mToken.mType != eCSSToken_Ident ||
2710 !mToken.mIdent.LowerCaseEqualsLiteral("important")) {
2711 REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
2712 UngetToken();
2713 return ePriority_Error;
2716 return ePriority_Important;
2719 nsSubstring*
2720 CSSParserImpl::NextIdent()
2722 // XXX Error reporting?
2723 if (!GetToken(true)) {
2724 return nullptr;
2726 if (eCSSToken_Ident != mToken.mType) {
2727 UngetToken();
2728 return nullptr;
2730 return &mToken.mIdent;
2733 bool
2734 CSSParserImpl::SkipAtRule(bool aInsideBlock)
2736 for (;;) {
2737 if (!GetToken(true)) {
2738 REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2);
2739 return false;
2741 if (eCSSToken_Symbol == mToken.mType) {
2742 char16_t symbol = mToken.mSymbol;
2743 if (symbol == ';') {
2744 break;
2746 if (aInsideBlock && symbol == '}') {
2747 // The closing } doesn't belong to us.
2748 UngetToken();
2749 break;
2751 if (symbol == '{') {
2752 SkipUntil('}');
2753 break;
2754 } else if (symbol == '(') {
2755 SkipUntil(')');
2756 } else if (symbol == '[') {
2757 SkipUntil(']');
2759 } else if (eCSSToken_Function == mToken.mType ||
2760 eCSSToken_Bad_URL == mToken.mType) {
2761 SkipUntil(')');
2764 return true;
2767 bool
2768 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
2769 void* aData,
2770 bool aInAtRule)
2773 nsCSSSection newSection;
2774 bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*);
2776 if ((mSection <= eCSSSection_Charset) &&
2777 (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
2778 parseFunc = &CSSParserImpl::ParseCharsetRule;
2779 newSection = eCSSSection_Import; // only one charset allowed
2781 } else if ((mSection <= eCSSSection_Import) &&
2782 mToken.mIdent.LowerCaseEqualsLiteral("import")) {
2783 parseFunc = &CSSParserImpl::ParseImportRule;
2784 newSection = eCSSSection_Import;
2786 } else if ((mSection <= eCSSSection_NameSpace) &&
2787 mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
2788 parseFunc = &CSSParserImpl::ParseNameSpaceRule;
2789 newSection = eCSSSection_NameSpace;
2791 } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
2792 parseFunc = &CSSParserImpl::ParseMediaRule;
2793 newSection = eCSSSection_General;
2795 } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
2796 parseFunc = &CSSParserImpl::ParseMozDocumentRule;
2797 newSection = eCSSSection_General;
2799 } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
2800 parseFunc = &CSSParserImpl::ParseFontFaceRule;
2801 newSection = eCSSSection_General;
2803 } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values")) {
2804 parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule;
2805 newSection = eCSSSection_General;
2807 } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
2808 parseFunc = &CSSParserImpl::ParsePageRule;
2809 newSection = eCSSSection_General;
2811 } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation) &&
2812 mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) ||
2813 mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) {
2814 parseFunc = &CSSParserImpl::ParseKeyframesRule;
2815 newSection = eCSSSection_General;
2817 } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports")) {
2818 parseFunc = &CSSParserImpl::ParseSupportsRule;
2819 newSection = eCSSSection_General;
2821 } else if (mToken.mIdent.LowerCaseEqualsLiteral("counter-style")) {
2822 parseFunc = &CSSParserImpl::ParseCounterStyleRule;
2823 newSection = eCSSSection_General;
2825 } else {
2826 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
2827 REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
2828 OUTPUT_ERROR();
2830 // Skip over unsupported at rule, don't advance section
2831 return SkipAtRule(aInAtRule);
2834 // Inside of @-rules, only the rules that can occur anywhere
2835 // are allowed.
2836 bool unnestable = aInAtRule && newSection != eCSSSection_General;
2837 if (unnestable) {
2838 REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule);
2841 if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) {
2842 // Skip over invalid at rule, don't advance section
2843 OUTPUT_ERROR();
2844 return SkipAtRule(aInAtRule);
2847 // Nested @-rules don't affect the top-level rule chain requirement
2848 if (!aInAtRule) {
2849 mSection = newSection;
2852 return true;
2855 bool
2856 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
2857 void* aData)
2859 uint32_t linenum, colnum;
2860 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
2861 !GetToken(true)) {
2862 REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
2863 return false;
2866 if (eCSSToken_String != mToken.mType) {
2867 UngetToken();
2868 REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
2869 return false;
2872 nsAutoString charset = mToken.mIdent;
2874 if (!ExpectSymbol(';', true)) {
2875 return false;
2878 nsRefPtr<css::CharsetRule> rule = new css::CharsetRule(charset,
2879 linenum, colnum);
2880 (*aAppendFunc)(rule, aData);
2882 return true;
2885 bool
2886 CSSParserImpl::ParseURLOrString(nsString& aURL)
2888 if (!GetToken(true)) {
2889 return false;
2891 if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) {
2892 aURL = mToken.mIdent;
2893 return true;
2895 UngetToken();
2896 return false;
2899 bool
2900 CSSParserImpl::ParseMediaQuery(eMediaQueryType aQueryType,
2901 nsMediaQuery **aQuery,
2902 bool *aHitStop)
2904 *aQuery = nullptr;
2905 *aHitStop = false;
2906 bool inAtRule = aQueryType == eMediaQueryAtRule;
2907 // Attempt to parse a single condition and stop
2908 bool singleCondition = aQueryType == eMediaQuerySingleCondition;
2910 // "If the comma-separated list is the empty list it is assumed to
2911 // specify the media query 'all'." (css3-mediaqueries, section
2912 // "Media Queries")
2913 if (!GetToken(true)) {
2914 *aHitStop = true;
2915 // expected termination by EOF
2916 if (!inAtRule)
2917 return true;
2919 // unexpected termination by EOF
2920 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
2921 return true;
2924 if (eCSSToken_Symbol == mToken.mType && inAtRule &&
2925 (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) {
2926 *aHitStop = true;
2927 UngetToken();
2928 return true;
2930 UngetToken();
2932 nsMediaQuery* query = new nsMediaQuery;
2933 *aQuery = query;
2935 if (ExpectSymbol('(', true)) {
2936 // we got an expression without a media type
2937 UngetToken(); // so ParseMediaQueryExpression can handle it
2938 query->SetType(nsGkAtoms::all);
2939 query->SetTypeOmitted();
2940 // Just parse the first expression here.
2941 if (!ParseMediaQueryExpression(query)) {
2942 OUTPUT_ERROR();
2943 query->SetHadUnknownExpression();
2945 } else if (singleCondition) {
2946 // Since we are only trying to consume a single condition, which precludes
2947 // media types and not/only, this should be the same as reaching immediate
2948 // EOF (no condition to parse)
2949 *aHitStop = true;
2950 return true;
2951 } else {
2952 nsCOMPtr<nsIAtom> mediaType;
2953 bool gotNotOrOnly = false;
2954 for (;;) {
2955 if (!GetToken(true)) {
2956 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
2957 return false;
2959 if (eCSSToken_Ident != mToken.mType) {
2960 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
2961 UngetToken();
2962 return false;
2964 // case insensitive from CSS - must be lower cased
2965 nsContentUtils::ASCIIToLower(mToken.mIdent);
2966 mediaType = do_GetAtom(mToken.mIdent);
2967 if (!mediaType) {
2968 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
2970 if (!gotNotOrOnly && mediaType == nsGkAtoms::_not) {
2971 gotNotOrOnly = true;
2972 query->SetNegated();
2973 } else if (!gotNotOrOnly && mediaType == nsGkAtoms::only) {
2974 gotNotOrOnly = true;
2975 query->SetHasOnly();
2976 } else if (mediaType == nsGkAtoms::_not ||
2977 mediaType == nsGkAtoms::only ||
2978 mediaType == nsGkAtoms::_and ||
2979 mediaType == nsGkAtoms::_or) {
2980 REPORT_UNEXPECTED_TOKEN(PEGatherMediaReservedMediaType);
2981 UngetToken();
2982 return false;
2983 } else {
2984 // valid media type
2985 break;
2988 query->SetType(mediaType);
2991 for (;;) {
2992 if (!GetToken(true)) {
2993 *aHitStop = true;
2994 // expected termination by EOF
2995 if (!inAtRule)
2996 break;
2998 // unexpected termination by EOF
2999 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
3000 break;
3003 if (eCSSToken_Symbol == mToken.mType && inAtRule &&
3004 (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) {
3005 *aHitStop = true;
3006 UngetToken();
3007 break;
3009 if (!singleCondition &&
3010 eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
3011 // Done with the expressions for this query
3012 break;
3014 if (eCSSToken_Ident != mToken.mType ||
3015 !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
3016 if (singleCondition) {
3017 // We have a condition at this point -- if we're not chained to other
3018 // conditions with and/or, we're done.
3019 UngetToken();
3020 break;
3021 } else {
3022 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
3023 UngetToken();
3024 return false;
3027 if (!ParseMediaQueryExpression(query)) {
3028 OUTPUT_ERROR();
3029 query->SetHadUnknownExpression();
3032 return true;
3035 // Returns false only when there is a low-level error in the scanner
3036 // (out-of-memory).
3037 bool
3038 CSSParserImpl::GatherMedia(nsMediaList* aMedia,
3039 bool aInAtRule)
3041 eMediaQueryType type = aInAtRule ? eMediaQueryAtRule : eMediaQueryNormal;
3042 for (;;) {
3043 nsAutoPtr<nsMediaQuery> query;
3044 bool hitStop;
3045 if (!ParseMediaQuery(type, getter_Transfers(query), &hitStop)) {
3046 NS_ASSERTION(!hitStop, "should return true when hit stop");
3047 OUTPUT_ERROR();
3048 if (query) {
3049 query->SetHadUnknownExpression();
3051 if (aInAtRule) {
3052 const char16_t stopChars[] =
3053 { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) };
3054 SkipUntilOneOf(stopChars);
3055 } else {
3056 SkipUntil(',');
3058 // Rely on SkipUntilOneOf leaving mToken around as the last token read.
3059 if (mToken.mType == eCSSToken_Symbol && aInAtRule &&
3060 (mToken.mSymbol == '{' || mToken.mSymbol == ';' || mToken.mSymbol == '}')) {
3061 UngetToken();
3062 hitStop = true;
3065 if (query) {
3066 aMedia->AppendQuery(query);
3068 if (hitStop) {
3069 break;
3072 return true;
3075 bool
3076 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
3078 if (!ExpectSymbol('(', true)) {
3079 REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
3080 return false;
3082 if (! GetToken(true)) {
3083 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
3084 return false;
3086 if (eCSSToken_Ident != mToken.mType) {
3087 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
3088 UngetToken();
3089 SkipUntil(')');
3090 return false;
3093 nsMediaExpression *expr = aQuery->NewExpression();
3095 // case insensitive from CSS - must be lower cased
3096 nsContentUtils::ASCIIToLower(mToken.mIdent);
3097 const char16_t *featureString;
3098 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
3099 expr->mRange = nsMediaExpression::eMin;
3100 featureString = mToken.mIdent.get() + 4;
3101 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
3102 expr->mRange = nsMediaExpression::eMax;
3103 featureString = mToken.mIdent.get() + 4;
3104 } else {
3105 expr->mRange = nsMediaExpression::eEqual;
3106 featureString = mToken.mIdent.get();
3109 nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
3110 if (!mediaFeatureAtom) {
3111 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
3113 const nsMediaFeature *feature = nsMediaFeatures::features;
3114 for (; feature->mName; ++feature) {
3115 if (*(feature->mName) == mediaFeatureAtom) {
3116 break;
3119 if (!feature->mName ||
3120 (expr->mRange != nsMediaExpression::eEqual &&
3121 feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
3122 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
3123 SkipUntil(')');
3124 return false;
3126 expr->mFeature = feature;
3128 if (!GetToken(true) || mToken.IsSymbol(')')) {
3129 // Query expressions for any feature can be given without a value.
3130 // However, min/max prefixes are not allowed.
3131 if (expr->mRange != nsMediaExpression::eEqual) {
3132 REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
3133 return false;
3135 expr->mValue.Reset();
3136 return true;
3139 if (!mToken.IsSymbol(':')) {
3140 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
3141 UngetToken();
3142 SkipUntil(')');
3143 return false;
3146 bool rv = false;
3147 switch (feature->mValueType) {
3148 case nsMediaFeature::eLength:
3149 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_LENGTH, nullptr);
3150 break;
3151 case nsMediaFeature::eInteger:
3152 case nsMediaFeature::eBoolInteger:
3153 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_INTEGER, nullptr);
3154 // Enforce extra restrictions for eBoolInteger
3155 if (rv &&
3156 feature->mValueType == nsMediaFeature::eBoolInteger &&
3157 expr->mValue.GetIntValue() > 1)
3158 rv = false;
3159 break;
3160 case nsMediaFeature::eFloat:
3161 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_NUMBER, nullptr);
3162 break;
3163 case nsMediaFeature::eIntRatio:
3165 // Two integers separated by '/', with optional whitespace on
3166 // either side of the '/'.
3167 nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
3168 expr->mValue.SetArrayValue(a, eCSSUnit_Array);
3169 // We don't bother with ParseNonNegativeVariant since we have to
3170 // check for != 0 as well; no need to worry about the UngetToken
3171 // since we're throwing out up to the next ')' anyway.
3172 rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nullptr) &&
3173 a->Item(0).GetIntValue() > 0 &&
3174 ExpectSymbol('/', true) &&
3175 ParseVariant(a->Item(1), VARIANT_INTEGER, nullptr) &&
3176 a->Item(1).GetIntValue() > 0;
3178 break;
3179 case nsMediaFeature::eResolution:
3180 rv = GetToken(true);
3181 if (!rv)
3182 break;
3183 rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f;
3184 if (!rv) {
3185 UngetToken();
3186 break;
3188 // No worries about whether unitless zero is allowed, since the
3189 // value must be positive (and we checked that above).
3190 NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
3191 if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
3192 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
3193 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) {
3194 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
3195 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
3196 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
3197 } else {
3198 rv = false;
3200 break;
3201 case nsMediaFeature::eEnumerated:
3202 rv = ParseVariant(expr->mValue, VARIANT_KEYWORD,
3203 feature->mData.mKeywordTable);
3204 break;
3205 case nsMediaFeature::eIdent:
3206 rv = ParseVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr);
3207 break;
3209 if (!rv || !ExpectSymbol(')', true)) {
3210 REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
3211 SkipUntil(')');
3212 return false;
3215 return true;
3218 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
3219 bool
3220 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
3222 nsRefPtr<nsMediaList> media = new nsMediaList();
3224 uint32_t linenum, colnum;
3225 nsAutoString url;
3226 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3227 !ParseURLOrString(url)) {
3228 REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
3229 return false;
3232 if (!ExpectSymbol(';', true)) {
3233 if (!GatherMedia(media, true) ||
3234 !ExpectSymbol(';', true)) {
3235 REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
3236 // don't advance section, simply ignore invalid @import
3237 return false;
3240 // Safe to assert this, since we ensured that there is something
3241 // other than the ';' coming after the @import's url() token.
3242 NS_ASSERTION(media->Length() != 0, "media list must be nonempty");
3245 ProcessImport(url, media, aAppendFunc, aData, linenum, colnum);
3246 return true;
3249 void
3250 CSSParserImpl::ProcessImport(const nsString& aURLSpec,
3251 nsMediaList* aMedia,
3252 RuleAppendFunc aAppendFunc,
3253 void* aData,
3254 uint32_t aLineNumber,
3255 uint32_t aColumnNumber)
3257 nsRefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec,
3258 aLineNumber,
3259 aColumnNumber);
3260 (*aAppendFunc)(rule, aData);
3262 // Diagnose bad URIs even if we don't have a child loader.
3263 nsCOMPtr<nsIURI> url;
3264 // Charset will be deduced from mBaseURI, which is more or less correct.
3265 nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI);
3267 if (NS_FAILED(rv)) {
3268 if (rv == NS_ERROR_MALFORMED_URI) {
3269 // import url is bad
3270 REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
3271 OUTPUT_ERROR();
3273 return;
3276 if (mChildLoader) {
3277 mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
3281 // Parse the {} part of an @media or @-moz-document rule.
3282 bool
3283 CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
3284 RuleAppendFunc aAppendFunc,
3285 void* aData)
3287 // XXXbz this could use better error reporting throughout the method
3288 if (!ExpectSymbol('{', true)) {
3289 return false;
3292 // push rule on stack, loop over children
3293 PushGroup(aRule);
3294 nsCSSSection holdSection = mSection;
3295 mSection = eCSSSection_General;
3297 for (;;) {
3298 // Get next non-whitespace token
3299 if (! GetToken(true)) {
3300 REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2);
3301 break;
3303 if (mToken.IsSymbol('}')) { // done!
3304 UngetToken();
3305 break;
3307 if (eCSSToken_AtKeyword == mToken.mType) {
3308 // Parse for nested rules
3309 ParseAtRule(aAppendFunc, aData, true);
3310 continue;
3312 UngetToken();
3313 ParseRuleSet(AppendRuleToSheet, this, true);
3315 PopGroup();
3317 if (!ExpectSymbol('}', true)) {
3318 mSection = holdSection;
3319 return false;
3321 (*aAppendFunc)(aRule, aData);
3322 return true;
3325 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
3326 bool
3327 CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
3329 nsRefPtr<nsMediaList> media = new nsMediaList();
3330 uint32_t linenum, colnum;
3331 if (GetNextTokenLocation(true, &linenum, &colnum) &&
3332 GatherMedia(media, true)) {
3333 // XXXbz this could use better error reporting throughout the method
3334 nsRefPtr<css::MediaRule> rule = new css::MediaRule(linenum, colnum);
3335 // Append first, so when we do SetMedia() the rule
3336 // knows what its stylesheet is.
3337 if (ParseGroupRule(rule, aAppendFunc, aData)) {
3338 rule->SetMedia(media);
3339 return true;
3343 return false;
3346 // Parse a @-moz-document rule. This is like an @media rule, but instead
3347 // of a medium it has a nonempty list of items where each item is either
3348 // url(), url-prefix(), or domain().
3349 bool
3350 CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
3352 css::DocumentRule::URL *urls = nullptr;
3353 css::DocumentRule::URL **next = &urls;
3355 uint32_t linenum, colnum;
3356 if (!GetNextTokenLocation(true, &linenum, &colnum)) {
3357 return false;
3360 do {
3361 if (!GetToken(true)) {
3362 REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF);
3363 delete urls;
3364 return false;
3367 if (!(eCSSToken_URL == mToken.mType ||
3368 (eCSSToken_Function == mToken.mType &&
3369 (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
3370 mToken.mIdent.LowerCaseEqualsLiteral("domain") ||
3371 mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) {
3372 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc2);
3373 UngetToken();
3374 delete urls;
3375 return false;
3377 css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL;
3378 next = &cur->next;
3379 if (mToken.mType == eCSSToken_URL) {
3380 cur->func = css::DocumentRule::eURL;
3381 CopyUTF16toUTF8(mToken.mIdent, cur->url);
3382 } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) {
3383 // regexp() is different from url-prefix() and domain() (but
3384 // probably the way they *should* have been* in that it requires a
3385 // string argument, and doesn't try to behave like url().
3386 cur->func = css::DocumentRule::eRegExp;
3387 GetToken(true);
3388 // copy before we know it's valid (but before ExpectSymbol changes
3389 // mToken.mIdent)
3390 CopyUTF16toUTF8(mToken.mIdent, cur->url);
3391 if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) {
3392 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString);
3393 SkipUntil(')');
3394 delete urls;
3395 return false;
3397 } else {
3398 if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
3399 cur->func = css::DocumentRule::eURLPrefix;
3400 } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
3401 cur->func = css::DocumentRule::eDomain;
3404 NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point");
3405 mScanner->NextURL(mToken);
3406 if (mToken.mType != eCSSToken_URL) {
3407 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
3408 SkipUntil(')');
3409 delete urls;
3410 return false;
3413 // We could try to make the URL (as long as it's not domain())
3414 // canonical and absolute with NS_NewURI and GetSpec, but I'm
3415 // inclined to think we shouldn't.
3416 CopyUTF16toUTF8(mToken.mIdent, cur->url);
3418 } while (ExpectSymbol(',', true));
3420 nsRefPtr<css::DocumentRule> rule = new css::DocumentRule(linenum, colnum);
3421 rule->SetURLs(urls);
3423 return ParseGroupRule(rule, aAppendFunc, aData);
3426 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
3427 bool
3428 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
3430 uint32_t linenum, colnum;
3431 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3432 !GetToken(true)) {
3433 REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
3434 return false;
3437 nsAutoString prefix;
3438 nsAutoString url;
3440 if (eCSSToken_Ident == mToken.mType) {
3441 prefix = mToken.mIdent;
3442 // user-specified identifiers are case-sensitive (bug 416106)
3443 } else {
3444 UngetToken();
3447 if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) {
3448 if (mHavePushBack) {
3449 REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
3450 } else {
3451 REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
3453 return false;
3456 ProcessNameSpace(prefix, url, aAppendFunc, aData, linenum, colnum);
3457 return true;
3460 void
3461 CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
3462 const nsString& aURLSpec,
3463 RuleAppendFunc aAppendFunc,
3464 void* aData,
3465 uint32_t aLineNumber,
3466 uint32_t aColumnNumber)
3468 nsCOMPtr<nsIAtom> prefix;
3470 if (!aPrefix.IsEmpty()) {
3471 prefix = do_GetAtom(aPrefix);
3472 if (!prefix) {
3473 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
3477 nsRefPtr<css::NameSpaceRule> rule = new css::NameSpaceRule(prefix, aURLSpec,
3478 aLineNumber,
3479 aColumnNumber);
3480 (*aAppendFunc)(rule, aData);
3482 // If this was the first namespace rule encountered, it will trigger
3483 // creation of a namespace map.
3484 if (!mNameSpaceMap) {
3485 mNameSpaceMap = mSheet->GetNameSpaceMap();
3489 // font-face-rule: '@font-face' '{' font-description '}'
3490 // font-description: font-descriptor+
3491 bool
3492 CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
3494 uint32_t linenum, colnum;
3495 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3496 !ExpectSymbol('{', true)) {
3497 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart);
3498 return false;
3501 nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule(linenum, colnum));
3503 for (;;) {
3504 if (!GetToken(true)) {
3505 REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
3506 break;
3508 if (mToken.IsSymbol('}')) { // done!
3509 UngetToken();
3510 break;
3513 // ignore extra semicolons
3514 if (mToken.IsSymbol(';'))
3515 continue;
3517 if (!ParseFontDescriptor(rule)) {
3518 REPORT_UNEXPECTED(PEDeclSkipped);
3519 OUTPUT_ERROR();
3520 if (!SkipDeclaration(true))
3521 break;
3524 if (!ExpectSymbol('}', true)) {
3525 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd);
3526 return false;
3528 (*aAppendFunc)(rule, aData);
3529 return true;
3532 // font-descriptor: font-family-desc
3533 // | font-style-desc
3534 // | font-weight-desc
3535 // | font-stretch-desc
3536 // | font-src-desc
3537 // | unicode-range-desc
3539 // All font-*-desc productions follow the pattern
3540 // IDENT ':' value ';'
3542 // On entry to this function, mToken is the IDENT.
3544 bool
3545 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
3547 if (eCSSToken_Ident != mToken.mType) {
3548 REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
3549 return false;
3552 nsString descName = mToken.mIdent;
3553 if (!ExpectSymbol(':', true)) {
3554 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3555 OUTPUT_ERROR();
3556 return false;
3559 nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
3560 nsCSSValue value;
3562 if (descID == eCSSFontDesc_UNKNOWN) {
3563 if (NonMozillaVendorIdentifier(descName)) {
3564 // silently skip other vendors' extensions
3565 SkipDeclaration(true);
3566 return true;
3567 } else {
3568 REPORT_UNEXPECTED_P(PEUnknownFontDesc, descName);
3569 return false;
3573 if (!ParseFontDescriptorValue(descID, value)) {
3574 REPORT_UNEXPECTED_P(PEValueParsingError, descName);
3575 return false;
3578 if (!ExpectEndProperty())
3579 return false;
3581 aRule->SetDesc(descID, value);
3582 return true;
3585 // @font-feature-values <font-family># {
3586 // @<feature-type> {
3587 // <feature-ident> : <feature-index>+;
3588 // <feature-ident> : <feature-index>+;
3589 // ...
3590 // }
3591 // ...
3592 // }
3594 bool
3595 CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
3596 void* aData)
3598 uint32_t linenum, colnum;
3599 if (!GetNextTokenLocation(true, &linenum, &colnum)) {
3600 return false;
3603 nsRefPtr<nsCSSFontFeatureValuesRule>
3604 valuesRule(new nsCSSFontFeatureValuesRule(linenum, colnum));
3606 // parse family list
3607 nsCSSValue fontlistValue;
3609 if (!ParseFamily(fontlistValue) ||
3610 fontlistValue.GetUnit() != eCSSUnit_FontFamilyList)
3612 REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily);
3613 return false;
3616 // add family to rule
3617 const FontFamilyList* fontlist = fontlistValue.GetFontFamilyListValue();
3619 // family list has generic ==> parse error
3620 if (fontlist->HasGeneric()) {
3621 REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList);
3622 return false;
3625 valuesRule->SetFamilyList(*fontlist);
3627 // open brace
3628 if (!ExpectSymbol('{', true)) {
3629 REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart);
3630 return false;
3633 // list of sets of feature values, each set bound to a specific
3634 // feature-type (e.g. swash, annotation)
3635 for (;;) {
3636 if (!GetToken(true)) {
3637 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
3638 break;
3640 if (mToken.IsSymbol('}')) { // done!
3641 UngetToken();
3642 break;
3645 if (!ParseFontFeatureValueSet(valuesRule)) {
3646 if (!SkipAtRule(false)) {
3647 break;
3651 if (!ExpectSymbol('}', true)) {
3652 REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd);
3653 SkipUntil('}');
3654 return false;
3657 (*aAppendFunc)(valuesRule, aData);
3658 return true;
3661 #define NUMVALUES_NO_LIMIT 0xFFFF
3663 // parse a single value set containing name-value pairs for a single feature type
3664 // @<feature-type> { [ <feature-ident> : <feature-index>+ ; ]* }
3665 // Ex: @swash { flowing: 1; delicate: 2; }
3666 bool
3667 CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule
3668 *aFeatureValuesRule)
3670 // -- @keyword (e.g. swash, styleset)
3671 if (eCSSToken_AtKeyword != mToken.mType) {
3672 REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt);
3673 OUTPUT_ERROR();
3674 UngetToken();
3675 return false;
3678 // which font-specific variant of font-variant-alternates
3679 int32_t whichVariant;
3680 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
3681 if (keyword == eCSSKeyword_UNKNOWN ||
3682 !nsCSSProps::FindKeyword(keyword,
3683 nsCSSProps::kFontVariantAlternatesFuncsKTable,
3684 whichVariant))
3686 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
3687 REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue);
3688 OUTPUT_ERROR();
3690 UngetToken();
3691 return false;
3694 nsAutoString featureType(mToken.mIdent);
3696 // open brace
3697 if (!ExpectSymbol('{', true)) {
3698 REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart);
3699 return false;
3702 // styleset and character-variant can be multi-valued, otherwise single value
3703 int32_t limitNumValues;
3705 switch (keyword) {
3706 case eCSSKeyword_styleset:
3707 limitNumValues = NUMVALUES_NO_LIMIT;
3708 break;
3709 case eCSSKeyword_character_variant:
3710 limitNumValues = 2;
3711 break;
3712 default:
3713 limitNumValues = 1;
3714 break;
3717 // -- ident integer+ [, ident integer+]
3718 nsAutoTArray<gfxFontFeatureValueSet::ValueList, 5> values;
3720 // list of font-feature-values-declaration's
3721 for (;;) {
3722 nsAutoString valueId;
3724 if (!GetToken(true)) {
3725 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
3726 break;
3729 // ignore extra semicolons
3730 if (mToken.IsSymbol(';')) {
3731 continue;
3734 // close brace ==> done
3735 if (mToken.IsSymbol('}')) {
3736 break;
3739 // ident
3740 if (eCSSToken_Ident != mToken.mType) {
3741 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent);
3742 if (!SkipDeclaration(true)) {
3743 break;
3745 continue;
3748 valueId.Assign(mToken.mIdent);
3750 // colon
3751 if (!ExpectSymbol(':', true)) {
3752 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3753 OUTPUT_ERROR();
3754 if (!SkipDeclaration(true)) {
3755 break;
3757 continue;
3760 // value list
3761 nsAutoTArray<uint32_t,4> featureSelectors;
3763 nsCSSValue intValue;
3764 while (ParseNonNegativeVariant(intValue, VARIANT_INTEGER, nullptr)) {
3765 featureSelectors.AppendElement(uint32_t(intValue.GetIntValue()));
3768 int32_t numValues = featureSelectors.Length();
3770 if (numValues == 0) {
3771 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue);
3772 OUTPUT_ERROR();
3773 if (!SkipDeclaration(true)) {
3774 break;
3776 continue;
3779 if (numValues > limitNumValues) {
3780 REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType);
3781 OUTPUT_ERROR();
3782 if (!SkipDeclaration(true)) {
3783 break;
3785 continue;
3788 if (!GetToken(true)) {
3789 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
3790 gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
3791 values.AppendElement(v);
3792 break;
3795 // ';' or '}' to end definition
3796 if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) {
3797 REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing);
3798 OUTPUT_ERROR();
3799 if (!SkipDeclaration(true)) {
3800 break;
3802 continue;
3805 gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
3806 values.AppendElement(v);
3808 if (mToken.IsSymbol('}')) {
3809 break;
3813 aFeatureValuesRule->AddValueList(whichVariant, values);
3814 return true;
3817 bool
3818 CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData)
3820 uint32_t linenum, colnum;
3821 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3822 !GetToken(true)) {
3823 REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF);
3824 return false;
3827 if (mToken.mType != eCSSToken_Ident) {
3828 REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
3829 UngetToken();
3830 return false;
3832 nsString name(mToken.mIdent);
3834 if (!ExpectSymbol('{', true)) {
3835 REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace);
3836 return false;
3839 nsRefPtr<nsCSSKeyframesRule> rule = new nsCSSKeyframesRule(name,
3840 linenum, colnum);
3842 while (!ExpectSymbol('}', true)) {
3843 nsRefPtr<nsCSSKeyframeRule> kid = ParseKeyframeRule();
3844 if (kid) {
3845 rule->AppendStyleRule(kid);
3846 } else {
3847 OUTPUT_ERROR();
3848 SkipRuleSet(true);
3852 (*aAppendFunc)(rule, aData);
3853 return true;
3856 bool
3857 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
3859 uint32_t linenum, colnum;
3860 if (!GetNextTokenLocation(true, &linenum, &colnum)) {
3861 return false;
3864 // TODO: There can be page selectors after @page such as ":first", ":left".
3865 uint32_t parseFlags = eParseDeclaration_InBraces |
3866 eParseDeclaration_AllowImportant;
3868 // Forbid viewport units in @page rules. See bug 811391.
3869 NS_ABORT_IF_FALSE(mViewportUnitsEnabled,
3870 "Viewport units should be enabled outside of @page rules.");
3871 mViewportUnitsEnabled = false;
3872 nsAutoPtr<css::Declaration> declaration(
3873 ParseDeclarationBlock(parseFlags,
3874 eCSSContext_Page));
3875 mViewportUnitsEnabled = true;
3877 if (!declaration) {
3878 return false;
3881 // Takes ownership of declaration.
3882 nsRefPtr<nsCSSPageRule> rule = new nsCSSPageRule(Move(declaration),
3883 linenum, colnum);
3885 (*aAppendFunc)(rule, aData);
3886 return true;
3889 already_AddRefed<nsCSSKeyframeRule>
3890 CSSParserImpl::ParseKeyframeRule()
3892 InfallibleTArray<float> selectorList;
3893 uint32_t linenum, colnum;
3894 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3895 !ParseKeyframeSelectorList(selectorList)) {
3896 REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
3897 return nullptr;
3900 // Ignore !important in keyframe rules
3901 uint32_t parseFlags = eParseDeclaration_InBraces;
3902 nsAutoPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags));
3903 if (!declaration) {
3904 return nullptr;
3907 // Takes ownership of declaration, and steals contents of selectorList.
3908 nsRefPtr<nsCSSKeyframeRule> rule =
3909 new nsCSSKeyframeRule(selectorList, Move(declaration), linenum, colnum);
3910 return rule.forget();
3913 bool
3914 CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList)
3916 for (;;) {
3917 if (!GetToken(true)) {
3918 // The first time through the loop, this means we got an empty
3919 // list. Otherwise, it means we have a trailing comma.
3920 return false;
3922 float value;
3923 switch (mToken.mType) {
3924 case eCSSToken_Percentage:
3925 value = mToken.mNumber;
3926 break;
3927 case eCSSToken_Ident:
3928 if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
3929 value = 0.0f;
3930 break;
3932 if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
3933 value = 1.0f;
3934 break;
3936 // fall through
3937 default:
3938 UngetToken();
3939 // The first time through the loop, this means we got an empty
3940 // list. Otherwise, it means we have a trailing comma.
3941 return false;
3943 aSelectorList.AppendElement(value);
3944 if (!ExpectSymbol(',', true)) {
3945 return true;
3950 // supports_rule
3951 // : "@supports" supports_condition group_rule_body
3952 // ;
3953 bool
3954 CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData)
3956 bool conditionMet = false;
3957 nsString condition;
3959 mScanner->StartRecording();
3961 uint32_t linenum, colnum;
3962 if (!GetNextTokenLocation(true, &linenum, &colnum)) {
3963 return false;
3966 bool parsed = ParseSupportsCondition(conditionMet);
3968 if (!parsed) {
3969 mScanner->StopRecording();
3970 return false;
3973 if (!ExpectSymbol('{', true)) {
3974 REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart);
3975 mScanner->StopRecording();
3976 return false;
3979 UngetToken();
3980 mScanner->StopRecording(condition);
3982 // Remove the "{" that would follow the condition.
3983 if (condition.Length() != 0) {
3984 condition.Truncate(condition.Length() - 1);
3987 // Remove spaces from the start and end of the recorded supports condition.
3988 condition.Trim(" ", true, true, false);
3990 // Record whether we are in a failing @supports, so that property parse
3991 // errors don't get reported.
3992 nsAutoFailingSupportsRule failing(this, conditionMet);
3994 nsRefPtr<css::GroupRule> rule = new CSSSupportsRule(conditionMet, condition,
3995 linenum, colnum);
3996 return ParseGroupRule(rule, aAppendFunc, aProcessData);
3999 // supports_condition
4000 // : supports_condition_in_parens supports_condition_terms
4001 // | supports_condition_negation
4002 // ;
4003 bool
4004 CSSParserImpl::ParseSupportsCondition(bool& aConditionMet)
4006 mInSupportsCondition = true;
4008 if (!GetToken(true)) {
4009 REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2);
4010 return false;
4013 UngetToken();
4015 mScanner->ClearSeenBadToken();
4017 if (mToken.IsSymbol('(') ||
4018 mToken.mType == eCSSToken_Function ||
4019 mToken.mType == eCSSToken_URL ||
4020 mToken.mType == eCSSToken_Bad_URL) {
4021 bool result = ParseSupportsConditionInParens(aConditionMet) &&
4022 ParseSupportsConditionTerms(aConditionMet) &&
4023 !mScanner->SeenBadToken();
4024 mInSupportsCondition = false;
4025 return result;
4028 if (mToken.mType == eCSSToken_Ident &&
4029 mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4030 bool result = ParseSupportsConditionNegation(aConditionMet) &&
4031 !mScanner->SeenBadToken();
4032 mInSupportsCondition = false;
4033 return result;
4036 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart);
4037 mInSupportsCondition = false;
4038 return false;
4041 // supports_condition_negation
4042 // : 'not' S+ supports_condition_in_parens
4043 // ;
4044 bool
4045 CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet)
4047 if (!GetToken(true)) {
4048 REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF);
4049 return false;
4052 if (mToken.mType != eCSSToken_Ident ||
4053 !mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4054 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot);
4055 return false;
4058 if (!RequireWhitespace()) {
4059 REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
4060 return false;
4063 if (ParseSupportsConditionInParens(aConditionMet)) {
4064 aConditionMet = !aConditionMet;
4065 return true;
4068 return false;
4071 // supports_condition_in_parens
4072 // : '(' S* supports_condition_in_parens_inside_parens ')' S*
4073 // | general_enclosed
4074 // ;
4075 bool
4076 CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet)
4078 if (!GetToken(true)) {
4079 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF);
4080 return false;
4083 if (mToken.mType == eCSSToken_URL) {
4084 aConditionMet = false;
4085 return true;
4088 if (mToken.mType == eCSSToken_Function ||
4089 mToken.mType == eCSSToken_Bad_URL) {
4090 if (!SkipUntil(')')) {
4091 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
4092 return false;
4094 aConditionMet = false;
4095 return true;
4098 if (!mToken.IsSymbol('(')) {
4099 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction);
4100 UngetToken();
4101 return false;
4104 if (!ParseSupportsConditionInParensInsideParens(aConditionMet)) {
4105 if (!SkipUntil(')')) {
4106 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
4107 return false;
4109 aConditionMet = false;
4110 return true;
4113 if (!(ExpectSymbol(')', true))) {
4114 SkipUntil(')');
4115 aConditionMet = false;
4116 return true;
4119 return true;
4122 // supports_condition_in_parens_inside_parens
4123 // : core_declaration
4124 // | supports_condition_negation
4125 // | supports_condition_in_parens supports_condition_terms
4126 // ;
4127 bool
4128 CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet)
4130 if (!GetToken(true)) {
4131 return false;
4134 if (mToken.mType == eCSSToken_Ident) {
4135 if (!mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4136 nsAutoString propertyName = mToken.mIdent;
4137 if (!ExpectSymbol(':', true)) {
4138 return false;
4141 nsCSSProperty propID = LookupEnabledProperty(propertyName);
4142 if (propID == eCSSProperty_UNKNOWN) {
4143 if (ExpectSymbol(')', true)) {
4144 UngetToken();
4145 return false;
4147 aConditionMet = false;
4148 SkipUntil(')');
4149 UngetToken();
4150 } else if (propID == eCSSPropertyExtra_variable) {
4151 if (ExpectSymbol(')', false)) {
4152 UngetToken();
4153 return false;
4155 CSSVariableDeclarations::Type variableType;
4156 nsString variableValue;
4157 aConditionMet =
4158 ParseVariableDeclaration(&variableType, variableValue) &&
4159 ParsePriority() != ePriority_Error;
4160 if (!aConditionMet) {
4161 SkipUntil(')');
4162 UngetToken();
4164 } else {
4165 if (ExpectSymbol(')', true)) {
4166 UngetToken();
4167 return false;
4169 aConditionMet = ParseProperty(propID) &&
4170 ParsePriority() != ePriority_Error;
4171 if (!aConditionMet) {
4172 SkipUntil(')');
4173 UngetToken();
4175 mTempData.ClearProperty(propID);
4176 mTempData.AssertInitialState();
4178 return true;
4181 UngetToken();
4182 return ParseSupportsConditionNegation(aConditionMet);
4185 UngetToken();
4186 return ParseSupportsConditionInParens(aConditionMet) &&
4187 ParseSupportsConditionTerms(aConditionMet);
4190 // supports_condition_terms
4191 // : S+ 'and' supports_condition_terms_after_operator('and')
4192 // | S+ 'or' supports_condition_terms_after_operator('or')
4193 // |
4194 // ;
4195 bool
4196 CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet)
4198 if (!RequireWhitespace() || !GetToken(false)) {
4199 return true;
4202 if (mToken.mType != eCSSToken_Ident) {
4203 UngetToken();
4204 return true;
4207 if (mToken.mIdent.LowerCaseEqualsLiteral("and")) {
4208 return ParseSupportsConditionTermsAfterOperator(aConditionMet, eAnd);
4211 if (mToken.mIdent.LowerCaseEqualsLiteral("or")) {
4212 return ParseSupportsConditionTermsAfterOperator(aConditionMet, eOr);
4215 UngetToken();
4216 return true;
4219 // supports_condition_terms_after_operator(operator)
4220 // : S+ supports_condition_in_parens ( <operator> supports_condition_in_parens )*
4221 // ;
4222 bool
4223 CSSParserImpl::ParseSupportsConditionTermsAfterOperator(
4224 bool& aConditionMet,
4225 CSSParserImpl::SupportsConditionTermOperator aOperator)
4227 if (!RequireWhitespace()) {
4228 REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
4229 return false;
4232 const char* token = aOperator == eAnd ? "and" : "or";
4233 for (;;) {
4234 bool termConditionMet = false;
4235 if (!ParseSupportsConditionInParens(termConditionMet)) {
4236 return false;
4238 aConditionMet = aOperator == eAnd ? aConditionMet && termConditionMet :
4239 aConditionMet || termConditionMet;
4241 if (!GetToken(true)) {
4242 return true;
4245 if (mToken.mType != eCSSToken_Ident ||
4246 !mToken.mIdent.LowerCaseEqualsASCII(token)) {
4247 UngetToken();
4248 return true;
4253 bool
4254 CSSParserImpl::ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aData)
4256 nsAutoString name;
4257 uint32_t linenum, colnum;
4258 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4259 !ParseCounterStyleName(name, true)) {
4260 REPORT_UNEXPECTED_TOKEN(PECounterStyleNotIdent);
4261 return false;
4264 if (!ExpectSymbol('{', true)) {
4265 REPORT_UNEXPECTED_TOKEN(PECounterStyleBadBlockStart);
4266 return false;
4269 nsRefPtr<nsCSSCounterStyleRule> rule = new nsCSSCounterStyleRule(name,
4270 linenum,
4271 colnum);
4272 for (;;) {
4273 if (!GetToken(true)) {
4274 REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
4275 break;
4277 if (mToken.IsSymbol('}')) {
4278 break;
4280 if (mToken.IsSymbol(';')) {
4281 continue;
4284 if (!ParseCounterDescriptor(rule)) {
4285 REPORT_UNEXPECTED(PEDeclSkipped);
4286 OUTPUT_ERROR();
4287 if (!SkipDeclaration(true)) {
4288 REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
4289 break;
4294 int32_t system = rule->GetSystem();
4295 bool isCorrect = false;
4296 switch (system) {
4297 case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
4298 case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
4299 case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
4300 case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
4301 case NS_STYLE_COUNTER_SYSTEM_FIXED: {
4302 // check whether symbols is set and the length is sufficient
4303 const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
4304 if (symbols.GetUnit() == eCSSUnit_List &&
4305 nsCSSCounterStyleRule::CheckDescValue(
4306 system, eCSSCounterDesc_Symbols, symbols)) {
4307 isCorrect = true;
4309 break;
4311 case NS_STYLE_COUNTER_SYSTEM_ADDITIVE: {
4312 // for additive system, additive-symbols must be set
4313 const nsCSSValue& symbols =
4314 rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
4315 if (symbols.GetUnit() == eCSSUnit_PairList) {
4316 isCorrect = true;
4318 break;
4320 case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
4321 // for extends system, symbols & additive-symbols must not be set
4322 const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
4323 const nsCSSValue& additiveSymbols =
4324 rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
4325 if (symbols.GetUnit() == eCSSUnit_Null &&
4326 additiveSymbols.GetUnit() == eCSSUnit_Null) {
4327 isCorrect = true;
4329 break;
4331 default:
4332 NS_NOTREACHED("unknown system");
4335 if (isCorrect) {
4336 (*aAppendFunc)(rule, aData);
4338 return true;
4341 bool
4342 CSSParserImpl::ParseCounterStyleName(nsAString& aName, bool aForDefinition)
4344 if (!GetToken(true)) {
4345 return false;
4348 if (mToken.mType != eCSSToken_Ident) {
4349 UngetToken();
4350 return false;
4353 static const nsCSSKeyword kReservedNames[] = {
4354 eCSSKeyword_none,
4355 eCSSKeyword_decimal,
4356 eCSSKeyword_UNKNOWN
4359 nsCSSValue value; // we don't actually care about the value
4360 if (!ParseCustomIdent(value, mToken.mIdent,
4361 aForDefinition ? kReservedNames : nullptr)) {
4362 REPORT_UNEXPECTED_TOKEN(PECounterStyleBadName);
4363 UngetToken();
4364 return false;
4367 aName = mToken.mIdent;
4368 if (nsCSSProps::IsPredefinedCounterStyle(aName)) {
4369 ToLowerCase(aName);
4371 return true;
4374 bool
4375 CSSParserImpl::ParseCounterStyleNameValue(nsCSSValue& aValue)
4377 nsString name;
4378 if (ParseCounterStyleName(name, false)) {
4379 aValue.SetStringValue(name, eCSSUnit_Ident);
4380 return true;
4382 return false;
4385 bool
4386 CSSParserImpl::ParseCounterDescriptor(nsCSSCounterStyleRule* aRule)
4388 if (eCSSToken_Ident != mToken.mType) {
4389 REPORT_UNEXPECTED_TOKEN(PECounterDescExpected);
4390 return false;
4393 nsString descName = mToken.mIdent;
4394 if (!ExpectSymbol(':', true)) {
4395 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
4396 OUTPUT_ERROR();
4397 return false;
4400 nsCSSCounterDesc descID = nsCSSProps::LookupCounterDesc(descName);
4401 nsCSSValue value;
4403 if (descID == eCSSCounterDesc_UNKNOWN) {
4404 REPORT_UNEXPECTED_P(PEUnknownCounterDesc, descName);
4405 return false;
4408 if (!ParseCounterDescriptorValue(descID, value)) {
4409 REPORT_UNEXPECTED_P(PEValueParsingError, descName);
4410 return false;
4413 if (!ExpectEndProperty()) {
4414 return false;
4417 aRule->SetDesc(descID, value);
4418 return true;
4421 bool
4422 CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
4423 nsCSSValue& aValue)
4425 // Should also include VARIANT_IMAGE, but it is not supported currently.
4426 // See bug 1024179.
4427 static const int32_t VARIANT_COUNTER_SYMBOL =
4428 VARIANT_STRING | VARIANT_IDENTIFIER;
4430 switch (aDescID) {
4431 case eCSSCounterDesc_System: {
4432 nsCSSValue system;
4433 if (!ParseEnum(system, nsCSSProps::kCounterSystemKTable)) {
4434 return false;
4436 switch (system.GetIntValue()) {
4437 case NS_STYLE_COUNTER_SYSTEM_FIXED: {
4438 nsCSSValue start;
4439 if (!ParseVariant(start, VARIANT_INTEGER, nullptr)) {
4440 start.SetIntValue(1, eCSSUnit_Integer);
4442 aValue.SetPairValue(system, start);
4443 return true;
4445 case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
4446 nsCSSValue name;
4447 if (!ParseCounterStyleNameValue(name)) {
4448 REPORT_UNEXPECTED_TOKEN(PECounterExtendsNotIdent);
4449 return false;
4451 aValue.SetPairValue(system, name);
4452 return true;
4454 default:
4455 aValue = system;
4456 return true;
4460 case eCSSCounterDesc_Negative: {
4461 nsCSSValue first, second;
4462 if (!ParseVariant(first, VARIANT_COUNTER_SYMBOL, nullptr)) {
4463 return false;
4465 if (!ParseVariant(second, VARIANT_COUNTER_SYMBOL, nullptr)) {
4466 aValue = first;
4467 } else {
4468 aValue.SetPairValue(first, second);
4470 return true;
4473 case eCSSCounterDesc_Prefix:
4474 case eCSSCounterDesc_Suffix:
4475 return ParseVariant(aValue, VARIANT_COUNTER_SYMBOL, nullptr);
4477 case eCSSCounterDesc_Range: {
4478 if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
4479 return true;
4481 nsCSSValuePairList* item = aValue.SetPairListValue();
4482 for (;;) {
4483 nsCSSValuePair pair;
4484 if (!ParseCounterRange(pair)) {
4485 return false;
4487 item->mXValue = pair.mXValue;
4488 item->mYValue = pair.mYValue;
4489 if (!ExpectSymbol(',', true)) {
4490 return true;
4492 item->mNext = new nsCSSValuePairList;
4493 item = item->mNext;
4495 // should always return in the loop
4498 case eCSSCounterDesc_Pad: {
4499 nsCSSValue width, symbol;
4500 bool hasWidth = ParseNonNegativeInteger(width);
4501 if (!ParseVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
4502 (!hasWidth && !ParseNonNegativeInteger(width))) {
4503 return false;
4505 aValue.SetPairValue(width, symbol);
4506 return true;
4509 case eCSSCounterDesc_Fallback:
4510 return ParseCounterStyleNameValue(aValue);
4512 case eCSSCounterDesc_Symbols: {
4513 nsCSSValueList* item = nullptr;
4514 for (;;) {
4515 nsCSSValue value;
4516 if (!ParseVariant(value, VARIANT_COUNTER_SYMBOL, nullptr)) {
4517 return !!item;
4519 if (!item) {
4520 item = aValue.SetListValue();
4521 } else {
4522 item->mNext = new nsCSSValueList;
4523 item = item->mNext;
4525 item->mValue = value;
4527 // should always return in the loop
4530 case eCSSCounterDesc_AdditiveSymbols: {
4531 nsCSSValuePairList* item = nullptr;
4532 int32_t lastWeight = -1;
4533 for (;;) {
4534 nsCSSValue weight, symbol;
4535 bool hasWeight = ParseNonNegativeInteger(weight);
4536 if (!ParseVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
4537 (!hasWeight && !ParseNonNegativeInteger(weight))) {
4538 return false;
4540 if (lastWeight != -1 && weight.GetIntValue() >= lastWeight) {
4541 REPORT_UNEXPECTED(PECounterASWeight);
4542 return false;
4544 lastWeight = weight.GetIntValue();
4545 if (!item) {
4546 item = aValue.SetPairListValue();
4547 } else {
4548 item->mNext = new nsCSSValuePairList;
4549 item = item->mNext;
4551 item->mXValue = weight;
4552 item->mYValue = symbol;
4553 if (!ExpectSymbol(',', true)) {
4554 return true;
4557 // should always return in the loop
4560 case eCSSCounterDesc_SpeakAs:
4561 if (ParseVariant(aValue, VARIANT_AUTO | VARIANT_KEYWORD,
4562 nsCSSProps::kCounterSpeakAsKTable)) {
4563 if (aValue.GetUnit() == eCSSUnit_Enumerated &&
4564 aValue.GetIntValue() == NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT) {
4565 // Currently spell-out is not supported, so it is explicitly
4566 // rejected here rather than parsed as a custom identifier.
4567 // See bug 1024178.
4568 return false;
4570 return true;
4572 return ParseCounterStyleNameValue(aValue);
4574 default:
4575 NS_NOTREACHED("unknown descriptor");
4576 return false;
4580 bool
4581 CSSParserImpl::ParseCounterRange(nsCSSValuePair& aPair)
4583 static const int32_t VARIANT_BOUND = VARIANT_INTEGER | VARIANT_KEYWORD;
4584 nsCSSValue lower, upper;
4585 if (!ParseVariant(lower, VARIANT_BOUND, nsCSSProps::kCounterRangeKTable) ||
4586 !ParseVariant(upper, VARIANT_BOUND, nsCSSProps::kCounterRangeKTable)) {
4587 return false;
4589 if (lower.GetUnit() != eCSSUnit_Enumerated &&
4590 upper.GetUnit() != eCSSUnit_Enumerated &&
4591 lower.GetIntValue() > upper.GetIntValue()) {
4592 return false;
4594 aPair = nsCSSValuePair(lower, upper);
4595 return true;
4598 bool
4599 CSSParserImpl::SkipUntil(char16_t aStopSymbol)
4601 nsCSSToken* tk = &mToken;
4602 nsAutoTArray<char16_t, 16> stack;
4603 stack.AppendElement(aStopSymbol);
4604 for (;;) {
4605 if (!GetToken(true)) {
4606 return false;
4608 if (eCSSToken_Symbol == tk->mType) {
4609 char16_t symbol = tk->mSymbol;
4610 uint32_t stackTopIndex = stack.Length() - 1;
4611 if (symbol == stack.ElementAt(stackTopIndex)) {
4612 stack.RemoveElementAt(stackTopIndex);
4613 if (stackTopIndex == 0) {
4614 return true;
4617 // Just handle out-of-memory by parsing incorrectly. It's
4618 // highly unlikely we're dealing with a legitimate style sheet
4619 // anyway.
4620 } else if ('{' == symbol) {
4621 stack.AppendElement('}');
4622 } else if ('[' == symbol) {
4623 stack.AppendElement(']');
4624 } else if ('(' == symbol) {
4625 stack.AppendElement(')');
4627 } else if (eCSSToken_Function == tk->mType ||
4628 eCSSToken_Bad_URL == tk->mType) {
4629 stack.AppendElement(')');
4634 bool
4635 CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol)
4637 nsCSSToken* tk = &mToken;
4638 nsAutoTArray<char16_t, 16> stack;
4639 stack.AppendElement(aStopSymbol);
4640 for (;;) {
4641 if (!GetToken(true)) {
4642 return true;
4644 if (eCSSToken_Symbol == tk->mType) {
4645 char16_t symbol = tk->mSymbol;
4646 uint32_t stackTopIndex = stack.Length() - 1;
4647 if (symbol == stack.ElementAt(stackTopIndex)) {
4648 stack.RemoveElementAt(stackTopIndex);
4649 if (stackTopIndex == 0) {
4650 return true;
4653 // Just handle out-of-memory by parsing incorrectly. It's
4654 // highly unlikely we're dealing with a legitimate style sheet
4655 // anyway.
4656 } else if ('{' == symbol) {
4657 stack.AppendElement('}');
4658 } else if ('[' == symbol) {
4659 stack.AppendElement(']');
4660 } else if ('(' == symbol) {
4661 stack.AppendElement(')');
4662 } else if (')' == symbol ||
4663 ']' == symbol ||
4664 '}' == symbol) {
4665 UngetToken();
4666 return false;
4668 } else if (eCSSToken_Function == tk->mType ||
4669 eCSSToken_Bad_URL == tk->mType) {
4670 stack.AppendElement(')');
4675 void
4676 CSSParserImpl::SkipUntilOneOf(const char16_t* aStopSymbolChars)
4678 nsCSSToken* tk = &mToken;
4679 nsDependentString stopSymbolChars(aStopSymbolChars);
4680 for (;;) {
4681 if (!GetToken(true)) {
4682 break;
4684 if (eCSSToken_Symbol == tk->mType) {
4685 char16_t symbol = tk->mSymbol;
4686 if (stopSymbolChars.FindChar(symbol) != -1) {
4687 break;
4688 } else if ('{' == symbol) {
4689 SkipUntil('}');
4690 } else if ('[' == symbol) {
4691 SkipUntil(']');
4692 } else if ('(' == symbol) {
4693 SkipUntil(')');
4695 } else if (eCSSToken_Function == tk->mType ||
4696 eCSSToken_Bad_URL == tk->mType) {
4697 SkipUntil(')');
4702 void
4703 CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars)
4705 uint32_t i = aStopSymbolChars.Length();
4706 while (i--) {
4707 SkipUntil(aStopSymbolChars[i]);
4711 bool
4712 CSSParserImpl::SkipDeclaration(bool aCheckForBraces)
4714 nsCSSToken* tk = &mToken;
4715 for (;;) {
4716 if (!GetToken(true)) {
4717 if (aCheckForBraces) {
4718 REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
4720 return false;
4722 if (eCSSToken_Symbol == tk->mType) {
4723 char16_t symbol = tk->mSymbol;
4724 if (';' == symbol) {
4725 break;
4727 if (aCheckForBraces) {
4728 if ('}' == symbol) {
4729 UngetToken();
4730 break;
4733 if ('{' == symbol) {
4734 SkipUntil('}');
4735 } else if ('(' == symbol) {
4736 SkipUntil(')');
4737 } else if ('[' == symbol) {
4738 SkipUntil(']');
4740 } else if (eCSSToken_Function == tk->mType ||
4741 eCSSToken_Bad_URL == tk->mType) {
4742 SkipUntil(')');
4745 return true;
4748 void
4749 CSSParserImpl::SkipRuleSet(bool aInsideBraces)
4751 nsCSSToken* tk = &mToken;
4752 for (;;) {
4753 if (!GetToken(true)) {
4754 REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
4755 break;
4757 if (eCSSToken_Symbol == tk->mType) {
4758 char16_t symbol = tk->mSymbol;
4759 if ('}' == symbol && aInsideBraces) {
4760 // leave block closer for higher-level grammar to consume
4761 UngetToken();
4762 break;
4763 } else if ('{' == symbol) {
4764 SkipUntil('}');
4765 break;
4766 } else if ('(' == symbol) {
4767 SkipUntil(')');
4768 } else if ('[' == symbol) {
4769 SkipUntil(']');
4771 } else if (eCSSToken_Function == tk->mType ||
4772 eCSSToken_Bad_URL == tk->mType) {
4773 SkipUntil(')');
4778 void
4779 CSSParserImpl::PushGroup(css::GroupRule* aRule)
4781 mGroupStack.AppendElement(aRule);
4784 void
4785 CSSParserImpl::PopGroup()
4787 uint32_t count = mGroupStack.Length();
4788 if (0 < count) {
4789 mGroupStack.RemoveElementAt(count - 1);
4793 void
4794 CSSParserImpl::AppendRule(css::Rule* aRule)
4796 uint32_t count = mGroupStack.Length();
4797 if (0 < count) {
4798 mGroupStack[count - 1]->AppendStyleRule(aRule);
4800 else {
4801 mSheet->AppendStyleRule(aRule);
4805 bool
4806 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
4807 bool aInsideBraces)
4809 // First get the list of selectors for the rule
4810 nsCSSSelectorList* slist = nullptr;
4811 uint32_t linenum, colnum;
4812 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4813 !ParseSelectorList(slist, char16_t('{'))) {
4814 REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
4815 OUTPUT_ERROR();
4816 SkipRuleSet(aInsideBraces);
4817 return false;
4819 NS_ASSERTION(nullptr != slist, "null selector list");
4820 CLEAR_ERROR();
4822 // Next parse the declaration block
4823 uint32_t parseFlags = eParseDeclaration_InBraces |
4824 eParseDeclaration_AllowImportant;
4825 css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
4826 if (nullptr == declaration) {
4827 delete slist;
4828 return false;
4831 #if 0
4832 slist->Dump();
4833 fputs("{\n", stdout);
4834 declaration->List();
4835 fputs("}\n", stdout);
4836 #endif
4838 // Translate the selector list and declaration block into style data
4840 nsRefPtr<css::StyleRule> rule = new css::StyleRule(slist, declaration,
4841 linenum, colnum);
4842 (*aAppendFunc)(rule, aData);
4844 return true;
4847 bool
4848 CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
4849 char16_t aStopChar)
4851 nsCSSSelectorList* list = nullptr;
4852 if (! ParseSelectorGroup(list)) {
4853 // must have at least one selector group
4854 aListHead = nullptr;
4855 return false;
4857 NS_ASSERTION(nullptr != list, "no selector list");
4858 aListHead = list;
4860 // After that there must either be a "," or a "{" (the latter if
4861 // StopChar is nonzero)
4862 nsCSSToken* tk = &mToken;
4863 for (;;) {
4864 if (! GetToken(true)) {
4865 if (aStopChar == char16_t(0)) {
4866 return true;
4869 REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
4870 break;
4873 if (eCSSToken_Symbol == tk->mType) {
4874 if (',' == tk->mSymbol) {
4875 nsCSSSelectorList* newList = nullptr;
4876 // Another selector group must follow
4877 if (! ParseSelectorGroup(newList)) {
4878 break;
4880 // add new list to the end of the selector list
4881 list->mNext = newList;
4882 list = newList;
4883 continue;
4884 } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) {
4885 UngetToken();
4886 return true;
4889 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
4890 UngetToken();
4891 break;
4894 delete aListHead;
4895 aListHead = nullptr;
4896 return false;
4899 static bool IsUniversalSelector(const nsCSSSelector& aSelector)
4901 return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
4902 (aSelector.mLowercaseTag == nullptr) &&
4903 (aSelector.mIDList == nullptr) &&
4904 (aSelector.mClassList == nullptr) &&
4905 (aSelector.mAttrList == nullptr) &&
4906 (aSelector.mNegations == nullptr) &&
4907 (aSelector.mPseudoClassList == nullptr));
4910 bool
4911 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
4913 char16_t combinator = 0;
4914 nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
4916 for (;;) {
4917 if (!ParseSelector(list, combinator)) {
4918 return false;
4921 // Look for a combinator.
4922 if (!GetToken(false)) {
4923 break; // EOF ok here
4926 combinator = char16_t(0);
4927 if (mToken.mType == eCSSToken_Whitespace) {
4928 if (!GetToken(true)) {
4929 break; // EOF ok here
4931 combinator = char16_t(' ');
4934 if (mToken.mType != eCSSToken_Symbol) {
4935 UngetToken(); // not a combinator
4936 } else {
4937 char16_t symbol = mToken.mSymbol;
4938 if (symbol == '+' || symbol == '>' || symbol == '~') {
4939 combinator = mToken.mSymbol;
4940 } else {
4941 UngetToken(); // not a combinator
4942 if (symbol == ',' || symbol == '{' || symbol == ')') {
4943 break; // end of selector group
4948 if (!combinator) {
4949 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
4950 return false;
4954 aList = list.forget();
4955 return true;
4958 #define SEL_MASK_NSPACE 0x01
4959 #define SEL_MASK_ELEM 0x02
4960 #define SEL_MASK_ID 0x04
4961 #define SEL_MASK_CLASS 0x08
4962 #define SEL_MASK_ATTRIB 0x10
4963 #define SEL_MASK_PCLASS 0x20
4964 #define SEL_MASK_PELEM 0x40
4967 // Parses an ID selector #name
4969 CSSParserImpl::nsSelectorParsingStatus
4970 CSSParserImpl::ParseIDSelector(int32_t& aDataMask,
4971 nsCSSSelector& aSelector)
4973 NS_ASSERTION(!mToken.mIdent.IsEmpty(),
4974 "Empty mIdent in eCSSToken_ID token?");
4975 aDataMask |= SEL_MASK_ID;
4976 aSelector.AddID(mToken.mIdent);
4977 return eSelectorParsingStatus_Continue;
4981 // Parses a class selector .name
4983 CSSParserImpl::nsSelectorParsingStatus
4984 CSSParserImpl::ParseClassSelector(int32_t& aDataMask,
4985 nsCSSSelector& aSelector)
4987 if (! GetToken(false)) { // get ident
4988 REPORT_UNEXPECTED_EOF(PEClassSelEOF);
4989 return eSelectorParsingStatus_Error;
4991 if (eCSSToken_Ident != mToken.mType) { // malformed selector
4992 REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
4993 UngetToken();
4994 return eSelectorParsingStatus_Error;
4996 aDataMask |= SEL_MASK_CLASS;
4998 aSelector.AddClass(mToken.mIdent);
5000 return eSelectorParsingStatus_Continue;
5004 // Parse a type element selector or a universal selector
5005 // namespace|type or namespace|* or *|* or *
5007 CSSParserImpl::nsSelectorParsingStatus
5008 CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
5009 nsCSSSelector& aSelector,
5010 bool aIsNegated)
5012 nsAutoString buffer;
5013 if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
5014 if (ExpectSymbol('|', false)) { // was namespace
5015 aDataMask |= SEL_MASK_NSPACE;
5016 aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
5018 if (! GetToken(false)) {
5019 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5020 return eSelectorParsingStatus_Error;
5022 if (eCSSToken_Ident == mToken.mType) { // element name
5023 aDataMask |= SEL_MASK_ELEM;
5025 aSelector.SetTag(mToken.mIdent);
5027 else if (mToken.IsSymbol('*')) { // universal selector
5028 aDataMask |= SEL_MASK_ELEM;
5029 // don't set tag
5031 else {
5032 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5033 UngetToken();
5034 return eSelectorParsingStatus_Error;
5037 else { // was universal element selector
5038 SetDefaultNamespaceOnSelector(aSelector);
5039 aDataMask |= SEL_MASK_ELEM;
5040 // don't set any tag in the selector
5042 if (! GetToken(false)) { // premature eof is ok (here!)
5043 return eSelectorParsingStatus_Done;
5046 else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
5047 buffer = mToken.mIdent; // hang on to ident
5049 if (ExpectSymbol('|', false)) { // was namespace
5050 aDataMask |= SEL_MASK_NSPACE;
5051 int32_t nameSpaceID = GetNamespaceIdForPrefix(buffer);
5052 if (nameSpaceID == kNameSpaceID_Unknown) {
5053 return eSelectorParsingStatus_Error;
5055 aSelector.SetNameSpace(nameSpaceID);
5057 if (! GetToken(false)) {
5058 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5059 return eSelectorParsingStatus_Error;
5061 if (eCSSToken_Ident == mToken.mType) { // element name
5062 aDataMask |= SEL_MASK_ELEM;
5063 aSelector.SetTag(mToken.mIdent);
5065 else if (mToken.IsSymbol('*')) { // universal selector
5066 aDataMask |= SEL_MASK_ELEM;
5067 // don't set tag
5069 else {
5070 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5071 UngetToken();
5072 return eSelectorParsingStatus_Error;
5075 else { // was element name
5076 SetDefaultNamespaceOnSelector(aSelector);
5077 aSelector.SetTag(buffer);
5079 aDataMask |= SEL_MASK_ELEM;
5081 if (! GetToken(false)) { // premature eof is ok (here!)
5082 return eSelectorParsingStatus_Done;
5085 else if (mToken.IsSymbol('|')) { // No namespace
5086 aDataMask |= SEL_MASK_NSPACE;
5087 aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
5089 // get mandatory tag
5090 if (! GetToken(false)) {
5091 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5092 return eSelectorParsingStatus_Error;
5094 if (eCSSToken_Ident == mToken.mType) { // element name
5095 aDataMask |= SEL_MASK_ELEM;
5096 aSelector.SetTag(mToken.mIdent);
5098 else if (mToken.IsSymbol('*')) { // universal selector
5099 aDataMask |= SEL_MASK_ELEM;
5100 // don't set tag
5102 else {
5103 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5104 UngetToken();
5105 return eSelectorParsingStatus_Error;
5107 if (! GetToken(false)) { // premature eof is ok (here!)
5108 return eSelectorParsingStatus_Done;
5111 else {
5112 SetDefaultNamespaceOnSelector(aSelector);
5115 if (aIsNegated) {
5116 // restore last token read in case of a negated type selector
5117 UngetToken();
5119 return eSelectorParsingStatus_Continue;
5123 // Parse attribute selectors [attr], [attr=value], [attr|=value],
5124 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
5126 CSSParserImpl::nsSelectorParsingStatus
5127 CSSParserImpl::ParseAttributeSelector(int32_t& aDataMask,
5128 nsCSSSelector& aSelector)
5130 if (! GetToken(true)) { // premature EOF
5131 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5132 return eSelectorParsingStatus_Error;
5135 int32_t nameSpaceID = kNameSpaceID_None;
5136 nsAutoString attr;
5137 if (mToken.IsSymbol('*')) { // wildcard namespace
5138 nameSpaceID = kNameSpaceID_Unknown;
5139 if (ExpectSymbol('|', false)) {
5140 if (! GetToken(false)) { // premature EOF
5141 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5142 return eSelectorParsingStatus_Error;
5144 if (eCSSToken_Ident == mToken.mType) { // attr name
5145 attr = mToken.mIdent;
5147 else {
5148 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5149 UngetToken();
5150 return eSelectorParsingStatus_Error;
5153 else {
5154 REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
5155 return eSelectorParsingStatus_Error;
5158 else if (mToken.IsSymbol('|')) { // NO namespace
5159 if (! GetToken(false)) { // premature EOF
5160 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5161 return eSelectorParsingStatus_Error;
5163 if (eCSSToken_Ident == mToken.mType) { // attr name
5164 attr = mToken.mIdent;
5166 else {
5167 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5168 UngetToken();
5169 return eSelectorParsingStatus_Error;
5172 else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
5173 attr = mToken.mIdent; // hang on to it
5174 if (ExpectSymbol('|', false)) { // was a namespace
5175 nameSpaceID = GetNamespaceIdForPrefix(attr);
5176 if (nameSpaceID == kNameSpaceID_Unknown) {
5177 return eSelectorParsingStatus_Error;
5179 if (! GetToken(false)) { // premature EOF
5180 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5181 return eSelectorParsingStatus_Error;
5183 if (eCSSToken_Ident == mToken.mType) { // attr name
5184 attr = mToken.mIdent;
5186 else {
5187 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5188 UngetToken();
5189 return eSelectorParsingStatus_Error;
5193 else { // malformed
5194 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
5195 UngetToken();
5196 return eSelectorParsingStatus_Error;
5199 if (! GetToken(true)) { // premature EOF
5200 REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
5201 return eSelectorParsingStatus_Error;
5203 if ((eCSSToken_Symbol == mToken.mType) ||
5204 (eCSSToken_Includes == mToken.mType) ||
5205 (eCSSToken_Dashmatch == mToken.mType) ||
5206 (eCSSToken_Beginsmatch == mToken.mType) ||
5207 (eCSSToken_Endsmatch == mToken.mType) ||
5208 (eCSSToken_Containsmatch == mToken.mType)) {
5209 uint8_t func;
5210 if (eCSSToken_Includes == mToken.mType) {
5211 func = NS_ATTR_FUNC_INCLUDES;
5213 else if (eCSSToken_Dashmatch == mToken.mType) {
5214 func = NS_ATTR_FUNC_DASHMATCH;
5216 else if (eCSSToken_Beginsmatch == mToken.mType) {
5217 func = NS_ATTR_FUNC_BEGINSMATCH;
5219 else if (eCSSToken_Endsmatch == mToken.mType) {
5220 func = NS_ATTR_FUNC_ENDSMATCH;
5222 else if (eCSSToken_Containsmatch == mToken.mType) {
5223 func = NS_ATTR_FUNC_CONTAINSMATCH;
5225 else if (']' == mToken.mSymbol) {
5226 aDataMask |= SEL_MASK_ATTRIB;
5227 aSelector.AddAttribute(nameSpaceID, attr);
5228 func = NS_ATTR_FUNC_SET;
5230 else if ('=' == mToken.mSymbol) {
5231 func = NS_ATTR_FUNC_EQUALS;
5233 else {
5234 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
5235 UngetToken(); // bad function
5236 return eSelectorParsingStatus_Error;
5238 if (NS_ATTR_FUNC_SET != func) { // get value
5239 if (! GetToken(true)) { // premature EOF
5240 REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
5241 return eSelectorParsingStatus_Error;
5243 if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
5244 nsAutoString value(mToken.mIdent);
5245 if (! GetToken(true)) { // premature EOF
5246 REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
5247 return eSelectorParsingStatus_Error;
5249 if (mToken.IsSymbol(']')) {
5250 bool isCaseSensitive = true;
5252 // For cases when this style sheet is applied to an HTML
5253 // element in an HTML document, and the attribute selector is
5254 // for a non-namespaced attribute, then check to see if it's
5255 // one of the known attributes whose VALUE is
5256 // case-insensitive.
5257 if (nameSpaceID == kNameSpaceID_None) {
5258 static const char* caseInsensitiveHTMLAttribute[] = {
5259 // list based on http://www.w3.org/TR/html4/
5260 "lang",
5261 "dir",
5262 "http-equiv",
5263 "text",
5264 "link",
5265 "vlink",
5266 "alink",
5267 "compact",
5268 "align",
5269 "frame",
5270 "rules",
5271 "valign",
5272 "scope",
5273 "axis",
5274 "nowrap",
5275 "hreflang",
5276 "rel",
5277 "rev",
5278 "charset",
5279 "codetype",
5280 "declare",
5281 "valuetype",
5282 "shape",
5283 "nohref",
5284 "media",
5285 "bgcolor",
5286 "clear",
5287 "color",
5288 "face",
5289 "noshade",
5290 "noresize",
5291 "scrolling",
5292 "target",
5293 "method",
5294 "enctype",
5295 "accept-charset",
5296 "accept",
5297 "checked",
5298 "multiple",
5299 "selected",
5300 "disabled",
5301 "readonly",
5302 "language",
5303 "defer",
5304 "type",
5305 // additional attributes not in HTML4
5306 "direction", // marquee
5307 nullptr
5309 short i = 0;
5310 const char* htmlAttr;
5311 while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
5312 if (attr.LowerCaseEqualsASCII(htmlAttr)) {
5313 isCaseSensitive = false;
5314 break;
5318 aDataMask |= SEL_MASK_ATTRIB;
5319 aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
5321 else {
5322 REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
5323 UngetToken();
5324 return eSelectorParsingStatus_Error;
5327 else {
5328 REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
5329 UngetToken();
5330 return eSelectorParsingStatus_Error;
5334 else {
5335 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
5336 UngetToken(); // bad dog, no biscut!
5337 return eSelectorParsingStatus_Error;
5339 return eSelectorParsingStatus_Continue;
5343 // Parse pseudo-classes and pseudo-elements
5345 CSSParserImpl::nsSelectorParsingStatus
5346 CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
5347 nsCSSSelector& aSelector,
5348 bool aIsNegated,
5349 nsIAtom** aPseudoElement,
5350 nsAtomList** aPseudoElementArgs,
5351 nsCSSPseudoElements::Type* aPseudoElementType)
5353 NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs),
5354 "expected location to store pseudo element");
5355 NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs),
5356 "negated selectors shouldn't have a place to store "
5357 "pseudo elements");
5358 if (! GetToken(false)) { // premature eof
5359 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
5360 return eSelectorParsingStatus_Error;
5363 // First, find out whether we are parsing a CSS3 pseudo-element
5364 bool parsingPseudoElement = false;
5365 if (mToken.IsSymbol(':')) {
5366 parsingPseudoElement = true;
5367 if (! GetToken(false)) { // premature eof
5368 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
5369 return eSelectorParsingStatus_Error;
5373 // Do some sanity-checking on the token
5374 if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
5375 // malformed selector
5376 REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
5377 UngetToken();
5378 return eSelectorParsingStatus_Error;
5381 // OK, now we know we have an mIdent. Atomize it. All the atoms, for
5382 // pseudo-classes as well as pseudo-elements, start with a single ':'.
5383 nsAutoString buffer;
5384 buffer.Append(char16_t(':'));
5385 buffer.Append(mToken.mIdent);
5386 nsContentUtils::ASCIIToLower(buffer);
5387 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
5388 if (!pseudo) {
5389 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
5392 // stash away some info about this pseudo so we only have to get it once.
5393 bool isTreePseudo = false;
5394 nsCSSPseudoElements::Type pseudoElementType =
5395 nsCSSPseudoElements::GetPseudoType(pseudo);
5396 nsCSSPseudoClasses::Type pseudoClassType =
5397 nsCSSPseudoClasses::GetPseudoType(pseudo);
5398 bool pseudoClassIsUserAction =
5399 nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
5401 if (!mUnsafeRulesEnabled &&
5402 ((pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount &&
5403 nsCSSPseudoElements::PseudoElementIsUASheetOnly(pseudoElementType)) ||
5404 (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass &&
5405 nsCSSPseudoClasses::PseudoClassIsUASheetOnly(pseudoClassType)))) {
5406 // This pseudo-element or pseudo-class is not exposed to content.
5407 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
5408 UngetToken();
5409 return eSelectorParsingStatus_Error;
5412 // We currently allow :-moz-placeholder and ::-moz-placeholder. We have to
5413 // be a bit stricter regarding the pseudo-element parsing rules.
5414 if (pseudoElementType == nsCSSPseudoElements::ePseudo_mozPlaceholder &&
5415 pseudoClassType == nsCSSPseudoClasses::ePseudoClass_mozPlaceholder) {
5416 if (parsingPseudoElement) {
5417 pseudoClassType = nsCSSPseudoClasses::ePseudoClass_NotPseudoClass;
5418 } else {
5419 pseudoElementType = nsCSSPseudoElements::ePseudo_NotPseudoElement;
5423 #ifdef MOZ_XUL
5424 isTreePseudo = (pseudoElementType == nsCSSPseudoElements::ePseudo_XULTree);
5425 // If a tree pseudo-element is using the function syntax, it will
5426 // get isTree set here and will pass the check below that only
5427 // allows functions if they are in our list of things allowed to be
5428 // functions. If it is _not_ using the function syntax, isTree will
5429 // be false, and it will still pass that check. So the tree
5430 // pseudo-elements are allowed to be either functions or not, as
5431 // desired.
5432 bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
5433 #endif
5434 bool isPseudoElement =
5435 (pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount);
5436 // anonymous boxes are only allowed if they're the tree boxes or we have
5437 // enabled unsafe rules
5438 bool isAnonBox = isTreePseudo ||
5439 (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox &&
5440 mUnsafeRulesEnabled);
5441 bool isPseudoClass =
5442 (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass);
5444 NS_ASSERTION(!isPseudoClass ||
5445 pseudoElementType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
5446 "Why is this atom both a pseudo-class and a pseudo-element?");
5447 NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1,
5448 "Shouldn't be more than one of these");
5450 if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
5451 // Not a pseudo-class, not a pseudo-element.... forget it
5452 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
5453 UngetToken();
5454 return eSelectorParsingStatus_Error;
5457 // If it's a function token, it better be on our "ok" list, and if the name
5458 // is that of a function pseudo it better be a function token
5459 if ((eCSSToken_Function == mToken.mType) !=
5461 #ifdef MOZ_XUL
5462 isTree ||
5463 #endif
5464 nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType ||
5465 nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
5466 nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
5467 nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
5468 // There are no other function pseudos
5469 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
5470 UngetToken();
5471 return eSelectorParsingStatus_Error;
5474 // If it starts with "::", it better be a pseudo-element
5475 if (parsingPseudoElement &&
5476 !isPseudoElement &&
5477 !isAnonBox) {
5478 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
5479 UngetToken();
5480 return eSelectorParsingStatus_Error;
5483 if (!parsingPseudoElement &&
5484 nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType) {
5485 if (aIsNegated) { // :not() can't be itself negated
5486 REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
5487 UngetToken();
5488 return eSelectorParsingStatus_Error;
5490 // CSS 3 Negation pseudo-class takes one simple selector as argument
5491 nsSelectorParsingStatus parsingStatus =
5492 ParseNegatedSimpleSelector(aDataMask, aSelector);
5493 if (eSelectorParsingStatus_Continue != parsingStatus) {
5494 return parsingStatus;
5497 else if (!parsingPseudoElement && isPseudoClass) {
5498 if (aSelector.IsPseudoElement()) {
5499 nsCSSPseudoElements::Type type = aSelector.PseudoType();
5500 if (!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
5501 // We only allow user action pseudo-classes on certain pseudo-elements.
5502 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
5503 UngetToken();
5504 return eSelectorParsingStatus_Error;
5506 if (!pseudoClassIsUserAction) {
5507 // CSS 4 Selectors says that pseudo-elements can only be followed by
5508 // a user action pseudo-class.
5509 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
5510 UngetToken();
5511 return eSelectorParsingStatus_Error;
5514 aDataMask |= SEL_MASK_PCLASS;
5515 if (eCSSToken_Function == mToken.mType) {
5516 nsSelectorParsingStatus parsingStatus;
5517 if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
5518 parsingStatus =
5519 ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
5521 else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) {
5522 parsingStatus =
5523 ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType);
5525 else {
5526 NS_ABORT_IF_FALSE(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
5527 "unexpected pseudo with function token");
5528 parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
5529 pseudoClassType);
5531 if (eSelectorParsingStatus_Continue != parsingStatus) {
5532 if (eSelectorParsingStatus_Error == parsingStatus) {
5533 SkipUntil(')');
5535 return parsingStatus;
5538 else {
5539 aSelector.AddPseudoClass(pseudoClassType);
5542 else if (isPseudoElement || isAnonBox) {
5543 // Pseudo-element. Make some more sanity checks.
5545 if (aIsNegated) { // pseudo-elements can't be negated
5546 REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
5547 UngetToken();
5548 return eSelectorParsingStatus_Error;
5550 // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
5551 // to have a single ':' on them. Others (CSS3+ pseudo-elements and
5552 // various -moz-* pseudo-elements) must have |parsingPseudoElement|
5553 // set.
5554 if (!parsingPseudoElement &&
5555 !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
5556 #ifdef MOZ_XUL
5557 && !isTreePseudo
5558 #endif
5560 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
5561 UngetToken();
5562 return eSelectorParsingStatus_Error;
5565 if (0 == (aDataMask & SEL_MASK_PELEM)) {
5566 aDataMask |= SEL_MASK_PELEM;
5567 NS_ADDREF(*aPseudoElement = pseudo);
5568 *aPseudoElementType = pseudoElementType;
5570 #ifdef MOZ_XUL
5571 if (isTree) {
5572 // We have encountered a pseudoelement of the form
5573 // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each
5574 // item in the list to the pseudoclass list. They will be pulled
5575 // from the list later along with the pseudo-element.
5576 if (!ParseTreePseudoElement(aPseudoElementArgs)) {
5577 return eSelectorParsingStatus_Error;
5580 #endif
5582 // Pseudo-elements can only be followed by user action pseudo-classes
5583 // or be the end of the selector. So the next non-whitespace token must
5584 // be ':', '{' or ',' or EOF.
5585 if (!GetToken(true)) { // premature eof is ok (here!)
5586 return eSelectorParsingStatus_Done;
5588 if (parsingPseudoElement && mToken.IsSymbol(':')) {
5589 UngetToken();
5590 return eSelectorParsingStatus_Continue;
5592 if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
5593 UngetToken();
5594 return eSelectorParsingStatus_Done;
5596 REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC);
5597 UngetToken();
5598 return eSelectorParsingStatus_Error;
5600 else { // multiple pseudo elements, not legal
5601 REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
5602 UngetToken();
5603 return eSelectorParsingStatus_Error;
5606 #ifdef DEBUG
5607 else {
5608 // We should never end up here. Indeed, if we ended up here, we know (from
5609 // the current if/else cascade) that !isPseudoElement and !isAnonBox. But
5610 // then due to our earlier check we know that isPseudoClass. Since we
5611 // didn't fall into the isPseudoClass case in this cascade, we must have
5612 // parsingPseudoElement. But we've already checked the
5613 // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
5614 // it's happened.
5615 NS_NOTREACHED("How did this happen?");
5617 #endif
5618 return eSelectorParsingStatus_Continue;
5622 // Parse the argument of a negation pseudo-class :not()
5624 CSSParserImpl::nsSelectorParsingStatus
5625 CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask,
5626 nsCSSSelector& aSelector)
5628 if (! GetToken(true)) { // premature eof
5629 REPORT_UNEXPECTED_EOF(PENegationEOF);
5630 return eSelectorParsingStatus_Error;
5633 if (mToken.IsSymbol(')')) {
5634 REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
5635 return eSelectorParsingStatus_Error;
5638 // Create a new nsCSSSelector and add it to the end of
5639 // aSelector.mNegations.
5640 // Given the current parsing rules, every selector in mNegations
5641 // contains only one simple selector (css3 definition) within it.
5642 // This could easily change in future versions of CSS, and the only
5643 // thing we need to change to support that is this parsing code and the
5644 // serialization code for nsCSSSelector.
5645 nsCSSSelector *newSel = new nsCSSSelector();
5646 nsCSSSelector* negations = &aSelector;
5647 while (negations->mNegations) {
5648 negations = negations->mNegations;
5650 negations->mNegations = newSel;
5652 nsSelectorParsingStatus parsingStatus;
5653 if (eCSSToken_ID == mToken.mType) { // #id
5654 parsingStatus = ParseIDSelector(aDataMask, *newSel);
5656 else if (mToken.IsSymbol('.')) { // .class
5657 parsingStatus = ParseClassSelector(aDataMask, *newSel);
5659 else if (mToken.IsSymbol(':')) { // :pseudo
5660 parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true,
5661 nullptr, nullptr, nullptr);
5663 else if (mToken.IsSymbol('[')) { // [attribute
5664 parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
5665 if (eSelectorParsingStatus_Error == parsingStatus) {
5666 // Skip forward to the matching ']'
5667 SkipUntil(']');
5670 else {
5671 // then it should be a type element or universal selector
5672 parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true);
5674 if (eSelectorParsingStatus_Error == parsingStatus) {
5675 REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
5676 SkipUntil(')');
5677 return parsingStatus;
5679 // close the parenthesis
5680 if (!ExpectSymbol(')', true)) {
5681 REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
5682 SkipUntil(')');
5683 return eSelectorParsingStatus_Error;
5686 NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown ||
5687 (!newSel->mIDList && !newSel->mClassList &&
5688 !newSel->mPseudoClassList && !newSel->mAttrList),
5689 "Need to fix the serialization code to deal with this");
5691 return eSelectorParsingStatus_Continue;
5695 // Parse the argument of a pseudo-class that has an ident arg
5697 CSSParserImpl::nsSelectorParsingStatus
5698 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
5699 nsCSSPseudoClasses::Type aType)
5701 if (! GetToken(true)) { // premature eof
5702 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5703 return eSelectorParsingStatus_Error;
5705 // We expect an identifier with a language abbreviation
5706 if (eCSSToken_Ident != mToken.mType) {
5707 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
5708 UngetToken();
5709 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5712 // -moz-locale-dir and -moz-dir take an identifier argument. While
5713 // only 'ltr' and 'rtl' (case-insensitively) will match anything, any
5714 // other identifier is still valid.
5715 if (aType == nsCSSPseudoClasses::ePseudoClass_mozLocaleDir ||
5716 aType == nsCSSPseudoClasses::ePseudoClass_dir) {
5717 nsContentUtils::ASCIIToLower(mToken.mIdent); // case insensitive
5720 // Add the pseudo with the language parameter
5721 aSelector.AddPseudoClass(aType, mToken.mIdent.get());
5723 // close the parenthesis
5724 if (!ExpectSymbol(')', true)) {
5725 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
5726 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5729 return eSelectorParsingStatus_Continue;
5732 CSSParserImpl::nsSelectorParsingStatus
5733 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
5734 nsCSSPseudoClasses::Type aType)
5736 int32_t numbers[2] = { 0, 0 };
5737 int32_t sign[2] = { 1, 1 };
5738 bool hasSign[2] = { false, false };
5739 bool lookForB = true;
5741 // Follow the whitespace rules as proposed in
5742 // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
5744 if (! GetToken(true)) {
5745 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5746 return eSelectorParsingStatus_Error;
5749 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
5750 hasSign[0] = true;
5751 if (mToken.IsSymbol('-')) {
5752 sign[0] = -1;
5754 if (! GetToken(false)) {
5755 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5756 return eSelectorParsingStatus_Error;
5760 if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
5761 // The CSS tokenization doesn't handle :nth-child() containing - well:
5762 // 2n-1 is a dimension
5763 // n-1 is an identifier
5764 // The easiest way to deal with that is to push everything from the
5765 // minus on back onto the scanner's pushback buffer.
5766 uint32_t truncAt = 0;
5767 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
5768 truncAt = 1;
5769 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-")) && !hasSign[0]) {
5770 truncAt = 2;
5772 if (truncAt != 0) {
5773 mScanner->Backup(mToken.mIdent.Length() - truncAt);
5774 mToken.mIdent.Truncate(truncAt);
5778 if (eCSSToken_Ident == mToken.mType) {
5779 if (mToken.mIdent.LowerCaseEqualsLiteral("odd") && !hasSign[0]) {
5780 numbers[0] = 2;
5781 numbers[1] = 1;
5782 lookForB = false;
5784 else if (mToken.mIdent.LowerCaseEqualsLiteral("even") && !hasSign[0]) {
5785 numbers[0] = 2;
5786 numbers[1] = 0;
5787 lookForB = false;
5789 else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
5790 numbers[0] = sign[0];
5792 else if (mToken.mIdent.LowerCaseEqualsLiteral("-n") && !hasSign[0]) {
5793 numbers[0] = -1;
5795 else {
5796 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5797 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5800 else if (eCSSToken_Number == mToken.mType) {
5801 if (!mToken.mIntegerValid) {
5802 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5803 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5805 // for +-an case
5806 if (mToken.mHasSign && hasSign[0]) {
5807 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5808 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5810 int32_t intValue = mToken.mInteger * sign[0];
5811 // for -a/**/n case
5812 if (! GetToken(false)) {
5813 numbers[1] = intValue;
5814 lookForB = false;
5816 else {
5817 if (eCSSToken_Ident == mToken.mType && mToken.mIdent.LowerCaseEqualsLiteral("n")) {
5818 numbers[0] = intValue;
5820 else if (eCSSToken_Ident == mToken.mType && StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
5821 numbers[0] = intValue;
5822 mScanner->Backup(mToken.mIdent.Length() - 1);
5824 else {
5825 UngetToken();
5826 numbers[1] = intValue;
5827 lookForB = false;
5831 else if (eCSSToken_Dimension == mToken.mType) {
5832 if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
5833 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5834 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5836 // for +-an case
5837 if ( mToken.mHasSign && hasSign[0] ) {
5838 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5839 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5841 numbers[0] = mToken.mInteger * sign[0];
5843 // XXX If it's a ')', is that valid? (as 0n+0)
5844 else {
5845 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5846 UngetToken();
5847 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5850 if (! GetToken(true)) {
5851 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5852 return eSelectorParsingStatus_Error;
5854 if (lookForB && !mToken.IsSymbol(')')) {
5855 // The '+' or '-' sign can optionally be separated by whitespace.
5856 // If it is separated by whitespace from what follows it, it appears
5857 // as a separate token rather than part of the number token.
5858 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
5859 hasSign[1] = true;
5860 if (mToken.IsSymbol('-')) {
5861 sign[1] = -1;
5863 if (! GetToken(true)) {
5864 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5865 return eSelectorParsingStatus_Error;
5868 if (eCSSToken_Number != mToken.mType ||
5869 !mToken.mIntegerValid || mToken.mHasSign == hasSign[1]) {
5870 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5871 UngetToken();
5872 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5874 numbers[1] = mToken.mInteger * sign[1];
5875 if (! GetToken(true)) {
5876 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5877 return eSelectorParsingStatus_Error;
5880 if (!mToken.IsSymbol(')')) {
5881 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
5882 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5884 aSelector.AddPseudoClass(aType, numbers);
5885 return eSelectorParsingStatus_Continue;
5889 // Parse the argument of a pseudo-class that has a selector list argument.
5890 // Such selector lists cannot contain combinators, but can contain
5891 // anything that goes between a pair of combinators.
5893 CSSParserImpl::nsSelectorParsingStatus
5894 CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
5895 nsCSSPseudoClasses::Type aType)
5897 nsAutoPtr<nsCSSSelectorList> slist;
5898 if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) {
5899 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5902 // Check that none of the selectors in the list have combinators or
5903 // pseudo-elements.
5904 for (nsCSSSelectorList *l = slist; l; l = l->mNext) {
5905 nsCSSSelector *s = l->mSelectors;
5906 if (s->mNext || s->IsPseudoElement()) {
5907 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5911 // Add the pseudo with the selector list parameter
5912 aSelector.AddPseudoClass(aType, slist.forget());
5914 // close the parenthesis
5915 if (!ExpectSymbol(')', true)) {
5916 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
5917 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5920 return eSelectorParsingStatus_Continue;
5925 * This is the format for selectors:
5926 * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
5928 bool
5929 CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
5930 char16_t aPrevCombinator)
5932 if (! GetToken(true)) {
5933 REPORT_UNEXPECTED_EOF(PESelectorEOF);
5934 return false;
5937 nsCSSSelector* selector = aList->AddSelector(aPrevCombinator);
5938 nsCOMPtr<nsIAtom> pseudoElement;
5939 nsAutoPtr<nsAtomList> pseudoElementArgs;
5940 nsCSSPseudoElements::Type pseudoElementType =
5941 nsCSSPseudoElements::ePseudo_NotPseudoElement;
5943 int32_t dataMask = 0;
5944 nsSelectorParsingStatus parsingStatus =
5945 ParseTypeOrUniversalSelector(dataMask, *selector, false);
5947 while (parsingStatus == eSelectorParsingStatus_Continue) {
5948 if (eCSSToken_ID == mToken.mType) { // #id
5949 parsingStatus = ParseIDSelector(dataMask, *selector);
5951 else if (mToken.IsSymbol('.')) { // .class
5952 parsingStatus = ParseClassSelector(dataMask, *selector);
5954 else if (mToken.IsSymbol(':')) { // :pseudo
5955 parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
5956 getter_AddRefs(pseudoElement),
5957 getter_Transfers(pseudoElementArgs),
5958 &pseudoElementType);
5959 if (pseudoElement &&
5960 pseudoElementType != nsCSSPseudoElements::ePseudo_AnonBox) {
5961 // Pseudo-elements other than anonymous boxes are represented with
5962 // a special ':' combinator.
5964 aList->mWeight += selector->CalcWeight();
5966 selector = aList->AddSelector(':');
5968 selector->mLowercaseTag.swap(pseudoElement);
5969 selector->mClassList = pseudoElementArgs.forget();
5970 selector->SetPseudoType(pseudoElementType);
5973 else if (mToken.IsSymbol('[')) { // [attribute
5974 parsingStatus = ParseAttributeSelector(dataMask, *selector);
5975 if (eSelectorParsingStatus_Error == parsingStatus) {
5976 SkipUntil(']');
5979 else { // not a selector token, we're done
5980 parsingStatus = eSelectorParsingStatus_Done;
5981 UngetToken();
5982 break;
5985 if (parsingStatus != eSelectorParsingStatus_Continue) {
5986 break;
5989 if (! GetToken(false)) { // premature eof is ok (here!)
5990 parsingStatus = eSelectorParsingStatus_Done;
5991 break;
5995 if (parsingStatus == eSelectorParsingStatus_Error) {
5996 return false;
5999 if (!dataMask) {
6000 if (selector->mNext) {
6001 REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
6002 } else {
6003 REPORT_UNEXPECTED(PESelectorGroupNoSelector);
6005 return false;
6008 if (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox) {
6009 // We got an anonymous box pseudo-element; it must be the only
6010 // thing in this selector group.
6011 if (selector->mNext || !IsUniversalSelector(*selector)) {
6012 REPORT_UNEXPECTED(PEAnonBoxNotAlone);
6013 return false;
6016 // Rewrite the current selector as this pseudo-element.
6017 // It does not contribute to selector weight.
6018 selector->mLowercaseTag.swap(pseudoElement);
6019 selector->mClassList = pseudoElementArgs.forget();
6020 selector->SetPseudoType(pseudoElementType);
6021 return true;
6024 aList->mWeight += selector->CalcWeight();
6026 return true;
6029 css::Declaration*
6030 CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext)
6032 bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
6034 if (checkForBraces) {
6035 if (!ExpectSymbol('{', true)) {
6036 REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
6037 OUTPUT_ERROR();
6038 return nullptr;
6041 css::Declaration* declaration = new css::Declaration();
6042 mData.AssertInitialState();
6043 for (;;) {
6044 bool changed;
6045 if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) {
6046 if (!SkipDeclaration(checkForBraces)) {
6047 break;
6049 if (checkForBraces) {
6050 if (ExpectSymbol('}', true)) {
6051 break;
6054 // Since the skipped declaration didn't end the block we parse
6055 // the next declaration.
6058 declaration->CompressFrom(&mData);
6059 return declaration;
6062 bool
6063 CSSParserImpl::ParseColor(nsCSSValue& aValue)
6065 if (!GetToken(true)) {
6066 REPORT_UNEXPECTED_EOF(PEColorEOF);
6067 return false;
6070 nsCSSToken* tk = &mToken;
6071 nscolor rgba;
6072 switch (tk->mType) {
6073 case eCSSToken_ID:
6074 case eCSSToken_Hash:
6075 // #xxyyzz
6076 if (NS_HexToRGB(tk->mIdent, &rgba)) {
6077 MOZ_ASSERT(tk->mIdent.Length() == 3 || tk->mIdent.Length() == 6,
6078 "unexpected hex color length");
6079 nsCSSUnit unit = tk->mIdent.Length() == 3 ?
6080 eCSSUnit_ShortHexColor :
6081 eCSSUnit_HexColor;
6082 aValue.SetIntegerColorValue(rgba, unit);
6083 return true;
6085 break;
6087 case eCSSToken_Ident:
6088 if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
6089 aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
6090 return true;
6092 else {
6093 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
6094 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
6095 int32_t value;
6096 if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
6097 aValue.SetIntValue(value, eCSSUnit_EnumColor);
6098 return true;
6102 break;
6103 case eCSSToken_Function:
6104 if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
6105 // rgb ( component , component , component )
6106 if (GetToken(true)) {
6107 UngetToken();
6109 if (mToken.mType == eCSSToken_Number) {
6110 uint8_t r, g, b;
6111 if (ParseNumberColorComponent(r, ',') &&
6112 ParseNumberColorComponent(g, ',') &&
6113 ParseNumberColorComponent(b, ')')) {
6114 aValue.SetIntegerColorValue(NS_RGB(r, g, b), eCSSUnit_RGBColor);
6115 return true;
6117 } else {
6118 float r, g, b;
6119 if (ParsePercentageColorComponent(r, ',') &&
6120 ParsePercentageColorComponent(g, ',') &&
6121 ParsePercentageColorComponent(b, ')')) {
6122 aValue.SetFloatColorValue(r, g, b, 1.0f,
6123 eCSSUnit_PercentageRGBColor);
6124 return true;
6127 SkipUntil(')');
6128 return false;
6130 else if (mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
6131 // rgba ( component , component , component , opacity )
6132 if (GetToken(true)) {
6133 UngetToken();
6135 if (mToken.mType == eCSSToken_Number) {
6136 uint8_t r, g, b, a;
6137 if (ParseNumberColorComponent(r, ',') &&
6138 ParseNumberColorComponent(g, ',') &&
6139 ParseNumberColorComponent(b, ',') &&
6140 ParseColorOpacity(a)) {
6141 aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a),
6142 eCSSUnit_RGBAColor);
6143 return true;
6145 } else {
6146 float r, g, b, a;
6147 if (ParsePercentageColorComponent(r, ',') &&
6148 ParsePercentageColorComponent(g, ',') &&
6149 ParsePercentageColorComponent(b, ',') &&
6150 ParseColorOpacity(a)) {
6151 aValue.SetFloatColorValue(r, g, b, a, eCSSUnit_PercentageRGBAColor);
6152 return true;
6155 SkipUntil(')');
6156 return false;
6158 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
6159 // hsl ( hue , saturation , lightness )
6160 // "hue" is a number, "saturation" and "lightness" are percentages.
6161 float h, s, l;
6162 if (ParseHSLColor(h, s, l, ')')) {
6163 aValue.SetFloatColorValue(h, s, l, 1.0f, eCSSUnit_HSLColor);
6164 return true;
6166 SkipUntil(')');
6167 return false;
6169 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
6170 // hsla ( hue , saturation , lightness , opacity )
6171 // "hue" is a number, "saturation" and "lightness" are percentages,
6172 // "opacity" is a number.
6173 float h, s, l, a;
6174 if (ParseHSLColor(h, s, l, ',') &&
6175 ParseColorOpacity(a)) {
6176 aValue.SetFloatColorValue(h, s, l, a, eCSSUnit_HSLAColor);
6177 return true;
6179 SkipUntil(')');
6180 return false;
6182 break;
6183 default:
6184 break;
6187 // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
6188 if (mHashlessColorQuirk) {
6189 // - If the string starts with 'a-f', the nsCSSScanner builds the
6190 // token as a eCSSToken_Ident and we can parse the string as a
6191 // 'xxyyzz' RGB color.
6192 // - If it only contains '0-9' digits, the token is a
6193 // eCSSToken_Number and it must be converted back to a 6
6194 // characters string to be parsed as a RGB color.
6195 // - If it starts with '0-9' and contains any 'a-f', the token is a
6196 // eCSSToken_Dimension, the mNumber part must be converted back to
6197 // a string and the mIdent part must be appended to that string so
6198 // that the resulting string has 6 characters.
6199 // Note: This is a hack for Nav compatibility. Do not attempt to
6200 // simplify it by hacking into the ncCSSScanner. This would be very
6201 // bad.
6202 nsAutoString str;
6203 char buffer[20];
6204 switch (tk->mType) {
6205 case eCSSToken_Ident:
6206 str.Assign(tk->mIdent);
6207 break;
6209 case eCSSToken_Number:
6210 if (tk->mIntegerValid) {
6211 PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
6212 str.AssignWithConversion(buffer);
6214 break;
6216 case eCSSToken_Dimension:
6217 if (tk->mIdent.Length() <= 6) {
6218 PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
6219 nsAutoString temp;
6220 temp.AssignWithConversion(buffer);
6221 temp.Right(str, 6 - tk->mIdent.Length());
6222 str.Append(tk->mIdent);
6224 break;
6225 default:
6226 // There is a whole bunch of cases that are
6227 // not handled by this switch. Ignore them.
6228 break;
6230 if (NS_HexToRGB(str, &rgba)) {
6231 aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor);
6232 return true;
6236 // It's not a color
6237 REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
6238 UngetToken();
6239 return false;
6242 bool
6243 CSSParserImpl::ParseNumberColorComponent(uint8_t& aComponent, char aStop)
6245 if (!GetToken(true)) {
6246 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
6247 return false;
6250 if (mToken.mType != eCSSToken_Number || !mToken.mIntegerValid) {
6251 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
6252 UngetToken();
6253 return false;
6256 float value = mToken.mNumber;
6257 if (value < 0.0f) value = 0.0f;
6258 if (value > 255.0f) value = 255.0f;
6260 if (ExpectSymbol(aStop, true)) {
6261 aComponent = NSToIntRound(value);
6262 return true;
6264 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
6265 return false;
6268 bool
6269 CSSParserImpl::ParsePercentageColorComponent(float& aComponent, char aStop)
6271 if (!GetToken(true)) {
6272 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
6273 return false;
6276 if (mToken.mType != eCSSToken_Percentage) {
6277 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
6278 UngetToken();
6279 return false;
6282 float value = mToken.mNumber;
6283 if (value < 0.0f) value = 0.0f;
6284 if (value > 1.0f) value = 1.0f;
6286 if (ExpectSymbol(aStop, true)) {
6287 aComponent = value;
6288 return true;
6290 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
6291 return false;
6295 bool
6296 CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
6297 char aStop)
6299 float h, s, l;
6301 // Get the hue
6302 if (!GetToken(true)) {
6303 REPORT_UNEXPECTED_EOF(PEColorHueEOF);
6304 return false;
6306 if (mToken.mType != eCSSToken_Number) {
6307 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
6308 UngetToken();
6309 return false;
6311 h = mToken.mNumber;
6312 h /= 360.0f;
6313 // hue values are wraparound
6314 h = h - floor(h);
6316 if (!ExpectSymbol(',', true)) {
6317 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
6318 return false;
6321 // Get the saturation
6322 if (!GetToken(true)) {
6323 REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
6324 return false;
6326 if (mToken.mType != eCSSToken_Percentage) {
6327 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
6328 UngetToken();
6329 return false;
6331 s = mToken.mNumber;
6332 if (s < 0.0f) s = 0.0f;
6333 if (s > 1.0f) s = 1.0f;
6335 if (!ExpectSymbol(',', true)) {
6336 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
6337 return false;
6340 // Get the lightness
6341 if (!GetToken(true)) {
6342 REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
6343 return false;
6345 if (mToken.mType != eCSSToken_Percentage) {
6346 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
6347 UngetToken();
6348 return false;
6350 l = mToken.mNumber;
6351 if (l < 0.0f) l = 0.0f;
6352 if (l > 1.0f) l = 1.0f;
6354 if (ExpectSymbol(aStop, true)) {
6355 aHue = h;
6356 aSaturation = s;
6357 aLightness = l;
6358 return true;
6361 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
6362 return false;
6366 bool
6367 CSSParserImpl::ParseColorOpacity(uint8_t& aOpacity)
6369 float floatOpacity;
6370 if (!ParseColorOpacity(floatOpacity)) {
6371 return false;
6374 uint8_t value = nsStyleUtil::FloatToColorComponent(floatOpacity);
6375 // Need to compare to something slightly larger
6376 // than 0.5 due to floating point inaccuracies.
6377 NS_ASSERTION(fabs(255.0f*mToken.mNumber - value) <= 0.51f,
6378 "FloatToColorComponent did something weird");
6380 aOpacity = value;
6381 return true;
6384 bool
6385 CSSParserImpl::ParseColorOpacity(float& aOpacity)
6387 if (!GetToken(true)) {
6388 REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
6389 return false;
6392 if (mToken.mType != eCSSToken_Number) {
6393 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
6394 UngetToken();
6395 return false;
6398 if (!ExpectSymbol(')', true)) {
6399 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
6400 return false;
6403 if (mToken.mNumber < 0.0f) {
6404 mToken.mNumber = 0.0f;
6405 } else if (mToken.mNumber > 1.0f) {
6406 mToken.mNumber = 1.0f;
6409 aOpacity = mToken.mNumber;
6410 return true;
6413 #ifdef MOZ_XUL
6414 bool
6415 CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs)
6417 // The argument to a tree pseudo-element is a sequence of identifiers
6418 // that are either space- or comma-separated. (Was the intent to
6419 // allow only comma-separated? That's not what was done.)
6420 nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass
6422 while (!ExpectSymbol(')', true)) {
6423 if (!GetToken(true)) {
6424 return false;
6426 if (eCSSToken_Ident == mToken.mType) {
6427 fakeSelector.AddClass(mToken.mIdent);
6429 else if (!mToken.IsSymbol(',')) {
6430 UngetToken();
6431 SkipUntil(')');
6432 return false;
6435 *aPseudoElementArgs = fakeSelector.mClassList;
6436 fakeSelector.mClassList = nullptr;
6437 return true;
6439 #endif
6441 //----------------------------------------------------------------------
6443 bool
6444 CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
6445 uint32_t aFlags,
6446 bool aMustCallValueAppended,
6447 bool* aChanged,
6448 nsCSSContextType aContext)
6450 NS_PRECONDITION(aContext == eCSSContext_General ||
6451 aContext == eCSSContext_Page,
6452 "Must be page or general context");
6454 bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
6456 mTempData.AssertInitialState();
6458 // Get property name
6459 nsCSSToken* tk = &mToken;
6460 nsAutoString propertyName;
6461 for (;;) {
6462 if (!GetToken(true)) {
6463 if (checkForBraces) {
6464 REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
6466 return false;
6468 if (eCSSToken_Ident == tk->mType) {
6469 propertyName = tk->mIdent;
6470 // grab the ident before the ExpectSymbol trashes the token
6471 if (!ExpectSymbol(':', true)) {
6472 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
6473 REPORT_UNEXPECTED(PEDeclDropped);
6474 OUTPUT_ERROR();
6475 return false;
6477 break;
6479 if (tk->IsSymbol(';')) {
6480 // dangling semicolons are skipped
6481 continue;
6484 if (!tk->IsSymbol('}')) {
6485 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
6486 REPORT_UNEXPECTED(PEDeclSkipped);
6487 OUTPUT_ERROR();
6489 if (eCSSToken_AtKeyword == tk->mType) {
6490 SkipAtRule(checkForBraces);
6491 return true; // Not a declaration, but don't skip until ';'
6494 // Not a declaration...
6495 UngetToken();
6496 return false;
6499 // Don't report property parse errors if we're inside a failing @supports
6500 // rule.
6501 nsAutoSuppressErrors suppressErrors(this, mInFailingSupportsRule);
6503 // Information about a parsed non-custom property.
6504 nsCSSProperty propID;
6506 // Information about a parsed custom property.
6507 CSSVariableDeclarations::Type variableType;
6508 nsString variableValue;
6510 // Check if the property name is a custom property.
6511 bool customProperty = nsLayoutUtils::CSSVariablesEnabled() &&
6512 nsCSSProps::IsCustomPropertyName(propertyName) &&
6513 aContext == eCSSContext_General;
6515 if (customProperty) {
6516 if (!ParseVariableDeclaration(&variableType, variableValue)) {
6517 REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
6518 REPORT_UNEXPECTED(PEDeclDropped);
6519 OUTPUT_ERROR();
6520 return false;
6522 } else {
6523 // Map property name to its ID.
6524 propID = LookupEnabledProperty(propertyName);
6525 if (eCSSProperty_UNKNOWN == propID ||
6526 eCSSPropertyExtra_variable == propID ||
6527 (aContext == eCSSContext_Page &&
6528 !nsCSSProps::PropHasFlags(propID,
6529 CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property
6530 if (!NonMozillaVendorIdentifier(propertyName)) {
6531 REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName);
6532 REPORT_UNEXPECTED(PEDeclDropped);
6533 OUTPUT_ERROR();
6535 return false;
6537 // Then parse the property.
6538 if (!ParseProperty(propID)) {
6539 // XXX Much better to put stuff in the value parsers instead...
6540 REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
6541 REPORT_UNEXPECTED(PEDeclDropped);
6542 OUTPUT_ERROR();
6543 mTempData.ClearProperty(propID);
6544 mTempData.AssertInitialState();
6545 return false;
6549 CLEAR_ERROR();
6551 // Look for "!important".
6552 PriorityParsingStatus status;
6553 if ((aFlags & eParseDeclaration_AllowImportant) != 0) {
6554 status = ParsePriority();
6555 } else {
6556 status = ePriority_None;
6559 // Look for a semicolon or close brace.
6560 if (status != ePriority_Error) {
6561 if (!GetToken(true)) {
6562 // EOF is always ok
6563 } else if (mToken.IsSymbol(';')) {
6564 // semicolon is always ok
6565 } else if (mToken.IsSymbol('}')) {
6566 // brace is ok if checkForBraces, but don't eat it
6567 UngetToken();
6568 if (!checkForBraces) {
6569 status = ePriority_Error;
6571 } else {
6572 UngetToken();
6573 status = ePriority_Error;
6577 if (status == ePriority_Error) {
6578 if (checkForBraces) {
6579 REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
6580 } else {
6581 REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
6583 REPORT_UNEXPECTED(PEDeclDropped);
6584 OUTPUT_ERROR();
6585 if (!customProperty) {
6586 mTempData.ClearProperty(propID);
6588 mTempData.AssertInitialState();
6589 return false;
6592 if (customProperty) {
6593 MOZ_ASSERT(Substring(propertyName, 0,
6594 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
6595 // remove '--'
6596 nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
6597 aDeclaration->AddVariableDeclaration(varName, variableType, variableValue,
6598 status == ePriority_Important, false);
6599 } else {
6600 *aChanged |= mData.TransferFromBlock(mTempData, propID,
6601 status == ePriority_Important,
6602 false, aMustCallValueAppended,
6603 aDeclaration);
6606 return true;
6609 static const nsCSSProperty kBorderTopIDs[] = {
6610 eCSSProperty_border_top_width,
6611 eCSSProperty_border_top_style,
6612 eCSSProperty_border_top_color
6614 static const nsCSSProperty kBorderRightIDs[] = {
6615 eCSSProperty_border_right_width_value,
6616 eCSSProperty_border_right_style_value,
6617 eCSSProperty_border_right_color_value,
6618 eCSSProperty_border_right_width,
6619 eCSSProperty_border_right_style,
6620 eCSSProperty_border_right_color
6622 static const nsCSSProperty kBorderBottomIDs[] = {
6623 eCSSProperty_border_bottom_width,
6624 eCSSProperty_border_bottom_style,
6625 eCSSProperty_border_bottom_color
6627 static const nsCSSProperty kBorderLeftIDs[] = {
6628 eCSSProperty_border_left_width_value,
6629 eCSSProperty_border_left_style_value,
6630 eCSSProperty_border_left_color_value,
6631 eCSSProperty_border_left_width,
6632 eCSSProperty_border_left_style,
6633 eCSSProperty_border_left_color
6635 static const nsCSSProperty kBorderStartIDs[] = {
6636 eCSSProperty_border_start_width_value,
6637 eCSSProperty_border_start_style_value,
6638 eCSSProperty_border_start_color_value,
6639 eCSSProperty_border_start_width,
6640 eCSSProperty_border_start_style,
6641 eCSSProperty_border_start_color
6643 static const nsCSSProperty kBorderEndIDs[] = {
6644 eCSSProperty_border_end_width_value,
6645 eCSSProperty_border_end_style_value,
6646 eCSSProperty_border_end_color_value,
6647 eCSSProperty_border_end_width,
6648 eCSSProperty_border_end_style,
6649 eCSSProperty_border_end_color
6651 static const nsCSSProperty kColumnRuleIDs[] = {
6652 eCSSProperty__moz_column_rule_width,
6653 eCSSProperty__moz_column_rule_style,
6654 eCSSProperty__moz_column_rule_color
6657 bool
6658 CSSParserImpl::ParseEnum(nsCSSValue& aValue,
6659 const KTableValue aKeywordTable[])
6661 nsSubstring* ident = NextIdent();
6662 if (nullptr == ident) {
6663 return false;
6665 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
6666 if (eCSSKeyword_UNKNOWN < keyword) {
6667 int32_t value;
6668 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
6669 aValue.SetIntValue(value, eCSSUnit_Enumerated);
6670 return true;
6674 // Put the unknown identifier back and return
6675 UngetToken();
6676 return false;
6680 struct UnitInfo {
6681 char name[6]; // needs to be long enough for the longest unit, with
6682 // terminating null.
6683 uint32_t length;
6684 nsCSSUnit unit;
6685 int32_t type;
6688 #define STR_WITH_LEN(_str) \
6689 _str, sizeof(_str) - 1
6691 const UnitInfo UnitData[] = {
6692 { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
6693 { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
6694 { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
6695 { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
6696 { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
6697 { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
6698 { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
6699 { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH },
6700 { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
6701 { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH },
6702 { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH },
6703 { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH },
6704 { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH },
6705 { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH },
6706 { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
6707 { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
6708 { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
6709 { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
6710 { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE },
6711 { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
6712 { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
6713 { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
6714 { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
6717 #undef STR_WITH_LEN
6719 bool
6720 CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
6721 int32_t aVariantMask,
6722 float aNumber,
6723 const nsString& aUnit)
6725 nsCSSUnit units;
6726 int32_t type = 0;
6727 if (!aUnit.IsEmpty()) {
6728 uint32_t i;
6729 for (i = 0; i < ArrayLength(UnitData); ++i) {
6730 if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
6731 UnitData[i].length)) {
6732 units = UnitData[i].unit;
6733 type = UnitData[i].type;
6734 break;
6738 if (i == ArrayLength(UnitData)) {
6739 // Unknown unit
6740 return false;
6743 if (!mViewportUnitsEnabled &&
6744 (eCSSUnit_ViewportWidth == units ||
6745 eCSSUnit_ViewportHeight == units ||
6746 eCSSUnit_ViewportMin == units ||
6747 eCSSUnit_ViewportMax == units)) {
6748 // Viewport units aren't allowed right now, probably because we're
6749 // inside an @page declaration. Fail.
6750 return false;
6752 } else {
6753 // Must be a zero number...
6754 NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
6755 if ((VARIANT_LENGTH & aVariantMask) != 0) {
6756 units = eCSSUnit_Pixel;
6757 type = VARIANT_LENGTH;
6759 else if ((VARIANT_ANGLE & aVariantMask) != 0) {
6760 NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE,
6761 "must have allowed zero angle");
6762 units = eCSSUnit_Degree;
6763 type = VARIANT_ANGLE;
6765 else {
6766 NS_ERROR("Variant mask does not include dimension; why were we called?");
6767 return false;
6770 if ((type & aVariantMask) != 0) {
6771 aValue.SetFloatValue(aNumber, units);
6772 return true;
6774 return false;
6777 // Note that this does include VARIANT_CALC, which is numeric. This is
6778 // because calc() parsing, as proposed, drops range restrictions inside
6779 // the calc() expression and clamps the result of the calculation to the
6780 // range.
6781 #define VARIANT_ALL_NONNUMERIC \
6782 VARIANT_KEYWORD | \
6783 VARIANT_COLOR | \
6784 VARIANT_URL | \
6785 VARIANT_STRING | \
6786 VARIANT_COUNTER | \
6787 VARIANT_ATTR | \
6788 VARIANT_IDENTIFIER | \
6789 VARIANT_IDENTIFIER_NO_INHERIT | \
6790 VARIANT_AUTO | \
6791 VARIANT_INHERIT | \
6792 VARIANT_NONE | \
6793 VARIANT_NORMAL | \
6794 VARIANT_SYSFONT | \
6795 VARIANT_GRADIENT | \
6796 VARIANT_TIMING_FUNCTION | \
6797 VARIANT_ALL | \
6798 VARIANT_CALC | \
6799 VARIANT_OPENTYPE_SVG_KEYWORD
6801 // Note that callers passing VARIANT_CALC in aVariantMask will get
6802 // full-range parsing inside the calc() expression, and the code that
6803 // computes the calc will be required to clamp the resulting value to an
6804 // appropriate range.
6805 bool
6806 CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue,
6807 int32_t aVariantMask,
6808 const KTableValue aKeywordTable[])
6810 // The variant mask must only contain non-numeric variants or the ones
6811 // that we specifically handle.
6812 NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
6813 VARIANT_NUMBER |
6814 VARIANT_LENGTH |
6815 VARIANT_PERCENT |
6816 VARIANT_INTEGER)) == 0,
6817 "need to update code below to handle additional variants");
6819 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
6820 if (eCSSUnit_Number == aValue.GetUnit() ||
6821 aValue.IsLengthUnit()){
6822 if (aValue.GetFloatValue() < 0) {
6823 UngetToken();
6824 return false;
6827 else if (aValue.GetUnit() == eCSSUnit_Percent) {
6828 if (aValue.GetPercentValue() < 0) {
6829 UngetToken();
6830 return false;
6832 } else if (aValue.GetUnit() == eCSSUnit_Integer) {
6833 if (aValue.GetIntValue() < 0) {
6834 UngetToken();
6835 return false;
6838 return true;
6840 return false;
6843 // Note that callers passing VARIANT_CALC in aVariantMask will get
6844 // full-range parsing inside the calc() expression, and the code that
6845 // computes the calc will be required to clamp the resulting value to an
6846 // appropriate range.
6847 bool
6848 CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue,
6849 int32_t aVariantMask,
6850 const KTableValue aKeywordTable[])
6852 // The variant mask must only contain non-numeric variants or the ones
6853 // that we specifically handle.
6854 NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
6855 VARIANT_NUMBER |
6856 VARIANT_INTEGER)) == 0,
6857 "need to update code below to handle additional variants");
6859 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
6860 if (aValue.GetUnit() == eCSSUnit_Integer) {
6861 if (aValue.GetIntValue() < 1) {
6862 UngetToken();
6863 return false;
6865 } else if (eCSSUnit_Number == aValue.GetUnit()) {
6866 if (aValue.GetFloatValue() < 1.0f) {
6867 UngetToken();
6868 return false;
6871 return true;
6873 return false;
6876 // Assigns to aValue iff it returns true.
6877 bool
6878 CSSParserImpl::ParseVariant(nsCSSValue& aValue,
6879 int32_t aVariantMask,
6880 const KTableValue aKeywordTable[])
6882 NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
6883 !(aVariantMask & VARIANT_NUMBER),
6884 "can't distinguish colors from numbers");
6885 NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
6886 !(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)),
6887 "can't distinguish colors from lengths");
6888 NS_ASSERTION(!(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)) ||
6889 !(aVariantMask & VARIANT_NUMBER),
6890 "can't distinguish lengths from numbers");
6891 NS_ABORT_IF_FALSE(!(aVariantMask & VARIANT_IDENTIFIER) ||
6892 !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT),
6893 "must not set both VARIANT_IDENTIFIER and "
6894 "VARIANT_IDENTIFIER_NO_INHERIT");
6896 if (!GetToken(true)) {
6897 return false;
6899 nsCSSToken* tk = &mToken;
6900 if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
6901 (eCSSToken_Ident == tk->mType)) {
6902 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
6903 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
6904 if ((aVariantMask & VARIANT_AUTO) != 0) {
6905 if (eCSSKeyword_auto == keyword) {
6906 aValue.SetAutoValue();
6907 return true;
6910 if ((aVariantMask & VARIANT_INHERIT) != 0) {
6911 // XXX Should we check IsParsingCompoundProperty, or do all
6912 // callers handle it? (Not all callers set it, though, since
6913 // they want the quirks that are disabled by setting it.)
6915 // IMPORTANT: If new keywords are added here,
6916 // they probably need to be added in ParseCustomIdent as well.
6917 if (eCSSKeyword_inherit == keyword) {
6918 aValue.SetInheritValue();
6919 return true;
6921 else if (eCSSKeyword_initial == keyword) {
6922 aValue.SetInitialValue();
6923 return true;
6925 else if (eCSSKeyword_unset == keyword &&
6926 nsLayoutUtils::UnsetValueEnabled()) {
6927 aValue.SetUnsetValue();
6928 return true;
6931 if ((aVariantMask & VARIANT_NONE) != 0) {
6932 if (eCSSKeyword_none == keyword) {
6933 aValue.SetNoneValue();
6934 return true;
6937 if ((aVariantMask & VARIANT_ALL) != 0) {
6938 if (eCSSKeyword_all == keyword) {
6939 aValue.SetAllValue();
6940 return true;
6943 if ((aVariantMask & VARIANT_NORMAL) != 0) {
6944 if (eCSSKeyword_normal == keyword) {
6945 aValue.SetNormalValue();
6946 return true;
6949 if ((aVariantMask & VARIANT_SYSFONT) != 0) {
6950 if (eCSSKeyword__moz_use_system_font == keyword &&
6951 !IsParsingCompoundProperty()) {
6952 aValue.SetSystemFontValue();
6953 return true;
6956 if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) {
6957 static bool sOpentypeSVGEnabled;
6958 static bool sOpentypeSVGEnabledCached = false;
6959 if (!sOpentypeSVGEnabledCached) {
6960 sOpentypeSVGEnabledCached = true;
6961 Preferences::AddBoolVarCache(&sOpentypeSVGEnabled,
6962 "gfx.font_rendering.opentype_svg.enabled");
6964 if (sOpentypeSVGEnabled) {
6965 aVariantMask |= VARIANT_KEYWORD;
6968 if ((aVariantMask & VARIANT_KEYWORD) != 0) {
6969 int32_t value;
6970 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
6971 aValue.SetIntValue(value, eCSSUnit_Enumerated);
6972 return true;
6977 // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or
6978 // VARIANT_ZERO_ANGLE.
6979 if (((aVariantMask & VARIANT_NUMBER) != 0) &&
6980 (eCSSToken_Number == tk->mType)) {
6981 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
6982 return true;
6984 if (((aVariantMask & VARIANT_INTEGER) != 0) &&
6985 (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
6986 aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
6987 return true;
6989 if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
6990 VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
6991 eCSSToken_Dimension == tk->mType) ||
6992 ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
6993 eCSSToken_Number == tk->mType &&
6994 tk->mNumber == 0.0f)) {
6995 if (((aVariantMask & VARIANT_POSITIVE_DIMENSION) != 0 &&
6996 tk->mNumber <= 0.0) ||
6997 ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 &&
6998 tk->mNumber < 0.0)) {
6999 UngetToken();
7000 return false;
7002 if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
7003 return true;
7005 // Put the token back; we didn't parse it, so we shouldn't consume it
7006 UngetToken();
7007 return false;
7009 if (((aVariantMask & VARIANT_PERCENT) != 0) &&
7010 (eCSSToken_Percentage == tk->mType)) {
7011 aValue.SetPercentValue(tk->mNumber);
7012 return true;
7014 if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px
7015 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
7016 (eCSSToken_Number == tk->mType)) {
7017 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
7018 return true;
7022 if (IsSVGMode() && !IsParsingCompoundProperty()) {
7023 // STANDARD: SVG Spec states that lengths and coordinates can be unitless
7024 // in which case they default to user-units (1 px = 1 user unit)
7025 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
7026 (eCSSToken_Number == tk->mType)) {
7027 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
7028 return true;
7032 if (((aVariantMask & VARIANT_URL) != 0) &&
7033 eCSSToken_URL == tk->mType) {
7034 SetValueToURL(aValue, tk->mIdent);
7035 return true;
7037 if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
7038 eCSSToken_Function == tk->mType) {
7039 // a generated gradient
7040 nsDependentString tmp(tk->mIdent, 0);
7041 bool isLegacy = false;
7042 if (StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) {
7043 tmp.Rebind(tmp, 5);
7044 isLegacy = true;
7046 bool isRepeating = false;
7047 if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) {
7048 tmp.Rebind(tmp, 10);
7049 isRepeating = true;
7052 if (tmp.LowerCaseEqualsLiteral("linear-gradient")) {
7053 return ParseLinearGradient(aValue, isRepeating, isLegacy);
7055 if (tmp.LowerCaseEqualsLiteral("radial-gradient")) {
7056 return ParseRadialGradient(aValue, isRepeating, isLegacy);
7059 if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
7060 eCSSToken_Function == tk->mType &&
7061 tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
7062 return ParseImageRect(aValue);
7064 if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
7065 eCSSToken_Function == tk->mType &&
7066 tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) {
7067 return ParseElement(aValue);
7069 if ((aVariantMask & VARIANT_COLOR) != 0) {
7070 if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
7071 (eCSSToken_ID == tk->mType) ||
7072 (eCSSToken_Hash == tk->mType) ||
7073 (eCSSToken_Ident == tk->mType) ||
7074 ((eCSSToken_Function == tk->mType) &&
7075 (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
7076 tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
7077 tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
7078 tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
7080 // Put token back so that parse color can get it
7081 UngetToken();
7082 if (ParseColor(aValue)) {
7083 return true;
7085 return false;
7088 if (((aVariantMask & VARIANT_STRING) != 0) &&
7089 (eCSSToken_String == tk->mType)) {
7090 nsAutoString buffer;
7091 buffer.Append(tk->mIdent);
7092 aValue.SetStringValue(buffer, eCSSUnit_String);
7093 return true;
7095 if (((aVariantMask &
7096 (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
7097 (eCSSToken_Ident == tk->mType) &&
7098 ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
7099 !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
7100 tk->mIdent.LowerCaseEqualsLiteral("initial") ||
7101 (tk->mIdent.LowerCaseEqualsLiteral("unset") &&
7102 nsLayoutUtils::UnsetValueEnabled())))) {
7103 aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
7104 return true;
7106 if (((aVariantMask & VARIANT_COUNTER) != 0) &&
7107 (eCSSToken_Function == tk->mType) &&
7108 (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
7109 tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
7110 return ParseCounter(aValue);
7112 if (((aVariantMask & VARIANT_ATTR) != 0) &&
7113 (eCSSToken_Function == tk->mType) &&
7114 tk->mIdent.LowerCaseEqualsLiteral("attr")) {
7115 if (!ParseAttr(aValue)) {
7116 SkipUntil(')');
7117 return false;
7119 return true;
7121 if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
7122 (eCSSToken_Function == tk->mType)) {
7123 if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
7124 if (!ParseTransitionTimingFunctionValues(aValue)) {
7125 SkipUntil(')');
7126 return false;
7128 return true;
7130 if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
7131 if (!ParseTransitionStepTimingFunctionValues(aValue)) {
7132 SkipUntil(')');
7133 return false;
7135 return true;
7138 if ((aVariantMask & VARIANT_CALC) &&
7139 (eCSSToken_Function == tk->mType) &&
7140 (tk->mIdent.LowerCaseEqualsLiteral("calc") ||
7141 tk->mIdent.LowerCaseEqualsLiteral("-moz-calc"))) {
7142 // calc() currently allows only lengths and percents inside it.
7143 return ParseCalc(aValue, aVariantMask & VARIANT_LP);
7146 UngetToken();
7147 return false;
7150 bool
7151 CSSParserImpl::ParseCustomIdent(nsCSSValue& aValue,
7152 const nsAutoString& aIdentValue,
7153 const nsCSSKeyword aExcludedKeywords[],
7154 const nsCSSProps::KTableValue aPropertyKTable[])
7156 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aIdentValue);
7157 if (keyword == eCSSKeyword_UNKNOWN) {
7158 // Fast path for identifiers that are not known CSS keywords:
7159 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
7160 return true;
7162 if (keyword == eCSSKeyword_inherit ||
7163 keyword == eCSSKeyword_initial ||
7164 keyword == eCSSKeyword_unset ||
7165 keyword == eCSSKeyword_default ||
7166 (aPropertyKTable &&
7167 nsCSSProps::FindIndexOfKeyword(keyword, aPropertyKTable) >= 0)) {
7168 return false;
7170 if (aExcludedKeywords) {
7171 for (uint32_t i = 0;; i++) {
7172 nsCSSKeyword excludedKeyword = aExcludedKeywords[i];
7173 if (excludedKeyword == eCSSKeyword_UNKNOWN) {
7174 break;
7176 if (excludedKeyword == keyword) {
7177 return false;
7181 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
7182 return true;
7185 bool
7186 CSSParserImpl::ParseCounter(nsCSSValue& aValue)
7188 nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
7189 eCSSUnit_Counter : eCSSUnit_Counters);
7191 // A non-iterative for loop to break out when an error occurs.
7192 for (;;) {
7193 if (!GetToken(true)) {
7194 break;
7196 if (eCSSToken_Ident != mToken.mType) {
7197 UngetToken();
7198 break;
7201 nsRefPtr<nsCSSValue::Array> val =
7202 nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
7204 val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
7206 if (eCSSUnit_Counters == unit) {
7207 // must have a comma and then a separator string
7208 if (!ExpectSymbol(',', true) || !GetToken(true)) {
7209 break;
7211 if (eCSSToken_String != mToken.mType) {
7212 UngetToken();
7213 break;
7215 val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
7218 // get optional type
7219 int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1;
7220 nsCSSValue& type = val->Item(typeItem);
7221 if (ExpectSymbol(',', true)) {
7222 if (!ParseCounterStyleNameValue(type) && !ParseSymbols(type)) {
7223 break;
7225 } else {
7226 type.SetStringValue(NS_LITERAL_STRING("decimal"), eCSSUnit_Ident);
7229 if (!ExpectSymbol(')', true)) {
7230 break;
7233 aValue.SetArrayValue(val, unit);
7234 return true;
7237 SkipUntil(')');
7238 return false;
7241 bool
7242 CSSParserImpl::ParseAttr(nsCSSValue& aValue)
7244 if (!GetToken(true)) {
7245 return false;
7248 nsAutoString attr;
7249 if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
7250 nsAutoString holdIdent(mToken.mIdent);
7251 if (ExpectSymbol('|', false)) { // namespace
7252 int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent);
7253 if (nameSpaceID == kNameSpaceID_Unknown) {
7254 return false;
7256 attr.AppendInt(nameSpaceID, 10);
7257 attr.Append(char16_t('|'));
7258 if (! GetToken(false)) {
7259 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
7260 return false;
7262 if (eCSSToken_Ident == mToken.mType) {
7263 attr.Append(mToken.mIdent);
7265 else {
7266 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
7267 UngetToken();
7268 return false;
7271 else { // no namespace
7272 attr = holdIdent;
7275 else if (mToken.IsSymbol('*')) { // namespace wildcard
7276 // Wildcard namespace makes no sense here and is not allowed
7277 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
7278 UngetToken();
7279 return false;
7281 else if (mToken.IsSymbol('|')) { // explicit NO namespace
7282 if (! GetToken(false)) {
7283 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
7284 return false;
7286 if (eCSSToken_Ident == mToken.mType) {
7287 attr.Append(mToken.mIdent);
7289 else {
7290 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
7291 UngetToken();
7292 return false;
7295 else {
7296 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
7297 UngetToken();
7298 return false;
7300 if (!ExpectSymbol(')', true)) {
7301 return false;
7303 aValue.SetStringValue(attr, eCSSUnit_Attr);
7304 return true;
7307 bool
7308 CSSParserImpl::ParseSymbols(nsCSSValue& aValue)
7310 if (!GetToken(true)) {
7311 return false;
7313 if (mToken.mType != eCSSToken_Function &&
7314 !mToken.mIdent.LowerCaseEqualsLiteral("symbols")) {
7315 UngetToken();
7316 return false;
7319 nsRefPtr<nsCSSValue::Array> params = nsCSSValue::Array::Create(2);
7320 nsCSSValue& type = params->Item(0);
7321 nsCSSValue& symbols = params->Item(1);
7323 if (!ParseEnum(type, nsCSSProps::kCounterSymbolsSystemKTable)) {
7324 type.SetIntValue(NS_STYLE_COUNTER_SYSTEM_SYMBOLIC, eCSSUnit_Enumerated);
7327 bool first = true;
7328 nsCSSValueList* item = symbols.SetListValue();
7329 for (;;) {
7330 // FIXME Should also include VARIANT_IMAGE. See bug 1071436.
7331 if (!ParseVariant(item->mValue, VARIANT_STRING, nullptr)) {
7332 break;
7334 if (ExpectSymbol(')', true)) {
7335 if (first) {
7336 switch (type.GetIntValue()) {
7337 case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
7338 case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
7339 // require at least two symbols
7340 return false;
7343 aValue.SetArrayValue(params, eCSSUnit_Symbols);
7344 return true;
7346 item->mNext = new nsCSSValueList;
7347 item = item->mNext;
7348 first = false;
7351 SkipUntil(')');
7352 return false;
7355 bool
7356 CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL)
7358 if (!mSheetPrincipal) {
7359 NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
7360 "origin principal");
7361 return false;
7364 nsRefPtr<nsStringBuffer> buffer(nsCSSValue::BufferFromString(aURL));
7366 // Note: urlVal retains its own reference to |buffer|.
7367 mozilla::css::URLValue *urlVal =
7368 new mozilla::css::URLValue(buffer, mBaseURI, mSheetURI, mSheetPrincipal);
7369 aValue.SetURLValue(urlVal);
7370 return true;
7374 * Parse the image-orientation property, which has the grammar:
7375 * <angle> flip? | flip | from-image
7377 bool
7378 CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue)
7380 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
7381 // 'inherit', 'initial' and 'unset' must be alone
7382 return true;
7385 // Check for an angle with optional 'flip'.
7386 nsCSSValue angle;
7387 if (ParseVariant(angle, VARIANT_ANGLE, nullptr)) {
7388 nsCSSValue flip;
7390 if (ParseVariant(flip, VARIANT_KEYWORD, nsCSSProps::kImageOrientationFlipKTable)) {
7391 nsRefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(2);
7392 array->Item(0) = angle;
7393 array->Item(1) = flip;
7394 aValue.SetArrayValue(array, eCSSUnit_Array);
7395 } else {
7396 aValue = angle;
7399 return true;
7402 // The remaining possibilities (bare 'flip' and 'from-image') are both
7403 // keywords, so we can handle them at the same time.
7404 nsCSSValue keyword;
7405 if (ParseVariant(keyword, VARIANT_KEYWORD, nsCSSProps::kImageOrientationKTable)) {
7406 aValue = keyword;
7407 return true;
7410 // All possibilities failed.
7411 return false;
7415 * Parse the arguments of -moz-image-rect() function.
7416 * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
7418 bool
7419 CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
7421 // A non-iterative for loop to break out when an error occurs.
7422 for (;;) {
7423 nsCSSValue newFunction;
7424 static const uint32_t kNumArgs = 5;
7425 nsCSSValue::Array* func =
7426 newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs);
7428 // func->Item(0) is reserved for the function name.
7429 nsCSSValue& url = func->Item(1);
7430 nsCSSValue& top = func->Item(2);
7431 nsCSSValue& right = func->Item(3);
7432 nsCSSValue& bottom = func->Item(4);
7433 nsCSSValue& left = func->Item(5);
7435 nsAutoString urlString;
7436 if (!ParseURLOrString(urlString) ||
7437 !SetValueToURL(url, urlString) ||
7438 !ExpectSymbol(',', true)) {
7439 break;
7442 static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT;
7443 if (!ParseNonNegativeVariant(top, VARIANT_SIDE, nullptr) ||
7444 !ExpectSymbol(',', true) ||
7445 !ParseNonNegativeVariant(right, VARIANT_SIDE, nullptr) ||
7446 !ExpectSymbol(',', true) ||
7447 !ParseNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) ||
7448 !ExpectSymbol(',', true) ||
7449 !ParseNonNegativeVariant(left, VARIANT_SIDE, nullptr) ||
7450 !ExpectSymbol(')', true))
7451 break;
7453 aImage = newFunction;
7454 return true;
7457 SkipUntil(')');
7458 return false;
7461 // <element>: -moz-element(# <element_id> )
7462 bool
7463 CSSParserImpl::ParseElement(nsCSSValue& aValue)
7465 // A non-iterative for loop to break out when an error occurs.
7466 for (;;) {
7467 if (!GetToken(true))
7468 break;
7470 if (mToken.mType == eCSSToken_ID) {
7471 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element);
7472 } else {
7473 UngetToken();
7474 break;
7477 if (!ExpectSymbol(')', true))
7478 break;
7480 return true;
7483 // If we detect a syntax error, we must match the opening parenthesis of the
7484 // function with the closing parenthesis and skip all the tokens in between.
7485 SkipUntil(')');
7486 return false;
7489 // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
7490 bool
7491 CSSParserImpl::ParseFlex()
7493 // First check for inherit / initial / unset
7494 nsCSSValue tmpVal;
7495 if (ParseVariant(tmpVal, VARIANT_INHERIT, nullptr)) {
7496 AppendValue(eCSSProperty_flex_grow, tmpVal);
7497 AppendValue(eCSSProperty_flex_shrink, tmpVal);
7498 AppendValue(eCSSProperty_flex_basis, tmpVal);
7499 return true;
7502 // Next, check for 'none' == '0 0 auto'
7503 if (ParseVariant(tmpVal, VARIANT_NONE, nullptr)) {
7504 AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number));
7505 AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number));
7506 AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto));
7507 return true;
7510 // OK, try parsing our value as individual per-subproperty components:
7511 // [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
7513 // Each subproperty has a default value that it takes when it's omitted in a
7514 // "flex" shorthand value. These default values are *only* for the shorthand
7515 // syntax -- they're distinct from the subproperties' own initial values. We
7516 // start with each subproperty at its default, as if we had "flex: 1 1 0%".
7517 nsCSSValue flexGrow(1.0f, eCSSUnit_Number);
7518 nsCSSValue flexShrink(1.0f, eCSSUnit_Number);
7519 nsCSSValue flexBasis(0.0f, eCSSUnit_Percent);
7521 // OVERVIEW OF PARSING STRATEGY:
7522 // =============================
7523 // a) Parse the first component as either flex-basis or flex-grow.
7524 // b) If it wasn't flex-grow, parse the _next_ component as flex-grow.
7525 // c) Now we've just parsed flex-grow -- so try parsing the next thing as
7526 // flex-shrink.
7527 // d) Finally: If we didn't get flex-basis at the beginning, try to parse
7528 // it now, at the end.
7530 // More details in each section below.
7532 uint32_t flexBasisVariantMask =
7533 (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT));
7535 // (a) Parse first component. It can be either be a 'flex-basis' value or a
7536 // 'flex-grow' value, so we use the flex-basis-specific variant mask, along
7537 // with VARIANT_NUMBER to accept 'flex-grow' values.
7539 // NOTE: if we encounter unitless 0 here, we *must* interpret it as a
7540 // 'flex-grow' value (a number), *not* as a 'flex-basis' value (a length).
7541 // Conveniently, that's the behavior this combined variant-mask gives us --
7542 // it'll treat unitless 0 as a number. The flexbox spec requires this:
7543 // "a unitless zero that is not already preceded by two flex factors must be
7544 // interpreted as a flex factor.
7545 if (!ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER,
7546 nsCSSProps::kWidthKTable)) {
7547 // First component was not a valid flex-basis or flex-grow value. Fail.
7548 return false;
7551 // Record what we just parsed as either flex-basis or flex-grow:
7552 bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number);
7553 (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal;
7555 // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow.
7556 bool doneParsing = false;
7557 if (wasFirstComponentFlexBasis) {
7558 if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) {
7559 flexGrow = tmpVal;
7560 } else {
7561 // Failed to parse anything after our flex-basis -- that's fine. We can
7562 // skip the remaining parsing.
7563 doneParsing = true;
7567 if (!doneParsing) {
7568 // (c) OK -- the last thing we parsed was flex-grow, so look for a
7569 // flex-shrink in the next position.
7570 if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) {
7571 flexShrink = tmpVal;
7574 // d) Finally: If we didn't get flex-basis at the beginning, try to parse
7575 // it now, at the end.
7577 // NOTE: If we encounter unitless 0 in this final position, we'll parse it
7578 // as a 'flex-basis' value. That's OK, because we know it must have
7579 // been "preceded by 2 flex factors" (justification below), which gets us
7580 // out of the spec's requirement of otherwise having to treat unitless 0
7581 // as a flex factor.
7583 // JUSTIFICATION: How do we know that a unitless 0 here must have been
7584 // preceded by 2 flex factors? Well, suppose we had a unitless 0 that
7585 // was preceded by only 1 flex factor. Then, we would have already
7586 // accepted this unitless 0 as the 'flex-shrink' value, up above (since
7587 // it's a valid flex-shrink value), and we'd have moved on to the next
7588 // token (if any). And of course, if we instead had a unitless 0 preceded
7589 // by *no* flex factors (if it were the first token), we would've already
7590 // parsed it in our very first call to ParseNonNegativeVariant(). So, any
7591 // unitless 0 encountered here *must* have been preceded by 2 flex factors.
7592 if (!wasFirstComponentFlexBasis &&
7593 ParseNonNegativeVariant(tmpVal, flexBasisVariantMask,
7594 nsCSSProps::kWidthKTable)) {
7595 flexBasis = tmpVal;
7599 AppendValue(eCSSProperty_flex_grow, flexGrow);
7600 AppendValue(eCSSProperty_flex_shrink, flexShrink);
7601 AppendValue(eCSSProperty_flex_basis, flexBasis);
7603 return true;
7606 // flex-flow: <flex-direction> || <flex-wrap>
7607 bool
7608 CSSParserImpl::ParseFlexFlow()
7610 static const nsCSSProperty kFlexFlowSubprops[] = {
7611 eCSSProperty_flex_direction,
7612 eCSSProperty_flex_wrap
7614 const size_t numProps = MOZ_ARRAY_LENGTH(kFlexFlowSubprops);
7615 nsCSSValue values[numProps];
7617 int32_t found = ParseChoice(values, kFlexFlowSubprops, numProps);
7619 // Bail if we didn't successfully parse anything
7620 if (found < 1) {
7621 return false;
7624 // If either property didn't get an explicit value, use its initial value.
7625 if ((found & 1) == 0) {
7626 values[0].SetIntValue(NS_STYLE_FLEX_DIRECTION_ROW, eCSSUnit_Enumerated);
7628 if ((found & 2) == 0) {
7629 values[1].SetIntValue(NS_STYLE_FLEX_WRAP_NOWRAP, eCSSUnit_Enumerated);
7632 // Store these values and declare success!
7633 for (size_t i = 0; i < numProps; i++) {
7634 AppendValue(kFlexFlowSubprops[i], values[i]);
7636 return true;
7639 bool
7640 CSSParserImpl::ParseGridAutoFlow()
7642 nsCSSValue value;
7643 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
7644 AppendValue(eCSSProperty_grid_auto_flow, value);
7645 return true;
7648 static const int32_t mask[] = {
7649 NS_STYLE_GRID_AUTO_FLOW_ROW | NS_STYLE_GRID_AUTO_FLOW_COLUMN,
7650 MASK_END_VALUE
7652 if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) {
7653 return false;
7655 int32_t bitField = value.GetIntValue();
7657 // If neither row nor column is provided, row is assumed.
7658 if (!(bitField & (NS_STYLE_GRID_AUTO_FLOW_ROW |
7659 NS_STYLE_GRID_AUTO_FLOW_COLUMN))) {
7660 value.SetIntValue(bitField | NS_STYLE_GRID_AUTO_FLOW_ROW,
7661 eCSSUnit_Enumerated);
7664 AppendValue(eCSSProperty_grid_auto_flow, value);
7665 return true;
7668 CSSParseResult
7669 CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue)
7671 if (!ExpectSymbol('(', true)) {
7672 return CSSParseResult::NotFound;
7674 if (!GetToken(true) || mToken.IsSymbol(')')) {
7675 return CSSParseResult::Ok;
7677 // 'return' so far leaves aValue untouched, to represent an empty list.
7679 nsCSSValueList* item;
7680 if (aValue.GetUnit() == eCSSUnit_List) {
7681 // Find the end of an existing list.
7682 // The grid-template shorthand uses this, at most once for a given list.
7684 // NOTE: we could avoid this traversal by somehow keeping around
7685 // a pointer to the last item from the previous call.
7686 // It's not yet clear if this is worth the additional code complexity.
7687 item = aValue.GetListValue();
7688 while (item->mNext) {
7689 item = item->mNext;
7691 item->mNext = new nsCSSValueList;
7692 item = item->mNext;
7693 } else {
7694 MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "Unexpected unit");
7695 item = aValue.SetListValue();
7697 for (;;) {
7698 if (!(eCSSToken_Ident == mToken.mType &&
7699 ParseCustomIdent(item->mValue, mToken.mIdent))) {
7700 UngetToken();
7701 SkipUntil(')');
7702 return CSSParseResult::Error;
7704 if (!GetToken(true) || mToken.IsSymbol(')')) {
7705 return CSSParseResult::Ok;
7707 item->mNext = new nsCSSValueList;
7708 item = item->mNext;
7712 // Assuming the 'repeat(' function token has already been consumed,
7713 // parse the rest of repeat(<positive-integer>, <line-names>+)
7714 // Append to the linked list whose end is given by |aTailPtr|,
7715 // and updated |aTailPtr| to point to the new end of the list.
7716 bool
7717 CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr)
7719 if (!(GetToken(true) &&
7720 mToken.mType == eCSSToken_Number &&
7721 mToken.mIntegerValid &&
7722 mToken.mInteger > 0)) {
7723 SkipUntil(')');
7724 return false;
7726 int32_t repetitions = std::min(mToken.mInteger,
7727 GRID_TEMPLATE_MAX_REPETITIONS);
7728 if (!ExpectSymbol(',', true)) {
7729 SkipUntil(')');
7730 return false;
7733 // Parse at least one <line-names>
7734 nsCSSValueList* tail = *aTailPtr;
7735 do {
7736 tail->mNext = new nsCSSValueList;
7737 tail = tail->mNext;
7738 if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) {
7739 SkipUntil(')');
7740 return false;
7742 } while (!ExpectSymbol(')', true));
7743 nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext;
7744 nsCSSValueList* lastRepeatedItem = tail;
7746 // Our repeated items are already in the target list once,
7747 // so they need to be repeated |repetitions - 1| more times.
7748 MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
7749 while (--repetitions) {
7750 nsCSSValueList* repeatedItem = firstRepeatedItem;
7751 for (;;) {
7752 tail->mNext = new nsCSSValueList;
7753 tail = tail->mNext;
7754 tail->mValue = repeatedItem->mValue;
7755 if (repeatedItem == lastRepeatedItem) {
7756 break;
7758 repeatedItem = repeatedItem->mNext;
7761 *aTailPtr = tail;
7762 return true;
7765 // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>?
7766 bool
7767 CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
7769 nsCSSValueList* item = aValue.SetListValue();
7770 // This marker distinguishes the value from a <track-list>.
7771 item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID,
7772 eCSSUnit_Enumerated);
7773 for (;;) {
7774 // First try to parse repeat(<positive-integer>, <line-names>+)
7775 if (!GetToken(true)) {
7776 return true;
7778 if (mToken.mType == eCSSToken_Function &&
7779 mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
7780 if (!ParseGridLineNameListRepeat(&item)) {
7781 return false;
7783 } else {
7784 UngetToken();
7786 // This was not a repeat() function. Try to parse <line-names>.
7787 nsCSSValue lineNames;
7788 CSSParseResult result = ParseGridLineNames(lineNames);
7789 if (result == CSSParseResult::NotFound) {
7790 return true;
7792 if (result == CSSParseResult::Error) {
7793 return false;
7795 item->mNext = new nsCSSValueList;
7796 item = item->mNext;
7797 item->mValue = lineNames;
7802 // Parse a <track-breadth>
7803 bool
7804 CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
7806 if (ParseNonNegativeVariant(aValue,
7807 VARIANT_LPCALC | VARIANT_KEYWORD,
7808 nsCSSProps::kGridTrackBreadthKTable)) {
7809 return true;
7812 // Attempt to parse <flex> (a dimension with the "fr" unit)
7813 if (!GetToken(true)) {
7814 return false;
7816 if (!(eCSSToken_Dimension == mToken.mType &&
7817 mToken.mIdent.LowerCaseEqualsLiteral("fr") &&
7818 mToken.mNumber >= 0)) {
7819 UngetToken();
7820 return false;
7822 aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction);
7823 return true;
7826 // Parse a <track-size>
7827 CSSParseResult
7828 CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue)
7830 // Attempt to parse 'auto' or a single <track-breadth>
7831 if (ParseGridTrackBreadth(aValue) ||
7832 ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
7833 return CSSParseResult::Ok;
7836 // Attempt to parse a minmax() function
7837 if (!GetToken(true)) {
7838 return CSSParseResult::NotFound;
7840 if (!(eCSSToken_Function == mToken.mType &&
7841 mToken.mIdent.LowerCaseEqualsLiteral("minmax"))) {
7842 UngetToken();
7843 return CSSParseResult::NotFound;
7845 nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2);
7846 if (ParseGridTrackBreadth(func->Item(1)) &&
7847 ExpectSymbol(',', true) &&
7848 ParseGridTrackBreadth(func->Item(2)) &&
7849 ExpectSymbol(')', true)) {
7850 return CSSParseResult::Ok;
7852 SkipUntil(')');
7853 return CSSParseResult::Error;
7856 bool
7857 CSSParserImpl::ParseGridAutoColumnsRows(nsCSSProperty aPropID)
7859 nsCSSValue value;
7860 if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
7861 ParseGridTrackSize(value) == CSSParseResult::Ok) {
7862 AppendValue(aPropID, value);
7863 return true;
7865 return false;
7868 bool
7869 CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
7870 const nsCSSValue& aFirstLineNames)
7872 nsCSSValueList* firstLineNamesItem = aValue.SetListValue();
7873 firstLineNamesItem->mValue = aFirstLineNames;
7875 // This function is trying to parse <track-list>, which is
7876 // [ <line-names>? [ <track-size> | <repeat()> ] ]+ <line-names>?
7877 // and we're already past the first "<line-names>?".
7879 // Each iteration of the following loop attempts to parse either a
7880 // repeat() or a <track-size> expression, and then an (optional)
7881 // <line-names> expression.
7883 // The only successful exit point from this loop is the ::NotFound
7884 // case after ParseGridTrackSize(); i.e. we'll greedily parse
7885 // repeat()/<track-size> until we can't find one.
7886 nsCSSValueList* item = firstLineNamesItem;
7887 for (;;) {
7888 // First try to parse repeat()
7889 if (!GetToken(true)) {
7890 break;
7892 if (mToken.mType == eCSSToken_Function &&
7893 mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
7894 if (!ParseGridTrackListRepeat(&item)) {
7895 return false;
7897 } else {
7898 UngetToken();
7900 // This was not a repeat() function. Try to parse <track-size>.
7901 nsCSSValue trackSize;
7902 CSSParseResult result = ParseGridTrackSize(trackSize);
7903 if (result == CSSParseResult::Error) {
7904 return false;
7906 if (result == CSSParseResult::NotFound) {
7907 // What we've parsed so far is a valid <track-list>
7908 // (modulo the "at least one <track-size>" check below.)
7909 // Stop here.
7910 break;
7912 item->mNext = new nsCSSValueList;
7913 item = item->mNext;
7914 item->mValue = trackSize;
7916 item->mNext = new nsCSSValueList;
7917 item = item->mNext;
7919 if (ParseGridLineNames(item->mValue) == CSSParseResult::Error) {
7920 return false;
7924 // Require at least one <track-size>.
7925 if (item == firstLineNamesItem) {
7926 return false;
7929 MOZ_ASSERT(aValue.GetListValue() &&
7930 aValue.GetListValue()->mNext &&
7931 aValue.GetListValue()->mNext->mNext,
7932 "<track-list> should have a minimum length of 3");
7933 return true;
7936 // Takes ownership of |aSecond|
7937 static void
7938 ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond)
7940 if (aSecond.GetUnit() == eCSSUnit_Null) {
7941 // Nothing to do.
7942 return;
7944 if (aFirst.GetUnit() == eCSSUnit_Null) {
7945 // Empty or omitted <line-names>. Replace it.
7946 aFirst = aSecond;
7947 return;
7950 // Join the two <line-names> lists.
7951 nsCSSValueList* source = aSecond.GetListValue();
7952 nsCSSValueList* target = aFirst.GetListValue();
7953 // Find the end:
7954 while (target->mNext) {
7955 target = target->mNext;
7957 // Copy the first name. We can't take ownership of it
7958 // as it'll be destroyed when |aSecond| goes out of scope.
7959 target->mNext = new nsCSSValueList;
7960 target = target->mNext;
7961 target->mValue = source->mValue;
7962 // Move the rest of the linked list.
7963 target->mNext = source->mNext;
7964 source->mNext = nullptr;
7967 // Assuming the 'repeat(' function token has already been consumed,
7968 // parse the rest of
7969 // repeat( <positive-integer> ,
7970 // [ <line-names>? <track-size> ]+ <line-names>? )
7971 // Append to the linked list whose end is given by |aTailPtr|,
7972 // and updated |aTailPtr| to point to the new end of the list.
7973 bool
7974 CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
7976 if (!(GetToken(true) &&
7977 mToken.mType == eCSSToken_Number &&
7978 mToken.mIntegerValid &&
7979 mToken.mInteger > 0)) {
7980 SkipUntil(')');
7981 return false;
7983 int32_t repetitions = std::min(mToken.mInteger,
7984 GRID_TEMPLATE_MAX_REPETITIONS);
7985 if (!ExpectSymbol(',', true)) {
7986 SkipUntil(')');
7987 return false;
7990 // Parse [ <line-names>? <track-size> ]+ <line-names>?
7991 // but keep the first and last <line-names> separate
7992 // because they'll need to be joined.
7993 // http://dev.w3.org/csswg/css-grid/#repeat-notation
7994 nsCSSValue firstLineNames;
7995 nsCSSValue trackSize;
7996 nsCSSValue lastLineNames;
7997 // Optional
7998 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) {
7999 SkipUntil(')');
8000 return false;
8002 // Required
8003 if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
8004 SkipUntil(')');
8005 return false;
8007 // Use nsAutoPtr to free the list in case of early return.
8008 nsAutoPtr<nsCSSValueList> firstTrackSizeItemAuto(new nsCSSValueList);
8009 firstTrackSizeItemAuto->mValue = trackSize;
8011 nsCSSValueList* item = firstTrackSizeItemAuto;
8012 for (;;) {
8013 // Optional
8014 if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) {
8015 SkipUntil(')');
8016 return false;
8019 if (ExpectSymbol(')', true)) {
8020 break;
8023 // Required
8024 if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
8025 SkipUntil(')');
8026 return false;
8029 item->mNext = new nsCSSValueList;
8030 item = item->mNext;
8031 item->mValue = lastLineNames;
8032 // Do not append to this list at the next iteration.
8033 lastLineNames.Reset();
8035 item->mNext = new nsCSSValueList;
8036 item = item->mNext;
8037 item->mValue = trackSize;
8039 nsCSSValueList* lastTrackSizeItem = item;
8041 // [ <line-names>? <track-size> ]+ <line-names>? is now parsed into:
8042 // * firstLineNames: the first <line-names>
8043 // * a linked list of odd length >= 1, from firstTrackSizeItem
8044 // (the first <track-size>) to lastTrackSizeItem (the last),
8045 // with the <line-names> sublists in between
8046 // * lastLineNames: the last <line-names>
8049 // Join the last and first <line-names> (in that order.)
8050 // For example, repeat(3, (a) 100px (b) 200px (c)) results in
8051 // (a) 100px (b) 200px (c a) 100px (b) 200px (c a) 100px (b) 200px (c)
8052 // This is (c a).
8053 // Make deep copies: the originals will be moved.
8054 nsCSSValue joinerLineNames;
8056 nsCSSValueList* target = nullptr;
8057 if (lastLineNames.GetUnit() != eCSSUnit_Null) {
8058 target = joinerLineNames.SetListValue();
8059 nsCSSValueList* source = lastLineNames.GetListValue();
8060 for (;;) {
8061 target->mValue = source->mValue;
8062 source = source->mNext;
8063 if (!source) {
8064 break;
8066 target->mNext = new nsCSSValueList;
8067 target = target->mNext;
8071 if (firstLineNames.GetUnit() != eCSSUnit_Null) {
8072 if (target) {
8073 target->mNext = new nsCSSValueList;
8074 target = target->mNext;
8075 } else {
8076 target = joinerLineNames.SetListValue();
8078 nsCSSValueList* source = firstLineNames.GetListValue();
8079 for (;;) {
8080 target->mValue = source->mValue;
8081 source = source->mNext;
8082 if (!source) {
8083 break;
8085 target->mNext = new nsCSSValueList;
8086 target = target->mNext;
8091 // Join our first <line-names> with the one before repeat().
8092 // (a) repeat(1, (b) 20px) expands to (a b) 20px
8093 nsCSSValueList* previousItemBeforeRepeat = *aTailPtr;
8094 ConcatLineNames(previousItemBeforeRepeat->mValue, firstLineNames);
8096 // Move our linked list
8097 // (first to last <track-size>, with the <line-names> sublists in between).
8098 // This is the first repetition.
8099 NS_ASSERTION(previousItemBeforeRepeat->mNext == nullptr,
8100 "Expected the end of a linked list");
8101 previousItemBeforeRepeat->mNext = firstTrackSizeItemAuto.forget();
8102 nsCSSValueList* firstTrackSizeItem = previousItemBeforeRepeat->mNext;
8103 nsCSSValueList* tail = lastTrackSizeItem;
8105 // Repeat |repetitions - 1| more times:
8106 // * the joiner <line-names>
8107 // * the linked list
8108 // (first to last <track-size>, with the <line-names> sublists in between)
8109 MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
8110 while (--repetitions) {
8111 tail->mNext = new nsCSSValueList;
8112 tail = tail->mNext;
8113 tail->mValue = joinerLineNames;
8115 nsCSSValueList* repeatedItem = firstTrackSizeItem;
8116 for (;;) {
8117 tail->mNext = new nsCSSValueList;
8118 tail = tail->mNext;
8119 tail->mValue = repeatedItem->mValue;
8120 if (repeatedItem == lastTrackSizeItem) {
8121 break;
8123 repeatedItem = repeatedItem->mNext;
8127 // Finally, move our last <line-names>.
8128 // Any <line-names> immediately after repeat() will append to it.
8129 tail->mNext = new nsCSSValueList;
8130 tail = tail->mNext;
8131 tail->mValue = lastLineNames;
8133 *aTailPtr = tail;
8134 return true;
8137 bool
8138 CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSProperty aPropID)
8140 nsCSSValue value;
8141 if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
8142 AppendValue(aPropID, value);
8143 return true;
8146 nsSubstring* ident = NextIdent();
8147 if (ident) {
8148 if (ident->LowerCaseEqualsLiteral("subgrid")) {
8149 if (!ParseOptionalLineNameListAfterSubgrid(value)) {
8150 return false;
8152 AppendValue(aPropID, value);
8153 return true;
8155 UngetToken();
8158 nsCSSValue firstLineNames;
8159 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
8160 !ParseGridTrackListWithFirstLineNames(value, firstLineNames)) {
8161 return false;
8163 AppendValue(aPropID, value);
8164 return true;
8167 bool
8168 CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput,
8169 css::GridTemplateAreasValue* aAreas,
8170 nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices)
8172 aAreas->mTemplates.AppendElement(mToken.mIdent);
8174 nsCSSGridTemplateAreaScanner scanner(aInput);
8175 nsCSSGridTemplateAreaToken token;
8176 css::GridNamedArea* currentArea = nullptr;
8177 uint32_t row = aAreas->NRows();
8178 // Column numbers starts at 1, but we might not have any, eg
8179 // grid-template-areas:""; which will result in mNColumns == 0.
8180 uint32_t column = 0;
8181 while (scanner.Next(token)) {
8182 ++column;
8183 if (token.isTrash) {
8184 return false;
8186 if (currentArea) {
8187 if (token.mName == currentArea->mName) {
8188 if (currentArea->mRowStart == row) {
8189 // Next column in the first row of this named area.
8190 currentArea->mColumnEnd++;
8192 continue;
8194 // We're exiting |currentArea|, so currentArea is ending at |column|.
8195 // Make sure that this is consistent with currentArea on previous rows:
8196 if (currentArea->mColumnEnd != column) {
8197 NS_ASSERTION(currentArea->mRowStart != row,
8198 "Inconsistent column end for the first row of a named area.");
8199 // Not a rectangle
8200 return false;
8202 currentArea = nullptr;
8204 if (!token.mName.IsEmpty()) {
8205 // Named cell that doesn't have a cell with the same name on its left.
8207 // Check if this is the continuation of an existing named area:
8208 uint32_t index;
8209 if (aAreaIndices.Get(token.mName, &index)) {
8210 MOZ_ASSERT(index < aAreas->mNamedAreas.Length(),
8211 "Invalid aAreaIndices hash table");
8212 currentArea = &aAreas->mNamedAreas[index];
8213 if (currentArea->mColumnStart != column ||
8214 currentArea->mRowEnd != row) {
8215 // Existing named area, but not forming a rectangle
8216 return false;
8218 // Next row of an existing named area
8219 currentArea->mRowEnd++;
8220 } else {
8221 // New named area
8222 aAreaIndices.Put(token.mName, aAreas->mNamedAreas.Length());
8223 currentArea = aAreas->mNamedAreas.AppendElement();
8224 currentArea->mName = token.mName;
8225 // For column or row N (starting at 1),
8226 // the start line is N, the end line is N + 1
8227 currentArea->mColumnStart = column;
8228 currentArea->mColumnEnd = column + 1;
8229 currentArea->mRowStart = row;
8230 currentArea->mRowEnd = row + 1;
8234 if (currentArea && currentArea->mColumnEnd != column + 1) {
8235 NS_ASSERTION(currentArea->mRowStart != row,
8236 "Inconsistent column end for the first row of a named area.");
8237 // Not a rectangle
8238 return false;
8241 // On the first row, set the number of columns
8242 // that grid-template-areas contributes to the explicit grid.
8243 // On other rows, check that the number of columns is consistent
8244 // between rows.
8245 if (row == 1) {
8246 aAreas->mNColumns = column;
8247 } else if (aAreas->mNColumns != column) {
8248 return false;
8250 return true;
8253 bool
8254 CSSParserImpl::ParseGridTemplateAreas()
8256 nsCSSValue value;
8257 if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
8258 AppendValue(eCSSProperty_grid_template_areas, value);
8259 return true;
8262 nsRefPtr<css::GridTemplateAreasValue> areas =
8263 new css::GridTemplateAreasValue();
8264 nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
8265 for (;;) {
8266 if (!GetToken(true)) {
8267 break;
8269 if (eCSSToken_String != mToken.mType) {
8270 UngetToken();
8271 break;
8273 if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
8274 return false;
8278 if (areas->NRows() == 0) {
8279 return false;
8282 AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
8283 return true;
8286 bool
8287 CSSParserImpl::ParseGridTemplate()
8289 // none |
8290 // subgrid |
8291 // <'grid-template-columns'> / <'grid-template-rows'> |
8292 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
8293 nsCSSValue value;
8294 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
8295 AppendValue(eCSSProperty_grid_template_areas, value);
8296 AppendValue(eCSSProperty_grid_template_columns, value);
8297 AppendValue(eCSSProperty_grid_template_rows, value);
8298 return true;
8301 // TODO (bug 983175): add parsing for 'subgrid' by itself
8303 // 'none' can appear either by itself,
8304 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
8305 if (ParseVariant(value, VARIANT_NONE, nullptr)) {
8306 AppendValue(eCSSProperty_grid_template_columns, value);
8307 if (ExpectSymbol('/', true)) {
8308 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
8310 AppendValue(eCSSProperty_grid_template_areas, value);
8311 AppendValue(eCSSProperty_grid_template_rows, value);
8312 return true;
8315 // 'subgrid' can appear either by itself,
8316 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
8317 nsSubstring* ident = NextIdent();
8318 if (ident) {
8319 if (ident->LowerCaseEqualsLiteral("subgrid")) {
8320 if (!ParseOptionalLineNameListAfterSubgrid(value)) {
8321 return false;
8323 AppendValue(eCSSProperty_grid_template_columns, value);
8324 if (ExpectSymbol('/', true)) {
8325 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
8327 if (value.GetListValue()->mNext) {
8328 // Non-empty <line-name-list> after 'subgrid'.
8329 // This is only valid as part of <'grid-template-columns'>,
8330 // which must be followed by a slash.
8331 return false;
8333 // 'subgrid' by itself sets both grid-template-columns
8334 // and grid-template-rows.
8335 AppendValue(eCSSProperty_grid_template_rows, value);
8336 value.SetNoneValue();
8337 AppendValue(eCSSProperty_grid_template_areas, value);
8338 return true;
8340 UngetToken();
8343 // [ <line-names>? ] here is ambiguous:
8344 // it can be either the start of a <track-list>,
8345 // or the start of [ <line-names>? <string> <track-size>? <line-names>? ]+
8346 nsCSSValue firstLineNames;
8347 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
8348 !GetToken(true)) {
8349 return false;
8351 if (mToken.mType == eCSSToken_String) {
8352 // [ <track-list> / ]? was omitted
8353 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
8354 value.SetNoneValue();
8355 AppendValue(eCSSProperty_grid_template_columns, value);
8356 return ParseGridTemplateAfterString(firstLineNames);
8358 UngetToken();
8360 if (!(ParseGridTrackListWithFirstLineNames(value, firstLineNames) &&
8361 ExpectSymbol('/', true))) {
8362 return false;
8364 AppendValue(eCSSProperty_grid_template_columns, value);
8365 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ true);
8368 // Helper for parsing the 'grid-template' shorthand
8370 // NOTE: This parses the portion after the slash, for *one* of the
8371 // following types of expressions:
8372 // - <'grid-template-columns'> / <'grid-template-rows'>
8373 // - <track-list> / [ <line-names>? <string> <track-size>? <line-names>? ]+
8375 // We don't know which type of expression we've got until we've parsed the
8376 // second half, since the pre-slash part is ambiguous. The various return
8377 // clauses below are labeled with the type of expression they're completing.
8378 bool
8379 CSSParserImpl::ParseGridTemplateAfterSlash(bool aColumnsIsTrackList)
8381 nsCSSValue rowsValue;
8382 if (ParseVariant(rowsValue, VARIANT_NONE, nullptr)) {
8383 // <'grid-template-columns'> / <'grid-template-rows'>
8384 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
8385 nsCSSValue areasValue(eCSSUnit_None); // implied
8386 AppendValue(eCSSProperty_grid_template_areas, areasValue);
8387 return true;
8390 nsSubstring* ident = NextIdent();
8391 if (ident) {
8392 if (ident->LowerCaseEqualsLiteral("subgrid")) {
8393 if (!ParseOptionalLineNameListAfterSubgrid(rowsValue)) {
8394 return false;
8396 // <'grid-template-columns'> / <'grid-template-rows'>
8397 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
8398 nsCSSValue areasValue(eCSSUnit_None); // implied
8399 AppendValue(eCSSProperty_grid_template_areas, areasValue);
8400 return true;
8402 UngetToken();
8405 nsCSSValue firstLineNames;
8406 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
8407 !GetToken(true)) {
8408 return false;
8410 if (aColumnsIsTrackList && mToken.mType == eCSSToken_String) {
8411 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
8412 return ParseGridTemplateAfterString(firstLineNames);
8414 UngetToken();
8416 if (!ParseGridTrackListWithFirstLineNames(rowsValue, firstLineNames)) {
8417 return false;
8420 // <'grid-template-columns'> / <'grid-template-rows'>
8421 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
8422 nsCSSValue areasValue(eCSSUnit_None); // implied
8423 AppendValue(eCSSProperty_grid_template_areas, areasValue);
8424 return true;
8427 // Helper for parsing the 'grid-template' shorthand:
8428 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
8429 // with a <line-names>? already consumed, stored in |aFirstLineNames|,
8430 // and the current token a <string>
8431 bool
8432 CSSParserImpl::ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames)
8434 MOZ_ASSERT(mToken.mType == eCSSToken_String,
8435 "ParseGridTemplateAfterString called with a non-string token");
8437 nsCSSValue rowsValue;
8438 nsRefPtr<css::GridTemplateAreasValue> areas =
8439 new css::GridTemplateAreasValue();
8440 nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
8441 nsCSSValueList* rowsItem = rowsValue.SetListValue();
8442 rowsItem->mValue = aFirstLineNames;
8444 for (;;) {
8445 if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
8446 return false;
8449 rowsItem->mNext = new nsCSSValueList;
8450 rowsItem = rowsItem->mNext;
8451 CSSParseResult result = ParseGridTrackSize(rowsItem->mValue);
8452 if (result == CSSParseResult::Error) {
8453 return false;
8455 if (result == CSSParseResult::NotFound) {
8456 rowsItem->mValue.SetAutoValue();
8459 rowsItem->mNext = new nsCSSValueList;
8460 rowsItem = rowsItem->mNext;
8461 result = ParseGridLineNames(rowsItem->mValue);
8462 if (result == CSSParseResult::Error) {
8463 return false;
8465 if (result == CSSParseResult::Ok) {
8466 // Append to the same list as the previous call to ParseGridLineNames.
8467 result = ParseGridLineNames(rowsItem->mValue);
8468 if (result == CSSParseResult::Error) {
8469 return false;
8471 if (result == CSSParseResult::Ok) {
8472 // Parsed <line-name> twice.
8473 // The property value can not end here, we expect a string next.
8474 if (!GetToken(true)) {
8475 return false;
8477 if (eCSSToken_String != mToken.mType) {
8478 UngetToken();
8479 return false;
8481 continue;
8485 // Did not find a <line-names>.
8486 // Next, we expect either a string or the end of the property value.
8487 if (!GetToken(true)) {
8488 break;
8490 if (eCSSToken_String != mToken.mType) {
8491 UngetToken();
8492 break;
8496 AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
8497 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
8498 return true;
8501 // <'grid-template'> |
8502 // [ <'grid-auto-flow'> [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]? ]
8503 bool
8504 CSSParserImpl::ParseGrid()
8506 nsCSSValue value;
8507 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
8508 for (const nsCSSProperty* subprops =
8509 nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid);
8510 *subprops != eCSSProperty_UNKNOWN; ++subprops) {
8511 AppendValue(*subprops, value);
8513 return true;
8516 // An empty value is always invalid.
8517 if (!GetToken(true)) {
8518 return false;
8521 // The values starts with a <'grid-auto-flow'> if and only if
8522 // it starts with a 'dense', 'column' or 'row' keyword.
8523 if (mToken.mType == eCSSToken_Ident) {
8524 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
8525 if (keyword == eCSSKeyword_dense ||
8526 keyword == eCSSKeyword_column ||
8527 keyword == eCSSKeyword_row) {
8528 UngetToken();
8529 return ParseGridAutoFlow() && ParseGridShorthandAutoProps();
8532 UngetToken();
8534 // Set other subproperties to their initial values
8535 // and parse <'grid-template'>.
8536 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_ROW, eCSSUnit_Enumerated);
8537 AppendValue(eCSSProperty_grid_auto_flow, value);
8538 value.SetAutoValue();
8539 AppendValue(eCSSProperty_grid_auto_columns, value);
8540 AppendValue(eCSSProperty_grid_auto_rows, value);
8541 return ParseGridTemplate();
8544 // Parse [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]?
8545 // for the 'grid' shorthand.
8546 // Assumes that <'grid-auto-flow'> was already parsed by the caller.
8547 bool
8548 CSSParserImpl::ParseGridShorthandAutoProps()
8550 nsCSSValue autoColumnsValue;
8551 nsCSSValue autoRowsValue;
8552 CSSParseResult result = ParseGridTrackSize(autoColumnsValue);
8553 if (result == CSSParseResult::Error) {
8554 return false;
8556 if (result == CSSParseResult::NotFound) {
8557 autoColumnsValue.SetAutoValue();
8558 autoRowsValue.SetAutoValue();
8559 } else {
8560 if (!ExpectSymbol('/', true)) {
8561 autoRowsValue.SetAutoValue();
8562 } else if (ParseGridTrackSize(autoRowsValue) != CSSParseResult::Ok) {
8563 return false;
8566 AppendValue(eCSSProperty_grid_auto_columns, autoColumnsValue);
8567 AppendValue(eCSSProperty_grid_auto_rows, autoRowsValue);
8568 nsCSSValue templateValue(eCSSUnit_None); // Initial values
8569 AppendValue(eCSSProperty_grid_template_areas, templateValue);
8570 AppendValue(eCSSProperty_grid_template_columns, templateValue);
8571 AppendValue(eCSSProperty_grid_template_rows, templateValue);
8572 return true;
8575 // Parse a <grid-line>.
8576 // If successful, set aValue to eCSSUnit_Auto,
8577 // or a eCSSUnit_List containing, in that order:
8579 // * An optional eCSSUnit_Enumerated marking a "span" keyword.
8580 // * An optional eCSSUnit_Integer
8581 // * An optional eCSSUnit_Ident
8583 // At least one of eCSSUnit_Integer or eCSSUnit_Ident is present.
8584 bool
8585 CSSParserImpl::ParseGridLine(nsCSSValue& aValue)
8587 // <grid-line> =
8588 // auto |
8589 // <custom-ident> |
8590 // [ <integer> && <custom-ident>? ] |
8591 // [ span && [ <integer> || <custom-ident> ] ]
8593 // Syntactically, this simplifies to:
8595 // <grid-line> =
8596 // auto |
8597 // [ span? && [ <integer> || <custom-ident> ] ]
8599 if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
8600 return true;
8603 static const nsCSSKeyword kGridLineKeywords[] = {
8604 eCSSKeyword_span,
8605 eCSSKeyword_UNKNOWN // End-of-array marker
8607 bool hasSpan = false;
8608 bool hasIdent = false;
8609 Maybe<int32_t> integer;
8610 nsCSSValue ident;
8612 if (!GetToken(true)) {
8613 return false;
8615 if (mToken.mType == eCSSToken_Ident &&
8616 mToken.mIdent.LowerCaseEqualsLiteral("span")) {
8617 hasSpan = true;
8618 if (!GetToken(true)) {
8619 return false;
8623 do {
8624 if (!hasIdent &&
8625 mToken.mType == eCSSToken_Ident &&
8626 ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) {
8627 hasIdent = true;
8628 } else if (integer.isNothing() &&
8629 mToken.mType == eCSSToken_Number &&
8630 mToken.mIntegerValid &&
8631 mToken.mInteger != 0) {
8632 integer.emplace(mToken.mInteger);
8633 } else {
8634 UngetToken();
8635 break;
8637 } while (!(integer.isSome() && hasIdent) && GetToken(true));
8639 // Require at least one of <integer> or <custom-ident>
8640 if (!(integer.isSome() || hasIdent)) {
8641 return false;
8644 if (!hasSpan && GetToken(true)) {
8645 if (mToken.mType == eCSSToken_Ident &&
8646 mToken.mIdent.LowerCaseEqualsLiteral("span")) {
8647 hasSpan = true;
8648 } else {
8649 UngetToken();
8653 nsCSSValueList* item = aValue.SetListValue();
8654 if (hasSpan) {
8655 // Given "span", a negative <integer> is invalid.
8656 if (integer.isSome() && integer.ref() < 0) {
8657 return false;
8659 // '1' here is a dummy value.
8660 // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword.
8661 item->mValue.SetIntValue(1, eCSSUnit_Enumerated);
8662 item->mNext = new nsCSSValueList;
8663 item = item->mNext;
8665 if (integer.isSome()) {
8666 item->mValue.SetIntValue(integer.ref(), eCSSUnit_Integer);
8667 if (hasIdent) {
8668 item->mNext = new nsCSSValueList;
8669 item = item->mNext;
8672 if (hasIdent) {
8673 item->mValue = ident;
8675 return true;
8678 bool
8679 CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSProperty aPropID)
8681 nsCSSValue value;
8682 if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
8683 ParseGridLine(value)) {
8684 AppendValue(aPropID, value);
8685 return true;
8687 return false;
8690 // If |aFallback| is a List containing a single Ident, set |aValue| to that.
8691 // Otherwise, set |aValue| to Auto.
8692 // Used with |aFallback| from ParseGridLine()
8693 static void
8694 HandleGridLineFallback(const nsCSSValue& aFallback, nsCSSValue& aValue)
8696 if (aFallback.GetUnit() == eCSSUnit_List &&
8697 aFallback.GetListValue()->mValue.GetUnit() == eCSSUnit_Ident &&
8698 !aFallback.GetListValue()->mNext) {
8699 aValue = aFallback;
8700 } else {
8701 aValue.SetAutoValue();
8705 bool
8706 CSSParserImpl::ParseGridColumnRow(nsCSSProperty aStartPropID,
8707 nsCSSProperty aEndPropID)
8709 nsCSSValue value;
8710 nsCSSValue secondValue;
8711 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
8712 AppendValue(aStartPropID, value);
8713 AppendValue(aEndPropID, value);
8714 return true;
8717 if (!ParseGridLine(value)) {
8718 return false;
8720 if (GetToken(true)) {
8721 if (mToken.IsSymbol('/')) {
8722 if (ParseGridLine(secondValue)) {
8723 AppendValue(aStartPropID, value);
8724 AppendValue(aEndPropID, secondValue);
8725 return true;
8726 } else {
8727 return false;
8730 UngetToken();
8733 // A single <custom-ident> is repeated to both properties,
8734 // anything else sets the grid-{column,row}-end property to 'auto'.
8735 HandleGridLineFallback(value, secondValue);
8737 AppendValue(aStartPropID, value);
8738 AppendValue(aEndPropID, secondValue);
8739 return true;
8742 bool
8743 CSSParserImpl::ParseGridArea()
8745 nsCSSValue values[4];
8746 if (ParseVariant(values[0], VARIANT_INHERIT, nullptr)) {
8747 AppendValue(eCSSProperty_grid_row_start, values[0]);
8748 AppendValue(eCSSProperty_grid_column_start, values[0]);
8749 AppendValue(eCSSProperty_grid_row_end, values[0]);
8750 AppendValue(eCSSProperty_grid_column_end, values[0]);
8751 return true;
8754 int32_t i = 0;
8755 for (;;) {
8756 if (!ParseGridLine(values[i])) {
8757 return false;
8759 if (++i == 4 || !GetToken(true)) {
8760 break;
8762 if (!mToken.IsSymbol('/')) {
8763 UngetToken();
8764 break;
8768 MOZ_ASSERT(i >= 1, "should have parsed at least one grid-line (or returned)");
8769 if (i < 2) {
8770 HandleGridLineFallback(values[0], values[1]);
8772 if (i < 3) {
8773 HandleGridLineFallback(values[0], values[2]);
8775 if (i < 4) {
8776 HandleGridLineFallback(values[1], values[3]);
8779 AppendValue(eCSSProperty_grid_row_start, values[0]);
8780 AppendValue(eCSSProperty_grid_column_start, values[1]);
8781 AppendValue(eCSSProperty_grid_row_end, values[2]);
8782 AppendValue(eCSSProperty_grid_column_end, values[3]);
8783 return true;
8786 // <color-stop> : <color> [ <percentage> | <length> ]?
8787 bool
8788 CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
8790 nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
8791 if (!ParseVariant(stop->mColor, VARIANT_COLOR, nullptr)) {
8792 stop->mIsInterpolationHint = true;
8795 // Stop positions do not have to fall between the starting-point and
8796 // ending-point, so we don't use ParseNonNegativeVariant.
8797 if (!ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr)) {
8798 if (stop->mIsInterpolationHint) {
8799 return false;
8801 stop->mLocation.SetNoneValue();
8803 return true;
8806 // <gradient>
8807 // : linear-gradient( <linear-gradient-line>? <color-stops> ')'
8808 // | radial-gradient( <radial-gradient-line>? <color-stops> ')'
8810 // <linear-gradient-line> : [ to [left | right] || [top | bottom] ] ,
8811 // | <legacy-gradient-line>
8812 // <radial-gradient-line> : [ <shape> || <size> ] [ at <position> ]? ,
8813 // | [ at <position> ] ,
8814 // | <legacy-gradient-line>? <legacy-shape-size>?
8815 // <shape> : circle | ellipse
8816 // <size> : closest-side | closest-corner | farthest-side | farthest-corner
8817 // | <length> | [<length> | <percentage>]{2}
8819 // <legacy-gradient-line> : [ <position> || <angle>] ,
8821 // <legacy-shape-size> : [ <shape> || <legacy-size> ] ,
8822 // <legacy-size> : closest-side | closest-corner | farthest-side
8823 // | farthest-corner | contain | cover
8825 // <color-stops> : <color-stop> , <color-stop> [, <color-stop>]*
8826 bool
8827 CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating,
8828 bool aIsLegacy)
8830 nsRefPtr<nsCSSValueGradient> cssGradient
8831 = new nsCSSValueGradient(false, aIsRepeating);
8833 if (!GetToken(true)) {
8834 return false;
8837 if (mToken.mType == eCSSToken_Ident &&
8838 mToken.mIdent.LowerCaseEqualsLiteral("to")) {
8840 // "to" syntax doesn't allow explicit "center"
8841 if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) {
8842 SkipUntil(')');
8843 return false;
8846 // [ to [left | right] || [top | bottom] ] ,
8847 const nsCSSValue& xValue = cssGradient->mBgPos.mXValue;
8848 const nsCSSValue& yValue = cssGradient->mBgPos.mYValue;
8849 if (xValue.GetUnit() != eCSSUnit_Enumerated ||
8850 !(xValue.GetIntValue() & (NS_STYLE_BG_POSITION_LEFT |
8851 NS_STYLE_BG_POSITION_CENTER |
8852 NS_STYLE_BG_POSITION_RIGHT)) ||
8853 yValue.GetUnit() != eCSSUnit_Enumerated ||
8854 !(yValue.GetIntValue() & (NS_STYLE_BG_POSITION_TOP |
8855 NS_STYLE_BG_POSITION_CENTER |
8856 NS_STYLE_BG_POSITION_BOTTOM))) {
8857 SkipUntil(')');
8858 return false;
8861 if (!ExpectSymbol(',', true)) {
8862 SkipUntil(')');
8863 return false;
8866 return ParseGradientColorStops(cssGradient, aValue);
8869 if (!aIsLegacy) {
8870 UngetToken();
8872 // <angle> ,
8873 if (ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) &&
8874 !ExpectSymbol(',', true)) {
8875 SkipUntil(')');
8876 return false;
8879 return ParseGradientColorStops(cssGradient, aValue);
8882 nsCSSTokenType ty = mToken.mType;
8883 nsString id = mToken.mIdent;
8884 UngetToken();
8886 // <legacy-gradient-line>
8887 bool haveGradientLine = IsLegacyGradientLine(ty, id);
8888 if (haveGradientLine) {
8889 cssGradient->mIsLegacySyntax = true;
8890 bool haveAngle =
8891 ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
8893 // if we got an angle, we might now have a comma, ending the gradient-line
8894 if (!haveAngle || !ExpectSymbol(',', true)) {
8895 if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
8896 SkipUntil(')');
8897 return false;
8900 if (!ExpectSymbol(',', true) &&
8901 // if we didn't already get an angle, we might have one now,
8902 // otherwise it's an error
8903 (haveAngle ||
8904 !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) ||
8905 // now we better have a comma
8906 !ExpectSymbol(',', true))) {
8907 SkipUntil(')');
8908 return false;
8913 return ParseGradientColorStops(cssGradient, aValue);
8916 bool
8917 CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating,
8918 bool aIsLegacy)
8920 nsRefPtr<nsCSSValueGradient> cssGradient
8921 = new nsCSSValueGradient(true, aIsRepeating);
8923 // [ <shape> || <size> ]
8924 bool haveShape =
8925 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
8926 nsCSSProps::kRadialGradientShapeKTable);
8928 bool haveSize = ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
8929 aIsLegacy ?
8930 nsCSSProps::kRadialGradientLegacySizeKTable :
8931 nsCSSProps::kRadialGradientSizeKTable);
8932 if (haveSize) {
8933 if (!haveShape) {
8934 // <size> <shape>
8935 haveShape = ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
8936 nsCSSProps::kRadialGradientShapeKTable);
8938 } else if (!aIsLegacy) {
8939 // Save RadialShape before parsing RadiusX because RadialShape and
8940 // RadiusX share the storage.
8941 int32_t shape =
8942 cssGradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated ?
8943 cssGradient->GetRadialShape().GetIntValue() : -1;
8944 // <length> | [<length> | <percentage>]{2}
8945 cssGradient->mIsExplicitSize = true;
8946 haveSize =
8947 ParseNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP, nullptr);
8948 if (!haveSize) {
8949 // It was not an explicit size after all.
8950 // Note that ParseNonNegativeVariant may have put something
8951 // invalid into our storage, but only in the case where it was
8952 // rejected only for being negative. Since this means the token
8953 // was a length or a percentage, we know it's not valid syntax
8954 // (which must be a comma, the 'at' keyword, or a color), so we
8955 // know this value will be dropped. This means it doesn't matter
8956 // that we have something invalid in our storage.
8957 cssGradient->mIsExplicitSize = false;
8958 } else {
8959 // vertical extent is optional
8960 bool haveYSize =
8961 ParseNonNegativeVariant(cssGradient->GetRadiusY(), VARIANT_LP, nullptr);
8962 if (!haveShape) {
8963 nsCSSValue shapeValue;
8964 haveShape = ParseVariant(shapeValue, VARIANT_KEYWORD,
8965 nsCSSProps::kRadialGradientShapeKTable);
8966 if (haveShape) {
8967 shape = shapeValue.GetIntValue();
8970 if (haveYSize
8971 ? shape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR
8972 : cssGradient->GetRadiusX().GetUnit() == eCSSUnit_Percent ||
8973 shape == NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL) {
8974 SkipUntil(')');
8975 return false;
8980 if ((haveShape || haveSize) && ExpectSymbol(',', true)) {
8981 // [ <shape> || <size> ] ,
8982 return ParseGradientColorStops(cssGradient, aValue);
8985 if (!GetToken(true)) {
8986 return false;
8989 if (!aIsLegacy) {
8990 if (mToken.mType == eCSSToken_Ident &&
8991 mToken.mIdent.LowerCaseEqualsLiteral("at")) {
8992 // [ <shape> || <size> ]? at <position> ,
8993 if (!ParseBoxPositionValues(cssGradient->mBgPos, false) ||
8994 !ExpectSymbol(',', true)) {
8995 SkipUntil(')');
8996 return false;
8999 return ParseGradientColorStops(cssGradient, aValue);
9002 // <color-stops> only
9003 UngetToken();
9004 return ParseGradientColorStops(cssGradient, aValue);
9006 MOZ_ASSERT(!cssGradient->mIsExplicitSize);
9008 nsCSSTokenType ty = mToken.mType;
9009 nsString id = mToken.mIdent;
9010 UngetToken();
9012 // <legacy-gradient-line>
9013 bool haveGradientLine = false;
9014 // if we already encountered a shape or size,
9015 // we can not have a gradient-line in legacy syntax
9016 if (!haveShape && !haveSize) {
9017 haveGradientLine = IsLegacyGradientLine(ty, id);
9019 if (haveGradientLine) {
9020 bool haveAngle =
9021 ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
9023 // if we got an angle, we might now have a comma, ending the gradient-line
9024 if (!haveAngle || !ExpectSymbol(',', true)) {
9025 if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
9026 SkipUntil(')');
9027 return false;
9030 if (!ExpectSymbol(',', true) &&
9031 // if we didn't already get an angle, we might have one now,
9032 // otherwise it's an error
9033 (haveAngle ||
9034 !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) ||
9035 // now we better have a comma
9036 !ExpectSymbol(',', true))) {
9037 SkipUntil(')');
9038 return false;
9042 if (cssGradient->mAngle.GetUnit() != eCSSUnit_None) {
9043 cssGradient->mIsLegacySyntax = true;
9047 // radial gradients might have a shape and size here for legacy syntax
9048 if (!haveShape && !haveSize) {
9049 haveShape =
9050 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
9051 nsCSSProps::kRadialGradientShapeKTable);
9052 haveSize =
9053 ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
9054 nsCSSProps::kRadialGradientLegacySizeKTable);
9056 // could be in either order
9057 if (!haveShape) {
9058 haveShape =
9059 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
9060 nsCSSProps::kRadialGradientShapeKTable);
9064 if ((haveShape || haveSize) && !ExpectSymbol(',', true)) {
9065 SkipUntil(')');
9066 return false;
9069 return ParseGradientColorStops(cssGradient, aValue);
9072 bool
9073 CSSParserImpl::IsLegacyGradientLine(const nsCSSTokenType& aType,
9074 const nsString& aId)
9076 // N.B. ParseBoxPositionValues is not guaranteed to put back
9077 // everything it scanned if it fails, so we must only call it
9078 // if there is no alternative to consuming a <box-position>.
9079 // ParseVariant, as used here, will either succeed and consume
9080 // a single token, or fail and consume none, so we can be more
9081 // cavalier about calling it.
9083 bool haveGradientLine = false;
9084 switch (aType) {
9085 case eCSSToken_Percentage:
9086 case eCSSToken_Number:
9087 case eCSSToken_Dimension:
9088 haveGradientLine = true;
9089 break;
9091 case eCSSToken_Function:
9092 if (aId.LowerCaseEqualsLiteral("calc") ||
9093 aId.LowerCaseEqualsLiteral("-moz-calc")) {
9094 haveGradientLine = true;
9095 break;
9097 // fall through
9098 case eCSSToken_ID:
9099 case eCSSToken_Hash:
9100 // this is a color
9101 break;
9103 case eCSSToken_Ident: {
9104 // This is only a gradient line if it's a box position keyword.
9105 nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(aId);
9106 int32_t junk;
9107 if (kw != eCSSKeyword_UNKNOWN &&
9108 nsCSSProps::FindKeyword(kw, nsCSSProps::kBackgroundPositionKTable,
9109 junk)) {
9110 haveGradientLine = true;
9112 break;
9115 default:
9116 // error
9117 break;
9120 return haveGradientLine;
9123 bool
9124 CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient,
9125 nsCSSValue& aValue)
9127 // At least two color stops are required
9128 if (!ParseColorStop(aGradient) ||
9129 !ExpectSymbol(',', true) ||
9130 !ParseColorStop(aGradient)) {
9131 SkipUntil(')');
9132 return false;
9135 // Additional color stops
9136 while (ExpectSymbol(',', true)) {
9137 if (!ParseColorStop(aGradient)) {
9138 SkipUntil(')');
9139 return false;
9143 if (!ExpectSymbol(')', true)) {
9144 SkipUntil(')');
9145 return false;
9148 // Check if interpolation hints are in the correct location
9149 bool previousPointWasInterpolationHint = true;
9150 for (size_t x = 0; x < aGradient->mStops.Length(); x++) {
9151 bool isInterpolationHint = aGradient->mStops[x].mIsInterpolationHint;
9152 if (isInterpolationHint && previousPointWasInterpolationHint) {
9153 return false;
9155 previousPointWasInterpolationHint = isInterpolationHint;
9158 if (previousPointWasInterpolationHint) {
9159 return false;
9162 aValue.SetGradientValue(aGradient);
9163 return true;
9166 int32_t
9167 CSSParserImpl::ParseChoice(nsCSSValue aValues[],
9168 const nsCSSProperty aPropIDs[], int32_t aNumIDs)
9170 int32_t found = 0;
9171 nsAutoParseCompoundProperty compound(this);
9173 int32_t loop;
9174 for (loop = 0; loop < aNumIDs; loop++) {
9175 // Try each property parser in order
9176 int32_t hadFound = found;
9177 int32_t index;
9178 for (index = 0; index < aNumIDs; index++) {
9179 int32_t bit = 1 << index;
9180 if ((found & bit) == 0) {
9181 if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) {
9182 found |= bit;
9183 // It's more efficient to break since it will reset |hadFound|
9184 // to |found|. Furthermore, ParseListStyle depends on our going
9185 // through the properties in order for each value..
9186 break;
9190 if (found == hadFound) { // found nothing new
9191 break;
9194 if (0 < found) {
9195 if (1 == found) { // only first property
9196 if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
9197 for (loop = 1; loop < aNumIDs; loop++) {
9198 aValues[loop].SetInheritValue();
9200 found = ((1 << aNumIDs) - 1);
9202 else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
9203 for (loop = 1; loop < aNumIDs; loop++) {
9204 aValues[loop].SetInitialValue();
9206 found = ((1 << aNumIDs) - 1);
9208 else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset
9209 for (loop = 1; loop < aNumIDs; loop++) {
9210 aValues[loop].SetUnsetValue();
9212 found = ((1 << aNumIDs) - 1);
9215 else { // more than one value, verify no inherits, initials or unsets
9216 for (loop = 0; loop < aNumIDs; loop++) {
9217 if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
9218 found = -1;
9219 break;
9221 else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
9222 found = -1;
9223 break;
9225 else if (eCSSUnit_Unset == aValues[loop].GetUnit()) {
9226 found = -1;
9227 break;
9232 return found;
9235 void
9236 CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue)
9238 mTempData.AddLonghandProperty(aPropID, aValue);
9242 * Parse a "box" property. Box properties have 1 to 4 values. When less
9243 * than 4 values are provided a standard mapping is used to replicate
9244 * existing values.
9246 bool
9247 CSSParserImpl::ParseBoxProperties(const nsCSSProperty aPropIDs[])
9249 // Get up to four values for the property
9250 int32_t count = 0;
9251 nsCSSRect result;
9252 NS_FOR_CSS_SIDES (index) {
9253 if (! ParseSingleValueProperty(result.*(nsCSSRect::sides[index]),
9254 aPropIDs[index])) {
9255 break;
9257 count++;
9259 if (count == 0) {
9260 return false;
9263 if (1 < count) { // verify no more than single inherit, initial or unset
9264 NS_FOR_CSS_SIDES (index) {
9265 nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
9266 if (eCSSUnit_Inherit == unit ||
9267 eCSSUnit_Initial == unit ||
9268 eCSSUnit_Unset == unit) {
9269 return false;
9274 // Provide missing values by replicating some of the values found
9275 switch (count) {
9276 case 1: // Make right == top
9277 result.mRight = result.mTop;
9278 case 2: // Make bottom == top
9279 result.mBottom = result.mTop;
9280 case 3: // Make left == right
9281 result.mLeft = result.mRight;
9284 NS_FOR_CSS_SIDES (index) {
9285 AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index]));
9287 return true;
9290 // Similar to ParseBoxProperties, except there is only one property
9291 // with the result as its value, not four. Requires values be nonnegative.
9292 bool
9293 CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask,
9294 /** outparam */ nsCSSValue& aValue)
9296 nsCSSRect& result = aValue.SetRectValue();
9298 int32_t count = 0;
9299 NS_FOR_CSS_SIDES (index) {
9300 if (!ParseNonNegativeVariant(result.*(nsCSSRect::sides[index]),
9301 aVariantMask, nullptr)) {
9302 break;
9304 count++;
9307 if (count == 0) {
9308 return false;
9311 // Provide missing values by replicating some of the values found
9312 switch (count) {
9313 case 1: // Make right == top
9314 result.mRight = result.mTop;
9315 case 2: // Make bottom == top
9316 result.mBottom = result.mTop;
9317 case 3: // Make left == right
9318 result.mLeft = result.mRight;
9321 return true;
9324 bool
9325 CSSParserImpl::ParseDirectionalBoxProperty(nsCSSProperty aProperty,
9326 int32_t aSourceType)
9328 const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty);
9329 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
9330 "not box property with physical vs. logical cascading");
9331 nsCSSValue value;
9332 if (!ParseSingleValueProperty(value, subprops[0])) {
9333 return false;
9336 AppendValue(subprops[0], value);
9337 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
9338 AppendValue(subprops[1], typeVal);
9339 AppendValue(subprops[2], typeVal);
9340 return true;
9343 bool
9344 CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID)
9346 nsCSSValue dimenX, dimenY;
9347 // required first value
9348 if (! ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr))
9349 return false;
9351 // optional second value (forbidden if first value is inherit/initial/unset)
9352 if (dimenX.GetUnit() != eCSSUnit_Inherit &&
9353 dimenX.GetUnit() != eCSSUnit_Initial &&
9354 dimenX.GetUnit() != eCSSUnit_Unset) {
9355 ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr);
9358 if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) {
9359 AppendValue(aPropID, dimenX);
9360 } else {
9361 nsCSSValue value;
9362 value.SetPairValue(dimenX, dimenY);
9363 AppendValue(aPropID, value);
9365 return true;
9368 bool
9369 CSSParserImpl::ParseBoxCornerRadiiInternals(nsCSSValue array[])
9371 // Rectangles are used as scratch storage.
9372 // top => top-left, right => top-right,
9373 // bottom => bottom-right, left => bottom-left.
9374 nsCSSRect dimenX, dimenY;
9375 int32_t countX = 0, countY = 0;
9377 NS_FOR_CSS_SIDES (side) {
9378 if (! ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side],
9379 (side > 0 ? 0 : VARIANT_INHERIT) |
9380 VARIANT_LP | VARIANT_CALC,
9381 nullptr))
9382 break;
9383 countX++;
9385 if (countX == 0)
9386 return false;
9388 if (ExpectSymbol('/', true)) {
9389 NS_FOR_CSS_SIDES (side) {
9390 if (! ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side],
9391 VARIANT_LP | VARIANT_CALC, nullptr))
9392 break;
9393 countY++;
9395 if (countY == 0)
9396 return false;
9399 // if 'initial', 'inherit' or 'unset' was used, it must be the only value
9400 if (countX > 1 || countY > 0) {
9401 nsCSSUnit unit = dimenX.mTop.GetUnit();
9402 if (eCSSUnit_Inherit == unit ||
9403 eCSSUnit_Initial == unit ||
9404 eCSSUnit_Unset == unit)
9405 return false;
9408 // if we have no Y-values, use the X-values
9409 if (countY == 0) {
9410 dimenY = dimenX;
9411 countY = countX;
9414 // Provide missing values by replicating some of the values found
9415 switch (countX) {
9416 case 1: dimenX.mRight = dimenX.mTop; // top-right same as top-left, and
9417 case 2: dimenX.mBottom = dimenX.mTop; // bottom-right same as top-left, and
9418 case 3: dimenX.mLeft = dimenX.mRight; // bottom-left same as top-right
9421 switch (countY) {
9422 case 1: dimenY.mRight = dimenY.mTop; // top-right same as top-left, and
9423 case 2: dimenY.mBottom = dimenY.mTop; // bottom-right same as top-left, and
9424 case 3: dimenY.mLeft = dimenY.mRight; // bottom-left same as top-right
9427 NS_FOR_CSS_SIDES(side) {
9428 nsCSSValue& x = dimenX.*nsCSSRect::sides[side];
9429 nsCSSValue& y = dimenY.*nsCSSRect::sides[side];
9431 if (x == y) {
9432 array[side] = x;
9433 } else {
9434 nsCSSValue pair;
9435 pair.SetPairValue(x, y);
9436 array[side] = pair;
9439 return true;
9442 bool
9443 CSSParserImpl::ParseBoxCornerRadii(const nsCSSProperty aPropIDs[])
9445 nsCSSValue value[4];
9446 if (!ParseBoxCornerRadiiInternals(value)) {
9447 return false;
9450 NS_FOR_CSS_SIDES(side) {
9451 AppendValue(aPropIDs[side], value[side]);
9453 return true;
9456 // These must be in CSS order (top,right,bottom,left) for indexing to work
9457 static const nsCSSProperty kBorderStyleIDs[] = {
9458 eCSSProperty_border_top_style,
9459 eCSSProperty_border_right_style_value,
9460 eCSSProperty_border_bottom_style,
9461 eCSSProperty_border_left_style_value
9463 static const nsCSSProperty kBorderWidthIDs[] = {
9464 eCSSProperty_border_top_width,
9465 eCSSProperty_border_right_width_value,
9466 eCSSProperty_border_bottom_width,
9467 eCSSProperty_border_left_width_value
9469 static const nsCSSProperty kBorderColorIDs[] = {
9470 eCSSProperty_border_top_color,
9471 eCSSProperty_border_right_color_value,
9472 eCSSProperty_border_bottom_color,
9473 eCSSProperty_border_left_color_value
9475 static const nsCSSProperty kBorderRadiusIDs[] = {
9476 eCSSProperty_border_top_left_radius,
9477 eCSSProperty_border_top_right_radius,
9478 eCSSProperty_border_bottom_right_radius,
9479 eCSSProperty_border_bottom_left_radius
9481 static const nsCSSProperty kOutlineRadiusIDs[] = {
9482 eCSSProperty__moz_outline_radius_topLeft,
9483 eCSSProperty__moz_outline_radius_topRight,
9484 eCSSProperty__moz_outline_radius_bottomRight,
9485 eCSSProperty__moz_outline_radius_bottomLeft
9488 void
9489 CSSParserImpl::SaveInputState(CSSParserInputState& aState)
9491 aState.mToken = mToken;
9492 aState.mHavePushBack = mHavePushBack;
9493 mScanner->SavePosition(aState.mPosition);
9496 void
9497 CSSParserImpl::RestoreSavedInputState(const CSSParserInputState& aState)
9499 mToken = aState.mToken;
9500 mHavePushBack = aState.mHavePushBack;
9501 mScanner->RestoreSavedPosition(aState.mPosition);
9504 bool
9505 CSSParserImpl::ParseProperty(nsCSSProperty aPropID)
9507 // Can't use AutoRestore<bool> because it's a bitfield.
9508 NS_ABORT_IF_FALSE(!mHashlessColorQuirk,
9509 "hashless color quirk should not be set");
9510 NS_ABORT_IF_FALSE(!mUnitlessLengthQuirk,
9511 "unitless length quirk should not be set");
9512 MOZ_ASSERT(aPropID != eCSSPropertyExtra_variable);
9514 if (mNavQuirkMode) {
9515 mHashlessColorQuirk =
9516 nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_HASHLESS_COLOR_QUIRK);
9517 mUnitlessLengthQuirk =
9518 nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_UNITLESS_LENGTH_QUIRK);
9521 // Save the current input state so that we can restore it later if we
9522 // have to re-parse the property value as a variable-reference-containing
9523 // token stream.
9524 CSSParserInputState stateBeforeProperty;
9525 SaveInputState(stateBeforeProperty);
9526 mScanner->ClearSeenVariableReference();
9528 NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
9529 bool allowVariables = true;
9530 bool result;
9531 switch (nsCSSProps::PropertyParseType(aPropID)) {
9532 case CSS_PROPERTY_PARSE_INACCESSIBLE: {
9533 // The user can't use these
9534 REPORT_UNEXPECTED(PEInaccessibleProperty2);
9535 allowVariables = false;
9536 result = false;
9537 break;
9539 case CSS_PROPERTY_PARSE_FUNCTION: {
9540 result = ParsePropertyByFunction(aPropID);
9541 break;
9543 case CSS_PROPERTY_PARSE_VALUE: {
9544 result = false;
9545 nsCSSValue value;
9546 if (ParseSingleValueProperty(value, aPropID)) {
9547 AppendValue(aPropID, value);
9548 result = true;
9550 // XXX Report errors?
9551 break;
9553 case CSS_PROPERTY_PARSE_VALUE_LIST: {
9554 result = ParseValueList(aPropID);
9555 break;
9557 default: {
9558 result = false;
9559 allowVariables = false;
9560 NS_ABORT_IF_FALSE(false,
9561 "Property's flags field in nsCSSPropList.h is missing "
9562 "one of the CSS_PROPERTY_PARSE_* constants");
9563 break;
9567 if (result) {
9568 // We need to call ExpectEndProperty() to decide whether to reparse
9569 // with variables. This is needed because the property parsing may
9570 // have stopped upon finding a variable (e.g., 'margin: 1px var(a)')
9571 // in a way that future variable substitutions will be valid, or
9572 // because it parsed everything that's possible but we still want to
9573 // act as though the property contains variables even though we know
9574 // the substitution will never work (e.g., for 'margin: 1px 2px 3px
9575 // 4px 5px var(a)').
9577 // It would be nice to find a better solution here
9578 // (and for the SkipUntilOneOf below), though, that doesn't depend
9579 // on using what we don't accept for doing parsing correctly.
9580 if (!ExpectEndProperty()) {
9581 result = false;
9585 bool seenVariable = mScanner->SeenVariableReference() ||
9586 (stateBeforeProperty.mHavePushBack &&
9587 stateBeforeProperty.mToken.mType == eCSSToken_Function &&
9588 stateBeforeProperty.mToken.mIdent.LowerCaseEqualsLiteral("var"));
9589 bool parseAsTokenStream;
9591 if (!result && allowVariables) {
9592 parseAsTokenStream = true;
9593 if (!seenVariable) {
9594 // We might have stopped parsing the property before its end and before
9595 // finding a variable reference. Keep checking until the end of the
9596 // property.
9597 CSSParserInputState stateAtError;
9598 SaveInputState(stateAtError);
9600 const char16_t stopChars[] = { ';', '!', '}', ')', 0 };
9601 SkipUntilOneOf(stopChars);
9602 UngetToken();
9603 parseAsTokenStream = mScanner->SeenVariableReference();
9605 if (!parseAsTokenStream) {
9606 // If we parsed to the end of the propery and didn't find any variable
9607 // references, then the real position we want to report the error at
9608 // is |stateAtError|.
9609 RestoreSavedInputState(stateAtError);
9612 } else {
9613 parseAsTokenStream = false;
9616 if (parseAsTokenStream) {
9617 // Go back to the start of the property value and parse it to make sure
9618 // its variable references are syntactically valid and is otherwise
9619 // balanced.
9620 RestoreSavedInputState(stateBeforeProperty);
9622 if (!mInSupportsCondition) {
9623 mScanner->StartRecording();
9626 CSSVariableDeclarations::Type type;
9627 bool dropBackslash;
9628 nsString impliedCharacters;
9629 nsCSSValue value;
9630 if (ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
9631 nullptr, nullptr)) {
9632 MOZ_ASSERT(type == CSSVariableDeclarations::eTokenStream,
9633 "a non-custom property reparsed since it contained variable "
9634 "references should not have been 'initial' or 'inherit'");
9636 nsString propertyValue;
9638 if (!mInSupportsCondition) {
9639 // If we are in an @supports condition, we don't need to store the
9640 // actual token stream on the nsCSSValue.
9641 mScanner->StopRecording(propertyValue);
9642 if (dropBackslash) {
9643 MOZ_ASSERT(!propertyValue.IsEmpty() &&
9644 propertyValue[propertyValue.Length() - 1] == '\\');
9645 propertyValue.Truncate(propertyValue.Length() - 1);
9647 propertyValue.Append(impliedCharacters);
9650 if (mHavePushBack) {
9651 // If we came to the end of a property value that had a variable
9652 // reference and a token was pushed back, then it would have been
9653 // ended by '!', ')', ';', ']' or '}'. We should remove it from the
9654 // recorded property value.
9655 MOZ_ASSERT(mToken.IsSymbol('!') ||
9656 mToken.IsSymbol(')') ||
9657 mToken.IsSymbol(';') ||
9658 mToken.IsSymbol(']') ||
9659 mToken.IsSymbol('}'));
9660 if (!mInSupportsCondition) {
9661 MOZ_ASSERT(!propertyValue.IsEmpty());
9662 MOZ_ASSERT(propertyValue[propertyValue.Length() - 1] ==
9663 mToken.mSymbol);
9664 propertyValue.Truncate(propertyValue.Length() - 1);
9668 if (!mInSupportsCondition) {
9669 if (nsCSSProps::IsShorthand(aPropID)) {
9670 // If this is a shorthand property, we store the token stream on each
9671 // of its corresponding longhand properties.
9672 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
9673 nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
9674 tokenStream->mPropertyID = *p;
9675 tokenStream->mShorthandPropertyID = aPropID;
9676 tokenStream->mTokenStream = propertyValue;
9677 tokenStream->mBaseURI = mBaseURI;
9678 tokenStream->mSheetURI = mSheetURI;
9679 tokenStream->mSheetPrincipal = mSheetPrincipal;
9680 tokenStream->mSheet = mSheet;
9681 tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
9682 tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
9683 value.SetTokenStreamValue(tokenStream);
9684 AppendValue(*p, value);
9686 } else {
9687 nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
9688 tokenStream->mPropertyID = aPropID;
9689 tokenStream->mTokenStream = propertyValue;
9690 tokenStream->mBaseURI = mBaseURI;
9691 tokenStream->mSheetURI = mSheetURI;
9692 tokenStream->mSheetPrincipal = mSheetPrincipal;
9693 tokenStream->mSheet = mSheet;
9694 tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
9695 tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
9696 value.SetTokenStreamValue(tokenStream);
9697 AppendValue(aPropID, value);
9700 result = true;
9701 } else {
9702 if (!mInSupportsCondition) {
9703 mScanner->StopRecording();
9708 if (mNavQuirkMode) {
9709 mHashlessColorQuirk = false;
9710 mUnitlessLengthQuirk = false;
9713 return result;
9716 bool
9717 CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID)
9719 switch (aPropID) { // handle shorthand or multiple properties
9720 case eCSSProperty_background:
9721 return ParseBackground();
9722 case eCSSProperty_background_repeat:
9723 return ParseBackgroundRepeat();
9724 case eCSSProperty_background_position:
9725 return ParseBackgroundPosition();
9726 case eCSSProperty_background_size:
9727 return ParseBackgroundSize();
9728 case eCSSProperty_border:
9729 return ParseBorderSide(kBorderTopIDs, true);
9730 case eCSSProperty_border_color:
9731 return ParseBorderColor();
9732 case eCSSProperty_border_spacing:
9733 return ParseBorderSpacing();
9734 case eCSSProperty_border_style:
9735 return ParseBorderStyle();
9736 case eCSSProperty_border_bottom:
9737 return ParseBorderSide(kBorderBottomIDs, false);
9738 case eCSSProperty_border_end:
9739 return ParseDirectionalBorderSide(kBorderEndIDs,
9740 NS_BOXPROP_SOURCE_LOGICAL);
9741 case eCSSProperty_border_left:
9742 return ParseDirectionalBorderSide(kBorderLeftIDs,
9743 NS_BOXPROP_SOURCE_PHYSICAL);
9744 case eCSSProperty_border_right:
9745 return ParseDirectionalBorderSide(kBorderRightIDs,
9746 NS_BOXPROP_SOURCE_PHYSICAL);
9747 case eCSSProperty_border_start:
9748 return ParseDirectionalBorderSide(kBorderStartIDs,
9749 NS_BOXPROP_SOURCE_LOGICAL);
9750 case eCSSProperty_border_top:
9751 return ParseBorderSide(kBorderTopIDs, false);
9752 case eCSSProperty_border_bottom_colors:
9753 case eCSSProperty_border_left_colors:
9754 case eCSSProperty_border_right_colors:
9755 case eCSSProperty_border_top_colors:
9756 return ParseBorderColors(aPropID);
9757 case eCSSProperty_border_image_slice:
9758 return ParseBorderImageSlice(true, nullptr);
9759 case eCSSProperty_border_image_width:
9760 return ParseBorderImageWidth(true);
9761 case eCSSProperty_border_image_outset:
9762 return ParseBorderImageOutset(true);
9763 case eCSSProperty_border_image_repeat:
9764 return ParseBorderImageRepeat(true);
9765 case eCSSProperty_border_image:
9766 return ParseBorderImage();
9767 case eCSSProperty_border_width:
9768 return ParseBorderWidth();
9769 case eCSSProperty_border_end_color:
9770 return ParseDirectionalBoxProperty(eCSSProperty_border_end_color,
9771 NS_BOXPROP_SOURCE_LOGICAL);
9772 case eCSSProperty_border_left_color:
9773 return ParseDirectionalBoxProperty(eCSSProperty_border_left_color,
9774 NS_BOXPROP_SOURCE_PHYSICAL);
9775 case eCSSProperty_border_right_color:
9776 return ParseDirectionalBoxProperty(eCSSProperty_border_right_color,
9777 NS_BOXPROP_SOURCE_PHYSICAL);
9778 case eCSSProperty_border_start_color:
9779 return ParseDirectionalBoxProperty(eCSSProperty_border_start_color,
9780 NS_BOXPROP_SOURCE_LOGICAL);
9781 case eCSSProperty_border_end_width:
9782 return ParseDirectionalBoxProperty(eCSSProperty_border_end_width,
9783 NS_BOXPROP_SOURCE_LOGICAL);
9784 case eCSSProperty_border_left_width:
9785 return ParseDirectionalBoxProperty(eCSSProperty_border_left_width,
9786 NS_BOXPROP_SOURCE_PHYSICAL);
9787 case eCSSProperty_border_right_width:
9788 return ParseDirectionalBoxProperty(eCSSProperty_border_right_width,
9789 NS_BOXPROP_SOURCE_PHYSICAL);
9790 case eCSSProperty_border_start_width:
9791 return ParseDirectionalBoxProperty(eCSSProperty_border_start_width,
9792 NS_BOXPROP_SOURCE_LOGICAL);
9793 case eCSSProperty_border_end_style:
9794 return ParseDirectionalBoxProperty(eCSSProperty_border_end_style,
9795 NS_BOXPROP_SOURCE_LOGICAL);
9796 case eCSSProperty_border_left_style:
9797 return ParseDirectionalBoxProperty(eCSSProperty_border_left_style,
9798 NS_BOXPROP_SOURCE_PHYSICAL);
9799 case eCSSProperty_border_right_style:
9800 return ParseDirectionalBoxProperty(eCSSProperty_border_right_style,
9801 NS_BOXPROP_SOURCE_PHYSICAL);
9802 case eCSSProperty_border_start_style:
9803 return ParseDirectionalBoxProperty(eCSSProperty_border_start_style,
9804 NS_BOXPROP_SOURCE_LOGICAL);
9805 case eCSSProperty_border_radius:
9806 return ParseBoxCornerRadii(kBorderRadiusIDs);
9807 case eCSSProperty__moz_outline_radius:
9808 return ParseBoxCornerRadii(kOutlineRadiusIDs);
9810 case eCSSProperty_border_top_left_radius:
9811 case eCSSProperty_border_top_right_radius:
9812 case eCSSProperty_border_bottom_right_radius:
9813 case eCSSProperty_border_bottom_left_radius:
9814 case eCSSProperty__moz_outline_radius_topLeft:
9815 case eCSSProperty__moz_outline_radius_topRight:
9816 case eCSSProperty__moz_outline_radius_bottomRight:
9817 case eCSSProperty__moz_outline_radius_bottomLeft:
9818 return ParseBoxCornerRadius(aPropID);
9820 case eCSSProperty_box_shadow:
9821 case eCSSProperty_text_shadow:
9822 return ParseShadowList(aPropID);
9824 case eCSSProperty_clip:
9825 return ParseRect(eCSSProperty_clip);
9826 case eCSSProperty__moz_columns:
9827 return ParseColumns();
9828 case eCSSProperty__moz_column_rule:
9829 return ParseBorderSide(kColumnRuleIDs, false);
9830 case eCSSProperty_content:
9831 return ParseContent();
9832 case eCSSProperty_counter_increment:
9833 case eCSSProperty_counter_reset:
9834 return ParseCounterData(aPropID);
9835 case eCSSProperty_cursor:
9836 return ParseCursor();
9837 case eCSSProperty_filter:
9838 return ParseFilter();
9839 case eCSSProperty_flex:
9840 return ParseFlex();
9841 case eCSSProperty_flex_flow:
9842 return ParseFlexFlow();
9843 case eCSSProperty_font:
9844 return ParseFont();
9845 case eCSSProperty_font_variant:
9846 return ParseFontVariant();
9847 case eCSSProperty_grid_auto_flow:
9848 return ParseGridAutoFlow();
9849 case eCSSProperty_grid_auto_columns:
9850 case eCSSProperty_grid_auto_rows:
9851 return ParseGridAutoColumnsRows(aPropID);
9852 case eCSSProperty_grid_template_areas:
9853 return ParseGridTemplateAreas();
9854 case eCSSProperty_grid_template_columns:
9855 case eCSSProperty_grid_template_rows:
9856 return ParseGridTemplateColumnsRows(aPropID);
9857 case eCSSProperty_grid_template:
9858 return ParseGridTemplate();
9859 case eCSSProperty_grid:
9860 return ParseGrid();
9861 case eCSSProperty_grid_column_start:
9862 case eCSSProperty_grid_column_end:
9863 case eCSSProperty_grid_row_start:
9864 case eCSSProperty_grid_row_end:
9865 return ParseGridColumnRowStartEnd(aPropID);
9866 case eCSSProperty_grid_column:
9867 return ParseGridColumnRow(eCSSProperty_grid_column_start,
9868 eCSSProperty_grid_column_end);
9869 case eCSSProperty_grid_row:
9870 return ParseGridColumnRow(eCSSProperty_grid_row_start,
9871 eCSSProperty_grid_row_end);
9872 case eCSSProperty_grid_area:
9873 return ParseGridArea();
9874 case eCSSProperty_image_region:
9875 return ParseRect(eCSSProperty_image_region);
9876 case eCSSProperty_list_style:
9877 return ParseListStyle();
9878 case eCSSProperty_margin:
9879 return ParseMargin();
9880 case eCSSProperty_margin_end:
9881 return ParseDirectionalBoxProperty(eCSSProperty_margin_end,
9882 NS_BOXPROP_SOURCE_LOGICAL);
9883 case eCSSProperty_margin_left:
9884 return ParseDirectionalBoxProperty(eCSSProperty_margin_left,
9885 NS_BOXPROP_SOURCE_PHYSICAL);
9886 case eCSSProperty_margin_right:
9887 return ParseDirectionalBoxProperty(eCSSProperty_margin_right,
9888 NS_BOXPROP_SOURCE_PHYSICAL);
9889 case eCSSProperty_margin_start:
9890 return ParseDirectionalBoxProperty(eCSSProperty_margin_start,
9891 NS_BOXPROP_SOURCE_LOGICAL);
9892 case eCSSProperty_object_position:
9893 return ParseObjectPosition();
9894 case eCSSProperty_outline:
9895 return ParseOutline();
9896 case eCSSProperty_overflow:
9897 return ParseOverflow();
9898 case eCSSProperty_padding:
9899 return ParsePadding();
9900 case eCSSProperty_padding_end:
9901 return ParseDirectionalBoxProperty(eCSSProperty_padding_end,
9902 NS_BOXPROP_SOURCE_LOGICAL);
9903 case eCSSProperty_padding_left:
9904 return ParseDirectionalBoxProperty(eCSSProperty_padding_left,
9905 NS_BOXPROP_SOURCE_PHYSICAL);
9906 case eCSSProperty_padding_right:
9907 return ParseDirectionalBoxProperty(eCSSProperty_padding_right,
9908 NS_BOXPROP_SOURCE_PHYSICAL);
9909 case eCSSProperty_padding_start:
9910 return ParseDirectionalBoxProperty(eCSSProperty_padding_start,
9911 NS_BOXPROP_SOURCE_LOGICAL);
9912 case eCSSProperty_quotes:
9913 return ParseQuotes();
9914 case eCSSProperty_size:
9915 return ParseSize();
9916 case eCSSProperty_text_decoration:
9917 return ParseTextDecoration();
9918 case eCSSProperty_will_change:
9919 return ParseWillChange();
9920 case eCSSProperty_transform:
9921 return ParseTransform(false);
9922 case eCSSProperty__moz_transform:
9923 return ParseTransform(true);
9924 case eCSSProperty_transform_origin:
9925 return ParseTransformOrigin(false);
9926 case eCSSProperty_perspective_origin:
9927 return ParseTransformOrigin(true);
9928 case eCSSProperty_transition:
9929 return ParseTransition();
9930 case eCSSProperty_animation:
9931 return ParseAnimation();
9932 case eCSSProperty_transition_property:
9933 return ParseTransitionProperty();
9934 case eCSSProperty_fill:
9935 case eCSSProperty_stroke:
9936 return ParsePaint(aPropID);
9937 case eCSSProperty_stroke_dasharray:
9938 return ParseDasharray();
9939 case eCSSProperty_marker:
9940 return ParseMarker();
9941 case eCSSProperty_paint_order:
9942 return ParsePaintOrder();
9943 case eCSSProperty_clip_path:
9944 return ParseClipPath();
9945 case eCSSProperty_all:
9946 return ParseAll();
9947 default:
9948 NS_ABORT_IF_FALSE(false, "should not be called");
9949 return false;
9953 // Bits used in determining which background position info we have
9954 #define BG_CENTER NS_STYLE_BG_POSITION_CENTER
9955 #define BG_TOP NS_STYLE_BG_POSITION_TOP
9956 #define BG_BOTTOM NS_STYLE_BG_POSITION_BOTTOM
9957 #define BG_LEFT NS_STYLE_BG_POSITION_LEFT
9958 #define BG_RIGHT NS_STYLE_BG_POSITION_RIGHT
9959 #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM)
9960 #define BG_TB (BG_TOP | BG_BOTTOM)
9961 #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT)
9962 #define BG_LR (BG_LEFT | BG_RIGHT)
9964 bool
9965 CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
9966 nsCSSProperty aPropID)
9968 if (aPropID == eCSSPropertyExtra_x_none_value) {
9969 return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nullptr);
9972 if (aPropID == eCSSPropertyExtra_x_auto_value) {
9973 return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nullptr);
9976 if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
9977 NS_ABORT_IF_FALSE(false, "not a single value property");
9978 return false;
9981 if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) {
9982 switch (aPropID) {
9983 case eCSSProperty_font_family:
9984 return ParseFamily(aValue);
9985 case eCSSProperty_font_synthesis:
9986 return ParseFontSynthesis(aValue);
9987 case eCSSProperty_font_variant_alternates:
9988 return ParseFontVariantAlternates(aValue);
9989 case eCSSProperty_font_variant_east_asian:
9990 return ParseFontVariantEastAsian(aValue);
9991 case eCSSProperty_font_variant_ligatures:
9992 return ParseFontVariantLigatures(aValue);
9993 case eCSSProperty_font_variant_numeric:
9994 return ParseFontVariantNumeric(aValue);
9995 case eCSSProperty_font_feature_settings:
9996 return ParseFontFeatureSettings(aValue);
9997 case eCSSProperty_font_weight:
9998 return ParseFontWeight(aValue);
9999 case eCSSProperty_image_orientation:
10000 return ParseImageOrientation(aValue);
10001 case eCSSProperty_list_style_type:
10002 return ParseListStyleType(aValue);
10003 case eCSSProperty_marks:
10004 return ParseMarks(aValue);
10005 case eCSSProperty_ruby_position:
10006 return ParseRubyPosition(aValue);
10007 case eCSSProperty_text_align:
10008 return ParseTextAlign(aValue);
10009 case eCSSProperty_text_align_last:
10010 return ParseTextAlignLast(aValue);
10011 case eCSSProperty_text_decoration_line:
10012 return ParseTextDecorationLine(aValue);
10013 case eCSSProperty_text_combine_upright:
10014 return ParseTextCombineUpright(aValue);
10015 case eCSSProperty_text_overflow:
10016 return ParseTextOverflow(aValue);
10017 case eCSSProperty_touch_action:
10018 return ParseTouchAction(aValue);
10019 default:
10020 NS_ABORT_IF_FALSE(false, "should not reach here");
10021 return false;
10025 uint32_t variant = nsCSSProps::ParserVariant(aPropID);
10026 if (variant == 0) {
10027 NS_ABORT_IF_FALSE(false, "not a single value property");
10028 return false;
10031 // We only allow 'script-level' when unsafe rules are enabled, because
10032 // otherwise it could interfere with rulenode optimizations if used in
10033 // a non-MathML-enabled document. We also only allow math-display when
10034 // unsafe rules are enabled.
10035 if (!mUnsafeRulesEnabled &&
10036 (aPropID == eCSSProperty_script_level ||
10037 aPropID == eCSSProperty_math_display))
10038 return false;
10040 const KTableValue *kwtable = nsCSSProps::kKeywordTableTable[aPropID];
10041 switch (nsCSSProps::ValueRestrictions(aPropID)) {
10042 default:
10043 NS_ABORT_IF_FALSE(false, "should not be reached");
10044 case 0:
10045 return ParseVariant(aValue, variant, kwtable);
10046 case CSS_PROPERTY_VALUE_NONNEGATIVE:
10047 return ParseNonNegativeVariant(aValue, variant, kwtable);
10048 case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
10049 return ParseOneOrLargerVariant(aValue, variant, kwtable);
10053 // font-descriptor: descriptor ':' value ';'
10054 // caller has advanced mToken to point at the descriptor
10055 bool
10056 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
10057 nsCSSValue& aValue)
10059 switch (aDescID) {
10060 // These four are similar to the properties of the same name,
10061 // possibly with more restrictions on the values they can take.
10062 case eCSSFontDesc_Family: {
10063 nsCSSValue value;
10064 if (!ParseFamily(value) ||
10065 value.GetUnit() != eCSSUnit_FontFamilyList)
10066 return false;
10068 // name can only be a single, non-generic name
10069 const FontFamilyList* f = value.GetFontFamilyListValue();
10070 const nsTArray<FontFamilyName>& fontlist = f->GetFontlist();
10072 if (fontlist.Length() != 1 || !fontlist[0].IsNamed()) {
10073 return false;
10076 aValue.SetStringValue(fontlist[0].mName, eCSSUnit_String);
10077 return true;
10080 case eCSSFontDesc_Style:
10081 // property is VARIANT_HMK|VARIANT_SYSFONT
10082 return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
10083 nsCSSProps::kFontStyleKTable);
10085 case eCSSFontDesc_Weight:
10086 return (ParseFontWeight(aValue) &&
10087 aValue.GetUnit() != eCSSUnit_Inherit &&
10088 aValue.GetUnit() != eCSSUnit_Initial &&
10089 aValue.GetUnit() != eCSSUnit_Unset &&
10090 (aValue.GetUnit() != eCSSUnit_Enumerated ||
10091 (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
10092 aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
10094 case eCSSFontDesc_Stretch:
10095 // property is VARIANT_HK|VARIANT_SYSFONT
10096 return ParseVariant(aValue, VARIANT_KEYWORD,
10097 nsCSSProps::kFontStretchKTable);
10099 // These two are unique to @font-face and have their own special grammar.
10100 case eCSSFontDesc_Src:
10101 return ParseFontSrc(aValue);
10103 case eCSSFontDesc_UnicodeRange:
10104 return ParseFontRanges(aValue);
10106 case eCSSFontDesc_FontFeatureSettings:
10107 return ParseFontFeatureSettings(aValue);
10109 case eCSSFontDesc_FontLanguageOverride:
10110 return ParseVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, nullptr);
10112 case eCSSFontDesc_UNKNOWN:
10113 case eCSSFontDesc_COUNT:
10114 NS_NOTREACHED("bad nsCSSFontDesc code");
10116 // explicitly do NOT have a default case to let the compiler
10117 // help find missing descriptors
10118 return false;
10121 void
10122 CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties)
10124 nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
10125 for (const nsCSSProperty *prop = aSourceProperties;
10126 *prop != eCSSProperty_UNKNOWN; ++prop) {
10127 AppendValue(*prop, physical);
10131 static nsCSSValue
10132 BoxPositionMaskToCSSValue(int32_t aMask, bool isX)
10134 int32_t val = NS_STYLE_BG_POSITION_CENTER;
10135 if (isX) {
10136 if (aMask & BG_LEFT) {
10137 val = NS_STYLE_BG_POSITION_LEFT;
10139 else if (aMask & BG_RIGHT) {
10140 val = NS_STYLE_BG_POSITION_RIGHT;
10143 else {
10144 if (aMask & BG_TOP) {
10145 val = NS_STYLE_BG_POSITION_TOP;
10147 else if (aMask & BG_BOTTOM) {
10148 val = NS_STYLE_BG_POSITION_BOTTOM;
10152 return nsCSSValue(val, eCSSUnit_Enumerated);
10155 bool
10156 CSSParserImpl::ParseBackground()
10158 nsAutoParseCompoundProperty compound(this);
10160 // background-color can only be set once, so it's not a list.
10161 nsCSSValue color;
10163 // Check first for inherit/initial/unset.
10164 if (ParseVariant(color, VARIANT_INHERIT, nullptr)) {
10165 // must be alone
10166 for (const nsCSSProperty* subprops =
10167 nsCSSProps::SubpropertyEntryFor(eCSSProperty_background);
10168 *subprops != eCSSProperty_UNKNOWN; ++subprops) {
10169 AppendValue(*subprops, color);
10171 return true;
10174 nsCSSValue image, repeat, attachment, clip, origin, position, size;
10175 BackgroundParseState state(color, image.SetListValue(),
10176 repeat.SetPairListValue(),
10177 attachment.SetListValue(), clip.SetListValue(),
10178 origin.SetListValue(), position.SetListValue(),
10179 size.SetPairListValue());
10181 for (;;) {
10182 if (!ParseBackgroundItem(state)) {
10183 return false;
10185 // If we saw a color, this must be the last item.
10186 if (color.GetUnit() != eCSSUnit_Null) {
10187 break;
10189 // If there's a comma, expect another item.
10190 if (!ExpectSymbol(',', true)) {
10191 break;
10193 // Chain another entry on all the lists.
10194 state.mImage->mNext = new nsCSSValueList;
10195 state.mImage = state.mImage->mNext;
10196 state.mRepeat->mNext = new nsCSSValuePairList;
10197 state.mRepeat = state.mRepeat->mNext;
10198 state.mAttachment->mNext = new nsCSSValueList;
10199 state.mAttachment = state.mAttachment->mNext;
10200 state.mClip->mNext = new nsCSSValueList;
10201 state.mClip = state.mClip->mNext;
10202 state.mOrigin->mNext = new nsCSSValueList;
10203 state.mOrigin = state.mOrigin->mNext;
10204 state.mPosition->mNext = new nsCSSValueList;
10205 state.mPosition = state.mPosition->mNext;
10206 state.mSize->mNext = new nsCSSValuePairList;
10207 state.mSize = state.mSize->mNext;
10210 // If we get to this point without seeing a color, provide a default.
10211 if (color.GetUnit() == eCSSUnit_Null) {
10212 color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor);
10215 AppendValue(eCSSProperty_background_image, image);
10216 AppendValue(eCSSProperty_background_repeat, repeat);
10217 AppendValue(eCSSProperty_background_attachment, attachment);
10218 AppendValue(eCSSProperty_background_clip, clip);
10219 AppendValue(eCSSProperty_background_origin, origin);
10220 AppendValue(eCSSProperty_background_position, position);
10221 AppendValue(eCSSProperty_background_size, size);
10222 AppendValue(eCSSProperty_background_color, color);
10223 return true;
10226 // Parse one item of the background shorthand property.
10227 bool
10228 CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState)
10231 // Fill in the values that the shorthand will set if we don't find
10232 // other values.
10233 aState.mImage->mValue.SetNoneValue();
10234 aState.mRepeat->mXValue.SetIntValue(NS_STYLE_BG_REPEAT_REPEAT,
10235 eCSSUnit_Enumerated);
10236 aState.mRepeat->mYValue.Reset();
10237 aState.mAttachment->mValue.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL,
10238 eCSSUnit_Enumerated);
10239 aState.mClip->mValue.SetIntValue(NS_STYLE_BG_CLIP_BORDER,
10240 eCSSUnit_Enumerated);
10241 aState.mOrigin->mValue.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING,
10242 eCSSUnit_Enumerated);
10243 nsRefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4);
10244 aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array);
10245 positionArr->Item(1).SetPercentValue(0.0f);
10246 positionArr->Item(3).SetPercentValue(0.0f);
10247 aState.mSize->mXValue.SetAutoValue();
10248 aState.mSize->mYValue.SetAutoValue();
10250 bool haveColor = false,
10251 haveImage = false,
10252 haveRepeat = false,
10253 haveAttach = false,
10254 havePositionAndSize = false,
10255 haveOrigin = false,
10256 haveSomething = false;
10258 while (GetToken(true)) {
10259 nsCSSTokenType tt = mToken.mType;
10260 UngetToken(); // ...but we'll still cheat and use mToken
10261 if (tt == eCSSToken_Symbol) {
10262 // ExpectEndProperty only looks for symbols, and nothing else will
10263 // show up as one.
10264 break;
10267 if (tt == eCSSToken_Ident) {
10268 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
10269 int32_t dummy;
10270 if (keyword == eCSSKeyword_inherit ||
10271 keyword == eCSSKeyword_initial ||
10272 keyword == eCSSKeyword_unset) {
10273 return false;
10274 } else if (keyword == eCSSKeyword_none) {
10275 if (haveImage)
10276 return false;
10277 haveImage = true;
10278 if (!ParseSingleValueProperty(aState.mImage->mValue,
10279 eCSSProperty_background_image)) {
10280 NS_NOTREACHED("should be able to parse");
10281 return false;
10283 } else if (nsCSSProps::FindKeyword(keyword,
10284 nsCSSProps::kBackgroundAttachmentKTable, dummy)) {
10285 if (haveAttach)
10286 return false;
10287 haveAttach = true;
10288 if (!ParseSingleValueProperty(aState.mAttachment->mValue,
10289 eCSSProperty_background_attachment)) {
10290 NS_NOTREACHED("should be able to parse");
10291 return false;
10293 } else if (nsCSSProps::FindKeyword(keyword,
10294 nsCSSProps::kBackgroundRepeatKTable, dummy)) {
10295 if (haveRepeat)
10296 return false;
10297 haveRepeat = true;
10298 nsCSSValuePair scratch;
10299 if (!ParseBackgroundRepeatValues(scratch)) {
10300 NS_NOTREACHED("should be able to parse");
10301 return false;
10303 aState.mRepeat->mXValue = scratch.mXValue;
10304 aState.mRepeat->mYValue = scratch.mYValue;
10305 } else if (nsCSSProps::FindKeyword(keyword,
10306 nsCSSProps::kBackgroundPositionKTable, dummy)) {
10307 if (havePositionAndSize)
10308 return false;
10309 havePositionAndSize = true;
10310 if (!ParsePositionValue(aState.mPosition->mValue)) {
10311 return false;
10313 if (ExpectSymbol('/', true)) {
10314 nsCSSValuePair scratch;
10315 if (!ParseBackgroundSizeValues(scratch)) {
10316 return false;
10318 aState.mSize->mXValue = scratch.mXValue;
10319 aState.mSize->mYValue = scratch.mYValue;
10321 } else if (nsCSSProps::FindKeyword(keyword,
10322 nsCSSProps::kBackgroundOriginKTable, dummy)) {
10323 if (haveOrigin)
10324 return false;
10325 haveOrigin = true;
10326 if (!ParseSingleValueProperty(aState.mOrigin->mValue,
10327 eCSSProperty_background_origin)) {
10328 NS_NOTREACHED("should be able to parse");
10329 return false;
10332 // The spec allows a second box value (for background-clip),
10333 // immediately following the first one (for background-origin).
10335 // 'background-clip' and 'background-origin' use the same keyword table
10336 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
10337 eCSSProperty_background_origin] ==
10338 nsCSSProps::kBackgroundOriginKTable);
10339 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
10340 eCSSProperty_background_clip] ==
10341 nsCSSProps::kBackgroundOriginKTable);
10342 static_assert(NS_STYLE_BG_CLIP_BORDER ==
10343 NS_STYLE_BG_ORIGIN_BORDER &&
10344 NS_STYLE_BG_CLIP_PADDING ==
10345 NS_STYLE_BG_ORIGIN_PADDING &&
10346 NS_STYLE_BG_CLIP_CONTENT ==
10347 NS_STYLE_BG_ORIGIN_CONTENT,
10348 "bg-clip and bg-origin style constants must agree");
10350 if (!ParseSingleValueProperty(aState.mClip->mValue,
10351 eCSSProperty_background_clip)) {
10352 // When exactly one <box> value is set, it is used for both
10353 // 'background-origin' and 'background-clip'.
10354 // See assertions above showing these values are compatible.
10355 aState.mClip->mValue = aState.mOrigin->mValue;
10357 } else {
10358 if (haveColor)
10359 return false;
10360 haveColor = true;
10361 if (!ParseSingleValueProperty(aState.mColor,
10362 eCSSProperty_background_color)) {
10363 return false;
10366 } else if (tt == eCSSToken_URL ||
10367 (tt == eCSSToken_Function &&
10368 (mToken.mIdent.LowerCaseEqualsLiteral("linear-gradient") ||
10369 mToken.mIdent.LowerCaseEqualsLiteral("radial-gradient") ||
10370 mToken.mIdent.LowerCaseEqualsLiteral("repeating-linear-gradient") ||
10371 mToken.mIdent.LowerCaseEqualsLiteral("repeating-radial-gradient") ||
10372 mToken.mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
10373 mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
10374 mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
10375 mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
10376 mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect") ||
10377 mToken.mIdent.LowerCaseEqualsLiteral("-moz-element")))) {
10378 if (haveImage)
10379 return false;
10380 haveImage = true;
10381 if (!ParseSingleValueProperty(aState.mImage->mValue,
10382 eCSSProperty_background_image)) {
10383 return false;
10385 } else if (tt == eCSSToken_Dimension ||
10386 tt == eCSSToken_Number ||
10387 tt == eCSSToken_Percentage ||
10388 (tt == eCSSToken_Function &&
10389 (mToken.mIdent.LowerCaseEqualsLiteral("calc") ||
10390 mToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")))) {
10391 if (havePositionAndSize)
10392 return false;
10393 havePositionAndSize = true;
10394 if (!ParsePositionValue(aState.mPosition->mValue)) {
10395 return false;
10397 if (ExpectSymbol('/', true)) {
10398 nsCSSValuePair scratch;
10399 if (!ParseBackgroundSizeValues(scratch)) {
10400 return false;
10402 aState.mSize->mXValue = scratch.mXValue;
10403 aState.mSize->mYValue = scratch.mYValue;
10405 } else {
10406 if (haveColor)
10407 return false;
10408 haveColor = true;
10409 // Note: This parses 'inherit', 'initial' and 'unset', but
10410 // we've already checked for them, so it's ok.
10411 if (!ParseSingleValueProperty(aState.mColor,
10412 eCSSProperty_background_color)) {
10413 return false;
10416 haveSomething = true;
10419 return haveSomething;
10422 // This function is very similar to ParseBackgroundPosition and
10423 // ParseBackgroundSize.
10424 bool
10425 CSSParserImpl::ParseValueList(nsCSSProperty aPropID)
10427 // aPropID is a single value prop-id
10428 nsCSSValue value;
10429 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10430 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10431 nsCSSValueList* item = value.SetListValue();
10432 for (;;) {
10433 if (!ParseSingleValueProperty(item->mValue, aPropID)) {
10434 return false;
10436 if (!ExpectSymbol(',', true)) {
10437 break;
10439 item->mNext = new nsCSSValueList;
10440 item = item->mNext;
10443 AppendValue(aPropID, value);
10444 return true;
10447 bool
10448 CSSParserImpl::ParseBackgroundRepeat()
10450 nsCSSValue value;
10451 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10452 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10453 nsCSSValuePair valuePair;
10454 if (!ParseBackgroundRepeatValues(valuePair)) {
10455 return false;
10457 nsCSSValuePairList* item = value.SetPairListValue();
10458 for (;;) {
10459 item->mXValue = valuePair.mXValue;
10460 item->mYValue = valuePair.mYValue;
10461 if (!ExpectSymbol(',', true)) {
10462 break;
10464 if (!ParseBackgroundRepeatValues(valuePair)) {
10465 return false;
10467 item->mNext = new nsCSSValuePairList;
10468 item = item->mNext;
10472 AppendValue(eCSSProperty_background_repeat, value);
10473 return true;
10476 bool
10477 CSSParserImpl::ParseBackgroundRepeatValues(nsCSSValuePair& aValue)
10479 nsCSSValue& xValue = aValue.mXValue;
10480 nsCSSValue& yValue = aValue.mYValue;
10482 if (ParseEnum(xValue, nsCSSProps::kBackgroundRepeatKTable)) {
10483 int32_t value = xValue.GetIntValue();
10484 // For single values set yValue as eCSSUnit_Null.
10485 if (value == NS_STYLE_BG_REPEAT_REPEAT_X ||
10486 value == NS_STYLE_BG_REPEAT_REPEAT_Y ||
10487 !ParseEnum(yValue, nsCSSProps::kBackgroundRepeatPartKTable)) {
10488 // the caller will fail cases like "repeat-x no-repeat"
10489 // by expecting a list separator or an end property.
10490 yValue.Reset();
10492 return true;
10495 return false;
10498 // This function is very similar to ParseBackgroundList and ParseBackgroundSize.
10499 bool
10500 CSSParserImpl::ParseBackgroundPosition()
10502 nsCSSValue value;
10503 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10504 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10505 nsCSSValue itemValue;
10506 if (!ParsePositionValue(itemValue)) {
10507 return false;
10509 nsCSSValueList* item = value.SetListValue();
10510 for (;;) {
10511 item->mValue = itemValue;
10512 if (!ExpectSymbol(',', true)) {
10513 break;
10515 if (!ParsePositionValue(itemValue)) {
10516 return false;
10518 item->mNext = new nsCSSValueList;
10519 item = item->mNext;
10522 AppendValue(eCSSProperty_background_position, value);
10523 return true;
10527 * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used
10528 * for parsing the CSS 2.1 background-position syntax (which has at
10529 * most two values). (Compare to the css3-background syntax which
10530 * takes up to four values.) Some current CSS specifications that
10531 * use background-position-like syntax still use this old syntax.
10533 * Parses two values that correspond to positions in a box. These can be
10534 * values corresponding to percentages of the box, raw offsets, or keywords
10535 * like "top," "left center," etc.
10537 * @param aOut The nsCSSValuePair in which to place the result.
10538 * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are
10539 * legal values
10540 * @param aAllowExplicitCenter If true, 'center' is a legal value
10541 * @return Whether or not the operation succeeded.
10543 bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
10544 bool aAcceptsInherit,
10545 bool aAllowExplicitCenter)
10547 // First try a percentage or a length value
10548 nsCSSValue &xValue = aOut.mXValue,
10549 &yValue = aOut.mYValue;
10550 int32_t variantMask =
10551 (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC;
10552 if (ParseVariant(xValue, variantMask, nullptr)) {
10553 if (eCSSUnit_Inherit == xValue.GetUnit() ||
10554 eCSSUnit_Initial == xValue.GetUnit() ||
10555 eCSSUnit_Unset == xValue.GetUnit()) { // both are inherit, initial or unset
10556 yValue = xValue;
10557 return true;
10559 // We have one percentage/length/calc. Get the optional second
10560 // percentage/length/calc/keyword.
10561 if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
10562 // We have two numbers
10563 return true;
10566 if (ParseEnum(yValue, nsCSSProps::kBackgroundPositionKTable)) {
10567 int32_t yVal = yValue.GetIntValue();
10568 if (!(yVal & BG_CTB)) {
10569 // The second keyword can only be 'center', 'top', or 'bottom'
10570 return false;
10572 yValue = BoxPositionMaskToCSSValue(yVal, false);
10573 return true;
10576 // If only one percentage or length value is given, it sets the
10577 // horizontal position only, and the vertical position will be 50%.
10578 yValue.SetPercentValue(0.5f);
10579 return true;
10582 // Now try keywords. We do this manually to allow for the first
10583 // appearance of "center" to apply to the either the x or y
10584 // position (it's ambiguous so we have to disambiguate). Each
10585 // allowed keyword value is assigned it's own bit. We don't allow
10586 // any duplicate keywords other than center. We try to get two
10587 // keywords but it's okay if there is only one.
10588 int32_t mask = 0;
10589 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
10590 int32_t bit = xValue.GetIntValue();
10591 mask |= bit;
10592 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
10593 bit = xValue.GetIntValue();
10594 if (mask & (bit & ~BG_CENTER)) {
10595 // Only the 'center' keyword can be duplicated.
10596 return false;
10598 mask |= bit;
10600 else {
10601 // Only one keyword. See if we have a length, percentage, or calc.
10602 if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
10603 if (!(mask & BG_CLR)) {
10604 // The first keyword can only be 'center', 'left', or 'right'
10605 return false;
10608 xValue = BoxPositionMaskToCSSValue(mask, true);
10609 return true;
10614 // Check for bad input. Bad input consists of no matching keywords,
10615 // or pairs of x keywords or pairs of y keywords.
10616 if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
10617 (mask == (BG_LEFT | BG_RIGHT)) ||
10618 (!aAllowExplicitCenter && (mask & BG_CENTER))) {
10619 return false;
10622 // Create style values
10623 xValue = BoxPositionMaskToCSSValue(mask, true);
10624 yValue = BoxPositionMaskToCSSValue(mask, false);
10625 return true;
10628 // Parses a CSS <position> value, for e.g. the 'background-position' property.
10629 // Spec reference: http://www.w3.org/TR/css3-background/#ltpositiongt
10630 bool
10631 CSSParserImpl::ParsePositionValue(nsCSSValue& aOut)
10633 nsRefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(4);
10634 aOut.SetArrayValue(value, eCSSUnit_Array);
10636 // The following clarifies organisation of the array.
10637 nsCSSValue &xEdge = value->Item(0),
10638 &xOffset = value->Item(1),
10639 &yEdge = value->Item(2),
10640 &yOffset = value->Item(3);
10642 // Parse all the values into the array.
10643 uint32_t valueCount = 0;
10644 for (int32_t i = 0; i < 4; i++) {
10645 if (!ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD,
10646 nsCSSProps::kBackgroundPositionKTable)) {
10647 break;
10649 ++valueCount;
10652 switch (valueCount) {
10653 case 4:
10654 // "If three or four values are given, then each <percentage> or <length>
10655 // represents an offset and must be preceded by a keyword, which specifies
10656 // from which edge the offset is given."
10657 if (eCSSUnit_Enumerated != xEdge.GetUnit() ||
10658 BG_CENTER == xEdge.GetIntValue() ||
10659 eCSSUnit_Enumerated == xOffset.GetUnit() ||
10660 eCSSUnit_Enumerated != yEdge.GetUnit() ||
10661 BG_CENTER == yEdge.GetIntValue() ||
10662 eCSSUnit_Enumerated == yOffset.GetUnit()) {
10663 return false;
10665 break;
10666 case 3:
10667 // "If three or four values are given, then each <percentage> or<length>
10668 // represents an offset and must be preceded by a keyword, which specifies
10669 // from which edge the offset is given." ... "If three values are given,
10670 // the missing offset is assumed to be zero."
10671 if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) {
10672 // keyword offset keyword
10673 // Second value is non-keyword, thus first value must be a non-center
10674 // keyword.
10675 if (eCSSUnit_Enumerated != value->Item(0).GetUnit() ||
10676 BG_CENTER == value->Item(0).GetIntValue()) {
10677 return false;
10680 // Remaining value must be a keyword.
10681 if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
10682 return false;
10685 yOffset.Reset(); // Everything else is in the correct position.
10686 } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
10687 // keyword keyword offset
10688 // Third value is non-keyword, thus second value must be non-center
10689 // keyword.
10690 if (BG_CENTER == value->Item(1).GetIntValue()) {
10691 return false;
10694 // Remaining value must be a keyword.
10695 if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) {
10696 return false;
10699 // Move the values to the correct position in the array.
10700 value->Item(3) = value->Item(2); // yOffset
10701 value->Item(2) = value->Item(1); // yEdge
10702 value->Item(1).Reset(); // xOffset
10703 } else {
10704 return false;
10706 break;
10707 case 2:
10708 // "If two values are given and at least one value is not a keyword, then
10709 // the first value represents the horizontal position (or offset) and the
10710 // second represents the vertical position (or offset)"
10711 if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
10712 if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
10713 // keyword keyword
10714 value->Item(2) = value->Item(1); // move yEdge to correct position
10715 xOffset.Reset();
10716 yOffset.Reset();
10717 } else {
10718 // keyword offset
10719 // First value must represent horizontal position.
10720 if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) {
10721 return false;
10723 value->Item(3) = value->Item(1); // move yOffset to correct position
10724 xOffset.Reset();
10725 yEdge.Reset();
10727 } else {
10728 if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
10729 // offset keyword
10730 // Second value must represent vertical position.
10731 if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) {
10732 return false;
10734 value->Item(2) = value->Item(1); // move yEdge to correct position
10735 value->Item(1) = value->Item(0); // move xOffset to correct position
10736 xEdge.Reset();
10737 yOffset.Reset();
10738 } else {
10739 // offset offset
10740 value->Item(3) = value->Item(1); // move yOffset to correct position
10741 value->Item(1) = value->Item(0); // move xOffset to correct position
10742 xEdge.Reset();
10743 yEdge.Reset();
10746 break;
10747 case 1:
10748 // "If only one value is specified, the second value is assumed to be
10749 // center."
10750 if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
10751 xOffset.Reset();
10752 } else {
10753 value->Item(1) = value->Item(0); // move xOffset to correct position
10754 xEdge.Reset();
10756 yEdge.SetIntValue(NS_STYLE_BG_POSITION_CENTER, eCSSUnit_Enumerated);
10757 yOffset.Reset();
10758 break;
10759 default:
10760 return false;
10763 // For compatibility with CSS2.1 code the edges can be unspecified.
10764 // Unspecified edges are recorded as nullptr.
10765 NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() ||
10766 eCSSUnit_Null == xEdge.GetUnit()) &&
10767 (eCSSUnit_Enumerated == yEdge.GetUnit() ||
10768 eCSSUnit_Null == yEdge.GetUnit()) &&
10769 eCSSUnit_Enumerated != xOffset.GetUnit() &&
10770 eCSSUnit_Enumerated != yOffset.GetUnit(),
10771 "Unexpected units");
10773 // Keywords in first and second pairs can not both be vertical or
10774 // horizontal keywords. (eg. left right, bottom top). Additionally,
10775 // non-center keyword can not be duplicated (eg. left left).
10776 int32_t xEdgeEnum =
10777 xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0;
10778 int32_t yEdgeEnum =
10779 yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0;
10780 if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) ||
10781 (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) ||
10782 (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) {
10783 return false;
10786 // The values could be in an order that is different than expected.
10787 // eg. x contains vertical information, y contains horizontal information.
10788 // Swap if incorrect order.
10789 if (xEdgeEnum & (BG_TOP | BG_BOTTOM) ||
10790 yEdgeEnum & (BG_LEFT | BG_RIGHT)) {
10791 nsCSSValue swapEdge = xEdge;
10792 nsCSSValue swapOffset = xOffset;
10793 xEdge = yEdge;
10794 xOffset = yOffset;
10795 yEdge = swapEdge;
10796 yOffset = swapOffset;
10799 return true;
10802 // This function is very similar to ParseBackgroundList and
10803 // ParseBackgroundPosition.
10804 bool
10805 CSSParserImpl::ParseBackgroundSize()
10807 nsCSSValue value;
10808 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10809 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10810 nsCSSValuePair valuePair;
10811 if (!ParseBackgroundSizeValues(valuePair)) {
10812 return false;
10814 nsCSSValuePairList* item = value.SetPairListValue();
10815 for (;;) {
10816 item->mXValue = valuePair.mXValue;
10817 item->mYValue = valuePair.mYValue;
10818 if (!ExpectSymbol(',', true)) {
10819 break;
10821 if (!ParseBackgroundSizeValues(valuePair)) {
10822 return false;
10824 item->mNext = new nsCSSValuePairList;
10825 item = item->mNext;
10828 AppendValue(eCSSProperty_background_size, value);
10829 return true;
10833 * Parses two values that correspond to lengths for the background-size
10834 * property. These can be one or two lengths (or the 'auto' keyword) or
10835 * percentages corresponding to the element's dimensions or the single keywords
10836 * 'contain' or 'cover'. 'initial', 'inherit' and 'unset' must be handled by
10837 * the caller if desired.
10839 * @param aOut The nsCSSValuePair in which to place the result.
10840 * @return Whether or not the operation succeeded.
10842 #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC)
10843 bool CSSParserImpl::ParseBackgroundSizeValues(nsCSSValuePair &aOut)
10845 // First try a percentage or a length value
10846 nsCSSValue &xValue = aOut.mXValue,
10847 &yValue = aOut.mYValue;
10848 if (ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr)) {
10849 // We have one percentage/length/calc/auto. Get the optional second
10850 // percentage/length/calc/keyword.
10851 if (ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr)) {
10852 // We have a second percentage/length/calc/auto.
10853 return true;
10856 // If only one percentage or length value is given, it sets the
10857 // horizontal size only, and the vertical size will be as if by 'auto'.
10858 yValue.SetAutoValue();
10859 return true;
10862 // Now address 'contain' and 'cover'.
10863 if (!ParseEnum(xValue, nsCSSProps::kBackgroundSizeKTable))
10864 return false;
10865 yValue.Reset();
10866 return true;
10868 #undef BG_SIZE_VARIANT
10870 bool
10871 CSSParserImpl::ParseBorderColor()
10873 static const nsCSSProperty kBorderColorSources[] = {
10874 eCSSProperty_border_left_color_ltr_source,
10875 eCSSProperty_border_left_color_rtl_source,
10876 eCSSProperty_border_right_color_ltr_source,
10877 eCSSProperty_border_right_color_rtl_source,
10878 eCSSProperty_UNKNOWN
10881 // do this now, in case 4 values weren't specified
10882 InitBoxPropsAsPhysical(kBorderColorSources);
10883 return ParseBoxProperties(kBorderColorIDs);
10886 void
10887 CSSParserImpl::SetBorderImageInitialValues()
10889 // border-image-source: none
10890 nsCSSValue source;
10891 source.SetNoneValue();
10892 AppendValue(eCSSProperty_border_image_source, source);
10894 // border-image-slice: 100%
10895 nsCSSValue sliceBoxValue;
10896 nsCSSRect& sliceBox = sliceBoxValue.SetRectValue();
10897 sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent));
10898 nsCSSValue slice;
10899 nsCSSValueList* sliceList = slice.SetListValue();
10900 sliceList->mValue = sliceBoxValue;
10901 AppendValue(eCSSProperty_border_image_slice, slice);
10903 // border-image-width: 1
10904 nsCSSValue width;
10905 nsCSSRect& widthBox = width.SetRectValue();
10906 widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number));
10907 AppendValue(eCSSProperty_border_image_width, width);
10909 // border-image-outset: 0
10910 nsCSSValue outset;
10911 nsCSSRect& outsetBox = outset.SetRectValue();
10912 outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number));
10913 AppendValue(eCSSProperty_border_image_outset, outset);
10915 // border-image-repeat: repeat
10916 nsCSSValue repeat;
10917 nsCSSValuePair repeatPair;
10918 repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
10919 eCSSUnit_Enumerated));
10920 repeat.SetPairValue(&repeatPair);
10921 AppendValue(eCSSProperty_border_image_repeat, repeat);
10924 bool
10925 CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
10926 bool* aConsumedTokens)
10928 // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
10929 nsCSSValue value;
10931 if (aConsumedTokens) {
10932 *aConsumedTokens = true;
10935 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10936 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10937 // are done.
10938 AppendValue(eCSSProperty_border_image_slice, value);
10939 return true;
10942 // Try parsing "fill" value.
10943 nsCSSValue imageSliceFillValue;
10944 bool hasFill = ParseEnum(imageSliceFillValue,
10945 nsCSSProps::kBorderImageSliceKTable);
10947 // Parse the box dimensions.
10948 nsCSSValue imageSliceBoxValue;
10949 if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue)) {
10950 if (!hasFill && aConsumedTokens) {
10951 *aConsumedTokens = false;
10954 return false;
10957 // Try parsing "fill" keyword again if the first time failed because keyword
10958 // and slice dimensions can be in any order.
10959 if (!hasFill) {
10960 hasFill = ParseEnum(imageSliceFillValue,
10961 nsCSSProps::kBorderImageSliceKTable);
10964 nsCSSValueList* borderImageSlice = value.SetListValue();
10965 // Put the box value into the list.
10966 borderImageSlice->mValue = imageSliceBoxValue;
10968 if (hasFill) {
10969 // Put the "fill" value into the list.
10970 borderImageSlice->mNext = new nsCSSValueList;
10971 borderImageSlice->mNext->mValue = imageSliceFillValue;
10974 AppendValue(eCSSProperty_border_image_slice, value);
10975 return true;
10978 bool
10979 CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
10981 // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
10982 nsCSSValue value;
10984 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10985 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10986 // are done.
10987 AppendValue(eCSSProperty_border_image_width, value);
10988 return true;
10991 // Parse the box dimensions.
10992 if (!ParseGroupedBoxProperty(VARIANT_ALPN, value)) {
10993 return false;
10996 AppendValue(eCSSProperty_border_image_width, value);
10997 return true;
11000 bool
11001 CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
11003 // border-image-outset: initial | [<length>|<number>]{1,4}
11004 nsCSSValue value;
11006 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
11007 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
11008 // are done.
11009 AppendValue(eCSSProperty_border_image_outset, value);
11010 return true;
11013 // Parse the box dimensions.
11014 if (!ParseGroupedBoxProperty(VARIANT_LN, value)) {
11015 return false;
11018 AppendValue(eCSSProperty_border_image_outset, value);
11019 return true;
11022 bool
11023 CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit)
11025 nsCSSValue value;
11026 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
11027 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
11028 // are done.
11029 AppendValue(eCSSProperty_border_image_repeat, value);
11030 return true;
11033 nsCSSValuePair result;
11034 if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) {
11035 return false;
11038 // optional second keyword, defaults to first
11039 if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) {
11040 result.mYValue = result.mXValue;
11043 value.SetPairValue(&result);
11044 AppendValue(eCSSProperty_border_image_repeat, value);
11045 return true;
11048 bool
11049 CSSParserImpl::ParseBorderImage()
11051 nsAutoParseCompoundProperty compound(this);
11053 // border-image: inherit | initial |
11054 // <border-image-source> ||
11055 // <border-image-slice>
11056 // [ / <border-image-width> |
11057 // / <border-image-width>? / <border-image-outset> ]? ||
11058 // <border-image-repeat>
11060 nsCSSValue value;
11061 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
11062 AppendValue(eCSSProperty_border_image_source, value);
11063 AppendValue(eCSSProperty_border_image_slice, value);
11064 AppendValue(eCSSProperty_border_image_width, value);
11065 AppendValue(eCSSProperty_border_image_outset, value);
11066 AppendValue(eCSSProperty_border_image_repeat, value);
11067 // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done.
11068 return true;
11071 // No empty property.
11072 if (CheckEndProperty()) {
11073 return false;
11076 // Shorthand properties are required to set everything they can.
11077 SetBorderImageInitialValues();
11079 bool foundSource = false;
11080 bool foundSliceWidthOutset = false;
11081 bool foundRepeat = false;
11083 // This loop is used to handle the parsing of border-image properties which
11084 // can appear in any order.
11085 nsCSSValue imageSourceValue;
11086 while (!CheckEndProperty()) {
11087 // <border-image-source>
11088 if (!foundSource && ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) {
11089 AppendValue(eCSSProperty_border_image_source, imageSourceValue);
11090 foundSource = true;
11091 continue;
11094 // <border-image-slice>
11095 // ParseBorderImageSlice is weird. It may consume tokens and then return
11096 // false, because it parses a property with two required components that
11097 // can appear in either order. Since the tokens that were consumed cannot
11098 // parse as anything else we care about, this isn't a problem.
11099 if (!foundSliceWidthOutset) {
11100 bool sliceConsumedTokens = false;
11101 if (ParseBorderImageSlice(false, &sliceConsumedTokens)) {
11102 foundSliceWidthOutset = true;
11104 // [ / <border-image-width>?
11105 if (ExpectSymbol('/', true)) {
11106 bool foundBorderImageWidth = ParseBorderImageWidth(false);
11108 // [ / <border-image-outset>
11109 if (ExpectSymbol('/', true)) {
11110 if (!ParseBorderImageOutset(false)) {
11111 return false;
11113 } else if (!foundBorderImageWidth) {
11114 // If this part has an trailing slash, the whole declaration is
11115 // invalid.
11116 return false;
11120 continue;
11121 } else {
11122 // If we consumed some tokens for <border-image-slice> but did not
11123 // successfully parse it, we have an error.
11124 if (sliceConsumedTokens) {
11125 return false;
11130 // <border-image-repeat>
11131 if (!foundRepeat && ParseBorderImageRepeat(false)) {
11132 foundRepeat = true;
11133 continue;
11136 return false;
11139 return true;
11142 bool
11143 CSSParserImpl::ParseBorderSpacing()
11145 nsCSSValue xValue, yValue;
11146 if (!ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr)) {
11147 return false;
11150 // If we have one length, get the optional second length.
11151 // set the second value equal to the first.
11152 if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) {
11153 ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC, nullptr);
11156 if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) {
11157 AppendValue(eCSSProperty_border_spacing, xValue);
11158 } else {
11159 nsCSSValue pair;
11160 pair.SetPairValue(xValue, yValue);
11161 AppendValue(eCSSProperty_border_spacing, pair);
11163 return true;
11166 bool
11167 CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[],
11168 bool aSetAllSides)
11170 const int32_t numProps = 3;
11171 nsCSSValue values[numProps];
11173 int32_t found = ParseChoice(values, aPropIDs, numProps);
11174 if (found < 1) {
11175 return false;
11178 if ((found & 1) == 0) { // Provide default border-width
11179 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
11181 if ((found & 2) == 0) { // Provide default border-style
11182 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
11184 if ((found & 4) == 0) { // text color will be used
11185 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
11188 if (aSetAllSides) {
11189 static const nsCSSProperty kBorderSources[] = {
11190 eCSSProperty_border_left_color_ltr_source,
11191 eCSSProperty_border_left_color_rtl_source,
11192 eCSSProperty_border_right_color_ltr_source,
11193 eCSSProperty_border_right_color_rtl_source,
11194 eCSSProperty_border_left_style_ltr_source,
11195 eCSSProperty_border_left_style_rtl_source,
11196 eCSSProperty_border_right_style_ltr_source,
11197 eCSSProperty_border_right_style_rtl_source,
11198 eCSSProperty_border_left_width_ltr_source,
11199 eCSSProperty_border_left_width_rtl_source,
11200 eCSSProperty_border_right_width_ltr_source,
11201 eCSSProperty_border_right_width_rtl_source,
11202 eCSSProperty_UNKNOWN
11205 InitBoxPropsAsPhysical(kBorderSources);
11207 // Parsing "border" shorthand; set all four sides to the same thing
11208 for (int32_t index = 0; index < 4; index++) {
11209 NS_ASSERTION(numProps == 3, "This code needs updating");
11210 AppendValue(kBorderWidthIDs[index], values[0]);
11211 AppendValue(kBorderStyleIDs[index], values[1]);
11212 AppendValue(kBorderColorIDs[index], values[2]);
11215 static const nsCSSProperty kBorderColorsProps[] = {
11216 eCSSProperty_border_top_colors,
11217 eCSSProperty_border_right_colors,
11218 eCSSProperty_border_bottom_colors,
11219 eCSSProperty_border_left_colors
11222 // Set the other properties that the border shorthand sets to their
11223 // initial values.
11224 nsCSSValue extraValue;
11225 switch (values[0].GetUnit()) {
11226 case eCSSUnit_Inherit:
11227 case eCSSUnit_Initial:
11228 case eCSSUnit_Unset:
11229 extraValue = values[0];
11230 // Set value of border-image properties to initial/inherit/unset
11231 AppendValue(eCSSProperty_border_image_source, extraValue);
11232 AppendValue(eCSSProperty_border_image_slice, extraValue);
11233 AppendValue(eCSSProperty_border_image_width, extraValue);
11234 AppendValue(eCSSProperty_border_image_outset, extraValue);
11235 AppendValue(eCSSProperty_border_image_repeat, extraValue);
11236 break;
11237 default:
11238 extraValue.SetNoneValue();
11239 SetBorderImageInitialValues();
11240 break;
11242 NS_FOR_CSS_SIDES(side) {
11243 AppendValue(kBorderColorsProps[side], extraValue);
11246 else {
11247 // Just set our one side
11248 for (int32_t index = 0; index < numProps; index++) {
11249 AppendValue(aPropIDs[index], values[index]);
11252 return true;
11255 bool
11256 CSSParserImpl::ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
11257 int32_t aSourceType)
11259 const int32_t numProps = 3;
11260 nsCSSValue values[numProps];
11262 int32_t found = ParseChoice(values, aPropIDs, numProps);
11263 if (found < 1) {
11264 return false;
11267 if ((found & 1) == 0) { // Provide default border-width
11268 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
11270 if ((found & 2) == 0) { // Provide default border-style
11271 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
11273 if ((found & 4) == 0) { // text color will be used
11274 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
11276 for (int32_t index = 0; index < numProps; index++) {
11277 const nsCSSProperty* subprops =
11278 nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]);
11279 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
11280 "not box property with physical vs. logical cascading");
11281 AppendValue(subprops[0], values[index]);
11282 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
11283 AppendValue(subprops[1], typeVal);
11284 AppendValue(subprops[2], typeVal);
11286 return true;
11289 bool
11290 CSSParserImpl::ParseBorderStyle()
11292 static const nsCSSProperty kBorderStyleSources[] = {
11293 eCSSProperty_border_left_style_ltr_source,
11294 eCSSProperty_border_left_style_rtl_source,
11295 eCSSProperty_border_right_style_ltr_source,
11296 eCSSProperty_border_right_style_rtl_source,
11297 eCSSProperty_UNKNOWN
11300 // do this now, in case 4 values weren't specified
11301 InitBoxPropsAsPhysical(kBorderStyleSources);
11302 return ParseBoxProperties(kBorderStyleIDs);
11305 bool
11306 CSSParserImpl::ParseBorderWidth()
11308 static const nsCSSProperty kBorderWidthSources[] = {
11309 eCSSProperty_border_left_width_ltr_source,
11310 eCSSProperty_border_left_width_rtl_source,
11311 eCSSProperty_border_right_width_ltr_source,
11312 eCSSProperty_border_right_width_rtl_source,
11313 eCSSProperty_UNKNOWN
11316 // do this now, in case 4 values weren't specified
11317 InitBoxPropsAsPhysical(kBorderWidthSources);
11318 return ParseBoxProperties(kBorderWidthIDs);
11321 bool
11322 CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty)
11324 nsCSSValue value;
11325 // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
11326 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
11327 nsCSSValueList *cur = value.SetListValue();
11328 for (;;) {
11329 if (!ParseVariant(cur->mValue, VARIANT_COLOR | VARIANT_KEYWORD,
11330 nsCSSProps::kBorderColorKTable)) {
11331 return false;
11333 if (CheckEndProperty()) {
11334 break;
11336 cur->mNext = new nsCSSValueList;
11337 cur = cur->mNext;
11340 AppendValue(aProperty, value);
11341 return true;
11344 // Parse the top level of a calc() expression.
11345 bool
11346 CSSParserImpl::ParseCalc(nsCSSValue &aValue, int32_t aVariantMask)
11348 // Parsing calc expressions requires, in a number of cases, looking
11349 // for a token that is *either* a value of the property or a number.
11350 // This can be done without lookahead when we assume that the property
11351 // values cannot themselves be numbers.
11352 NS_ASSERTION(!(aVariantMask & VARIANT_NUMBER), "unexpected variant mask");
11353 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
11355 bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk;
11356 mUnitlessLengthQuirk = false;
11358 // One-iteration loop so we can break to the error-handling case.
11359 do {
11360 // The toplevel of a calc() is always an nsCSSValue::Array of length 1.
11361 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
11363 if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask))
11364 break;
11366 if (!ExpectSymbol(')', true))
11367 break;
11369 aValue.SetArrayValue(arr, eCSSUnit_Calc);
11370 mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
11371 return true;
11372 } while (false);
11374 SkipUntil(')');
11375 mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
11376 return false;
11379 // We optimize away the <value-expression> production given that
11380 // ParseVariant consumes initial whitespace and we call
11381 // ExpectSymbol(')') with true for aSkipWS.
11382 // * If aVariantMask is VARIANT_NUMBER, this function parses the
11383 // <number-additive-expression> production.
11384 // * If aVariantMask does not contain VARIANT_NUMBER, this function
11385 // parses the <value-additive-expression> production.
11386 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
11387 // whichever one of the productions matches ***and modifies
11388 // aVariantMask*** to reflect which one it has parsed by either
11389 // removing VARIANT_NUMBER or removing all other bits.
11390 // It does so iteratively, but builds the correct recursive
11391 // data structure.
11392 bool
11393 CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue,
11394 int32_t& aVariantMask)
11396 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
11397 nsCSSValue *storage = &aValue;
11398 for (;;) {
11399 bool haveWS;
11400 if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS))
11401 return false;
11403 if (!haveWS || !GetToken(false))
11404 return true;
11405 nsCSSUnit unit;
11406 if (mToken.IsSymbol('+')) {
11407 unit = eCSSUnit_Calc_Plus;
11408 } else if (mToken.IsSymbol('-')) {
11409 unit = eCSSUnit_Calc_Minus;
11410 } else {
11411 UngetToken();
11412 return true;
11414 if (!RequireWhitespace())
11415 return false;
11417 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
11418 arr->Item(0) = aValue;
11419 storage = &arr->Item(1);
11420 aValue.SetArrayValue(arr, unit);
11424 struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
11425 public mozilla::css::CSSValueInputCalcOps
11427 result_type ComputeLeafValue(const nsCSSValue& aValue)
11429 NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
11430 return aValue.GetFloatValue();
11433 float ComputeNumber(const nsCSSValue& aValue)
11435 return mozilla::css::ComputeCalc(aValue, *this);
11439 // * If aVariantMask is VARIANT_NUMBER, this function parses the
11440 // <number-multiplicative-expression> production.
11441 // * If aVariantMask does not contain VARIANT_NUMBER, this function
11442 // parses the <value-multiplicative-expression> production.
11443 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
11444 // whichever one of the productions matches ***and modifies
11445 // aVariantMask*** to reflect which one it has parsed by either
11446 // removing VARIANT_NUMBER or removing all other bits.
11447 // It does so iteratively, but builds the correct recursive data
11448 // structure.
11449 // This function always consumes *trailing* whitespace when it returns
11450 // true; whether there was any such whitespace is returned in the
11451 // aHadFinalWS parameter.
11452 bool
11453 CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
11454 int32_t& aVariantMask,
11455 bool *aHadFinalWS)
11457 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
11458 bool gotValue = false; // already got the part with the unit
11459 bool afterDivision = false;
11461 nsCSSValue *storage = &aValue;
11462 for (;;) {
11463 int32_t variantMask;
11464 if (afterDivision || gotValue) {
11465 variantMask = VARIANT_NUMBER;
11466 } else {
11467 variantMask = aVariantMask | VARIANT_NUMBER;
11469 if (!ParseCalcTerm(*storage, variantMask))
11470 return false;
11471 NS_ABORT_IF_FALSE(variantMask != 0,
11472 "ParseCalcTerm did not set variantMask appropriately");
11473 NS_ABORT_IF_FALSE(!(variantMask & VARIANT_NUMBER) ||
11474 !(variantMask & ~int32_t(VARIANT_NUMBER)),
11475 "ParseCalcTerm did not set variantMask appropriately");
11477 if (variantMask & VARIANT_NUMBER) {
11478 // Simplify the value immediately so we can check for division by
11479 // zero.
11480 ReduceNumberCalcOps ops;
11481 float number = mozilla::css::ComputeCalc(*storage, ops);
11482 if (number == 0.0 && afterDivision)
11483 return false;
11484 storage->SetFloatValue(number, eCSSUnit_Number);
11485 } else {
11486 gotValue = true;
11488 if (storage != &aValue) {
11489 // Simplify any numbers in the Times_L position (which are
11490 // not simplified by the check above).
11491 NS_ABORT_IF_FALSE(storage == &aValue.GetArrayValue()->Item(1),
11492 "unexpected relationship to current storage");
11493 nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
11494 ReduceNumberCalcOps ops;
11495 float number = mozilla::css::ComputeCalc(leftValue, ops);
11496 leftValue.SetFloatValue(number, eCSSUnit_Number);
11500 bool hadWS = RequireWhitespace();
11501 if (!GetToken(false)) {
11502 *aHadFinalWS = hadWS;
11503 break;
11505 nsCSSUnit unit;
11506 if (mToken.IsSymbol('*')) {
11507 unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
11508 afterDivision = false;
11509 } else if (mToken.IsSymbol('/')) {
11510 unit = eCSSUnit_Calc_Divided;
11511 afterDivision = true;
11512 } else {
11513 UngetToken();
11514 *aHadFinalWS = hadWS;
11515 break;
11518 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
11519 arr->Item(0) = aValue;
11520 storage = &arr->Item(1);
11521 aValue.SetArrayValue(arr, unit);
11524 // Adjust aVariantMask (see comments above function) to reflect which
11525 // option we took.
11526 if (aVariantMask & VARIANT_NUMBER) {
11527 if (gotValue) {
11528 aVariantMask &= ~int32_t(VARIANT_NUMBER);
11529 } else {
11530 aVariantMask = VARIANT_NUMBER;
11532 } else {
11533 if (!gotValue) {
11534 // We had to find a value, but we didn't.
11535 return false;
11539 return true;
11542 // * If aVariantMask is VARIANT_NUMBER, this function parses the
11543 // <number-term> production.
11544 // * If aVariantMask does not contain VARIANT_NUMBER, this function
11545 // parses the <value-term> production.
11546 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
11547 // whichever one of the productions matches ***and modifies
11548 // aVariantMask*** to reflect which one it has parsed by either
11549 // removing VARIANT_NUMBER or removing all other bits.
11550 bool
11551 CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask)
11553 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
11554 if (!GetToken(true))
11555 return false;
11556 // Either an additive expression in parentheses...
11557 if (mToken.IsSymbol('(')) {
11558 if (!ParseCalcAdditiveExpression(aValue, aVariantMask) ||
11559 !ExpectSymbol(')', true)) {
11560 SkipUntil(')');
11561 return false;
11563 return true;
11565 // ... or just a value
11566 UngetToken();
11567 // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
11568 // always gets picked up
11569 if (!ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr)) {
11570 return false;
11572 // ...and do the VARIANT_NUMBER check ourselves.
11573 if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
11574 return false;
11576 // If we did the value parsing, we need to adjust aVariantMask to
11577 // reflect which option we took (see above).
11578 if (aVariantMask & VARIANT_NUMBER) {
11579 if (aValue.GetUnit() == eCSSUnit_Number) {
11580 aVariantMask = VARIANT_NUMBER;
11581 } else {
11582 aVariantMask &= ~int32_t(VARIANT_NUMBER);
11585 return true;
11588 // This function consumes all consecutive whitespace and returns whether
11589 // there was any.
11590 bool
11591 CSSParserImpl::RequireWhitespace()
11593 if (!GetToken(false))
11594 return false;
11595 if (mToken.mType != eCSSToken_Whitespace) {
11596 UngetToken();
11597 return false;
11599 // Skip any additional whitespace tokens.
11600 if (GetToken(true)) {
11601 UngetToken();
11603 return true;
11606 bool
11607 CSSParserImpl::ParseRect(nsCSSProperty aPropID)
11609 nsCSSValue val;
11610 if (ParseVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) {
11611 AppendValue(aPropID, val);
11612 return true;
11615 if (! GetToken(true)) {
11616 return false;
11619 if (mToken.mType == eCSSToken_Function &&
11620 mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
11621 nsCSSRect& rect = val.SetRectValue();
11622 bool useCommas;
11623 NS_FOR_CSS_SIDES(side) {
11624 if (! ParseVariant(rect.*(nsCSSRect::sides[side]),
11625 VARIANT_AL, nullptr)) {
11626 return false;
11628 if (side == 0) {
11629 useCommas = ExpectSymbol(',', true);
11630 } else if (useCommas && side < 3) {
11631 // Skip optional commas between elements, but only if the first
11632 // separator was a comma.
11633 if (!ExpectSymbol(',', true)) {
11634 return false;
11638 if (!ExpectSymbol(')', true)) {
11639 return false;
11641 } else {
11642 UngetToken();
11643 return false;
11646 AppendValue(aPropID, val);
11647 return true;
11650 bool
11651 CSSParserImpl::ParseColumns()
11653 // We use a similar "fake value" hack to ParseListStyle, because
11654 // "auto" is acceptable for both column-count and column-width.
11655 // If the fake "auto" value is found, and one of the real values isn't,
11656 // that means the fake auto value is meant for the real value we didn't
11657 // find.
11658 static const nsCSSProperty columnIDs[] = {
11659 eCSSPropertyExtra_x_auto_value,
11660 eCSSProperty__moz_column_count,
11661 eCSSProperty__moz_column_width
11663 const int32_t numProps = MOZ_ARRAY_LENGTH(columnIDs);
11665 nsCSSValue values[numProps];
11666 int32_t found = ParseChoice(values, columnIDs, numProps);
11667 if (found < 1) {
11668 return false;
11670 if ((found & (1|2|4)) == (1|2|4) &&
11671 values[0].GetUnit() == eCSSUnit_Auto) {
11672 // We filled all 3 values, which is invalid
11673 return false;
11676 if ((found & 2) == 0) {
11677 // Provide auto column-count
11678 values[1].SetAutoValue();
11680 if ((found & 4) == 0) {
11681 // Provide auto column-width
11682 values[2].SetAutoValue();
11685 // Start at index 1 to skip the fake auto value.
11686 for (int32_t index = 1; index < numProps; index++) {
11687 AppendValue(columnIDs[index], values[index]);
11689 return true;
11692 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
11693 VARIANT_KEYWORD)
11694 bool
11695 CSSParserImpl::ParseContent()
11697 // We need to divide the 'content' keywords into two classes for
11698 // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable.
11699 static const KTableValue kContentListKWs[] = {
11700 eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE,
11701 eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE,
11702 eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE,
11703 eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE,
11704 eCSSKeyword_UNKNOWN,-1
11707 static const KTableValue kContentSolitaryKWs[] = {
11708 eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT,
11709 eCSSKeyword_UNKNOWN,-1
11712 // Verify that these two lists add up to the size of
11713 // nsCSSProps::kContentKTable.
11714 NS_ABORT_IF_FALSE(nsCSSProps::kContentKTable[
11715 ArrayLength(kContentListKWs) +
11716 ArrayLength(kContentSolitaryKWs) - 4] ==
11717 eCSSKeyword_UNKNOWN &&
11718 nsCSSProps::kContentKTable[
11719 ArrayLength(kContentListKWs) +
11720 ArrayLength(kContentSolitaryKWs) - 3] == -1,
11721 "content keyword tables out of sync");
11723 nsCSSValue value;
11724 // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must
11725 // be alone
11726 if (!ParseVariant(value, VARIANT_HMK | VARIANT_NONE,
11727 kContentSolitaryKWs)) {
11728 nsCSSValueList* cur = value.SetListValue();
11729 for (;;) {
11730 if (!ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs)) {
11731 return false;
11733 if (CheckEndProperty()) {
11734 break;
11736 cur->mNext = new nsCSSValueList;
11737 cur = cur->mNext;
11740 AppendValue(eCSSProperty_content, value);
11741 return true;
11744 bool
11745 CSSParserImpl::ParseCounterData(nsCSSProperty aPropID)
11747 static const nsCSSKeyword kCounterDataKTable[] = {
11748 eCSSKeyword_none,
11749 eCSSKeyword_UNKNOWN
11751 nsCSSValue value;
11752 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
11753 if (!GetToken(true)) {
11754 return false;
11756 if (mToken.mType != eCSSToken_Ident) {
11757 UngetToken();
11758 return false;
11761 nsCSSValuePairList *cur = value.SetPairListValue();
11762 for (;;) {
11763 if (!ParseCustomIdent(cur->mXValue, mToken.mIdent, kCounterDataKTable)) {
11764 return false;
11766 if (!GetToken(true)) {
11767 break;
11769 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
11770 cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
11771 } else {
11772 UngetToken();
11774 if (!GetToken(true)) {
11775 break;
11777 if (mToken.mType != eCSSToken_Ident) {
11778 UngetToken();
11779 break;
11781 cur->mNext = new nsCSSValuePairList;
11782 cur = cur->mNext;
11785 AppendValue(aPropID, value);
11786 return true;
11789 bool
11790 CSSParserImpl::ParseCursor()
11792 nsCSSValue value;
11793 // 'inherit', 'initial' and 'unset' must be alone
11794 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
11795 nsCSSValueList* cur = value.SetListValue();
11796 for (;;) {
11797 if (!ParseVariant(cur->mValue, VARIANT_UK, nsCSSProps::kCursorKTable)) {
11798 return false;
11800 if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last
11801 break;
11804 // We have a URL, so make a value array with three values.
11805 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
11806 val->Item(0) = cur->mValue;
11808 // Parse optional x and y position of cursor hotspot (css3-ui).
11809 if (ParseVariant(val->Item(1), VARIANT_NUMBER, nullptr)) {
11810 // If we have one number, we must have two.
11811 if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nullptr)) {
11812 return false;
11815 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
11817 if (!ExpectSymbol(',', true)) { // url must not be last
11818 return false;
11820 cur->mNext = new nsCSSValueList;
11821 cur = cur->mNext;
11824 AppendValue(eCSSProperty_cursor, value);
11825 return true;
11829 bool
11830 CSSParserImpl::ParseFont()
11832 static const nsCSSProperty fontIDs[] = {
11833 eCSSProperty_font_style,
11834 eCSSProperty_font_variant_caps,
11835 eCSSProperty_font_weight
11838 nsCSSValue family;
11839 if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
11840 if (eCSSUnit_Inherit == family.GetUnit() ||
11841 eCSSUnit_Initial == family.GetUnit() ||
11842 eCSSUnit_Unset == family.GetUnit()) {
11843 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
11844 AppendValue(eCSSProperty_font_family, family);
11845 AppendValue(eCSSProperty_font_style, family);
11846 AppendValue(eCSSProperty_font_weight, family);
11847 AppendValue(eCSSProperty_font_size, family);
11848 AppendValue(eCSSProperty_line_height, family);
11849 AppendValue(eCSSProperty_font_stretch, family);
11850 AppendValue(eCSSProperty_font_size_adjust, family);
11851 AppendValue(eCSSProperty_font_feature_settings, family);
11852 AppendValue(eCSSProperty_font_language_override, family);
11853 AppendValue(eCSSProperty_font_kerning, family);
11854 AppendValue(eCSSProperty_font_synthesis, family);
11855 AppendValue(eCSSProperty_font_variant_alternates, family);
11856 AppendValue(eCSSProperty_font_variant_caps, family);
11857 AppendValue(eCSSProperty_font_variant_east_asian, family);
11858 AppendValue(eCSSProperty_font_variant_ligatures, family);
11859 AppendValue(eCSSProperty_font_variant_numeric, family);
11860 AppendValue(eCSSProperty_font_variant_position, family);
11862 else {
11863 AppendValue(eCSSProperty__x_system_font, family);
11864 nsCSSValue systemFont(eCSSUnit_System_Font);
11865 AppendValue(eCSSProperty_font_family, systemFont);
11866 AppendValue(eCSSProperty_font_style, systemFont);
11867 AppendValue(eCSSProperty_font_weight, systemFont);
11868 AppendValue(eCSSProperty_font_size, systemFont);
11869 AppendValue(eCSSProperty_line_height, systemFont);
11870 AppendValue(eCSSProperty_font_stretch, systemFont);
11871 AppendValue(eCSSProperty_font_size_adjust, systemFont);
11872 AppendValue(eCSSProperty_font_feature_settings, systemFont);
11873 AppendValue(eCSSProperty_font_language_override, systemFont);
11874 AppendValue(eCSSProperty_font_kerning, systemFont);
11875 AppendValue(eCSSProperty_font_synthesis, systemFont);
11876 AppendValue(eCSSProperty_font_variant_alternates, systemFont);
11877 AppendValue(eCSSProperty_font_variant_caps, systemFont);
11878 AppendValue(eCSSProperty_font_variant_east_asian, systemFont);
11879 AppendValue(eCSSProperty_font_variant_ligatures, systemFont);
11880 AppendValue(eCSSProperty_font_variant_numeric, systemFont);
11881 AppendValue(eCSSProperty_font_variant_position, systemFont);
11883 return true;
11886 // Get optional font-style, font-variant and font-weight (in any order)
11887 const int32_t numProps = 3;
11888 nsCSSValue values[numProps];
11889 int32_t found = ParseChoice(values, fontIDs, numProps);
11890 if (found < 0 ||
11891 eCSSUnit_Inherit == values[0].GetUnit() ||
11892 eCSSUnit_Initial == values[0].GetUnit() ||
11893 eCSSUnit_Unset == values[0].GetUnit()) { // illegal data
11894 return false;
11896 if ((found & 1) == 0) {
11897 // Provide default font-style
11898 values[0].SetIntValue(NS_FONT_STYLE_NORMAL, eCSSUnit_Enumerated);
11900 if ((found & 2) == 0) {
11901 // Provide default font-variant
11902 values[1].SetNormalValue();
11903 } else {
11904 if (values[1].GetUnit() == eCSSUnit_Enumerated &&
11905 values[1].GetIntValue() != NS_FONT_VARIANT_CAPS_SMALLCAPS) {
11906 // only normal or small-caps is allowed in font shorthand
11907 // this also assumes other values for font-variant-caps never overlap
11908 // possible values for style or weight
11909 return false;
11912 if ((found & 4) == 0) {
11913 // Provide default font-weight
11914 values[2].SetIntValue(NS_FONT_WEIGHT_NORMAL, eCSSUnit_Enumerated);
11917 // Get mandatory font-size
11918 nsCSSValue size;
11919 if (! ParseNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP,
11920 nsCSSProps::kFontSizeKTable)) {
11921 return false;
11924 // Get optional "/" line-height
11925 nsCSSValue lineHeight;
11926 if (ExpectSymbol('/', true)) {
11927 if (! ParseNonNegativeVariant(lineHeight,
11928 VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
11929 nullptr)) {
11930 return false;
11933 else {
11934 lineHeight.SetNormalValue();
11937 // Get final mandatory font-family
11938 nsAutoParseCompoundProperty compound(this);
11939 if (ParseFamily(family)) {
11940 if (eCSSUnit_Inherit != family.GetUnit() &&
11941 eCSSUnit_Initial != family.GetUnit() &&
11942 eCSSUnit_Unset != family.GetUnit()) {
11943 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
11944 AppendValue(eCSSProperty_font_family, family);
11945 AppendValue(eCSSProperty_font_style, values[0]);
11946 AppendValue(eCSSProperty_font_variant_caps, values[1]);
11947 AppendValue(eCSSProperty_font_weight, values[2]);
11948 AppendValue(eCSSProperty_font_size, size);
11949 AppendValue(eCSSProperty_line_height, lineHeight);
11950 AppendValue(eCSSProperty_font_stretch,
11951 nsCSSValue(NS_FONT_STRETCH_NORMAL, eCSSUnit_Enumerated));
11952 AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
11953 AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal));
11954 AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal));
11955 AppendValue(eCSSProperty_font_kerning,
11956 nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated));
11957 AppendValue(eCSSProperty_font_synthesis,
11958 nsCSSValue(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE,
11959 eCSSUnit_Enumerated));
11960 AppendValue(eCSSProperty_font_variant_alternates,
11961 nsCSSValue(eCSSUnit_Normal));
11962 AppendValue(eCSSProperty_font_variant_east_asian,
11963 nsCSSValue(eCSSUnit_Normal));
11964 AppendValue(eCSSProperty_font_variant_ligatures,
11965 nsCSSValue(eCSSUnit_Normal));
11966 AppendValue(eCSSProperty_font_variant_numeric,
11967 nsCSSValue(eCSSUnit_Normal));
11968 AppendValue(eCSSProperty_font_variant_position,
11969 nsCSSValue(eCSSUnit_Normal));
11970 return true;
11973 return false;
11976 bool
11977 CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue)
11979 if (!ParseVariant(aValue, VARIANT_HK | VARIANT_NONE,
11980 nsCSSProps::kFontSynthesisKTable)) {
11981 return false;
11984 // first value 'none' ==> done
11985 if (eCSSUnit_None == aValue.GetUnit() ||
11986 eCSSUnit_Initial == aValue.GetUnit() ||
11987 eCSSUnit_Inherit == aValue.GetUnit() ||
11988 eCSSUnit_Unset == aValue.GetUnit())
11990 return true;
11993 // look for a second value
11994 int32_t intValue = aValue.GetIntValue();
11995 nsCSSValue nextValue;
11997 if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) {
11998 int32_t nextIntValue = nextValue.GetIntValue();
11999 if (nextIntValue & intValue) {
12000 return false;
12002 aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
12005 return true;
12008 // font-variant-alternates allows for a combination of multiple
12009 // simple enumerated values and functional values. Functional values have
12010 // parameter lists with one or more idents which are later resolved
12011 // based on values defined in @font-feature-value rules.
12013 // font-variant-alternates: swash(flowing) historical-forms styleset(alt-g, alt-m);
12015 // So for this the nsCSSValue is set to a pair value, with one
12016 // value for a bitmask of both simple and functional property values
12017 // and another value containing a ValuePairList with lists of idents
12018 // for each functional property value.
12020 // pairValue
12021 // o intValue
12022 // NS_FONT_VARIANT_ALTERNATES_SWASH |
12023 // NS_FONT_VARIANT_ALTERNATES_STYLESET
12024 // o valuePairList, each element with
12025 // - intValue - indicates which alternate
12026 // - string or valueList of strings
12028 // Note: when only 'historical-forms' is specified, there are no
12029 // functional values to store, in which case the valuePairList is a
12030 // single element dummy list. In all other cases, the length of the
12031 // list will match the number of functional values.
12033 #define MAX_ALLOWED_FEATURES 512
12035 static uint16_t
12036 MaxElementsForAlternateType(nsCSSKeyword keyword)
12038 uint16_t maxElems = 1;
12039 if (keyword == eCSSKeyword_styleset ||
12040 keyword == eCSSKeyword_character_variant) {
12041 maxElems = MAX_ALLOWED_FEATURES;
12043 return maxElems;
12046 bool
12047 CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature,
12048 nsCSSValue& aValue)
12050 if (!GetToken(true)) {
12051 return false;
12054 bool isIdent = (mToken.mType == eCSSToken_Ident);
12055 if (mToken.mType != eCSSToken_Function && !isIdent) {
12056 UngetToken();
12057 return false;
12060 // ident ==> simple enumerated prop val (e.g. historical-forms)
12061 // function ==> e.g. swash(flowing) styleset(alt-g, alt-m)
12063 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
12064 if (!(eCSSKeyword_UNKNOWN < keyword &&
12065 nsCSSProps::FindKeyword(keyword,
12066 (isIdent ?
12067 nsCSSProps::kFontVariantAlternatesKTable :
12068 nsCSSProps::kFontVariantAlternatesFuncsKTable),
12069 aWhichFeature)))
12071 // failed, pop token
12072 UngetToken();
12073 return false;
12076 if (isIdent) {
12077 aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated);
12078 return true;
12081 return ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER,
12082 1, MaxElementsForAlternateType(keyword), aValue);
12085 bool
12086 CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue)
12088 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
12089 return true;
12092 // iterate through parameters
12093 nsCSSValue listValue;
12094 int32_t feature, featureFlags = 0;
12096 // if no functional values, this may be a list with a single, unused element
12097 listValue.SetListValue();
12099 nsCSSValueList* list = nullptr;
12100 nsCSSValue value;
12101 while (ParseSingleAlternate(feature, value)) {
12103 // check to make sure value not already set
12104 if (feature == 0 ||
12105 feature & featureFlags) {
12106 return false;
12109 featureFlags |= feature;
12111 // if function, need to add to the list of functions
12112 if (value.GetUnit() == eCSSUnit_Function) {
12113 if (!list) {
12114 list = listValue.GetListValue();
12115 } else {
12116 list->mNext = new nsCSSValueList;
12117 list = list->mNext;
12119 list->mValue = value;
12123 if (featureFlags == 0) {
12124 // ParseSingleAlternate failed the first time through the loop.
12125 return false;
12128 nsCSSValue featureValue;
12129 featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated);
12130 aValue.SetPairValue(featureValue, listValue);
12132 return true;
12135 bool
12136 CSSParserImpl::MergeBitmaskValue(int32_t aNewValue,
12137 const int32_t aMasks[],
12138 int32_t& aMergedValue)
12140 // check to make sure value not already set
12141 if (aNewValue & aMergedValue) {
12142 return false;
12145 const int32_t *m = aMasks;
12146 int32_t c = 0;
12148 while (*m != MASK_END_VALUE) {
12149 if (*m & aNewValue) {
12150 c = aMergedValue & *m;
12151 break;
12153 m++;
12156 if (c) {
12157 return false;
12160 aMergedValue |= aNewValue;
12161 return true;
12164 // aMasks - array of masks for mutually-exclusive property values,
12165 // e.g. proportial-nums, tabular-nums
12167 bool
12168 CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue,
12169 const KTableValue aKeywordTable[],
12170 const int32_t aMasks[])
12172 // Parse at least one keyword
12173 if (!ParseEnum(aValue, aKeywordTable)) {
12174 return false;
12177 // look for more values
12178 nsCSSValue nextValue;
12179 int32_t mergedValue = aValue.GetIntValue();
12181 while (ParseEnum(nextValue, aKeywordTable))
12183 if (!MergeBitmaskValue(nextValue.GetIntValue(), aMasks, mergedValue)) {
12184 return false;
12188 aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated);
12190 return true;
12193 static const int32_t maskEastAsian[] = {
12194 NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK,
12195 NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK,
12196 MASK_END_VALUE
12199 bool
12200 CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue)
12202 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
12203 return true;
12206 NS_ASSERTION(maskEastAsian[ArrayLength(maskEastAsian) - 1] ==
12207 MASK_END_VALUE,
12208 "incorrectly terminated array");
12210 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable,
12211 maskEastAsian);
12214 static const int32_t maskLigatures[] = {
12215 NS_FONT_VARIANT_LIGATURES_COMMON_MASK,
12216 NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK,
12217 NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK,
12218 NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK,
12219 MASK_END_VALUE
12222 bool
12223 CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue)
12225 if (ParseVariant(aValue,
12226 VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE,
12227 nullptr)) {
12228 return true;
12231 NS_ASSERTION(maskLigatures[ArrayLength(maskLigatures) - 1] ==
12232 MASK_END_VALUE,
12233 "incorrectly terminated array");
12235 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable,
12236 maskLigatures);
12239 static const int32_t maskNumeric[] = {
12240 NS_FONT_VARIANT_NUMERIC_FIGURE_MASK,
12241 NS_FONT_VARIANT_NUMERIC_SPACING_MASK,
12242 NS_FONT_VARIANT_NUMERIC_FRACTION_MASK,
12243 MASK_END_VALUE
12246 bool
12247 CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue)
12249 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
12250 return true;
12253 NS_ASSERTION(maskNumeric[ArrayLength(maskNumeric) - 1] ==
12254 MASK_END_VALUE,
12255 "incorrectly terminated array");
12257 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable,
12258 maskNumeric);
12261 bool
12262 CSSParserImpl::ParseFontVariant()
12264 // parse single values - normal/inherit/none
12265 nsCSSValue value;
12266 nsCSSValue normal(eCSSUnit_Normal);
12268 if (ParseVariant(value,
12269 VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE,
12270 nullptr)) {
12271 AppendValue(eCSSProperty_font_variant_ligatures, value);
12272 if (eCSSUnit_None == value.GetUnit()) {
12273 // 'none' applies the value 'normal' to all properties other
12274 // than 'font-variant-ligatures'
12275 value.SetNormalValue();
12277 AppendValue(eCSSProperty_font_variant_alternates, value);
12278 AppendValue(eCSSProperty_font_variant_caps, value);
12279 AppendValue(eCSSProperty_font_variant_east_asian, value);
12280 AppendValue(eCSSProperty_font_variant_numeric, value);
12281 AppendValue(eCSSProperty_font_variant_position, value);
12282 return true;
12285 // set each of the individual subproperties
12286 int32_t altFeatures = 0, capsFeatures = 0, eastAsianFeatures = 0,
12287 ligFeatures = 0, numericFeatures = 0, posFeatures = 0;
12288 nsCSSValue altListValue;
12289 nsCSSValueList* altList = nullptr;
12291 // if no functional values, this may be a list with a single, unused element
12292 altListValue.SetListValue();
12294 bool foundValid = false; // found at least one proper value
12295 while (GetToken(true)) {
12296 // only an ident or a function at this point
12297 bool isFunction = (mToken.mType == eCSSToken_Function);
12298 if (mToken.mType != eCSSToken_Ident && !isFunction) {
12299 UngetToken();
12300 break;
12303 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
12304 if (keyword == eCSSKeyword_UNKNOWN) {
12305 UngetToken();
12306 return false;
12309 int32_t feature;
12311 // function? ==> font-variant-alternates
12312 if (isFunction) {
12313 if (!nsCSSProps::FindKeyword(keyword,
12314 nsCSSProps::kFontVariantAlternatesFuncsKTable,
12315 feature) ||
12316 (feature & altFeatures)) {
12317 UngetToken();
12318 return false;
12321 altFeatures |= feature;
12322 nsCSSValue funcValue;
12323 if (!ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER, 1,
12324 MaxElementsForAlternateType(keyword), funcValue) ||
12325 funcValue.GetUnit() != eCSSUnit_Function) {
12326 UngetToken();
12327 return false;
12330 if (!altList) {
12331 altList = altListValue.GetListValue();
12332 } else {
12333 altList->mNext = new nsCSSValueList;
12334 altList = altList->mNext;
12336 altList->mValue = funcValue;
12337 } else if (nsCSSProps::FindKeyword(keyword,
12338 nsCSSProps::kFontVariantCapsKTable,
12339 feature)) {
12340 if (capsFeatures != 0) {
12341 // multiple values for font-variant-caps
12342 UngetToken();
12343 return false;
12345 capsFeatures = feature;
12346 } else if (nsCSSProps::FindKeyword(keyword,
12347 nsCSSProps::kFontVariantAlternatesKTable,
12348 feature)) {
12349 if (feature & altFeatures) {
12350 // same value repeated
12351 UngetToken();
12352 return false;
12354 altFeatures |= feature;
12355 } else if (nsCSSProps::FindKeyword(keyword,
12356 nsCSSProps::kFontVariantEastAsianKTable,
12357 feature)) {
12358 if (!MergeBitmaskValue(feature, maskEastAsian, eastAsianFeatures)) {
12359 // multiple mutually exclusive values
12360 UngetToken();
12361 return false;
12363 } else if (nsCSSProps::FindKeyword(keyword,
12364 nsCSSProps::kFontVariantLigaturesKTable,
12365 feature)) {
12366 if (keyword == eCSSKeyword_none ||
12367 !MergeBitmaskValue(feature, maskLigatures, ligFeatures)) {
12368 // none or multiple mutually exclusive values
12369 UngetToken();
12370 return false;
12372 } else if (nsCSSProps::FindKeyword(keyword,
12373 nsCSSProps::kFontVariantNumericKTable,
12374 feature)) {
12375 if (!MergeBitmaskValue(feature, maskNumeric, numericFeatures)) {
12376 // multiple mutually exclusive values
12377 UngetToken();
12378 return false;
12380 } else if (nsCSSProps::FindKeyword(keyword,
12381 nsCSSProps::kFontVariantPositionKTable,
12382 feature)) {
12383 if (posFeatures != 0) {
12384 // multiple values for font-variant-caps
12385 UngetToken();
12386 return false;
12388 posFeatures = feature;
12389 } else {
12390 // bogus keyword, bail...
12391 UngetToken();
12392 return false;
12395 foundValid = true;
12398 if (!foundValid) {
12399 return false;
12402 if (altFeatures) {
12403 nsCSSValue featureValue;
12404 featureValue.SetIntValue(altFeatures, eCSSUnit_Enumerated);
12405 value.SetPairValue(featureValue, altListValue);
12406 AppendValue(eCSSProperty_font_variant_alternates, value);
12407 } else {
12408 AppendValue(eCSSProperty_font_variant_alternates, normal);
12411 if (capsFeatures) {
12412 value.SetIntValue(capsFeatures, eCSSUnit_Enumerated);
12413 AppendValue(eCSSProperty_font_variant_caps, value);
12414 } else {
12415 AppendValue(eCSSProperty_font_variant_caps, normal);
12418 if (eastAsianFeatures) {
12419 value.SetIntValue(eastAsianFeatures, eCSSUnit_Enumerated);
12420 AppendValue(eCSSProperty_font_variant_east_asian, value);
12421 } else {
12422 AppendValue(eCSSProperty_font_variant_east_asian, normal);
12425 if (ligFeatures) {
12426 value.SetIntValue(ligFeatures, eCSSUnit_Enumerated);
12427 AppendValue(eCSSProperty_font_variant_ligatures, value);
12428 } else {
12429 AppendValue(eCSSProperty_font_variant_ligatures, normal);
12432 if (numericFeatures) {
12433 value.SetIntValue(numericFeatures, eCSSUnit_Enumerated);
12434 AppendValue(eCSSProperty_font_variant_numeric, value);
12435 } else {
12436 AppendValue(eCSSProperty_font_variant_numeric, normal);
12439 if (posFeatures) {
12440 value.SetIntValue(posFeatures, eCSSUnit_Enumerated);
12441 AppendValue(eCSSProperty_font_variant_position, value);
12442 } else {
12443 AppendValue(eCSSProperty_font_variant_position, normal);
12446 return true;
12449 bool
12450 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
12452 if (ParseVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT,
12453 nsCSSProps::kFontWeightKTable)) {
12454 if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
12455 int32_t intValue = aValue.GetIntValue();
12456 if ((100 <= intValue) &&
12457 (intValue <= 900) &&
12458 (0 == (intValue % 100))) {
12459 return true;
12460 } else {
12461 UngetToken();
12462 return false;
12465 return true;
12467 return false;
12470 bool
12471 CSSParserImpl::ParseOneFamily(nsAString& aFamily,
12472 bool& aOneKeyword,
12473 bool& aQuoted)
12475 if (!GetToken(true))
12476 return false;
12478 nsCSSToken* tk = &mToken;
12480 aOneKeyword = false;
12481 aQuoted = false;
12482 if (eCSSToken_Ident == tk->mType) {
12483 aOneKeyword = true;
12484 aFamily.Append(tk->mIdent);
12485 for (;;) {
12486 if (!GetToken(false))
12487 break;
12489 if (eCSSToken_Ident == tk->mType) {
12490 aOneKeyword = false;
12491 // We had at least another keyword before.
12492 // "If a sequence of identifiers is given as a font family name,
12493 // the computed value is the name converted to a string by joining
12494 // all the identifiers in the sequence by single spaces."
12495 // -- CSS 2.1, section 15.3
12496 // Whitespace tokens do not actually matter,
12497 // identifier tokens can be separated by comments.
12498 aFamily.Append(char16_t(' '));
12499 aFamily.Append(tk->mIdent);
12500 } else if (eCSSToken_Whitespace != tk->mType) {
12501 UngetToken();
12502 break;
12505 return true;
12507 } else if (eCSSToken_String == tk->mType) {
12508 aQuoted = true;
12509 aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
12510 return true;
12512 } else {
12513 UngetToken();
12514 return false;
12519 static bool
12520 AppendGeneric(nsCSSKeyword aKeyword, FontFamilyList *aFamilyList)
12522 switch (aKeyword) {
12523 case eCSSKeyword_serif:
12524 aFamilyList->Append(FontFamilyName(eFamily_serif));
12525 return true;
12526 case eCSSKeyword_sans_serif:
12527 aFamilyList->Append(FontFamilyName(eFamily_sans_serif));
12528 return true;
12529 case eCSSKeyword_monospace:
12530 aFamilyList->Append(FontFamilyName(eFamily_monospace));
12531 return true;
12532 case eCSSKeyword_cursive:
12533 aFamilyList->Append(FontFamilyName(eFamily_cursive));
12534 return true;
12535 case eCSSKeyword_fantasy:
12536 aFamilyList->Append(FontFamilyName(eFamily_fantasy));
12537 return true;
12538 case eCSSKeyword__moz_fixed:
12539 aFamilyList->Append(FontFamilyName(eFamily_moz_fixed));
12540 return true;
12541 default:
12542 break;
12545 return false;
12548 bool
12549 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
12551 nsRefPtr<css::FontFamilyListRefCnt> familyList =
12552 new css::FontFamilyListRefCnt();
12553 nsAutoString family;
12554 bool single, quoted;
12556 // keywords only have meaning in the first position
12557 if (!ParseOneFamily(family, single, quoted))
12558 return false;
12560 // check for keywords, but only when keywords appear by themselves
12561 // i.e. not in compounds such as font-family: default blah;
12562 bool foundGeneric = false;
12563 if (single) {
12564 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
12565 switch (keyword) {
12566 case eCSSKeyword_inherit:
12567 aValue.SetInheritValue();
12568 return true;
12569 case eCSSKeyword_default:
12570 // 605231 - don't parse unquoted 'default' reserved keyword
12571 return false;
12572 case eCSSKeyword_initial:
12573 aValue.SetInitialValue();
12574 return true;
12575 case eCSSKeyword_unset:
12576 if (nsLayoutUtils::UnsetValueEnabled()) {
12577 aValue.SetUnsetValue();
12578 return true;
12580 break;
12581 case eCSSKeyword__moz_use_system_font:
12582 if (!IsParsingCompoundProperty()) {
12583 aValue.SetSystemFontValue();
12584 return true;
12586 break;
12587 default:
12588 foundGeneric = AppendGeneric(keyword, familyList);
12592 if (!foundGeneric) {
12593 familyList->Append(
12594 FontFamilyName(family, (quoted ? eQuotedName : eUnquotedName)));
12597 for (;;) {
12598 if (!ExpectSymbol(',', true))
12599 break;
12601 nsAutoString nextFamily;
12602 if (!ParseOneFamily(nextFamily, single, quoted))
12603 return false;
12605 // at this point unquoted keywords are not allowed
12606 // as font family names but can appear within names
12607 foundGeneric = false;
12608 if (single) {
12609 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily);
12610 switch (keyword) {
12611 case eCSSKeyword_inherit:
12612 case eCSSKeyword_initial:
12613 case eCSSKeyword_default:
12614 case eCSSKeyword__moz_use_system_font:
12615 return false;
12616 case eCSSKeyword_unset:
12617 if (nsLayoutUtils::UnsetValueEnabled()) {
12618 return false;
12620 break;
12621 default:
12622 foundGeneric = AppendGeneric(keyword, familyList);
12623 break;
12627 if (!foundGeneric) {
12628 familyList->Append(
12629 FontFamilyName(nextFamily, (quoted ? eQuotedName : eUnquotedName)));
12633 if (familyList->IsEmpty()) {
12634 return false;
12637 aValue.SetFontFamilyListValue(familyList);
12638 return true;
12641 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
12642 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
12643 // local-src: 'local(' ( string | ident ) ')'
12645 bool
12646 CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
12648 // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>?
12649 InfallibleTArray<nsCSSValue> values;
12650 nsCSSValue cur;
12651 for (;;) {
12652 if (!GetToken(true))
12653 break;
12655 if (mToken.mType == eCSSToken_URL) {
12656 SetValueToURL(cur, mToken.mIdent);
12657 values.AppendElement(cur);
12658 if (!ParseFontSrcFormat(values))
12659 return false;
12661 } else if (mToken.mType == eCSSToken_Function &&
12662 mToken.mIdent.LowerCaseEqualsLiteral("local")) {
12663 // css3-fonts does not specify a formal grammar for local().
12664 // The text permits both unquoted identifiers and quoted
12665 // strings. We resolve this ambiguity in the spec by
12666 // assuming that the appropriate production is a single
12667 // <family-name>, possibly surrounded by whitespace.
12669 nsAutoString family;
12670 bool single, quoted;
12671 if (!ParseOneFamily(family, single, quoted)) {
12672 SkipUntil(')');
12673 return false;
12675 if (!ExpectSymbol(')', true)) {
12676 SkipUntil(')');
12677 return false;
12680 // reject generics
12681 if (single) {
12682 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
12683 switch (keyword) {
12684 case eCSSKeyword_serif:
12685 case eCSSKeyword_sans_serif:
12686 case eCSSKeyword_monospace:
12687 case eCSSKeyword_cursive:
12688 case eCSSKeyword_fantasy:
12689 case eCSSKeyword__moz_fixed:
12690 return false;
12691 default:
12692 break;
12696 cur.SetStringValue(family, eCSSUnit_Local_Font);
12697 values.AppendElement(cur);
12698 } else {
12699 // We don't know what to do with this token; unget it and error out
12700 UngetToken();
12701 return false;
12704 if (!ExpectSymbol(',', true))
12705 break;
12708 if (values.Length() == 0)
12709 return false;
12711 nsRefPtr<nsCSSValue::Array> srcVals
12712 = nsCSSValue::Array::Create(values.Length());
12714 uint32_t i;
12715 for (i = 0; i < values.Length(); i++)
12716 srcVals->Item(i) = values[i];
12717 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
12718 return true;
12721 bool
12722 CSSParserImpl::ParseFontSrcFormat(InfallibleTArray<nsCSSValue> & values)
12724 if (!GetToken(true))
12725 return true; // EOF harmless here
12726 if (mToken.mType != eCSSToken_Function ||
12727 !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
12728 UngetToken();
12729 return true;
12732 do {
12733 if (!GetToken(true))
12734 return false; // EOF - no need for SkipUntil
12736 if (mToken.mType != eCSSToken_String) {
12737 UngetToken();
12738 SkipUntil(')');
12739 return false;
12742 nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
12743 values.AppendElement(cur);
12744 } while (ExpectSymbol(',', true));
12746 if (!ExpectSymbol(')', true)) {
12747 SkipUntil(')');
12748 return false;
12751 return true;
12754 // font-ranges: urange ( ',' urange )*
12755 bool
12756 CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
12758 InfallibleTArray<uint32_t> ranges;
12759 for (;;) {
12760 if (!GetToken(true))
12761 break;
12763 if (mToken.mType != eCSSToken_URange) {
12764 UngetToken();
12765 break;
12768 // An invalid range token is a parsing error, causing the entire
12769 // descriptor to be ignored.
12770 if (!mToken.mIntegerValid)
12771 return false;
12773 uint32_t low = mToken.mInteger;
12774 uint32_t high = mToken.mInteger2;
12776 // A range that descends, or a range that is entirely outside the
12777 // current range of Unicode (U+0-10FFFF) is ignored, but does not
12778 // invalidate the descriptor. A range that straddles the high end
12779 // is clipped.
12780 if (low <= 0x10FFFF && low <= high) {
12781 if (high > 0x10FFFF)
12782 high = 0x10FFFF;
12784 ranges.AppendElement(low);
12785 ranges.AppendElement(high);
12787 if (!ExpectSymbol(',', true))
12788 break;
12791 if (ranges.Length() == 0)
12792 return false;
12794 nsRefPtr<nsCSSValue::Array> srcVals
12795 = nsCSSValue::Array::Create(ranges.Length());
12797 for (uint32_t i = 0; i < ranges.Length(); i++)
12798 srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer);
12799 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
12800 return true;
12803 // font-feature-settings: normal | <feature-tag-value> [, <feature-tag-value>]*
12804 // <feature-tag-value> = <string> [ <integer> | on | off ]?
12806 // minimum - "tagx", "tagy", "tagz"
12807 // edge error case - "tagx" on 1, "tagx" "tagy", "tagx" -1, "tagx" big
12809 // pair value is always x = string, y = int
12811 // font feature tags must be four ASCII characters
12812 #define FEATURE_TAG_LENGTH 4
12814 static bool
12815 ValidFontFeatureTag(const nsString& aTag)
12817 if (aTag.Length() != FEATURE_TAG_LENGTH) {
12818 return false;
12820 uint32_t i;
12821 for (i = 0; i < FEATURE_TAG_LENGTH; i++) {
12822 uint32_t ch = aTag[i];
12823 if (ch < 0x20 || ch > 0x7e) {
12824 return false;
12827 return true;
12830 bool
12831 CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue)
12833 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
12834 return true;
12837 nsCSSValuePairList *cur = aValue.SetPairListValue();
12838 for (;;) {
12839 // feature tag
12840 if (!GetToken(true)) {
12841 return false;
12844 if (mToken.mType != eCSSToken_String ||
12845 !ValidFontFeatureTag(mToken.mIdent)) {
12846 UngetToken();
12847 return false;
12849 cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
12851 if (!GetToken(true)) {
12852 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
12853 break;
12856 // optional value or on/off keyword
12857 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid &&
12858 mToken.mInteger >= 0) {
12859 cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
12860 } else if (mToken.mType == eCSSToken_Ident &&
12861 mToken.mIdent.LowerCaseEqualsLiteral("on")) {
12862 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
12863 } else if (mToken.mType == eCSSToken_Ident &&
12864 mToken.mIdent.LowerCaseEqualsLiteral("off")) {
12865 cur->mYValue.SetIntValue(0, eCSSUnit_Integer);
12866 } else {
12867 // something other than value/on/off, set default value
12868 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
12869 UngetToken();
12872 if (!ExpectSymbol(',', true)) {
12873 break;
12876 cur->mNext = new nsCSSValuePairList;
12877 cur = cur->mNext;
12880 return true;
12883 bool
12884 CSSParserImpl::ParseListStyle()
12886 // 'list-style' can accept 'none' for two different subproperties,
12887 // 'list-style-type' and 'list-style-image'. In order to accept
12888 // 'none' as the value of either but still allow another value for
12889 // either, we need to ensure that the first 'none' we find gets
12890 // allocated to a dummy property instead. Since parse function for
12891 // 'list-style-type' could accept values for 'list-style-position',
12892 // we put position in front of type.
12893 static const nsCSSProperty listStyleIDs[] = {
12894 eCSSPropertyExtra_x_none_value,
12895 eCSSProperty_list_style_position,
12896 eCSSProperty_list_style_type,
12897 eCSSProperty_list_style_image
12900 nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)];
12901 int32_t found =
12902 ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs));
12903 if (found < 1) {
12904 return false;
12907 if ((found & (1|4|8)) == (1|4|8)) {
12908 if (values[0].GetUnit() == eCSSUnit_None) {
12909 // We found a 'none' plus another value for both of
12910 // 'list-style-type' and 'list-style-image'. This is a parse
12911 // error, since the 'none' has to count for at least one of them.
12912 return false;
12913 } else {
12914 NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] &&
12915 values[0] == values[2] && values[0] == values[3],
12916 "should be a special value");
12920 if ((found & 2) == 0) {
12921 values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
12922 eCSSUnit_Enumerated);
12924 if ((found & 4) == 0) {
12925 // Provide default values
12926 nsString type = (found & 1) ?
12927 NS_LITERAL_STRING("none") : NS_LITERAL_STRING("disc");
12928 values[2].SetStringValue(type, eCSSUnit_Ident);
12930 if ((found & 8) == 0) {
12931 values[3].SetNoneValue();
12934 // Start at 1 to avoid appending fake value.
12935 for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) {
12936 AppendValue(listStyleIDs[index], values[index]);
12938 return true;
12941 bool
12942 CSSParserImpl::ParseListStyleType(nsCSSValue& aValue)
12944 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
12945 return true;
12948 if (ParseCounterStyleNameValue(aValue) || ParseSymbols(aValue)) {
12949 return true;
12952 return false;
12955 bool
12956 CSSParserImpl::ParseMargin()
12958 static const nsCSSProperty kMarginSideIDs[] = {
12959 eCSSProperty_margin_top,
12960 eCSSProperty_margin_right_value,
12961 eCSSProperty_margin_bottom,
12962 eCSSProperty_margin_left_value
12964 static const nsCSSProperty kMarginSources[] = {
12965 eCSSProperty_margin_left_ltr_source,
12966 eCSSProperty_margin_left_rtl_source,
12967 eCSSProperty_margin_right_ltr_source,
12968 eCSSProperty_margin_right_rtl_source,
12969 eCSSProperty_UNKNOWN
12972 // do this now, in case 4 values weren't specified
12973 InitBoxPropsAsPhysical(kMarginSources);
12974 return ParseBoxProperties(kMarginSideIDs);
12977 bool
12978 CSSParserImpl::ParseMarks(nsCSSValue& aValue)
12980 if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPageMarksKTable)) {
12981 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
12982 if (NS_STYLE_PAGE_MARKS_NONE != aValue.GetIntValue() &&
12983 false == CheckEndProperty()) {
12984 nsCSSValue second;
12985 if (ParseEnum(second, nsCSSProps::kPageMarksKTable)) {
12986 // 'none' keyword in conjuction with others is not allowed
12987 if (NS_STYLE_PAGE_MARKS_NONE != second.GetIntValue()) {
12988 aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(),
12989 eCSSUnit_Enumerated);
12990 return true;
12993 return false;
12996 return true;
12998 return false;
13001 bool
13002 CSSParserImpl::ParseObjectPosition()
13004 nsCSSValue value;
13005 if (!ParseVariant(value, VARIANT_INHERIT, nullptr) &&
13006 !ParsePositionValue(value)) {
13007 return false;
13009 AppendValue(eCSSProperty_object_position, value);
13010 return true;
13013 bool
13014 CSSParserImpl::ParseOutline()
13016 const int32_t numProps = 3;
13017 static const nsCSSProperty kOutlineIDs[] = {
13018 eCSSProperty_outline_color,
13019 eCSSProperty_outline_style,
13020 eCSSProperty_outline_width
13023 nsCSSValue values[numProps];
13024 int32_t found = ParseChoice(values, kOutlineIDs, numProps);
13025 if (found < 1) {
13026 return false;
13029 // Provide default values
13030 if ((found & 1) == 0) { // Provide default outline-color
13031 values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
13033 if ((found & 2) == 0) { // Provide default outline-style
13034 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
13036 if ((found & 4) == 0) { // Provide default outline-width
13037 values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
13040 int32_t index;
13041 for (index = 0; index < numProps; index++) {
13042 AppendValue(kOutlineIDs[index], values[index]);
13044 return true;
13047 bool
13048 CSSParserImpl::ParseOverflow()
13050 nsCSSValue overflow;
13051 if (!ParseVariant(overflow, VARIANT_HK, nsCSSProps::kOverflowKTable)) {
13052 return false;
13055 nsCSSValue overflowX(overflow);
13056 nsCSSValue overflowY(overflow);
13057 if (eCSSUnit_Enumerated == overflow.GetUnit())
13058 switch(overflow.GetIntValue()) {
13059 case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
13060 overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
13061 overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
13062 break;
13063 case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
13064 overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
13065 overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
13066 break;
13068 AppendValue(eCSSProperty_overflow_x, overflowX);
13069 AppendValue(eCSSProperty_overflow_y, overflowY);
13070 return true;
13073 bool
13074 CSSParserImpl::ParsePadding()
13076 static const nsCSSProperty kPaddingSideIDs[] = {
13077 eCSSProperty_padding_top,
13078 eCSSProperty_padding_right_value,
13079 eCSSProperty_padding_bottom,
13080 eCSSProperty_padding_left_value
13082 static const nsCSSProperty kPaddingSources[] = {
13083 eCSSProperty_padding_left_ltr_source,
13084 eCSSProperty_padding_left_rtl_source,
13085 eCSSProperty_padding_right_ltr_source,
13086 eCSSProperty_padding_right_rtl_source,
13087 eCSSProperty_UNKNOWN
13090 // do this now, in case 4 values weren't specified
13091 InitBoxPropsAsPhysical(kPaddingSources);
13092 return ParseBoxProperties(kPaddingSideIDs);
13095 bool
13096 CSSParserImpl::ParseQuotes()
13098 nsCSSValue value;
13099 if (!ParseVariant(value, VARIANT_HOS, nullptr)) {
13100 return false;
13102 if (value.GetUnit() == eCSSUnit_String) {
13103 nsCSSValue open = value;
13104 nsCSSValuePairList* quotes = value.SetPairListValue();
13105 for (;;) {
13106 quotes->mXValue = open;
13107 // get mandatory close
13108 if (!ParseVariant(quotes->mYValue, VARIANT_STRING, nullptr)) {
13109 return false;
13111 // look for another open
13112 if (!ParseVariant(open, VARIANT_STRING, nullptr)) {
13113 break;
13115 quotes->mNext = new nsCSSValuePairList;
13116 quotes = quotes->mNext;
13119 AppendValue(eCSSProperty_quotes, value);
13120 return true;
13123 static const int32_t gRubyPositionMask[] = {
13124 // vertical values
13125 NS_STYLE_RUBY_POSITION_OVER |
13126 NS_STYLE_RUBY_POSITION_UNDER |
13127 NS_STYLE_RUBY_POSITION_INTER_CHARACTER,
13128 // horizontal values
13129 NS_STYLE_RUBY_POSITION_RIGHT |
13130 NS_STYLE_RUBY_POSITION_LEFT,
13131 // end
13132 MASK_END_VALUE
13135 bool
13136 CSSParserImpl::ParseRubyPosition(nsCSSValue& aValue)
13138 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
13139 return true;
13141 if (!ParseBitmaskValues(aValue, nsCSSProps::kRubyPositionKTable,
13142 gRubyPositionMask)) {
13143 return false;
13145 auto value = aValue.GetIntValue();
13146 // The specified value must include *both* a vertical keyword *and*
13147 // a horizontal keyword. We reject it here if either is missing.
13148 return (value & gRubyPositionMask[0]) && (value & gRubyPositionMask[1]);
13151 bool
13152 CSSParserImpl::ParseSize()
13154 nsCSSValue width, height;
13155 if (!ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
13156 return false;
13158 if (width.IsLengthUnit()) {
13159 ParseVariant(height, VARIANT_LENGTH, nullptr);
13162 if (width == height || height.GetUnit() == eCSSUnit_Null) {
13163 AppendValue(eCSSProperty_size, width);
13164 } else {
13165 nsCSSValue pair;
13166 pair.SetPairValue(width, height);
13167 AppendValue(eCSSProperty_size, pair);
13169 return true;
13172 bool
13173 CSSParserImpl::ParseTextDecoration()
13175 static const nsCSSProperty kTextDecorationIDs[] = {
13176 eCSSProperty_text_decoration_line,
13177 eCSSProperty_text_decoration_style,
13178 eCSSProperty_text_decoration_color
13180 const int32_t numProps = MOZ_ARRAY_LENGTH(kTextDecorationIDs);
13181 nsCSSValue values[numProps];
13183 int32_t found = ParseChoice(values, kTextDecorationIDs, numProps);
13184 if (found < 1) {
13185 return false;
13188 // Provide default values
13189 if ((found & 1) == 0) { // Provide default text-decoration-line
13190 values[0].SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE,
13191 eCSSUnit_Enumerated);
13193 if ((found & 2) == 0) { // Provide default text-decoration-style
13194 values[1].SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
13195 eCSSUnit_Enumerated);
13197 if ((found & 4) == 0) { // Provide default text-decoration-color
13198 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR,
13199 eCSSUnit_Enumerated);
13202 for (int32_t index = 0; index < numProps; index++) {
13203 AppendValue(kTextDecorationIDs[index], values[index]);
13205 return true;
13208 bool
13209 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableValue aTable[])
13211 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
13212 // 'inherit', 'initial' and 'unset' must be alone
13213 return true;
13216 nsCSSValue left;
13217 if (!ParseVariant(left, VARIANT_KEYWORD, aTable)) {
13218 return false;
13221 if (!nsLayoutUtils::IsTextAlignTrueValueEnabled()) {
13222 aValue = left;
13223 return true;
13226 nsCSSValue right;
13227 if (ParseVariant(right, VARIANT_KEYWORD, aTable)) {
13228 // 'true' must be combined with some other value than 'true'.
13229 if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE &&
13230 right.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
13231 return false;
13233 aValue.SetPairValue(left, right);
13234 } else {
13235 // Single value 'true' is not allowed.
13236 if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
13237 return false;
13239 aValue = left;
13241 return true;
13244 bool
13245 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue)
13247 return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable);
13250 bool
13251 CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue)
13253 return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable);
13256 bool
13257 CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
13259 static_assert((NS_STYLE_TEXT_DECORATION_LINE_NONE ^
13260 NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE ^
13261 NS_STYLE_TEXT_DECORATION_LINE_OVERLINE ^
13262 NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH ^
13263 NS_STYLE_TEXT_DECORATION_LINE_BLINK ^
13264 NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS) ==
13265 (NS_STYLE_TEXT_DECORATION_LINE_NONE |
13266 NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE |
13267 NS_STYLE_TEXT_DECORATION_LINE_OVERLINE |
13268 NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH |
13269 NS_STYLE_TEXT_DECORATION_LINE_BLINK |
13270 NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS),
13271 "text decoration constants need to be bitmasks");
13272 if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTextDecorationLineKTable)) {
13273 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
13274 int32_t intValue = aValue.GetIntValue();
13275 if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
13276 // look for more keywords
13277 nsCSSValue keyword;
13278 int32_t index;
13279 for (index = 0; index < 3; index++) {
13280 if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) {
13281 int32_t newValue = keyword.GetIntValue();
13282 if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE ||
13283 newValue & intValue) {
13284 // 'none' keyword in conjuction with others is not allowed, and
13285 // duplicate keyword is not allowed.
13286 return false;
13288 intValue |= newValue;
13290 else {
13291 break;
13294 aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
13297 return true;
13299 return false;
13302 bool
13303 CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
13305 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
13306 // 'inherit', 'initial' and 'unset' must be alone
13307 return true;
13310 nsCSSValue left;
13311 if (!ParseVariant(left, VARIANT_KEYWORD | VARIANT_STRING,
13312 nsCSSProps::kTextOverflowKTable))
13313 return false;
13315 nsCSSValue right;
13316 if (ParseVariant(right, VARIANT_KEYWORD | VARIANT_STRING,
13317 nsCSSProps::kTextOverflowKTable))
13318 aValue.SetPairValue(left, right);
13319 else {
13320 aValue = left;
13322 return true;
13325 bool
13326 CSSParserImpl::ParseTouchAction(nsCSSValue& aValue)
13328 // Avaliable values of property touch-action:
13329 // auto | none | [pan-x || pan-y] | manipulation
13331 if (!ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTouchActionKTable)) {
13332 return false;
13335 // Auto and None keywords aren't allowed in conjunction with others.
13336 // Also inherit, initial and unset values are available.
13337 if (eCSSUnit_Enumerated != aValue.GetUnit()) {
13338 return true;
13341 int32_t intValue = aValue.GetIntValue();
13342 nsCSSValue nextValue;
13343 if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) {
13344 int32_t nextIntValue = nextValue.GetIntValue();
13346 // duplicates aren't allowed.
13347 if (nextIntValue & intValue) {
13348 return false;
13351 // Auto and None and Manipulation is not allowed in conjunction with others.
13352 if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE |
13353 NS_STYLE_TOUCH_ACTION_AUTO |
13354 NS_STYLE_TOUCH_ACTION_MANIPULATION)) {
13355 return false;
13358 aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
13361 return true;
13364 bool
13365 CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue)
13367 if (!ParseVariant(aValue, VARIANT_HK,
13368 nsCSSProps::kTextCombineUprightKTable)) {
13369 return false;
13372 // if 'digits', need to check for an explicit number [2, 3, 4]
13373 if (eCSSUnit_Enumerated == aValue.GetUnit() &&
13374 aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) {
13375 if (!GetToken(true)) {
13376 return true;
13378 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
13379 switch (mToken.mInteger) {
13380 case 2: // already set, nothing to do
13381 break;
13382 case 3:
13383 aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3,
13384 eCSSUnit_Enumerated);
13385 break;
13386 case 4:
13387 aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4,
13388 eCSSUnit_Enumerated);
13389 break;
13390 default:
13391 // invalid digits value
13392 return false;
13394 } else {
13395 UngetToken();
13398 return true;
13401 ///////////////////////////////////////////////////////
13402 // transform Parsing Implementation
13404 /* Reads a function list of arguments and consumes the closing parenthesis.
13405 * Do not call this function directly; it's meant to be called from
13406 * ParseFunction.
13408 bool
13409 CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[],
13410 int32_t aVariantMaskAll,
13411 uint16_t aMinElems,
13412 uint16_t aMaxElems,
13413 InfallibleTArray<nsCSSValue> &aOutput)
13415 NS_ASSERTION((aVariantMask && !aVariantMaskAll) ||
13416 (!aVariantMask && aVariantMaskAll),
13417 "only one of the two variant mask parameters can be set");
13419 for (uint16_t index = 0; index < aMaxElems; ++index) {
13420 nsCSSValue newValue;
13421 int32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index];
13422 if (!ParseVariant(newValue, m, nullptr)) {
13423 break;
13426 aOutput.AppendElement(newValue);
13428 if (ExpectSymbol(',', true)) {
13429 // Move on to the next argument if we see a comma.
13430 continue;
13433 if (ExpectSymbol(')', true)) {
13434 // Make sure we've read enough symbols if we see a closing parenthesis.
13435 return (index + 1) >= aMinElems;
13438 // Only a comma or a closing parenthesis is valid after an argument.
13439 break;
13442 // If we're here, we've hit an error without seeing a closing parenthesis or
13443 // we've read too many elements without seeing a closing parenthesis.
13444 SkipUntil(')');
13445 return false;
13448 /* Parses a function [ input of the form (a [, b]*) ] and stores it
13449 * as an nsCSSValue that holds a function of the form
13450 * function-name arg1 arg2 ... argN
13452 * On error, the return value is false.
13454 * @param aFunction The name of the function that we're reading.
13455 * @param aAllowedTypes An array of values corresponding to the legal
13456 * types for each element in the function. The zeroth element in the
13457 * array corresponds to the first function parameter, etc. The length
13458 * of this array _must_ be greater than or equal to aMaxElems or the
13459 * behavior is undefined. If not null, aAllowTypesAll must be 0.
13460 * @param aAllowedTypesAll If set, every element tested for these types
13461 * @param aMinElems Minimum number of elements to read. Reading fewer than
13462 * this many elements will result in the function failing.
13463 * @param aMaxElems Maximum number of elements to read. Reading more than
13464 * this many elements will result in the function failing.
13465 * @param aValue (out) The value that was parsed.
13467 bool
13468 CSSParserImpl::ParseFunction(nsCSSKeyword aFunction,
13469 const int32_t aAllowedTypes[],
13470 int32_t aAllowedTypesAll,
13471 uint16_t aMinElems, uint16_t aMaxElems,
13472 nsCSSValue &aValue)
13474 NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) ||
13475 (!aAllowedTypes && aAllowedTypesAll),
13476 "only one of the two allowed type parameter can be set");
13477 typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
13479 /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
13480 * elements stored in the the nsCSSValue::Array.
13482 static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
13484 /* Read in a list of values as an array, failing if we can't or if
13485 * it's out of bounds.
13487 * We reserve 16 entries in the foundValues array in order to avoid
13488 * having to resize the array dynamically when parsing some well-formed
13489 * functions. The number 16 is coming from the number of arguments that
13490 * matrix3d() accepts.
13492 AutoInfallibleTArray<nsCSSValue, 16> foundValues;
13493 if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems,
13494 aMaxElems, foundValues)) {
13495 return false;
13499 * In case the user has given us more than 2^16 - 2 arguments,
13500 * we'll truncate them at 2^16 - 2 arguments.
13502 uint16_t numArgs = std::min(foundValues.Length(), MAX_ALLOWED_ELEMS);
13503 nsRefPtr<nsCSSValue::Array> convertedArray =
13504 aValue.InitFunction(aFunction, numArgs);
13506 /* Copy things over. */
13507 for (uint16_t index = 0; index < numArgs; ++index)
13508 convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
13510 /* Return it! */
13511 return true;
13515 * Given a token, determines the minimum and maximum number of function
13516 * parameters to read, along with the mask that should be used to read
13517 * those function parameters. If the token isn't a transform function,
13518 * returns an error.
13520 * @param aToken The token identifying the function.
13521 * @param aMinElems [out] The minimum number of elements to read.
13522 * @param aMaxElems [out] The maximum number of elements to read
13523 * @param aVariantMask [out] The variant mask to use during parsing
13524 * @return Whether the information was loaded successfully.
13526 static bool GetFunctionParseInformation(nsCSSKeyword aToken,
13527 bool aIsPrefixed,
13528 uint16_t &aMinElems,
13529 uint16_t &aMaxElems,
13530 const int32_t *& aVariantMask)
13532 /* These types represent the common variant masks that will be used to
13533 * parse out the individual functions. The order in the enumeration
13534 * must match the order in which the masks are declared.
13536 enum { eLengthPercentCalc,
13537 eLengthCalc,
13538 eTwoLengthPercentCalcs,
13539 eTwoLengthPercentCalcsOneLengthCalc,
13540 eAngle,
13541 eTwoAngles,
13542 eNumber,
13543 ePositiveLength,
13544 eTwoNumbers,
13545 eThreeNumbers,
13546 eThreeNumbersOneAngle,
13547 eMatrix,
13548 eMatrixPrefixed,
13549 eMatrix3d,
13550 eMatrix3dPrefixed,
13551 eNumVariantMasks };
13552 static const int32_t kMaxElemsPerFunction = 16;
13553 static const int32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
13554 {VARIANT_LPCALC},
13555 {VARIANT_LENGTH|VARIANT_CALC},
13556 {VARIANT_LPCALC, VARIANT_LPCALC},
13557 {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LENGTH|VARIANT_CALC},
13558 {VARIANT_ANGLE_OR_ZERO},
13559 {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
13560 {VARIANT_NUMBER},
13561 {VARIANT_LENGTH|VARIANT_POSITIVE_DIMENSION},
13562 {VARIANT_NUMBER, VARIANT_NUMBER},
13563 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
13564 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
13565 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13566 VARIANT_NUMBER, VARIANT_NUMBER},
13567 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13568 VARIANT_LPNCALC, VARIANT_LPNCALC},
13569 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13570 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13571 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13572 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
13573 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13574 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13575 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13576 VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
13578 #ifdef DEBUG
13579 static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
13580 {1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 6, 16, 16};
13581 #endif
13583 int32_t variantIndex = eNumVariantMasks;
13585 switch (aToken) {
13586 case eCSSKeyword_translatex:
13587 case eCSSKeyword_translatey:
13588 /* Exactly one length or percent. */
13589 variantIndex = eLengthPercentCalc;
13590 aMinElems = 1U;
13591 aMaxElems = 1U;
13592 break;
13593 case eCSSKeyword_translatez:
13594 /* Exactly one length */
13595 variantIndex = eLengthCalc;
13596 aMinElems = 1U;
13597 aMaxElems = 1U;
13598 break;
13599 case eCSSKeyword_translate3d:
13600 /* Exactly two lengthds or percents and a number */
13601 variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
13602 aMinElems = 3U;
13603 aMaxElems = 3U;
13604 break;
13605 case eCSSKeyword_scalez:
13606 case eCSSKeyword_scalex:
13607 case eCSSKeyword_scaley:
13608 /* Exactly one scale factor. */
13609 variantIndex = eNumber;
13610 aMinElems = 1U;
13611 aMaxElems = 1U;
13612 break;
13613 case eCSSKeyword_scale3d:
13614 /* Exactly three scale factors. */
13615 variantIndex = eThreeNumbers;
13616 aMinElems = 3U;
13617 aMaxElems = 3U;
13618 break;
13619 case eCSSKeyword_rotatex:
13620 case eCSSKeyword_rotatey:
13621 case eCSSKeyword_rotate:
13622 case eCSSKeyword_rotatez:
13623 /* Exactly one angle. */
13624 variantIndex = eAngle;
13625 aMinElems = 1U;
13626 aMaxElems = 1U;
13627 break;
13628 case eCSSKeyword_rotate3d:
13629 variantIndex = eThreeNumbersOneAngle;
13630 aMinElems = 4U;
13631 aMaxElems = 4U;
13632 break;
13633 case eCSSKeyword_translate:
13634 /* One or two lengths or percents. */
13635 variantIndex = eTwoLengthPercentCalcs;
13636 aMinElems = 1U;
13637 aMaxElems = 2U;
13638 break;
13639 case eCSSKeyword_skew:
13640 /* Exactly one or two angles. */
13641 variantIndex = eTwoAngles;
13642 aMinElems = 1U;
13643 aMaxElems = 2U;
13644 break;
13645 case eCSSKeyword_scale:
13646 /* One or two scale factors. */
13647 variantIndex = eTwoNumbers;
13648 aMinElems = 1U;
13649 aMaxElems = 2U;
13650 break;
13651 case eCSSKeyword_skewx:
13652 /* Exactly one angle. */
13653 variantIndex = eAngle;
13654 aMinElems = 1U;
13655 aMaxElems = 1U;
13656 break;
13657 case eCSSKeyword_skewy:
13658 /* Exactly one angle. */
13659 variantIndex = eAngle;
13660 aMinElems = 1U;
13661 aMaxElems = 1U;
13662 break;
13663 case eCSSKeyword_matrix:
13664 /* Six values, all numbers. */
13665 variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
13666 aMinElems = 6U;
13667 aMaxElems = 6U;
13668 break;
13669 case eCSSKeyword_matrix3d:
13670 /* 16 matrix values, all numbers */
13671 variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
13672 aMinElems = 16U;
13673 aMaxElems = 16U;
13674 break;
13675 case eCSSKeyword_perspective:
13676 /* Exactly one scale number. */
13677 variantIndex = ePositiveLength;
13678 aMinElems = 1U;
13679 aMaxElems = 1U;
13680 break;
13681 default:
13682 /* Oh dear, we didn't match. Report an error. */
13683 return false;
13686 NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
13687 NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
13688 NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
13689 NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
13690 NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
13691 #ifdef DEBUG
13692 NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
13693 "Invalid aMaxElems for this variant mask.");
13694 #endif
13696 // Convert the index into a mask.
13697 aVariantMask = kVariantMasks[variantIndex];
13699 return true;
13702 bool CSSParserImpl::ParseWillChange()
13704 nsCSSValue listValue;
13705 nsCSSValueList* currentListValue = listValue.SetListValue();
13706 bool first = true;
13707 for (;;) {
13708 const uint32_t variantMask = VARIANT_IDENTIFIER |
13709 VARIANT_INHERIT |
13710 VARIANT_NONE |
13711 VARIANT_ALL |
13712 VARIANT_AUTO;
13713 nsCSSValue value;
13714 if (!ParseVariant(value, variantMask, nullptr)) {
13715 return false;
13718 if (value.GetUnit() == eCSSUnit_None ||
13719 value.GetUnit() == eCSSUnit_All)
13721 return false;
13724 if (value.GetUnit() != eCSSUnit_Ident) {
13725 if (first) {
13726 AppendValue(eCSSProperty_will_change, value);
13727 return true;
13728 } else {
13729 return false;
13733 nsString str;
13734 value.GetStringValue(str);
13735 if (str.LowerCaseEqualsLiteral("default")) {
13736 return false;
13739 currentListValue->mValue = value;
13741 if (!ExpectSymbol(',', true)) {
13742 break;
13744 currentListValue->mNext = new nsCSSValueList;
13745 currentListValue = currentListValue->mNext;
13746 first = false;
13749 AppendValue(eCSSProperty_will_change, listValue);
13750 return true;
13753 /* Reads a single transform function from the tokenizer stream, reporting an
13754 * error if something goes wrong.
13756 bool
13757 CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue)
13759 if (!GetToken(true))
13760 return false;
13762 if (mToken.mType != eCSSToken_Function) {
13763 UngetToken();
13764 return false;
13767 const int32_t* variantMask;
13768 uint16_t minElems, maxElems;
13769 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
13771 if (!GetFunctionParseInformation(keyword, aIsPrefixed,
13772 minElems, maxElems, variantMask))
13773 return false;
13775 return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue);
13778 /* Parses a transform property list by continuously reading in properties
13779 * and constructing a matrix from it.
13781 bool CSSParserImpl::ParseTransform(bool aIsPrefixed)
13783 nsCSSValue value;
13784 // 'inherit', 'initial', 'unset' and 'none' must be alone
13785 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
13786 nsCSSValueSharedList* list = new nsCSSValueSharedList;
13787 value.SetSharedListValue(list);
13788 list->mHead = new nsCSSValueList;
13789 nsCSSValueList* cur = list->mHead;
13790 for (;;) {
13791 if (!ParseSingleTransform(aIsPrefixed, cur->mValue)) {
13792 return false;
13794 if (CheckEndProperty()) {
13795 break;
13797 cur->mNext = new nsCSSValueList;
13798 cur = cur->mNext;
13801 AppendValue(eCSSProperty_transform, value);
13802 return true;
13805 /* Reads a polygon function's argument list.
13807 bool
13808 CSSParserImpl::ParsePolygonFunction(nsCSSValue& aValue)
13810 uint16_t numArgs = 1;
13812 nsCSSValue fillRuleValue;
13813 if (ParseEnum(fillRuleValue, nsCSSProps::kFillRuleKTable)) {
13814 numArgs++;
13816 // The fill-rule must be comma separated from the polygon points.
13817 if (!ExpectSymbol(',', true)) {
13818 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
13819 SkipUntil(')');
13820 return false;
13824 nsCSSValue coordinates;
13825 nsCSSValuePairList* item = coordinates.SetPairListValue();
13826 for (;;) {
13827 nsCSSValue xValue, yValue;
13828 if (!ParseVariant(xValue, VARIANT_LPCALC, nullptr) ||
13829 !ParseVariant(yValue, VARIANT_LPCALC, nullptr)) {
13830 REPORT_UNEXPECTED_TOKEN(PECoordinatePair);
13831 SkipUntil(')');
13832 return false;
13834 item->mXValue = xValue;
13835 item->mYValue = yValue;
13837 // See whether to continue or whether to look for end of function.
13838 if (!ExpectSymbol(',', true)) {
13839 // We need to read the closing parenthesis.
13840 if (!ExpectSymbol(')', true)) {
13841 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
13842 SkipUntil(')');
13843 return false;
13845 break;
13847 item->mNext = new nsCSSValuePairList;
13848 item = item->mNext;
13851 nsRefPtr<nsCSSValue::Array> functionArray =
13852 aValue.InitFunction(eCSSKeyword_polygon, numArgs);
13853 functionArray->Item(numArgs) = coordinates;
13854 if (numArgs > 1) {
13855 functionArray->Item(1) = fillRuleValue;
13858 return true;
13861 bool
13862 CSSParserImpl::ParseCircleOrEllipseFunction(nsCSSKeyword aKeyword,
13863 nsCSSValue& aValue)
13865 nsCSSValue radiusX, radiusY, position;
13866 bool hasRadius = false, hasPosition = false;
13868 int32_t mask = VARIANT_LPCALC | VARIANT_NONNEGATIVE_DIMENSION |
13869 VARIANT_KEYWORD;
13870 if (ParseVariant(radiusX, mask, nsCSSProps::kShapeRadiusKTable)) {
13871 if (aKeyword == eCSSKeyword_ellipse) {
13872 if (!ParseVariant(radiusY, mask, nsCSSProps::kShapeRadiusKTable)) {
13873 REPORT_UNEXPECTED_TOKEN(PEExpectedRadius);
13874 SkipUntil(')');
13875 return false;
13878 hasRadius = true;
13881 if (!ExpectSymbol(')', true)) {
13882 if (!GetToken(true)) {
13883 REPORT_UNEXPECTED_EOF(PEPositionEOF);
13884 return false;
13887 if (mToken.mType != eCSSToken_Ident ||
13888 !mToken.mIdent.LowerCaseEqualsLiteral("at") ||
13889 !ParsePositionValue(position)) {
13890 REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
13891 SkipUntil(')');
13892 return false;
13894 if (!ExpectSymbol(')', true)) {
13895 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
13896 SkipUntil(')');
13897 return false;
13899 hasPosition = true;
13902 size_t count = aKeyword == eCSSKeyword_circle ? 2 : 3;
13903 nsRefPtr<nsCSSValue::Array> functionArray =
13904 aValue.InitFunction(aKeyword, count);
13905 if (hasRadius) {
13906 functionArray->Item(1) = radiusX;
13907 if (aKeyword == eCSSKeyword_ellipse) {
13908 functionArray->Item(2) = radiusY;
13911 if (hasPosition) {
13912 functionArray->Item(count) = position;
13915 return true;
13918 bool
13919 CSSParserImpl::ParseInsetFunction(nsCSSValue& aValue)
13921 nsRefPtr<nsCSSValue::Array> functionArray =
13922 aValue.InitFunction(eCSSKeyword_inset, 5);
13924 if (ParseVariant(functionArray->Item(1), VARIANT_LPCALC, nullptr)) {
13925 // Consume up to 4, but only require one.
13926 ParseVariant(functionArray->Item(2), VARIANT_LPCALC, nullptr) &&
13927 ParseVariant(functionArray->Item(3), VARIANT_LPCALC, nullptr) &&
13928 ParseVariant(functionArray->Item(4), VARIANT_LPCALC, nullptr);
13929 } else {
13930 REPORT_UNEXPECTED_TOKEN(PEExpectedShapeArg);
13931 SkipUntil(')');
13932 return false;
13935 if (!ExpectSymbol(')', true)) {
13936 if (!GetToken(true)) {
13937 NS_NOTREACHED("ExpectSymbol should have returned true");
13938 return false;
13941 nsRefPtr<nsCSSValue::Array> radiusArray = nsCSSValue::Array::Create(4);
13942 functionArray->Item(5).SetArrayValue(radiusArray, eCSSUnit_Array);
13943 if (mToken.mType != eCSSToken_Ident ||
13944 !mToken.mIdent.LowerCaseEqualsLiteral("round") ||
13945 !ParseBoxCornerRadiiInternals(radiusArray->ItemStorage())) {
13946 REPORT_UNEXPECTED_TOKEN(PEExpectedRadius);
13947 SkipUntil(')');
13948 return false;
13951 if (!ExpectSymbol(')', true)) {
13952 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
13953 SkipUntil(')');
13954 return false;
13958 return true;
13961 bool
13962 CSSParserImpl::ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens)
13964 if (!GetToken(true)) {
13965 return false;
13968 if (mToken.mType != eCSSToken_Function) {
13969 UngetToken();
13970 return false;
13973 // Specific shape function parsing always consumes tokens.
13974 *aConsumedTokens = true;
13975 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
13976 switch (keyword) {
13977 case eCSSKeyword_polygon:
13978 return ParsePolygonFunction(aValue);
13979 case eCSSKeyword_circle:
13980 case eCSSKeyword_ellipse:
13981 return ParseCircleOrEllipseFunction(keyword, aValue);
13982 case eCSSKeyword_inset:
13983 return ParseInsetFunction(aValue);
13984 default:
13985 return false;
13989 /* Parse a clip-path url to a <clipPath> element or a basic shape. */
13990 bool CSSParserImpl::ParseClipPath()
13992 nsCSSValue value;
13993 if (!ParseVariant(value, VARIANT_HUO, nullptr)) {
13994 if (!nsLayoutUtils::CSSClipPathShapesEnabled()) {
13995 // With CSS Clip Path Shapes disabled, we should only accept
13996 // SVG clipPath reference and none.
13997 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
13998 return false;
14001 nsCSSValueList* cur = value.SetListValue();
14003 nsCSSValue referenceBox;
14004 bool hasBox = ParseEnum(referenceBox, nsCSSProps::kClipShapeSizingKTable);
14006 nsCSSValue basicShape;
14007 bool basicShapeConsumedTokens = false;
14008 bool hasShape = ParseBasicShape(basicShape, &basicShapeConsumedTokens);
14010 // Parsing wasn't successful if ParseBasicShape consumed tokens but failed
14011 // or if the token was neither a reference box nor a basic shape.
14012 if ((!hasShape && basicShapeConsumedTokens) || (!hasBox && !hasShape)) {
14013 return false;
14016 // We need to preserve the specified order of arguments for inline style.
14017 if (hasBox) {
14018 cur->mValue = referenceBox;
14021 if (hasShape) {
14022 if (hasBox) {
14023 cur->mNext = new nsCSSValueList;
14024 cur = cur->mNext;
14026 cur->mValue = basicShape;
14029 // Check if the second argument is a reference box if the first wasn't.
14030 if (!hasBox &&
14031 ParseEnum(referenceBox, nsCSSProps::kClipShapeSizingKTable)) {
14032 cur->mNext = new nsCSSValueList;
14033 cur = cur->mNext;
14034 cur->mValue = referenceBox;
14038 AppendValue(eCSSProperty_clip_path, value);
14039 return true;
14042 bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
14044 nsCSSValuePair position;
14045 if (!ParseBoxPositionValues(position, true))
14046 return false;
14048 nsCSSProperty prop = eCSSProperty_transform_origin;
14049 if (aPerspective) {
14050 prop = eCSSProperty_perspective_origin;
14053 // Unlike many other uses of pairs, this position should always be stored
14054 // as a pair, even if the values are the same, so it always serializes as
14055 // a pair, and to keep the computation code simple.
14056 if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
14057 position.mXValue.GetUnit() == eCSSUnit_Initial ||
14058 position.mXValue.GetUnit() == eCSSUnit_Unset) {
14059 NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
14060 "inherit/initial/unset only half?");
14061 AppendValue(prop, position.mXValue);
14062 } else {
14063 nsCSSValue value;
14064 if (aPerspective) {
14065 value.SetPairValue(position.mXValue, position.mYValue);
14066 } else {
14067 nsCSSValue depth;
14068 if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr)) {
14069 depth.SetFloatValue(0.0f, eCSSUnit_Pixel);
14071 value.SetTripletValue(position.mXValue, position.mYValue, depth);
14074 AppendValue(prop, value);
14076 return true;
14080 * Reads a drop-shadow value. At the moment the Filter Effects specification
14081 * just expects one shadow item. Should this ever change to a list of shadow
14082 * items, use ParseShadowList instead.
14084 bool
14085 CSSParserImpl::ParseDropShadow(nsCSSValue* aValue)
14087 // Use nsCSSValueList to reuse the shadow resolving code in
14088 // nsRuleNode and nsComputedDOMStyle.
14089 nsCSSValue shadow;
14090 nsCSSValueList* cur = shadow.SetListValue();
14091 if (!ParseShadowItem(cur->mValue, false))
14092 return false;
14094 if (!ExpectSymbol(')', true))
14095 return false;
14097 nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1);
14099 // Copy things over.
14100 dropShadow->Item(1) = shadow;
14102 return true;
14106 * Reads a single url or filter function from the tokenizer stream, reporting an
14107 * error if something goes wrong.
14109 bool
14110 CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
14112 if (ParseVariant(*aValue, VARIANT_URL, nullptr)) {
14113 return true;
14116 if (!nsLayoutUtils::CSSFiltersEnabled()) {
14117 // With CSS Filters disabled, we should only accept an SVG reference filter.
14118 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
14119 return false;
14122 if (!GetToken(true)) {
14123 REPORT_UNEXPECTED_EOF(PEFilterEOF);
14124 return false;
14127 if (mToken.mType != eCSSToken_Function) {
14128 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
14129 UngetToken();
14130 return false;
14133 nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
14134 // Parse drop-shadow independently of the other filter functions
14135 // because of its more complex characteristics.
14136 if (functionName == eCSSKeyword_drop_shadow) {
14137 if (ParseDropShadow(aValue)) {
14138 return true;
14139 } else {
14140 // Unrecognized filter function.
14141 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
14142 SkipUntil(')');
14143 return false;
14147 // Set up the parsing rules based on the filter function.
14148 int32_t variantMask = VARIANT_PN;
14149 bool rejectNegativeArgument = true;
14150 bool clampArgumentToOne = false;
14151 switch (functionName) {
14152 case eCSSKeyword_blur:
14153 variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
14154 // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
14155 rejectNegativeArgument = false;
14156 break;
14157 case eCSSKeyword_brightness:
14158 case eCSSKeyword_contrast:
14159 case eCSSKeyword_saturate:
14160 break;
14161 case eCSSKeyword_grayscale:
14162 case eCSSKeyword_invert:
14163 case eCSSKeyword_sepia:
14164 case eCSSKeyword_opacity:
14165 clampArgumentToOne = true;
14166 break;
14167 case eCSSKeyword_hue_rotate:
14168 variantMask = VARIANT_ANGLE;
14169 rejectNegativeArgument = false;
14170 break;
14171 default:
14172 // Unrecognized filter function.
14173 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
14174 SkipUntil(')');
14175 return false;
14178 // Parse the function.
14179 uint16_t minElems = 1U;
14180 uint16_t maxElems = 1U;
14181 uint32_t allVariants = 0;
14182 if (!ParseFunction(functionName, &variantMask, allVariants,
14183 minElems, maxElems, *aValue)) {
14184 REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError);
14185 return false;
14188 // Get the first and only argument to the filter function.
14189 NS_ABORT_IF_FALSE(aValue->GetUnit() == eCSSUnit_Function,
14190 "expected a filter function");
14191 NS_ABORT_IF_FALSE(aValue->UnitHasArrayValue(),
14192 "filter function should be an array");
14193 NS_ABORT_IF_FALSE(aValue->GetArrayValue()->Count() == 2,
14194 "filter function should have exactly one argument");
14195 nsCSSValue& arg = aValue->GetArrayValue()->Item(1);
14197 if (rejectNegativeArgument &&
14198 ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) ||
14199 (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) {
14200 REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
14201 return false;
14204 if (clampArgumentToOne) {
14205 if (arg.GetUnit() == eCSSUnit_Number &&
14206 arg.GetFloatValue() > 1.0f) {
14207 arg.SetFloatValue(1.0f, arg.GetUnit());
14208 } else if (arg.GetUnit() == eCSSUnit_Percent &&
14209 arg.GetPercentValue() > 1.0f) {
14210 arg.SetPercentValue(1.0f);
14214 return true;
14218 * Parses a filter property value by continuously reading in urls and/or filter
14219 * functions and constructing a list.
14221 * When CSS Filters are enabled, the filter property accepts one or more SVG
14222 * reference filters and/or CSS filter functions.
14223 * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%);
14225 * When CSS Filters are disabled, the filter property only accepts one SVG
14226 * reference filter.
14227 * e.g. filter: url(#my-filter);
14229 bool
14230 CSSParserImpl::ParseFilter()
14232 nsCSSValue value;
14233 // 'inherit', 'initial', 'unset' and 'none' must be alone
14234 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
14235 nsCSSValueList* cur = value.SetListValue();
14236 while (cur) {
14237 if (!ParseSingleFilter(&cur->mValue)) {
14238 return false;
14240 if (CheckEndProperty()) {
14241 break;
14243 if (!nsLayoutUtils::CSSFiltersEnabled()) {
14244 // With CSS Filters disabled, we should only accept one SVG reference
14245 // filter.
14246 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
14247 return false;
14249 cur->mNext = new nsCSSValueList;
14250 cur = cur->mNext;
14253 AppendValue(eCSSProperty_filter, value);
14254 return true;
14257 bool
14258 CSSParserImpl::ParseTransitionProperty()
14260 nsCSSValue value;
14261 // 'inherit', 'initial', 'unset' and 'none' must be alone
14262 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
14263 // Accept a list of arbitrary identifiers. They should be
14264 // CSS properties, but we want to accept any so that we
14265 // accept properties that we don't know about yet, e.g.
14266 // transition-property: invalid-property, left, opacity;
14267 nsCSSValueList* cur = value.SetListValue();
14268 for (;;) {
14269 if (!ParseVariant(cur->mValue, VARIANT_IDENTIFIER | VARIANT_ALL, nullptr)) {
14270 return false;
14272 if (cur->mValue.GetUnit() == eCSSUnit_Ident) {
14273 nsDependentString str(cur->mValue.GetStringBufferValue());
14274 // Exclude 'none', 'inherit', 'initial' and 'unset' according to the
14275 // same rules as for 'counter-reset' in CSS 2.1.
14276 if (str.LowerCaseEqualsLiteral("none") ||
14277 str.LowerCaseEqualsLiteral("inherit") ||
14278 str.LowerCaseEqualsLiteral("initial") ||
14279 (str.LowerCaseEqualsLiteral("unset") &&
14280 nsLayoutUtils::UnsetValueEnabled())) {
14281 return false;
14284 if (!ExpectSymbol(',', true)) {
14285 break;
14287 cur->mNext = new nsCSSValueList;
14288 cur = cur->mNext;
14291 AppendValue(eCSSProperty_transition_property, value);
14292 return true;
14295 bool
14296 CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue)
14298 NS_ASSERTION(!mHavePushBack &&
14299 mToken.mType == eCSSToken_Function &&
14300 mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"),
14301 "unexpected initial state");
14303 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(4);
14305 float x1, x2, y1, y2;
14306 if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) ||
14307 !ParseTransitionTimingFunctionValueComponent(y1, ',', false) ||
14308 !ParseTransitionTimingFunctionValueComponent(x2, ',', true) ||
14309 !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) {
14310 return false;
14313 val->Item(0).SetFloatValue(x1, eCSSUnit_Number);
14314 val->Item(1).SetFloatValue(y1, eCSSUnit_Number);
14315 val->Item(2).SetFloatValue(x2, eCSSUnit_Number);
14316 val->Item(3).SetFloatValue(y2, eCSSUnit_Number);
14318 aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier);
14320 return true;
14323 bool
14324 CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent,
14325 char aStop,
14326 bool aCheckRange)
14328 if (!GetToken(true)) {
14329 return false;
14331 nsCSSToken* tk = &mToken;
14332 if (tk->mType == eCSSToken_Number) {
14333 float num = tk->mNumber;
14334 if (aCheckRange && (num < 0.0 || num > 1.0)) {
14335 return false;
14337 aComponent = num;
14338 if (ExpectSymbol(aStop, true)) {
14339 return true;
14342 return false;
14345 bool
14346 CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
14348 NS_ASSERTION(!mHavePushBack &&
14349 mToken.mType == eCSSToken_Function &&
14350 mToken.mIdent.LowerCaseEqualsLiteral("steps"),
14351 "unexpected initial state");
14353 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
14355 if (!ParseOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, nullptr)) {
14356 return false;
14359 int32_t type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
14360 if (ExpectSymbol(',', true)) {
14361 if (!GetToken(true)) {
14362 return false;
14364 type = -1;
14365 if (mToken.mType == eCSSToken_Ident) {
14366 if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
14367 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
14368 } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) {
14369 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
14372 if (type == -1) {
14373 UngetToken();
14374 return false;
14377 val->Item(1).SetIntValue(type, eCSSUnit_Enumerated);
14379 if (!ExpectSymbol(')', true)) {
14380 return false;
14383 aValue.SetArrayValue(val, eCSSUnit_Steps);
14384 return true;
14387 static nsCSSValueList*
14388 AppendValueToList(nsCSSValue& aContainer,
14389 nsCSSValueList* aTail,
14390 const nsCSSValue& aValue)
14392 nsCSSValueList* entry;
14393 if (aContainer.GetUnit() == eCSSUnit_Null) {
14394 NS_ABORT_IF_FALSE(!aTail, "should not have an entry");
14395 entry = aContainer.SetListValue();
14396 } else {
14397 NS_ABORT_IF_FALSE(!aTail->mNext, "should not have a next entry");
14398 NS_ABORT_IF_FALSE(aContainer.GetUnit() == eCSSUnit_List, "not a list");
14399 entry = new nsCSSValueList;
14400 aTail->mNext = entry;
14402 entry->mValue = aValue;
14403 return entry;
14406 CSSParserImpl::ParseAnimationOrTransitionShorthandResult
14407 CSSParserImpl::ParseAnimationOrTransitionShorthand(
14408 const nsCSSProperty* aProperties,
14409 const nsCSSValue* aInitialValues,
14410 nsCSSValue* aValues,
14411 size_t aNumProperties)
14413 nsCSSValue tempValue;
14414 // first see if 'inherit', 'initial' or 'unset' is specified. If one is,
14415 // it can be the only thing specified, so don't attempt to parse any
14416 // additional properties
14417 if (ParseVariant(tempValue, VARIANT_INHERIT, nullptr)) {
14418 for (uint32_t i = 0; i < aNumProperties; ++i) {
14419 AppendValue(aProperties[i], tempValue);
14421 return eParseAnimationOrTransitionShorthand_Inherit;
14424 static const size_t maxNumProperties = 8;
14425 NS_ABORT_IF_FALSE(aNumProperties <= maxNumProperties,
14426 "can't handle this many properties");
14427 nsCSSValueList *cur[maxNumProperties];
14428 bool parsedProperty[maxNumProperties];
14430 for (size_t i = 0; i < aNumProperties; ++i) {
14431 cur[i] = nullptr;
14433 bool atEOP = false; // at end of property?
14434 for (;;) { // loop over comma-separated transitions or animations
14435 // whether a particular subproperty was specified for this
14436 // transition or animation
14437 bool haveAnyProperty = false;
14438 for (size_t i = 0; i < aNumProperties; ++i) {
14439 parsedProperty[i] = false;
14441 for (;;) { // loop over values within a transition or animation
14442 bool foundProperty = false;
14443 // check to see if we're at the end of one full transition or
14444 // animation definition (either because we hit a comma or because
14445 // we hit the end of the property definition)
14446 if (ExpectSymbol(',', true))
14447 break;
14448 if (CheckEndProperty()) {
14449 atEOP = true;
14450 break;
14453 // else, try to parse the next transition or animation sub-property
14454 for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) {
14455 if (!parsedProperty[i]) {
14456 // if we haven't found this property yet, try to parse it
14457 if (ParseSingleValueProperty(tempValue, aProperties[i])) {
14458 parsedProperty[i] = true;
14459 cur[i] = AppendValueToList(aValues[i], cur[i], tempValue);
14460 foundProperty = true;
14461 haveAnyProperty = true;
14462 break; // out of inner loop; continue looking for next sub-property
14466 if (!foundProperty) {
14467 // We're not at a ',' or at the end of the property, but we couldn't
14468 // parse any of the sub-properties, so the declaration is invalid.
14469 return eParseAnimationOrTransitionShorthand_Error;
14473 if (!haveAnyProperty) {
14474 // Got an empty item.
14475 return eParseAnimationOrTransitionShorthand_Error;
14478 // We hit the end of the property or the end of one transition
14479 // or animation definition, add its components to the list.
14480 for (uint32_t i = 0; i < aNumProperties; ++i) {
14481 // If all of the subproperties were not explicitly specified, fill
14482 // in the missing ones with initial values.
14483 if (!parsedProperty[i]) {
14484 cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]);
14488 if (atEOP)
14489 break;
14490 // else we just hit a ',' so continue parsing the next compound transition
14493 return eParseAnimationOrTransitionShorthand_Values;
14496 bool
14497 CSSParserImpl::ParseTransition()
14499 static const nsCSSProperty kTransitionProperties[] = {
14500 eCSSProperty_transition_duration,
14501 eCSSProperty_transition_timing_function,
14502 // Must check 'transition-delay' after 'transition-duration', since
14503 // that's our assumption about what the spec means for the shorthand
14504 // syntax (the first time given is the duration, and the second
14505 // given is the delay).
14506 eCSSProperty_transition_delay,
14507 // Must check 'transition-property' after
14508 // 'transition-timing-function' since 'transition-property' accepts
14509 // any keyword.
14510 eCSSProperty_transition_property
14512 static const uint32_t numProps = MOZ_ARRAY_LENGTH(kTransitionProperties);
14513 // this is a shorthand property that accepts -property, -delay,
14514 // -duration, and -timing-function with some components missing.
14515 // there can be multiple transitions, separated with commas
14517 nsCSSValue initialValues[numProps];
14518 initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
14519 initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
14520 eCSSUnit_Enumerated);
14521 initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
14522 initialValues[3].SetAllValue();
14524 nsCSSValue values[numProps];
14526 ParseAnimationOrTransitionShorthandResult spres =
14527 ParseAnimationOrTransitionShorthand(kTransitionProperties,
14528 initialValues, values, numProps);
14529 if (spres != eParseAnimationOrTransitionShorthand_Values) {
14530 return spres != eParseAnimationOrTransitionShorthand_Error;
14533 // Make two checks on the list for 'transition-property':
14534 // + If there is more than one item, then none of the items can be
14535 // 'none'.
14536 // + None of the items can be 'inherit', 'initial' or 'unset'.
14538 NS_ABORT_IF_FALSE(kTransitionProperties[3] ==
14539 eCSSProperty_transition_property,
14540 "array index mismatch");
14541 nsCSSValueList *l = values[3].GetListValue();
14542 bool multipleItems = !!l->mNext;
14543 do {
14544 const nsCSSValue& val = l->mValue;
14545 if (val.GetUnit() == eCSSUnit_None) {
14546 if (multipleItems) {
14547 // This is a syntax error.
14548 return false;
14551 // Unbox a solitary 'none'.
14552 values[3].SetNoneValue();
14553 break;
14555 if (val.GetUnit() == eCSSUnit_Ident) {
14556 nsDependentString str(val.GetStringBufferValue());
14557 if (str.EqualsLiteral("inherit") ||
14558 str.EqualsLiteral("initial") ||
14559 (str.EqualsLiteral("unset") &&
14560 nsLayoutUtils::UnsetValueEnabled())) {
14561 return false;
14564 } while ((l = l->mNext));
14567 // Save all parsed transition sub-properties in mTempData
14568 for (uint32_t i = 0; i < numProps; ++i) {
14569 AppendValue(kTransitionProperties[i], values[i]);
14571 return true;
14574 bool
14575 CSSParserImpl::ParseAnimation()
14577 static const nsCSSProperty kAnimationProperties[] = {
14578 eCSSProperty_animation_duration,
14579 eCSSProperty_animation_timing_function,
14580 // Must check 'animation-delay' after 'animation-duration', since
14581 // that's our assumption about what the spec means for the shorthand
14582 // syntax (the first time given is the duration, and the second
14583 // given is the delay).
14584 eCSSProperty_animation_delay,
14585 eCSSProperty_animation_direction,
14586 eCSSProperty_animation_fill_mode,
14587 eCSSProperty_animation_iteration_count,
14588 eCSSProperty_animation_play_state,
14589 // Must check 'animation-name' after 'animation-timing-function',
14590 // 'animation-direction', 'animation-fill-mode',
14591 // 'animation-iteration-count', and 'animation-play-state' since
14592 // 'animation-name' accepts any keyword.
14593 eCSSProperty_animation_name
14595 static const uint32_t numProps = MOZ_ARRAY_LENGTH(kAnimationProperties);
14596 // this is a shorthand property that accepts -property, -delay,
14597 // -duration, and -timing-function with some components missing.
14598 // there can be multiple animations, separated with commas
14600 nsCSSValue initialValues[numProps];
14601 initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
14602 initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
14603 eCSSUnit_Enumerated);
14604 initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
14605 initialValues[3].SetIntValue(NS_STYLE_ANIMATION_DIRECTION_NORMAL, eCSSUnit_Enumerated);
14606 initialValues[4].SetIntValue(NS_STYLE_ANIMATION_FILL_MODE_NONE, eCSSUnit_Enumerated);
14607 initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
14608 initialValues[6].SetIntValue(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING, eCSSUnit_Enumerated);
14609 initialValues[7].SetNoneValue();
14611 nsCSSValue values[numProps];
14613 ParseAnimationOrTransitionShorthandResult spres =
14614 ParseAnimationOrTransitionShorthand(kAnimationProperties,
14615 initialValues, values, numProps);
14616 if (spres != eParseAnimationOrTransitionShorthand_Values) {
14617 return spres != eParseAnimationOrTransitionShorthand_Error;
14620 // Save all parsed animation sub-properties in mTempData
14621 for (uint32_t i = 0; i < numProps; ++i) {
14622 AppendValue(kAnimationProperties[i], values[i]);
14624 return true;
14627 bool
14628 CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow)
14630 // A shadow list item is an array, with entries in this sequence:
14631 enum {
14632 IndexX,
14633 IndexY,
14634 IndexRadius,
14635 IndexSpread, // only for box-shadow
14636 IndexColor,
14637 IndexInset // only for box-shadow
14640 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6);
14642 if (aIsBoxShadow) {
14643 // Optional inset keyword (ignore errors)
14644 ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
14645 nsCSSProps::kBoxShadowTypeKTable);
14648 nsCSSValue xOrColor;
14649 bool haveColor = false;
14650 if (!ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC,
14651 nullptr)) {
14652 return false;
14654 if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) {
14655 val->Item(IndexX) = xOrColor;
14656 } else {
14657 // Must be a color (as string or color value)
14658 NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident ||
14659 xOrColor.GetUnit() == eCSSUnit_EnumColor ||
14660 xOrColor.IsNumericColorUnit(),
14661 "Must be a color value");
14662 val->Item(IndexColor) = xOrColor;
14663 haveColor = true;
14665 // X coordinate mandatory after color
14666 if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC,
14667 nullptr)) {
14668 return false;
14672 // Y coordinate; mandatory
14673 if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC,
14674 nullptr)) {
14675 return false;
14678 // Optional radius. Ignore errors except if they pass a negative
14679 // value which we must reject. If we use ParseNonNegativeVariant
14680 // we can't tell the difference between an unspecified radius
14681 // and a negative radius.
14682 if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC,
14683 nullptr) &&
14684 val->Item(IndexRadius).IsLengthUnit() &&
14685 val->Item(IndexRadius).GetFloatValue() < 0) {
14686 return false;
14689 if (aIsBoxShadow) {
14690 // Optional spread
14691 ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC, nullptr);
14694 if (!haveColor) {
14695 // Optional color
14696 ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr);
14699 if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) {
14700 // Optional inset keyword
14701 ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
14702 nsCSSProps::kBoxShadowTypeKTable);
14705 aValue.SetArrayValue(val, eCSSUnit_Array);
14706 return true;
14709 bool
14710 CSSParserImpl::ParseShadowList(nsCSSProperty aProperty)
14712 nsAutoParseCompoundProperty compound(this);
14713 bool isBoxShadow = aProperty == eCSSProperty_box_shadow;
14715 nsCSSValue value;
14716 // 'inherit', 'initial', 'unset' and 'none' must be alone
14717 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
14718 nsCSSValueList* cur = value.SetListValue();
14719 for (;;) {
14720 if (!ParseShadowItem(cur->mValue, isBoxShadow)) {
14721 return false;
14723 if (!ExpectSymbol(',', true)) {
14724 break;
14726 cur->mNext = new nsCSSValueList;
14727 cur = cur->mNext;
14730 AppendValue(aProperty, value);
14731 return true;
14734 int32_t
14735 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix)
14737 NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
14739 int32_t nameSpaceID = kNameSpaceID_Unknown;
14740 if (mNameSpaceMap) {
14741 // user-specified identifiers are case-sensitive (bug 416106)
14742 nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
14743 if (!prefix) {
14744 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
14746 nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
14748 // else no declared namespaces
14750 if (nameSpaceID == kNameSpaceID_Unknown) { // unknown prefix, dump it
14751 REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, aPrefix);
14754 return nameSpaceID;
14757 void
14758 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
14760 if (mNameSpaceMap) {
14761 aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nullptr));
14762 } else {
14763 aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
14767 bool
14768 CSSParserImpl::ParsePaint(nsCSSProperty aPropID)
14770 nsCSSValue x, y;
14772 if (!ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL |
14773 VARIANT_OPENTYPE_SVG_KEYWORD,
14774 nsCSSProps::kContextPatternKTable)) {
14775 return false;
14778 bool canHaveFallback = x.GetUnit() == eCSSUnit_URL ||
14779 x.GetUnit() == eCSSUnit_Enumerated;
14780 if (canHaveFallback) {
14781 if (!ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr))
14782 y.SetNoneValue();
14785 if (!canHaveFallback) {
14786 AppendValue(aPropID, x);
14787 } else {
14788 nsCSSValue val;
14789 val.SetPairValue(x, y);
14790 AppendValue(aPropID, val);
14792 return true;
14795 bool
14796 CSSParserImpl::ParseDasharray()
14798 nsCSSValue value;
14800 // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
14801 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE |
14802 VARIANT_OPENTYPE_SVG_KEYWORD,
14803 nsCSSProps::kStrokeContextValueKTable)) {
14804 nsCSSValueList *cur = value.SetListValue();
14805 for (;;) {
14806 if (!ParseNonNegativeVariant(cur->mValue, VARIANT_LPN, nullptr)) {
14807 return false;
14809 if (CheckEndProperty()) {
14810 break;
14812 // skip optional commas between elements
14813 (void)ExpectSymbol(',', true);
14815 cur->mNext = new nsCSSValueList;
14816 cur = cur->mNext;
14819 AppendValue(eCSSProperty_stroke_dasharray, value);
14820 return true;
14823 bool
14824 CSSParserImpl::ParseMarker()
14826 nsCSSValue marker;
14827 if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) {
14828 AppendValue(eCSSProperty_marker_end, marker);
14829 AppendValue(eCSSProperty_marker_mid, marker);
14830 AppendValue(eCSSProperty_marker_start, marker);
14831 return true;
14833 return false;
14836 bool
14837 CSSParserImpl::ParsePaintOrder()
14839 static_assert
14840 ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) > NS_STYLE_PAINT_ORDER_LAST_VALUE,
14841 "bitfield width insufficient for paint-order constants");
14843 static const KTableValue kPaintOrderKTable[] = {
14844 eCSSKeyword_normal, NS_STYLE_PAINT_ORDER_NORMAL,
14845 eCSSKeyword_fill, NS_STYLE_PAINT_ORDER_FILL,
14846 eCSSKeyword_stroke, NS_STYLE_PAINT_ORDER_STROKE,
14847 eCSSKeyword_markers, NS_STYLE_PAINT_ORDER_MARKERS,
14848 eCSSKeyword_UNKNOWN,-1
14851 static_assert(MOZ_ARRAY_LENGTH(kPaintOrderKTable) ==
14852 2 * (NS_STYLE_PAINT_ORDER_LAST_VALUE + 2),
14853 "missing paint-order values in kPaintOrderKTable");
14855 nsCSSValue value;
14856 if (!ParseVariant(value, VARIANT_HK, kPaintOrderKTable)) {
14857 return false;
14860 uint32_t seen = 0;
14861 uint32_t order = 0;
14862 uint32_t position = 0;
14864 // Ensure that even cast to a signed int32_t when stored in CSSValue,
14865 // we have enough space for the entire paint-order value.
14866 static_assert
14867 (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE < 32,
14868 "seen and order not big enough");
14870 if (value.GetUnit() == eCSSUnit_Enumerated) {
14871 uint32_t component = static_cast<uint32_t>(value.GetIntValue());
14872 if (component != NS_STYLE_PAINT_ORDER_NORMAL) {
14873 bool parsedOK = true;
14874 for (;;) {
14875 if (seen & (1 << component)) {
14876 // Already seen this component.
14877 UngetToken();
14878 parsedOK = false;
14879 break;
14881 seen |= (1 << component);
14882 order |= (component << position);
14883 position += NS_STYLE_PAINT_ORDER_BITWIDTH;
14884 if (!ParseEnum(value, kPaintOrderKTable)) {
14885 break;
14887 component = value.GetIntValue();
14888 if (component == NS_STYLE_PAINT_ORDER_NORMAL) {
14889 // Can't have "normal" in the middle of the list of paint components.
14890 UngetToken();
14891 parsedOK = false;
14892 break;
14896 // Fill in the remaining paint-order components in the order of their
14897 // constant values.
14898 if (parsedOK) {
14899 for (component = 1;
14900 component <= NS_STYLE_PAINT_ORDER_LAST_VALUE;
14901 component++) {
14902 if (!(seen & (1 << component))) {
14903 order |= (component << position);
14904 position += NS_STYLE_PAINT_ORDER_BITWIDTH;
14910 static_assert(NS_STYLE_PAINT_ORDER_NORMAL == 0,
14911 "unexpected value for NS_STYLE_PAINT_ORDER_NORMAL");
14912 value.SetIntValue(static_cast<int32_t>(order), eCSSUnit_Enumerated);
14915 AppendValue(eCSSProperty_paint_order, value);
14916 return true;
14919 bool
14920 CSSParserImpl::BackslashDropped()
14922 return mScanner->GetEOFCharacters() &
14923 nsCSSScanner::eEOFCharacters_DropBackslash;
14926 void
14927 CSSParserImpl::AppendImpliedEOFCharacters(nsAString& aResult)
14929 nsCSSScanner::AppendImpliedEOFCharacters(mScanner->GetEOFCharacters(),
14930 aResult);
14933 bool
14934 CSSParserImpl::ParseAll()
14936 nsCSSValue value;
14937 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
14938 return false;
14941 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all) {
14942 AppendValue(*p, value);
14944 return true;
14947 bool
14948 CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
14949 nsString& aValue)
14951 CSSVariableDeclarations::Type type;
14952 nsString variableValue;
14953 bool dropBackslash;
14954 nsString impliedCharacters;
14956 // Record the token stream while parsing a variable value.
14957 if (!mInSupportsCondition) {
14958 mScanner->StartRecording();
14960 if (!ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
14961 nullptr, nullptr)) {
14962 if (!mInSupportsCondition) {
14963 mScanner->StopRecording();
14965 return false;
14968 if (!mInSupportsCondition) {
14969 if (type == CSSVariableDeclarations::eTokenStream) {
14970 // This was indeed a token stream value, so store it in variableValue.
14971 mScanner->StopRecording(variableValue);
14972 if (dropBackslash) {
14973 MOZ_ASSERT(!variableValue.IsEmpty() &&
14974 variableValue[variableValue.Length() - 1] == '\\');
14975 variableValue.Truncate(variableValue.Length() - 1);
14977 variableValue.Append(impliedCharacters);
14978 } else {
14979 // This was either 'inherit' or 'initial'; we don't need the recorded
14980 // input.
14981 mScanner->StopRecording();
14985 if (mHavePushBack && type == CSSVariableDeclarations::eTokenStream) {
14986 // If we came to the end of a valid variable declaration and a token was
14987 // pushed back, then it would have been ended by '!', ')', ';', ']' or '}'.
14988 // We need to remove it from the recorded variable value.
14989 MOZ_ASSERT(mToken.IsSymbol('!') ||
14990 mToken.IsSymbol(')') ||
14991 mToken.IsSymbol(';') ||
14992 mToken.IsSymbol(']') ||
14993 mToken.IsSymbol('}'));
14994 if (!mInSupportsCondition) {
14995 MOZ_ASSERT(!variableValue.IsEmpty());
14996 MOZ_ASSERT(variableValue[variableValue.Length() - 1] == mToken.mSymbol);
14997 variableValue.Truncate(variableValue.Length() - 1);
15001 *aType = type;
15002 aValue = variableValue;
15003 return true;
15006 bool
15007 CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
15008 bool* aDropBackslash,
15009 nsString& aImpliedCharacters,
15010 void (*aFunc)(const nsAString&, void*),
15011 void* aData)
15013 // A property value is invalid if it contains variable references and also:
15015 // * has unbalanced parens, brackets or braces
15016 // * has any BAD_STRING or BAD_URL tokens
15017 // * has any ';' or '!' tokens at the top level of a variable reference's
15018 // fallback
15020 // If the property is a custom property (i.e. a variable declaration), then
15021 // it is also invalid if it consists of no tokens, such as:
15023 // --invalid:;
15025 // Note that is valid for a custom property to have a value that consists
15026 // solely of white space, such as:
15028 // --valid: ;
15030 // Stack of closing characters for currently open constructs.
15031 StopSymbolCharStack stack;
15033 // Indexes into ')' characters in |stack| that correspond to "var(". This
15034 // is used to stop parsing when we encounter a '!' or ';' at the top level
15035 // of a variable reference's fallback.
15036 nsAutoTArray<uint32_t, 16> references;
15038 if (!GetToken(false)) {
15039 // Variable value was empty since we reached EOF.
15040 REPORT_UNEXPECTED_EOF(PEVariableEOF);
15041 return false;
15044 if (mToken.mType == eCSSToken_Symbol &&
15045 (mToken.mSymbol == '!' ||
15046 mToken.mSymbol == ')' ||
15047 mToken.mSymbol == ';' ||
15048 mToken.mSymbol == ']' ||
15049 mToken.mSymbol == '}')) {
15050 // Variable value was empty since we reached the end of the construct.
15051 UngetToken();
15052 REPORT_UNEXPECTED_TOKEN(PEVariableEmpty);
15053 return false;
15056 if (mToken.mType == eCSSToken_Whitespace) {
15057 if (!GetToken(true)) {
15058 // Variable value was white space only. This is valid.
15059 MOZ_ASSERT(!BackslashDropped());
15060 *aType = CSSVariableDeclarations::eTokenStream;
15061 *aDropBackslash = false;
15062 AppendImpliedEOFCharacters(aImpliedCharacters);
15063 return true;
15067 // Look for 'initial', 'inherit' or 'unset' as the first non-white space
15068 // token.
15069 CSSVariableDeclarations::Type type = CSSVariableDeclarations::eTokenStream;
15070 if (mToken.mType == eCSSToken_Ident) {
15071 if (mToken.mIdent.LowerCaseEqualsLiteral("initial")) {
15072 type = CSSVariableDeclarations::eInitial;
15073 } else if (mToken.mIdent.LowerCaseEqualsLiteral("inherit")) {
15074 type = CSSVariableDeclarations::eInherit;
15075 } else if (mToken.mIdent.LowerCaseEqualsLiteral("unset")) {
15076 type = CSSVariableDeclarations::eUnset;
15080 if (type != CSSVariableDeclarations::eTokenStream) {
15081 if (!GetToken(true)) {
15082 // Variable value was 'initial' or 'inherit' followed by EOF.
15083 MOZ_ASSERT(!BackslashDropped());
15084 *aType = type;
15085 *aDropBackslash = false;
15086 AppendImpliedEOFCharacters(aImpliedCharacters);
15087 return true;
15089 UngetToken();
15090 if (mToken.mType == eCSSToken_Symbol &&
15091 (mToken.mSymbol == '!' ||
15092 mToken.mSymbol == ')' ||
15093 mToken.mSymbol == ';' ||
15094 mToken.mSymbol == ']' ||
15095 mToken.mSymbol == '}')) {
15096 // Variable value was 'initial' or 'inherit' followed by the end
15097 // of the declaration.
15098 MOZ_ASSERT(!BackslashDropped());
15099 *aType = type;
15100 *aDropBackslash = false;
15101 return true;
15105 do {
15106 switch (mToken.mType) {
15107 case eCSSToken_Symbol:
15108 if (mToken.mSymbol == '(') {
15109 stack.AppendElement(')');
15110 } else if (mToken.mSymbol == '[') {
15111 stack.AppendElement(']');
15112 } else if (mToken.mSymbol == '{') {
15113 stack.AppendElement('}');
15114 } else if (mToken.mSymbol == ';' ||
15115 mToken.mSymbol == '!') {
15116 if (stack.IsEmpty()) {
15117 UngetToken();
15118 MOZ_ASSERT(!BackslashDropped());
15119 *aType = CSSVariableDeclarations::eTokenStream;
15120 *aDropBackslash = false;
15121 return true;
15122 } else if (!references.IsEmpty() &&
15123 references.LastElement() == stack.Length() - 1) {
15124 REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback);
15125 SkipUntilAllOf(stack);
15126 return false;
15128 } else if (mToken.mSymbol == ')' ||
15129 mToken.mSymbol == ']' ||
15130 mToken.mSymbol == '}') {
15131 for (;;) {
15132 if (stack.IsEmpty()) {
15133 UngetToken();
15134 MOZ_ASSERT(!BackslashDropped());
15135 *aType = CSSVariableDeclarations::eTokenStream;
15136 *aDropBackslash = false;
15137 return true;
15139 char16_t c = stack.LastElement();
15140 stack.TruncateLength(stack.Length() - 1);
15141 if (!references.IsEmpty() &&
15142 references.LastElement() == stack.Length()) {
15143 references.TruncateLength(references.Length() - 1);
15145 if (mToken.mSymbol == c) {
15146 break;
15150 break;
15152 case eCSSToken_Function:
15153 if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
15154 if (!GetToken(true)) {
15155 // EOF directly after "var(".
15156 REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF);
15157 return false;
15159 if (mToken.mType != eCSSToken_Ident ||
15160 !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
15161 // There must be an identifier directly after the "var(" and
15162 // it must be a custom property name.
15163 UngetToken();
15164 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName);
15165 SkipUntil(')');
15166 SkipUntilAllOf(stack);
15167 return false;
15169 if (aFunc) {
15170 MOZ_ASSERT(Substring(mToken.mIdent, 0,
15171 CSS_CUSTOM_NAME_PREFIX_LENGTH).
15172 EqualsLiteral("--"));
15173 // remove '--'
15174 const nsDependentSubstring varName =
15175 Substring(mToken.mIdent, CSS_CUSTOM_NAME_PREFIX_LENGTH);
15176 aFunc(varName, aData);
15178 if (!GetToken(true)) {
15179 // EOF right after "var(<ident>".
15180 stack.AppendElement(')');
15181 } else if (mToken.IsSymbol(',')) {
15182 // Variable reference with fallback.
15183 if (!GetToken(false) || mToken.IsSymbol(')')) {
15184 // Comma must be followed by at least one fallback token.
15185 REPORT_UNEXPECTED(PEExpectedVariableFallback);
15186 SkipUntilAllOf(stack);
15187 return false;
15189 UngetToken();
15190 references.AppendElement(stack.Length());
15191 stack.AppendElement(')');
15192 } else if (mToken.IsSymbol(')')) {
15193 // Correctly closed variable reference.
15194 } else {
15195 // Malformed variable reference.
15196 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen);
15197 SkipUntil(')');
15198 SkipUntilAllOf(stack);
15199 return false;
15201 } else {
15202 stack.AppendElement(')');
15204 break;
15206 case eCSSToken_Bad_String:
15207 SkipUntilAllOf(stack);
15208 return false;
15210 case eCSSToken_Bad_URL:
15211 SkipUntil(')');
15212 SkipUntilAllOf(stack);
15213 return false;
15215 default:
15216 break;
15218 } while (GetToken(true));
15220 // Append any implied closing characters.
15221 *aDropBackslash = BackslashDropped();
15222 AppendImpliedEOFCharacters(aImpliedCharacters);
15223 uint32_t i = stack.Length();
15224 while (i--) {
15225 aImpliedCharacters.Append(stack[i]);
15228 *aType = type;
15229 return true;
15232 bool
15233 CSSParserImpl::IsValueValidForProperty(const nsCSSProperty aPropID,
15234 const nsAString& aPropValue)
15236 mData.AssertInitialState();
15237 mTempData.AssertInitialState();
15239 nsCSSScanner scanner(aPropValue, 0);
15240 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr);
15241 InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
15243 nsAutoSuppressErrors suppressErrors(this);
15245 mSection = eCSSSection_General;
15246 scanner.SetSVGMode(false);
15248 // Check for unknown properties
15249 if (eCSSProperty_UNKNOWN == aPropID) {
15250 ReleaseScanner();
15251 return false;
15254 // Check that the property and value parse successfully
15255 bool parsedOK = ParseProperty(aPropID);
15257 // Check for priority
15258 parsedOK = parsedOK && ParsePriority() != ePriority_Error;
15260 // We should now be at EOF
15261 parsedOK = parsedOK && !GetToken(true);
15263 mTempData.ClearProperty(aPropID);
15264 mTempData.AssertInitialState();
15265 mData.AssertInitialState();
15267 CLEAR_ERROR();
15268 ReleaseScanner();
15270 return parsedOK;
15273 } // anonymous namespace
15275 // Recycling of parser implementation objects
15277 static CSSParserImpl* gFreeList = nullptr;
15279 nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
15280 CSSStyleSheet* aSheet)
15282 CSSParserImpl *impl = gFreeList;
15283 if (impl) {
15284 gFreeList = impl->mNextFree;
15285 impl->mNextFree = nullptr;
15286 } else {
15287 impl = new CSSParserImpl();
15290 if (aLoader) {
15291 impl->SetChildLoader(aLoader);
15292 impl->SetQuirkMode(aLoader->GetCompatibilityMode() ==
15293 eCompatibility_NavQuirks);
15295 if (aSheet) {
15296 impl->SetStyleSheet(aSheet);
15299 mImpl = static_cast<void*>(impl);
15302 nsCSSParser::~nsCSSParser()
15304 CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl);
15305 impl->Reset();
15306 impl->mNextFree = gFreeList;
15307 gFreeList = impl;
15310 /* static */ void
15311 nsCSSParser::Shutdown()
15313 CSSParserImpl *tofree = gFreeList;
15314 CSSParserImpl *next;
15315 while (tofree)
15317 next = tofree->mNextFree;
15318 delete tofree;
15319 tofree = next;
15323 // Wrapper methods
15325 nsresult
15326 nsCSSParser::SetStyleSheet(CSSStyleSheet* aSheet)
15328 return static_cast<CSSParserImpl*>(mImpl)->
15329 SetStyleSheet(aSheet);
15332 nsresult
15333 nsCSSParser::SetQuirkMode(bool aQuirkMode)
15335 return static_cast<CSSParserImpl*>(mImpl)->
15336 SetQuirkMode(aQuirkMode);
15339 nsresult
15340 nsCSSParser::SetChildLoader(mozilla::css::Loader* aChildLoader)
15342 return static_cast<CSSParserImpl*>(mImpl)->
15343 SetChildLoader(aChildLoader);
15346 nsresult
15347 nsCSSParser::ParseSheet(const nsAString& aInput,
15348 nsIURI* aSheetURI,
15349 nsIURI* aBaseURI,
15350 nsIPrincipal* aSheetPrincipal,
15351 uint32_t aLineNumber,
15352 bool aAllowUnsafeRules)
15354 return static_cast<CSSParserImpl*>(mImpl)->
15355 ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
15356 aAllowUnsafeRules);
15359 nsresult
15360 nsCSSParser::ParseStyleAttribute(const nsAString& aAttributeValue,
15361 nsIURI* aDocURI,
15362 nsIURI* aBaseURI,
15363 nsIPrincipal* aNodePrincipal,
15364 css::StyleRule** aResult)
15366 return static_cast<CSSParserImpl*>(mImpl)->
15367 ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI,
15368 aNodePrincipal, aResult);
15371 nsresult
15372 nsCSSParser::ParseDeclarations(const nsAString& aBuffer,
15373 nsIURI* aSheetURI,
15374 nsIURI* aBaseURI,
15375 nsIPrincipal* aSheetPrincipal,
15376 css::Declaration* aDeclaration,
15377 bool* aChanged)
15379 return static_cast<CSSParserImpl*>(mImpl)->
15380 ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal,
15381 aDeclaration, aChanged);
15384 nsresult
15385 nsCSSParser::ParseRule(const nsAString& aRule,
15386 nsIURI* aSheetURI,
15387 nsIURI* aBaseURI,
15388 nsIPrincipal* aSheetPrincipal,
15389 css::Rule** aResult)
15391 return static_cast<CSSParserImpl*>(mImpl)->
15392 ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult);
15395 nsresult
15396 nsCSSParser::ParseProperty(const nsCSSProperty aPropID,
15397 const nsAString& aPropValue,
15398 nsIURI* aSheetURI,
15399 nsIURI* aBaseURI,
15400 nsIPrincipal* aSheetPrincipal,
15401 css::Declaration* aDeclaration,
15402 bool* aChanged,
15403 bool aIsImportant,
15404 bool aIsSVGMode)
15406 return static_cast<CSSParserImpl*>(mImpl)->
15407 ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
15408 aSheetPrincipal, aDeclaration, aChanged,
15409 aIsImportant, aIsSVGMode);
15412 nsresult
15413 nsCSSParser::ParseVariable(const nsAString& aVariableName,
15414 const nsAString& aPropValue,
15415 nsIURI* aSheetURI,
15416 nsIURI* aBaseURI,
15417 nsIPrincipal* aSheetPrincipal,
15418 css::Declaration* aDeclaration,
15419 bool* aChanged,
15420 bool aIsImportant)
15422 return static_cast<CSSParserImpl*>(mImpl)->
15423 ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI,
15424 aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
15427 void
15428 nsCSSParser::ParseMediaList(const nsSubstring& aBuffer,
15429 nsIURI* aURI,
15430 uint32_t aLineNumber,
15431 nsMediaList* aMediaList,
15432 bool aHTMLMode)
15434 static_cast<CSSParserImpl*>(mImpl)->
15435 ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode);
15438 bool
15439 nsCSSParser::ParseSourceSizeList(const nsAString& aBuffer,
15440 nsIURI* aURI,
15441 uint32_t aLineNumber,
15442 InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
15443 InfallibleTArray<nsCSSValue>& aValues,
15444 bool aHTMLMode)
15446 return static_cast<CSSParserImpl*>(mImpl)->
15447 ParseSourceSizeList(aBuffer, aURI, aLineNumber, aQueries, aValues,
15448 aHTMLMode);
15451 bool
15452 nsCSSParser::ParseFontFamilyListString(const nsSubstring& aBuffer,
15453 nsIURI* aURI,
15454 uint32_t aLineNumber,
15455 nsCSSValue& aValue)
15457 return static_cast<CSSParserImpl*>(mImpl)->
15458 ParseFontFamilyListString(aBuffer, aURI, aLineNumber, aValue);
15461 bool
15462 nsCSSParser::ParseColorString(const nsSubstring& aBuffer,
15463 nsIURI* aURI,
15464 uint32_t aLineNumber,
15465 nsCSSValue& aValue,
15466 bool aSuppressErrors /* false */)
15468 return static_cast<CSSParserImpl*>(mImpl)->
15469 ParseColorString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors);
15472 nsresult
15473 nsCSSParser::ParseSelectorString(const nsSubstring& aSelectorString,
15474 nsIURI* aURI,
15475 uint32_t aLineNumber,
15476 nsCSSSelectorList** aSelectorList)
15478 return static_cast<CSSParserImpl*>(mImpl)->
15479 ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList);
15482 already_AddRefed<nsCSSKeyframeRule>
15483 nsCSSParser::ParseKeyframeRule(const nsSubstring& aBuffer,
15484 nsIURI* aURI,
15485 uint32_t aLineNumber)
15487 return static_cast<CSSParserImpl*>(mImpl)->
15488 ParseKeyframeRule(aBuffer, aURI, aLineNumber);
15491 bool
15492 nsCSSParser::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
15493 nsIURI* aURI,
15494 uint32_t aLineNumber,
15495 InfallibleTArray<float>& aSelectorList)
15497 return static_cast<CSSParserImpl*>(mImpl)->
15498 ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber,
15499 aSelectorList);
15502 bool
15503 nsCSSParser::EvaluateSupportsDeclaration(const nsAString& aProperty,
15504 const nsAString& aValue,
15505 nsIURI* aDocURL,
15506 nsIURI* aBaseURL,
15507 nsIPrincipal* aDocPrincipal)
15509 return static_cast<CSSParserImpl*>(mImpl)->
15510 EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL,
15511 aDocPrincipal);
15514 bool
15515 nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition,
15516 nsIURI* aDocURL,
15517 nsIURI* aBaseURL,
15518 nsIPrincipal* aDocPrincipal)
15520 return static_cast<CSSParserImpl*>(mImpl)->
15521 EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL, aDocPrincipal);
15524 bool
15525 nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue,
15526 VariableEnumFunc aFunc,
15527 void* aData)
15529 return static_cast<CSSParserImpl*>(mImpl)->
15530 EnumerateVariableReferences(aPropertyValue, aFunc, aData);
15533 bool
15534 nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue,
15535 const CSSVariableValues* aVariables,
15536 nsString& aResult,
15537 nsCSSTokenSerializationType& aFirstToken,
15538 nsCSSTokenSerializationType& aLastToken)
15540 return static_cast<CSSParserImpl*>(mImpl)->
15541 ResolveVariableValue(aPropertyValue, aVariables,
15542 aResult, aFirstToken, aLastToken);
15545 void
15546 nsCSSParser::ParsePropertyWithVariableReferences(
15547 nsCSSProperty aPropertyID,
15548 nsCSSProperty aShorthandPropertyID,
15549 const nsAString& aValue,
15550 const CSSVariableValues* aVariables,
15551 nsRuleData* aRuleData,
15552 nsIURI* aDocURL,
15553 nsIURI* aBaseURL,
15554 nsIPrincipal* aDocPrincipal,
15555 CSSStyleSheet* aSheet,
15556 uint32_t aLineNumber,
15557 uint32_t aLineOffset)
15559 static_cast<CSSParserImpl*>(mImpl)->
15560 ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID,
15561 aValue, aVariables, aRuleData, aDocURL,
15562 aBaseURL, aDocPrincipal, aSheet,
15563 aLineNumber, aLineOffset);
15566 bool
15567 nsCSSParser::ParseCounterStyleName(const nsAString& aBuffer,
15568 nsIURI* aURL,
15569 nsAString& aName)
15571 return static_cast<CSSParserImpl*>(mImpl)->
15572 ParseCounterStyleName(aBuffer, aURL, aName);
15575 bool
15576 nsCSSParser::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
15577 const nsAString& aBuffer,
15578 nsIURI* aSheetURL,
15579 nsIURI* aBaseURL,
15580 nsIPrincipal* aSheetPrincipal,
15581 nsCSSValue& aValue)
15583 return static_cast<CSSParserImpl*>(mImpl)->
15584 ParseCounterDescriptor(aDescID, aBuffer,
15585 aSheetURL, aBaseURL, aSheetPrincipal, aValue);
15588 bool
15589 nsCSSParser::ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
15590 const nsAString& aBuffer,
15591 nsIURI* aSheetURL,
15592 nsIURI* aBaseURL,
15593 nsIPrincipal* aSheetPrincipal,
15594 nsCSSValue& aValue)
15596 return static_cast<CSSParserImpl*>(mImpl)->
15597 ParseFontFaceDescriptor(aDescID, aBuffer,
15598 aSheetURL, aBaseURL, aSheetPrincipal, aValue);
15601 bool
15602 nsCSSParser::IsValueValidForProperty(const nsCSSProperty aPropID,
15603 const nsAString& aPropValue)
15605 return static_cast<CSSParserImpl*>(mImpl)->
15606 IsValueValidForProperty(aPropID, aPropValue);