Bumping manifests a=b2g-bump
[gecko.git] / layout / style / nsCSSParser.cpp
blob49aee70a262e2967062ac3818ce3b41028383717
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 IsValueValidForProperty(const nsCSSProperty aPropID,
221 const nsAString& aPropValue);
223 typedef nsCSSParser::VariableEnumFunc VariableEnumFunc;
226 * Parses a CSS token stream value and invokes a callback function for each
227 * variable reference that is encountered.
229 * @param aPropertyValue The CSS token stream value.
230 * @param aFunc The callback function to invoke; its parameters are the
231 * variable name found and the aData argument passed in to this function.
232 * @param aData User data to pass in to the callback.
233 * @return Whether aPropertyValue could be parsed as a valid CSS token stream
234 * value (e.g., without syntactic errors in variable references).
236 bool EnumerateVariableReferences(const nsAString& aPropertyValue,
237 VariableEnumFunc aFunc,
238 void* aData);
241 * Parses aPropertyValue as a CSS token stream value and resolves any
242 * variable references using the variables in aVariables.
244 * @param aPropertyValue The CSS token stream value.
245 * @param aVariables The set of variable values to use when resolving variable
246 * references.
247 * @param aResult Out parameter that gets the resolved value.
248 * @param aFirstToken Out parameter that gets the type of the first token in
249 * aResult.
250 * @param aLastToken Out parameter that gets the type of the last token in
251 * aResult.
252 * @return Whether aResult could be parsed successfully and variable reference
253 * substitution succeeded.
255 bool ResolveVariableValue(const nsAString& aPropertyValue,
256 const CSSVariableValues* aVariables,
257 nsString& aResult,
258 nsCSSTokenSerializationType& aFirstToken,
259 nsCSSTokenSerializationType& aLastToken);
262 * Parses a string as a CSS token stream value for particular property,
263 * resolving any variable references. The parsed property value is stored
264 * in the specified nsRuleData object. If aShorthandPropertyID has a value
265 * other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
266 * otherwise, aPropertyID will be parsed. Either way, only aPropertyID,
267 * a longhand property, will be copied over to the rule data.
269 * If the property cannot be parsed, it will be treated as if 'initial' or
270 * 'inherit' were specified, for non-inherited and inherited properties
271 * respectively.
273 * @param aPropertyID The ID of the longhand property whose value is to be
274 * copied to the rule data.
275 * @param aShorthandPropertyID The ID of the shorthand property to be parsed.
276 * If a longhand property is to be parsed, aPropertyID is that property,
277 * and aShorthandPropertyID must be eCSSProperty_UNKNOWN.
278 * @param aValue The CSS token stream value.
279 * @param aVariables The set of variable values to use when resolving variable
280 * references.
281 * @param aRuleData The rule data object into which parsed property value for
282 * aPropertyID will be stored.
284 void ParsePropertyWithVariableReferences(nsCSSProperty aPropertyID,
285 nsCSSProperty aShorthandPropertyID,
286 const nsAString& aValue,
287 const CSSVariableValues* aVariables,
288 nsRuleData* aRuleData,
289 nsIURI* aDocURL,
290 nsIURI* aBaseURL,
291 nsIPrincipal* aDocPrincipal,
292 CSSStyleSheet* aSheet,
293 uint32_t aLineNumber,
294 uint32_t aLineOffset);
296 nsCSSProperty LookupEnabledProperty(const nsAString& aProperty) {
297 static_assert(nsCSSProps::eEnabledForAllContent == 0,
298 "nsCSSProps::eEnabledForAllContent should be zero for "
299 "this bitfield to work");
300 nsCSSProps::EnabledState enabledState = nsCSSProps::eEnabledForAllContent;
301 if (mUnsafeRulesEnabled) {
302 enabledState |= nsCSSProps::eEnabledInUASheets;
304 if (mIsChromeOrCertifiedApp) {
305 enabledState |= nsCSSProps::eEnabledInChromeOrCertifiedApp;
307 return nsCSSProps::LookupProperty(aProperty, enabledState);
310 protected:
311 class nsAutoParseCompoundProperty;
312 friend class nsAutoParseCompoundProperty;
314 class nsAutoFailingSupportsRule;
315 friend class nsAutoFailingSupportsRule;
317 class nsAutoSuppressErrors;
318 friend class nsAutoSuppressErrors;
320 void AppendRule(css::Rule* aRule);
321 friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule
324 * This helper class automatically calls SetParsingCompoundProperty in its
325 * constructor and takes care of resetting it to false in its destructor.
327 class nsAutoParseCompoundProperty {
328 public:
329 explicit nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
331 NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
332 "already parsing compound property");
333 NS_ASSERTION(aParser, "Null parser?");
334 aParser->SetParsingCompoundProperty(true);
337 ~nsAutoParseCompoundProperty()
339 mParser->SetParsingCompoundProperty(false);
341 private:
342 CSSParserImpl* mParser;
346 * This helper class conditionally sets mInFailingSupportsRule to
347 * true if aCondition = false, and resets it to its original value in its
348 * destructor. If we are already somewhere within a failing @supports
349 * rule, passing in aCondition = true does not change mInFailingSupportsRule.
351 class nsAutoFailingSupportsRule {
352 public:
353 nsAutoFailingSupportsRule(CSSParserImpl* aParser,
354 bool aCondition)
355 : mParser(aParser),
356 mOriginalValue(aParser->mInFailingSupportsRule)
358 if (!aCondition) {
359 mParser->mInFailingSupportsRule = true;
363 ~nsAutoFailingSupportsRule()
365 mParser->mInFailingSupportsRule = mOriginalValue;
368 private:
369 CSSParserImpl* mParser;
370 bool mOriginalValue;
374 * Auto class to set aParser->mSuppressErrors to the specified value
375 * and restore it to its original value later.
377 class nsAutoSuppressErrors {
378 public:
379 explicit nsAutoSuppressErrors(CSSParserImpl* aParser,
380 bool aSuppressErrors = true)
381 : mParser(aParser),
382 mOriginalValue(aParser->mSuppressErrors)
384 mParser->mSuppressErrors = aSuppressErrors;
387 ~nsAutoSuppressErrors()
389 mParser->mSuppressErrors = mOriginalValue;
392 private:
393 CSSParserImpl* mParser;
394 bool mOriginalValue;
397 // the caller must hold on to aString until parsing is done
398 void InitScanner(nsCSSScanner& aScanner,
399 css::ErrorReporter& aReporter,
400 nsIURI* aSheetURI, nsIURI* aBaseURI,
401 nsIPrincipal* aSheetPrincipal);
402 void ReleaseScanner(void);
403 bool IsSVGMode() const {
404 return mScanner->IsSVGMode();
408 * Saves the current input state, which includes any currently pushed
409 * back token, and the current position of the scanner.
411 void SaveInputState(CSSParserInputState& aState);
414 * Restores the saved input state by pushing back any saved pushback
415 * token and calling RestoreSavedPosition on the scanner.
417 void RestoreSavedInputState(const CSSParserInputState& aState);
419 bool GetToken(bool aSkipWS);
420 void UngetToken();
421 bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum);
423 bool ExpectSymbol(char16_t aSymbol, bool aSkipWS);
424 bool ExpectEndProperty();
425 bool CheckEndProperty();
426 nsSubstring* NextIdent();
428 // returns true when the stop symbol is found, and false for EOF
429 bool SkipUntil(char16_t aStopSymbol);
430 void SkipUntilOneOf(const char16_t* aStopSymbolChars);
431 // For each character in aStopSymbolChars from the end of the array
432 // to the start, calls SkipUntil with that character.
433 typedef nsAutoTArray<char16_t, 16> StopSymbolCharStack;
434 void SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars);
435 // returns true if the stop symbol or EOF is found, and false for an
436 // unexpected ')', ']' or '}'; this not safe to call outside variable
437 // resolution, as it doesn't handle mismatched content
438 bool SkipBalancedContentUntil(char16_t aStopSymbol);
440 void SkipRuleSet(bool aInsideBraces);
441 bool SkipAtRule(bool aInsideBlock);
442 bool SkipDeclaration(bool aCheckForBraces);
444 void PushGroup(css::GroupRule* aRule);
445 void PopGroup();
447 bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData,
448 bool aInsideBraces = false);
449 bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData,
450 bool aInAtRule);
451 bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
452 bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
453 bool ParseURLOrString(nsString& aURL);
454 bool GatherMedia(nsMediaList* aMedia, bool aInAtRule);
456 enum eMediaQueryType { eMediaQueryNormal,
457 // Parsing an at rule
458 eMediaQueryAtRule,
459 // Attempt to consume a single media-condition and
460 // stop. Note that the spec defines "expression and/or
461 // expression" as one condition but "expression,
462 // expression" as two.
463 eMediaQuerySingleCondition };
464 bool ParseMediaQuery(eMediaQueryType aMode, nsMediaQuery **aQuery,
465 bool *aHitStop);
466 bool ParseMediaQueryExpression(nsMediaQuery* aQuery);
467 void ProcessImport(const nsString& aURLSpec,
468 nsMediaList* aMedia,
469 RuleAppendFunc aAppendFunc,
470 void* aProcessData,
471 uint32_t aLineNumber,
472 uint32_t aColumnNumber);
473 bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc,
474 void* aProcessData);
475 bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
476 bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
477 bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
478 void ProcessNameSpace(const nsString& aPrefix,
479 const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
480 void* aProcessData,
481 uint32_t aLineNumber, uint32_t aColumnNumber);
483 bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
484 bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
485 void* aProcessData);
486 bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule);
487 bool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
488 bool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
489 nsCSSValue& aValue);
491 bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
492 bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData);
493 already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule();
494 bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList);
496 bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData);
497 bool ParseSupportsCondition(bool& aConditionMet);
498 bool ParseSupportsConditionNegation(bool& aConditionMet);
499 bool ParseSupportsConditionInParens(bool& aConditionMet);
500 bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet);
501 bool ParseSupportsConditionTerms(bool& aConditionMet);
502 enum SupportsConditionTermOperator { eAnd, eOr };
503 bool ParseSupportsConditionTermsAfterOperator(
504 bool& aConditionMet,
505 SupportsConditionTermOperator aOperator);
507 bool ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aProcessData);
508 bool ParseCounterStyleName(nsAString& aName, bool aForDefinition);
509 bool ParseCounterDescriptor(nsCSSCounterStyleRule *aRule);
510 bool ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
511 nsCSSValue& aValue);
512 bool ParseCounterRange(nsCSSValuePair& aPair);
515 * Parses the current input stream for a CSS token stream value and resolves
516 * any variable references using the variables in aVariables.
518 * @param aVariables The set of variable values to use when resolving variable
519 * references.
520 * @param aResult Out parameter that, if the function returns true, will be
521 * replaced with the resolved value.
522 * @return Whether aResult could be parsed successfully and variable reference
523 * substitution succeeded.
525 bool ResolveValueWithVariableReferences(
526 const CSSVariableValues* aVariables,
527 nsString& aResult,
528 nsCSSTokenSerializationType& aResultFirstToken,
529 nsCSSTokenSerializationType& aResultLastToken);
530 // Helper function for ResolveValueWithVariableReferences.
531 bool ResolveValueWithVariableReferencesRec(
532 nsString& aResult,
533 nsCSSTokenSerializationType& aResultFirstToken,
534 nsCSSTokenSerializationType& aResultLastToken,
535 const CSSVariableValues* aVariables);
537 enum nsSelectorParsingStatus {
538 // we have parsed a selector and we saw a token that cannot be
539 // part of a selector:
540 eSelectorParsingStatus_Done,
541 // we should continue parsing the selector:
542 eSelectorParsingStatus_Continue,
543 // we saw an unexpected token or token value,
544 // or we saw end-of-file with an unfinished selector:
545 eSelectorParsingStatus_Error
547 nsSelectorParsingStatus ParseIDSelector(int32_t& aDataMask,
548 nsCSSSelector& aSelector);
550 nsSelectorParsingStatus ParseClassSelector(int32_t& aDataMask,
551 nsCSSSelector& aSelector);
553 // aPseudoElement and aPseudoElementArgs are the location where
554 // pseudo-elements (as opposed to pseudo-classes) are stored;
555 // pseudo-classes are stored on aSelector. aPseudoElement and
556 // aPseudoElementArgs must be non-null iff !aIsNegated.
557 nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask,
558 nsCSSSelector& aSelector,
559 bool aIsNegated,
560 nsIAtom** aPseudoElement,
561 nsAtomList** aPseudoElementArgs,
562 nsCSSPseudoElements::Type* aPseudoElementType);
564 nsSelectorParsingStatus ParseAttributeSelector(int32_t& aDataMask,
565 nsCSSSelector& aSelector);
567 nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask,
568 nsCSSSelector& aSelector,
569 bool aIsNegated);
571 nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
572 nsCSSPseudoClasses::Type aType);
574 nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
575 nsCSSPseudoClasses::Type aType);
577 nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
578 nsCSSPseudoClasses::Type aType);
580 nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask,
581 nsCSSSelector& aSelector);
583 // If aStopChar is non-zero, the selector list is done when we hit
584 // aStopChar. Otherwise, it's done when we hit EOF.
585 bool ParseSelectorList(nsCSSSelectorList*& aListHead,
586 char16_t aStopChar);
587 bool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
588 bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator);
590 enum {
591 eParseDeclaration_InBraces = 1 << 0,
592 eParseDeclaration_AllowImportant = 1 << 1
594 enum nsCSSContextType {
595 eCSSContext_General,
596 eCSSContext_Page
599 css::Declaration* ParseDeclarationBlock(uint32_t aFlags,
600 nsCSSContextType aContext = eCSSContext_General);
601 bool ParseDeclaration(css::Declaration* aDeclaration,
602 uint32_t aFlags,
603 bool aMustCallValueAppended,
604 bool* aChanged,
605 nsCSSContextType aContext = eCSSContext_General);
607 bool ParseProperty(nsCSSProperty aPropID);
608 bool ParsePropertyByFunction(nsCSSProperty aPropID);
609 bool ParseSingleValueProperty(nsCSSValue& aValue,
610 nsCSSProperty aPropID);
612 enum PriorityParsingStatus {
613 ePriority_None,
614 ePriority_Important,
615 ePriority_Error
617 PriorityParsingStatus ParsePriority();
619 #ifdef MOZ_XUL
620 bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs);
621 #endif
623 void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties);
625 // Property specific parsing routines
626 bool ParseBackground();
628 struct BackgroundParseState {
629 nsCSSValue& mColor;
630 nsCSSValueList* mImage;
631 nsCSSValuePairList* mRepeat;
632 nsCSSValueList* mAttachment;
633 nsCSSValueList* mClip;
634 nsCSSValueList* mOrigin;
635 nsCSSValueList* mPosition;
636 nsCSSValuePairList* mSize;
637 BackgroundParseState(
638 nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
639 nsCSSValueList* aAttachment, nsCSSValueList* aClip,
640 nsCSSValueList* aOrigin, nsCSSValueList* aPosition,
641 nsCSSValuePairList* aSize) :
642 mColor(aColor), mImage(aImage), mRepeat(aRepeat),
643 mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
644 mPosition(aPosition), mSize(aSize) {};
647 bool ParseBackgroundItem(BackgroundParseState& aState);
649 bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id
650 bool ParseBackgroundRepeat();
651 bool ParseBackgroundRepeatValues(nsCSSValuePair& aValue);
652 bool ParseBackgroundPosition();
654 // ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
655 // which is still used by some properties. See ParseBackgroundPositionValues
656 // for the css3-background syntax.
657 bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit,
658 bool aAllowExplicitCenter = true); // deprecated
659 bool ParseBackgroundPositionValues(nsCSSValue& aOut, bool aAcceptsInherit);
661 bool ParseBackgroundSize();
662 bool ParseBackgroundSizeValues(nsCSSValuePair& aOut);
663 bool ParseBorderColor();
664 bool ParseBorderColors(nsCSSProperty aProperty);
665 void SetBorderImageInitialValues();
666 bool ParseBorderImageRepeat(bool aAcceptsInherit);
667 // If ParseBorderImageSlice returns false, aConsumedTokens indicates
668 // whether or not any tokens were consumed (in other words, was the property
669 // in error or just not present). If ParseBorderImageSlice returns true
670 // aConsumedTokens is always true.
671 bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens);
672 bool ParseBorderImageWidth(bool aAcceptsInherit);
673 bool ParseBorderImageOutset(bool aAcceptsInherit);
674 bool ParseBorderImage();
675 bool ParseBorderSpacing();
676 bool ParseBorderSide(const nsCSSProperty aPropIDs[],
677 bool aSetAllSides);
678 bool ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
679 int32_t aSourceType);
680 bool ParseBorderStyle();
681 bool ParseBorderWidth();
683 bool ParseCalc(nsCSSValue &aValue, int32_t aVariantMask);
684 bool ParseCalcAdditiveExpression(nsCSSValue& aValue,
685 int32_t& aVariantMask);
686 bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
687 int32_t& aVariantMask,
688 bool *aHadFinalWS);
689 bool ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask);
690 bool RequireWhitespace();
692 // For "flex" shorthand property, defined in CSS Flexbox spec
693 bool ParseFlex();
694 // For "flex-flow" shorthand property, defined in CSS Flexbox spec
695 bool ParseFlexFlow();
697 // CSS Grid
698 bool ParseGridAutoFlow();
700 // Parse a <line-names> expression.
701 // If successful, either leave aValue untouched,
702 // to indicate that we parsed the empty list,
703 // or set it to a eCSSUnit_List of eCSSUnit_Ident.
705 // To parse an optional <line-names> (ie. if not finding an open paren
706 // is considered the same as an empty list),
707 // treat CSSParseResult::NotFound the same as CSSParseResult::Ok.
709 // If aValue is already a eCSSUnit_List, append to that list.
710 CSSParseResult ParseGridLineNames(nsCSSValue& aValue);
711 bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr);
712 bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue);
713 bool ParseGridTrackBreadth(nsCSSValue& aValue);
714 CSSParseResult ParseGridTrackSize(nsCSSValue& aValue);
715 bool ParseGridAutoColumnsRows(nsCSSProperty aPropID);
716 bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr);
718 // Assuming a [ <line-names>? ] has already been parsed,
719 // parse the rest of a <track-list>.
721 // This exists because [ <line-names>? ] is ambiguous in the
722 // 'grid-template' shorthand: it can be either the start of a <track-list>,
723 // or of the intertwined syntax that sets both
724 // grid-template-rows and grid-template-areas.
726 // On success, |aValue| will be a list of odd length >= 3,
727 // starting with a <line-names> (which is itself a list)
728 // and alternating between that and <track-size>.
729 bool ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
730 const nsCSSValue& aFirstLineNames);
731 bool ParseGridTemplateColumnsRows(nsCSSProperty aPropID);
733 // |aAreaIndices| is a lookup table to help us parse faster,
734 // mapping area names to indices in |aResult.mNamedAreas|.
735 bool ParseGridTemplateAreasLine(const nsAutoString& aInput,
736 css::GridTemplateAreasValue* aResult,
737 nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices);
738 bool ParseGridTemplateAreas();
739 bool ParseGridTemplate();
740 bool ParseGridTemplateAfterSlash(bool aColumnsIsTrackList);
741 bool ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames);
742 bool ParseGrid();
743 bool ParseGridShorthandAutoProps();
744 bool ParseGridLine(nsCSSValue& aValue);
745 bool ParseGridColumnRowStartEnd(nsCSSProperty aPropID);
746 bool ParseGridColumnRow(nsCSSProperty aStartPropID,
747 nsCSSProperty aEndPropID);
748 bool ParseGridArea();
750 // for 'clip' and '-moz-image-region'
751 bool ParseRect(nsCSSProperty aPropID);
752 bool ParseColumns();
753 bool ParseContent();
754 bool ParseCounterData(nsCSSProperty aPropID);
755 bool ParseCursor();
756 bool ParseFont();
757 bool ParseFontSynthesis(nsCSSValue& aValue);
758 bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue);
759 bool ParseFontVariantAlternates(nsCSSValue& aValue);
760 bool MergeBitmaskValue(int32_t aNewValue, const int32_t aMasks[],
761 int32_t& aMergedValue);
762 bool ParseBitmaskValues(nsCSSValue& aValue,
763 const KTableValue aKeywordTable[],
764 const int32_t aMasks[]);
765 bool ParseFontVariantEastAsian(nsCSSValue& aValue);
766 bool ParseFontVariantLigatures(nsCSSValue& aValue);
767 bool ParseFontVariantNumeric(nsCSSValue& aValue);
768 bool ParseFontVariant();
769 bool ParseFontWeight(nsCSSValue& aValue);
770 bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword, bool& aQuoted);
771 bool ParseFamily(nsCSSValue& aValue);
772 bool ParseFontFeatureSettings(nsCSSValue& aValue);
773 bool ParseFontSrc(nsCSSValue& aValue);
774 bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
775 bool ParseFontRanges(nsCSSValue& aValue);
776 bool ParseListStyle();
777 bool ParseListStyleType(nsCSSValue& aValue);
778 bool ParseMargin();
779 bool ParseMarks(nsCSSValue& aValue);
780 bool ParseTransform(bool aIsPrefixed);
781 bool ParseOutline();
782 bool ParseOverflow();
783 bool ParsePadding();
784 bool ParseQuotes();
785 bool ParseSize();
786 bool ParseTextAlign(nsCSSValue& aValue,
787 const KTableValue aTable[]);
788 bool ParseTextAlign(nsCSSValue& aValue);
789 bool ParseTextAlignLast(nsCSSValue& aValue);
790 bool ParseTextDecoration();
791 bool ParseTextDecorationLine(nsCSSValue& aValue);
792 bool ParseTextCombineUpright(nsCSSValue& aValue);
793 bool ParseTextOverflow(nsCSSValue& aValue);
794 bool ParseTouchAction(nsCSSValue& aValue);
796 bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow);
797 bool ParseShadowList(nsCSSProperty aProperty);
798 bool ParseTransitionProperty();
799 bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
800 bool ParseTransitionTimingFunctionValueComponent(float& aComponent,
801 char aStop,
802 bool aCheckRange);
803 bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
804 enum ParseAnimationOrTransitionShorthandResult {
805 eParseAnimationOrTransitionShorthand_Values,
806 eParseAnimationOrTransitionShorthand_Inherit,
807 eParseAnimationOrTransitionShorthand_Error
809 ParseAnimationOrTransitionShorthandResult
810 ParseAnimationOrTransitionShorthand(const nsCSSProperty* aProperties,
811 const nsCSSValue* aInitialValues,
812 nsCSSValue* aValues,
813 size_t aNumProperties);
814 bool ParseTransition();
815 bool ParseAnimation();
816 bool ParseWillChange();
818 bool ParsePaint(nsCSSProperty aPropID);
819 bool ParseDasharray();
820 bool ParseMarker();
821 bool ParsePaintOrder();
822 bool ParseAll();
825 * Parses a variable value from a custom property declaration.
827 * @param aType Out parameter into which will be stored the type of variable
828 * value, indicating whether the parsed value was a token stream or one of
829 * the CSS-wide keywords.
830 * @param aValue Out parameter into which will be stored the token stream
831 * as a string, if the parsed custom property did take a token stream.
832 * @return Whether parsing succeeded.
834 bool ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
835 nsString& aValue);
838 * Parses a CSS variable value. This could be 'initial', 'inherit', 'unset'
839 * or a token stream, which may or may not include variable references.
841 * @param aType Out parameter into which the type of the variable value
842 * will be stored.
843 * @param aDropBackslash Out parameter indicating whether during variable
844 * value parsing there was a trailing backslash before EOF that must
845 * be dropped when serializing the variable value.
846 * @param aImpliedCharacters Out parameter appended to which will be any
847 * characters that were implied when encountering EOF and which
848 * must be included at the end of the serialized variable value.
849 * @param aFunc A callback function to invoke when a variable reference
850 * is encountered. May be null. Arguments are the variable name
851 * and the aData argument passed in to this function.
852 * @param User data to pass in to the callback.
853 * @return Whether parsing succeeded.
855 bool ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
856 bool* aDropBackslash,
857 nsString& aImpliedCharacters,
858 void (*aFunc)(const nsAString&, void*),
859 void* aData);
862 * Returns whether the scanner dropped a backslash just before EOF.
864 bool BackslashDropped();
867 * Calls AppendImpliedEOFCharacters on mScanner.
869 void AppendImpliedEOFCharacters(nsAString& aResult);
871 // Reused utility parsing routines
872 void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
873 bool ParseBoxProperties(const nsCSSProperty aPropIDs[]);
874 bool ParseGroupedBoxProperty(int32_t aVariantMask,
875 nsCSSValue& aValue);
876 bool ParseDirectionalBoxProperty(nsCSSProperty aProperty,
877 int32_t aSourceType);
878 bool ParseBoxCornerRadius(const nsCSSProperty aPropID);
879 bool ParseBoxCornerRadii(const nsCSSProperty aPropIDs[]);
880 int32_t ParseChoice(nsCSSValue aValues[],
881 const nsCSSProperty aPropIDs[], int32_t aNumIDs);
882 bool ParseColor(nsCSSValue& aValue);
883 bool ParseNumberColorComponent(uint8_t& aComponent, char aStop);
884 bool ParsePercentageColorComponent(float& aComponent, char aStop);
885 // ParseHSLColor parses everything starting with the opening '('
886 // up through and including the aStop char.
887 bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
888 char aStop);
889 // ParseColorOpacity will enforce that the color ends with a ')'
890 // after the opacity
891 bool ParseColorOpacity(uint8_t& aOpacity);
892 bool ParseColorOpacity(float& aOpacity);
893 bool ParseEnum(nsCSSValue& aValue,
894 const KTableValue aKeywordTable[]);
895 bool ParseVariant(nsCSSValue& aValue,
896 int32_t aVariantMask,
897 const KTableValue aKeywordTable[]);
898 bool ParseNonNegativeVariant(nsCSSValue& aValue,
899 int32_t aVariantMask,
900 const KTableValue aKeywordTable[]);
901 bool ParseOneOrLargerVariant(nsCSSValue& aValue,
902 int32_t aVariantMask,
903 const KTableValue aKeywordTable[]);
904 bool ParseNonNegativeInteger(nsCSSValue& aValue)
906 return ParseNonNegativeVariant(aValue, VARIANT_INTEGER, nullptr);
909 // http://dev.w3.org/csswg/css-values/#custom-idents
910 // Parse an identifier that is none of:
911 // * a CSS-wide keyword
912 // * "default"
913 // * a keyword in |aExcludedKeywords|
914 // * a keyword in |aPropertyKTable|
916 // |aExcludedKeywords| is an array of nsCSSKeyword
917 // that ends with a eCSSKeyword_UNKNOWN marker.
919 // |aPropertyKTable| can be used if some of the keywords to exclude
920 // also appear in an existing nsCSSProps::KTableValue,
921 // to avoid duplicating them.
922 bool ParseCustomIdent(nsCSSValue& aValue,
923 const nsAutoString& aIdentValue,
924 const nsCSSKeyword aExcludedKeywords[] = nullptr,
925 const nsCSSProps::KTableValue aPropertyKTable[] = nullptr);
926 bool ParseCounter(nsCSSValue& aValue);
927 bool ParseAttr(nsCSSValue& aValue);
928 bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL);
929 bool TranslateDimension(nsCSSValue& aValue, int32_t aVariantMask,
930 float aNumber, const nsString& aUnit);
931 bool ParseImageOrientation(nsCSSValue& aAngle);
932 bool ParseImageRect(nsCSSValue& aImage);
933 bool ParseElement(nsCSSValue& aValue);
934 bool ParseColorStop(nsCSSValueGradient* aGradient);
935 bool ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating,
936 bool aIsLegacy);
937 bool ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating,
938 bool aIsLegacy);
939 bool IsLegacyGradientLine(const nsCSSTokenType& aType,
940 const nsString& aId);
941 bool ParseGradientColorStops(nsCSSValueGradient* aGradient,
942 nsCSSValue& aValue);
944 void SetParsingCompoundProperty(bool aBool) {
945 mParsingCompoundProperty = aBool;
947 bool IsParsingCompoundProperty(void) const {
948 return mParsingCompoundProperty;
951 /* Functions for transform Parsing */
952 bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue);
953 bool ParseFunction(nsCSSKeyword aFunction, const int32_t aAllowedTypes[],
954 int32_t aVariantMaskAll, uint16_t aMinElems,
955 uint16_t aMaxElems, nsCSSValue &aValue);
956 bool ParseFunctionInternals(const int32_t aVariantMask[],
957 int32_t aVariantMaskAll,
958 uint16_t aMinElems,
959 uint16_t aMaxElems,
960 InfallibleTArray<nsCSSValue>& aOutput);
962 /* Functions for transform-origin/perspective-origin Parsing */
963 bool ParseTransformOrigin(bool aPerspective);
965 /* Functions for filter parsing */
966 bool ParseFilter();
967 bool ParseSingleFilter(nsCSSValue* aValue);
968 bool ParseDropShadow(nsCSSValue* aValue);
970 /* Find and return the namespace ID associated with aPrefix.
971 If aPrefix has not been declared in an @namespace rule, returns
972 kNameSpaceID_Unknown. */
973 int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
975 /* Find the correct default namespace, and set it on aSelector. */
976 void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
978 // Current token. The value is valid after calling GetToken and invalidated
979 // by UngetToken.
980 nsCSSToken mToken;
982 // Our scanner.
983 nsCSSScanner* mScanner;
985 // Our error reporter.
986 css::ErrorReporter* mReporter;
988 // The URI to be used as a base for relative URIs.
989 nsCOMPtr<nsIURI> mBaseURI;
991 // The URI to be used as an HTTP "Referer" and for error reporting.
992 nsCOMPtr<nsIURI> mSheetURI;
994 // The principal of the sheet involved
995 nsCOMPtr<nsIPrincipal> mSheetPrincipal;
997 // The sheet we're parsing into
998 nsRefPtr<CSSStyleSheet> mSheet;
1000 // Used for @import rules
1001 mozilla::css::Loader* mChildLoader; // not ref counted, it owns us
1003 // Sheet section we're in. This is used to enforce correct ordering of the
1004 // various rule types (eg the fact that a @charset rule must come before
1005 // anything else). Note that there are checks of similar things in various
1006 // places in CSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
1007 enum nsCSSSection {
1008 eCSSSection_Charset,
1009 eCSSSection_Import,
1010 eCSSSection_NameSpace,
1011 eCSSSection_General
1013 nsCSSSection mSection;
1015 nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it
1017 // After an UngetToken is done this flag is true. The next call to
1018 // GetToken clears the flag.
1019 bool mHavePushBack : 1;
1021 // True if we are in quirks mode; false in standards or almost standards mode
1022 bool mNavQuirkMode : 1;
1024 // True when the hashless color quirk applies.
1025 bool mHashlessColorQuirk : 1;
1027 // True when the unitless length quirk applies.
1028 bool mUnitlessLengthQuirk : 1;
1030 // True if unsafe rules should be allowed
1031 bool mUnsafeRulesEnabled : 1;
1033 // True if we are in parsing rules for Chrome or Certified App content,
1034 // in which case CSS properties with the
1035 // CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP
1036 // flag should be allowed.
1037 bool mIsChromeOrCertifiedApp : 1;
1039 // True if viewport units should be allowed.
1040 bool mViewportUnitsEnabled : 1;
1042 // True for parsing media lists for HTML attributes, where we have to
1043 // ignore CSS comments.
1044 bool mHTMLMediaMode : 1;
1046 // This flag is set when parsing a non-box shorthand; it's used to not apply
1047 // some quirks during shorthand parsing
1048 bool mParsingCompoundProperty : 1;
1050 // True if we are in the middle of parsing an @supports condition.
1051 // This is used to avoid recording the input stream when variable references
1052 // are encountered in a property declaration in the @supports condition.
1053 bool mInSupportsCondition : 1;
1055 // True if we are somewhere within a @supports rule whose condition is
1056 // false.
1057 bool mInFailingSupportsRule : 1;
1059 // True if we will suppress all parse errors (except unexpected EOFs).
1060 // This is used to prevent errors for declarations inside a failing
1061 // @supports rule.
1062 bool mSuppressErrors : 1;
1064 // Stack of rule groups; used for @media and such.
1065 InfallibleTArray<nsRefPtr<css::GroupRule> > mGroupStack;
1067 // During the parsing of a property (which may be a shorthand), the data
1068 // are stored in |mTempData|. (It is needed to ensure that parser
1069 // errors cause the data to be ignored, and to ensure that a
1070 // non-'!important' declaration does not override an '!important'
1071 // one.)
1072 nsCSSExpandedDataBlock mTempData;
1074 // All data from successfully parsed properties are placed into |mData|.
1075 nsCSSExpandedDataBlock mData;
1077 public:
1078 // Used from nsCSSParser constructors and destructors
1079 CSSParserImpl* mNextFree;
1082 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer)
1084 css::Rule **pointer = static_cast<css::Rule**>(aPointer);
1085 NS_ADDREF(*pointer = aRule);
1088 static void AppendRuleToSheet(css::Rule* aRule, void* aParser)
1090 CSSParserImpl* parser = (CSSParserImpl*) aParser;
1091 parser->AppendRule(aRule);
1094 #define REPORT_UNEXPECTED(msg_) \
1095 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_); }
1097 #define REPORT_UNEXPECTED_P(msg_, param_) \
1098 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_); }
1100 #define REPORT_UNEXPECTED_TOKEN(msg_) \
1101 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken); }
1103 #define REPORT_UNEXPECTED_TOKEN_CHAR(msg_, ch_) \
1104 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken, ch_); }
1106 #define REPORT_UNEXPECTED_EOF(lf_) \
1107 mReporter->ReportUnexpectedEOF(#lf_)
1109 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
1110 mReporter->ReportUnexpectedEOF(ch_)
1112 #define OUTPUT_ERROR() \
1113 mReporter->OutputError()
1115 #define OUTPUT_ERROR_WITH_POSITION(linenum_, lineoff_) \
1116 mReporter->OutputError(linenum_, lineoff_)
1118 #define CLEAR_ERROR() \
1119 mReporter->ClearError()
1121 CSSParserImpl::CSSParserImpl()
1122 : mToken(),
1123 mScanner(nullptr),
1124 mReporter(nullptr),
1125 mChildLoader(nullptr),
1126 mSection(eCSSSection_Charset),
1127 mNameSpaceMap(nullptr),
1128 mHavePushBack(false),
1129 mNavQuirkMode(false),
1130 mHashlessColorQuirk(false),
1131 mUnitlessLengthQuirk(false),
1132 mUnsafeRulesEnabled(false),
1133 mIsChromeOrCertifiedApp(false),
1134 mViewportUnitsEnabled(true),
1135 mHTMLMediaMode(false),
1136 mParsingCompoundProperty(false),
1137 mInSupportsCondition(false),
1138 mInFailingSupportsRule(false),
1139 mSuppressErrors(false),
1140 mNextFree(nullptr)
1144 CSSParserImpl::~CSSParserImpl()
1146 mData.AssertInitialState();
1147 mTempData.AssertInitialState();
1150 nsresult
1151 CSSParserImpl::SetStyleSheet(CSSStyleSheet* aSheet)
1153 if (aSheet != mSheet) {
1154 // Switch to using the new sheet, if any
1155 mGroupStack.Clear();
1156 mSheet = aSheet;
1157 if (mSheet) {
1158 mNameSpaceMap = mSheet->GetNameSpaceMap();
1159 } else {
1160 mNameSpaceMap = nullptr;
1162 } else if (mSheet) {
1163 mNameSpaceMap = mSheet->GetNameSpaceMap();
1166 return NS_OK;
1169 nsresult
1170 CSSParserImpl::SetQuirkMode(bool aQuirkMode)
1172 mNavQuirkMode = aQuirkMode;
1173 return NS_OK;
1176 nsresult
1177 CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader)
1179 mChildLoader = aChildLoader; // not ref counted, it owns us
1180 return NS_OK;
1183 void
1184 CSSParserImpl::Reset()
1186 NS_ASSERTION(!mScanner, "resetting with scanner active");
1187 SetStyleSheet(nullptr);
1188 SetQuirkMode(false);
1189 SetChildLoader(nullptr);
1192 void
1193 CSSParserImpl::InitScanner(nsCSSScanner& aScanner,
1194 css::ErrorReporter& aReporter,
1195 nsIURI* aSheetURI, nsIURI* aBaseURI,
1196 nsIPrincipal* aSheetPrincipal)
1198 NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
1199 NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
1200 NS_PRECONDITION(!mScanner, "already have scanner");
1202 mScanner = &aScanner;
1203 mReporter = &aReporter;
1204 mScanner->SetErrorReporter(mReporter);
1206 mBaseURI = aBaseURI;
1207 mSheetURI = aSheetURI;
1208 mSheetPrincipal = aSheetPrincipal;
1209 mHavePushBack = false;
1212 void
1213 CSSParserImpl::ReleaseScanner()
1215 mScanner = nullptr;
1216 mReporter = nullptr;
1217 mBaseURI = nullptr;
1218 mSheetURI = nullptr;
1219 mSheetPrincipal = nullptr;
1222 nsresult
1223 CSSParserImpl::ParseSheet(const nsAString& aInput,
1224 nsIURI* aSheetURI,
1225 nsIURI* aBaseURI,
1226 nsIPrincipal* aSheetPrincipal,
1227 uint32_t aLineNumber,
1228 bool aAllowUnsafeRules)
1230 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1231 NS_PRECONDITION(aBaseURI, "need base URI");
1232 NS_PRECONDITION(aSheetURI, "need sheet URI");
1233 NS_PRECONDITION(mSheet, "Must have sheet to parse into");
1234 NS_ENSURE_STATE(mSheet);
1236 #ifdef DEBUG
1237 nsIURI* uri = mSheet->GetSheetURI();
1238 bool equal;
1239 NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
1240 "Sheet URI does not match passed URI");
1241 NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
1242 &equal)) &&
1243 equal,
1244 "Sheet principal does not match passed principal");
1245 #endif
1247 nsCSSScanner scanner(aInput, aLineNumber);
1248 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1249 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1251 int32_t ruleCount = mSheet->StyleRuleCount();
1252 if (0 < ruleCount) {
1253 const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1);
1254 if (lastRule) {
1255 switch (lastRule->GetType()) {
1256 case css::Rule::CHARSET_RULE:
1257 case css::Rule::IMPORT_RULE:
1258 mSection = eCSSSection_Import;
1259 break;
1260 case css::Rule::NAMESPACE_RULE:
1261 mSection = eCSSSection_NameSpace;
1262 break;
1263 default:
1264 mSection = eCSSSection_General;
1265 break;
1269 else {
1270 mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
1273 mUnsafeRulesEnabled = aAllowUnsafeRules;
1274 mIsChromeOrCertifiedApp =
1275 dom::IsChromeURI(aSheetURI) ||
1276 aSheetPrincipal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
1278 nsCSSToken* tk = &mToken;
1279 for (;;) {
1280 // Get next non-whitespace token
1281 if (!GetToken(true)) {
1282 OUTPUT_ERROR();
1283 break;
1285 if (eCSSToken_HTMLComment == tk->mType) {
1286 continue; // legal here only
1288 if (eCSSToken_AtKeyword == tk->mType) {
1289 ParseAtRule(AppendRuleToSheet, this, false);
1290 continue;
1292 UngetToken();
1293 if (ParseRuleSet(AppendRuleToSheet, this)) {
1294 mSection = eCSSSection_General;
1297 ReleaseScanner();
1299 mUnsafeRulesEnabled = false;
1300 mIsChromeOrCertifiedApp = false;
1302 // XXX check for low level errors
1303 return NS_OK;
1307 * Determines whether the identifier contained in the given string is a
1308 * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
1310 static bool
1311 NonMozillaVendorIdentifier(const nsAString& ident)
1313 return (ident.First() == char16_t('-') &&
1314 !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
1315 ident.First() == char16_t('_');
1319 nsresult
1320 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
1321 nsIURI* aDocURI,
1322 nsIURI* aBaseURI,
1323 nsIPrincipal* aNodePrincipal,
1324 css::StyleRule** aResult)
1326 NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
1327 NS_PRECONDITION(aBaseURI, "need base URI");
1329 // XXX line number?
1330 nsCSSScanner scanner(aAttributeValue, 0);
1331 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI);
1332 InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal);
1334 mSection = eCSSSection_General;
1336 uint32_t parseFlags = eParseDeclaration_AllowImportant;
1338 css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
1339 if (declaration) {
1340 // Create a style rule for the declaration
1341 NS_ADDREF(*aResult = new css::StyleRule(nullptr, declaration, 0, 0));
1342 } else {
1343 *aResult = nullptr;
1346 ReleaseScanner();
1348 // XXX check for low level errors
1349 return NS_OK;
1352 nsresult
1353 CSSParserImpl::ParseDeclarations(const nsAString& aBuffer,
1354 nsIURI* aSheetURI,
1355 nsIURI* aBaseURI,
1356 nsIPrincipal* aSheetPrincipal,
1357 css::Declaration* aDeclaration,
1358 bool* aChanged)
1360 *aChanged = false;
1362 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1364 nsCSSScanner scanner(aBuffer, 0);
1365 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1366 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1368 mSection = eCSSSection_General;
1370 mData.AssertInitialState();
1371 aDeclaration->ClearData();
1372 // We could check if it was already empty, but...
1373 *aChanged = true;
1375 for (;;) {
1376 // If we cleared the old decl, then we want to be calling
1377 // ValueAppended as we parse.
1378 if (!ParseDeclaration(aDeclaration, eParseDeclaration_AllowImportant,
1379 true, aChanged)) {
1380 if (!SkipDeclaration(false)) {
1381 break;
1386 aDeclaration->CompressFrom(&mData);
1387 ReleaseScanner();
1388 return NS_OK;
1391 nsresult
1392 CSSParserImpl::ParseRule(const nsAString& aRule,
1393 nsIURI* aSheetURI,
1394 nsIURI* aBaseURI,
1395 nsIPrincipal* aSheetPrincipal,
1396 css::Rule** aResult)
1398 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1399 NS_PRECONDITION(aBaseURI, "need base URI");
1401 *aResult = nullptr;
1403 nsCSSScanner scanner(aRule, 0);
1404 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1405 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1407 mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
1409 nsCSSToken* tk = &mToken;
1410 // Get first non-whitespace token
1411 nsresult rv = NS_OK;
1412 if (!GetToken(true)) {
1413 REPORT_UNEXPECTED(PEParseRuleWSOnly);
1414 OUTPUT_ERROR();
1415 rv = NS_ERROR_DOM_SYNTAX_ERR;
1416 } else {
1417 if (eCSSToken_AtKeyword == tk->mType) {
1418 // FIXME: perhaps aInsideBlock should be true when we are?
1419 ParseAtRule(AssignRuleToPointer, aResult, false);
1420 } else {
1421 UngetToken();
1422 ParseRuleSet(AssignRuleToPointer, aResult);
1425 if (*aResult && GetToken(true)) {
1426 // garbage after rule
1427 REPORT_UNEXPECTED_TOKEN(PERuleTrailing);
1428 NS_RELEASE(*aResult);
1431 if (!*aResult) {
1432 rv = NS_ERROR_DOM_SYNTAX_ERR;
1433 OUTPUT_ERROR();
1437 ReleaseScanner();
1438 return rv;
1441 // See Bug 723197
1442 #ifdef _MSC_VER
1443 #pragma optimize( "", off )
1444 #pragma warning( push )
1445 #pragma warning( disable : 4748 )
1446 #endif
1448 nsresult
1449 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
1450 const nsAString& aPropValue,
1451 nsIURI* aSheetURI,
1452 nsIURI* aBaseURI,
1453 nsIPrincipal* aSheetPrincipal,
1454 css::Declaration* aDeclaration,
1455 bool* aChanged,
1456 bool aIsImportant,
1457 bool aIsSVGMode)
1459 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1460 NS_PRECONDITION(aBaseURI, "need base URI");
1461 NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
1463 mData.AssertInitialState();
1464 mTempData.AssertInitialState();
1465 aDeclaration->AssertMutable();
1467 nsCSSScanner scanner(aPropValue, 0);
1468 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1469 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1470 mSection = eCSSSection_General;
1471 scanner.SetSVGMode(aIsSVGMode);
1473 *aChanged = false;
1475 // Check for unknown or preffed off properties
1476 if (eCSSProperty_UNKNOWN == aPropID ||
1477 !(nsCSSProps::IsEnabled(aPropID) ||
1478 (mUnsafeRulesEnabled &&
1479 nsCSSProps::PropHasFlags(aPropID,
1480 CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS)))) {
1481 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1482 REPORT_UNEXPECTED_P(PEUnknownProperty, propName);
1483 REPORT_UNEXPECTED(PEDeclDropped);
1484 OUTPUT_ERROR();
1485 ReleaseScanner();
1486 return NS_OK;
1489 bool parsedOK = ParseProperty(aPropID);
1490 // We should now be at EOF
1491 if (parsedOK && GetToken(true)) {
1492 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1493 parsedOK = false;
1496 if (!parsedOK) {
1497 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1498 REPORT_UNEXPECTED_P(PEValueParsingError, propName);
1499 REPORT_UNEXPECTED(PEDeclDropped);
1500 OUTPUT_ERROR();
1501 mTempData.ClearProperty(aPropID);
1502 } else {
1504 // We know we don't need to force a ValueAppended call for the new
1505 // value. So if we are not processing a shorthand, and there's
1506 // already a value for this property in the declaration at the
1507 // same importance level, then we can just copy our parsed value
1508 // directly into the declaration without going through the whole
1509 // expand/compress thing.
1510 if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData,
1511 aChanged)) {
1512 // Do it the slow way
1513 aDeclaration->ExpandTo(&mData);
1514 *aChanged = mData.TransferFromBlock(mTempData, aPropID, aIsImportant,
1515 true, false, aDeclaration);
1516 aDeclaration->CompressFrom(&mData);
1518 CLEAR_ERROR();
1521 mTempData.AssertInitialState();
1523 ReleaseScanner();
1524 return NS_OK;
1527 nsresult
1528 CSSParserImpl::ParseVariable(const nsAString& aVariableName,
1529 const nsAString& aPropValue,
1530 nsIURI* aSheetURI,
1531 nsIURI* aBaseURI,
1532 nsIPrincipal* aSheetPrincipal,
1533 css::Declaration* aDeclaration,
1534 bool* aChanged,
1535 bool aIsImportant)
1537 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1538 NS_PRECONDITION(aBaseURI, "need base URI");
1539 NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
1540 NS_PRECONDITION(nsLayoutUtils::CSSVariablesEnabled(),
1541 "expected Variables to be enabled");
1543 mData.AssertInitialState();
1544 mTempData.AssertInitialState();
1545 aDeclaration->AssertMutable();
1547 nsCSSScanner scanner(aPropValue, 0);
1548 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1549 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1550 mSection = eCSSSection_General;
1552 *aChanged = false;
1554 CSSVariableDeclarations::Type variableType;
1555 nsString variableValue;
1557 bool parsedOK = ParseVariableDeclaration(&variableType, variableValue);
1559 // We should now be at EOF
1560 if (parsedOK && GetToken(true)) {
1561 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1562 parsedOK = false;
1565 if (!parsedOK) {
1566 REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("--") +
1567 aVariableName);
1568 REPORT_UNEXPECTED(PEDeclDropped);
1569 OUTPUT_ERROR();
1570 } else {
1571 CLEAR_ERROR();
1572 aDeclaration->AddVariableDeclaration(aVariableName, variableType,
1573 variableValue, aIsImportant, true);
1574 *aChanged = true;
1577 mTempData.AssertInitialState();
1579 ReleaseScanner();
1580 return NS_OK;
1583 #ifdef _MSC_VER
1584 #pragma warning( pop )
1585 #pragma optimize( "", on )
1586 #endif
1588 void
1589 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
1590 nsIURI* aURI, // for error reporting
1591 uint32_t aLineNumber, // for error reporting
1592 nsMediaList* aMediaList,
1593 bool aHTMLMode)
1595 // XXX Are there cases where the caller wants to keep what it already
1596 // has in case of parser error? If GatherMedia ever changes to return
1597 // a value other than true, we probably should avoid modifying aMediaList.
1598 aMediaList->Clear();
1600 // fake base URI since media lists don't have URIs in them
1601 nsCSSScanner scanner(aBuffer, aLineNumber);
1602 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1603 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1605 mHTMLMediaMode = aHTMLMode;
1607 // XXXldb We need to make the scanner not skip CSS comments! (Or
1608 // should we?)
1610 // For aHTMLMode, we used to follow the parsing rules in
1611 // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
1612 // which wouldn't work for media queries since they remove all but the
1613 // first word. However, they're changed in
1614 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
1615 // (as of 2008-05-29) which says that the media attribute just points
1616 // to a media query. (The main substative difference is the relative
1617 // precedence of commas and paretheses.)
1619 DebugOnly<bool> parsedOK = GatherMedia(aMediaList, false);
1620 NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid "
1621 "trashing aMediaList");
1623 CLEAR_ERROR();
1624 ReleaseScanner();
1625 mHTMLMediaMode = false;
1628 // <source-size-list> = <source-size>#?
1629 // <source-size> = <media-condition>? <length>
1630 bool
1631 CSSParserImpl::ParseSourceSizeList(const nsAString& aBuffer,
1632 nsIURI* aURI, // for error reporting
1633 uint32_t aLineNumber, // for error reporting
1634 InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
1635 InfallibleTArray<nsCSSValue>& aValues,
1636 bool aHTMLMode)
1638 aQueries.Clear();
1639 aValues.Clear();
1641 // fake base URI since media value lists don't have URIs in them
1642 nsCSSScanner scanner(aBuffer, aLineNumber);
1643 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1644 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1646 // See ParseMediaList comment about HTML mode
1647 mHTMLMediaMode = aHTMLMode;
1649 bool hitError = false;
1650 for (;;) {
1651 nsAutoPtr<nsMediaQuery> query;
1652 nsCSSValue value;
1654 bool hitStop;
1655 if (!ParseMediaQuery(eMediaQuerySingleCondition, getter_Transfers(query),
1656 &hitStop)) {
1657 NS_ASSERTION(!hitStop, "should return true when hit stop");
1658 hitError = true;
1659 break;
1662 if (!query) {
1663 REPORT_UNEXPECTED_EOF(PEParseSourceSizeListEOF);
1664 NS_ASSERTION(hitStop,
1665 "should return hitStop or an error if returning no query");
1666 hitError = true;
1667 break;
1670 if (hitStop) {
1671 // Empty conditions (e.g. just a bare value) should be treated as always
1672 // matching (a query with no expressions fails to match, so a negated one
1673 // always matches.)
1674 query->SetNegated();
1677 if (!ParseNonNegativeVariant(value, VARIANT_LPCALC, nullptr)) {
1678 hitError = true;
1679 break;
1682 aQueries.AppendElement(query.forget());
1683 aValues.AppendElement(value);
1685 if (!GetToken(true)) {
1686 // Expected EOF
1687 break;
1690 if (eCSSToken_Symbol != mToken.mType || mToken.mSymbol != ',') {
1691 REPORT_UNEXPECTED_TOKEN(PEParseSourceSizeListNotComma);
1692 hitError = true;
1693 break;
1697 if (hitError) {
1698 // Per spec, a parse failure in this list invalidates it
1699 // entirely. Currently, this grammar is specified standalone and not part of
1700 // any larger grammar, so it doesn't make sense to try to advance the token
1701 // beyond it.
1702 OUTPUT_ERROR();
1705 CLEAR_ERROR();
1706 ReleaseScanner();
1707 mHTMLMediaMode = false;
1709 return !hitError;
1712 bool
1713 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
1714 nsIURI* aURI, // for error reporting
1715 uint32_t aLineNumber, // for error reporting
1716 nsCSSValue& aValue,
1717 bool aSuppressErrors /* false */)
1719 nsCSSScanner scanner(aBuffer, aLineNumber);
1720 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1721 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1723 nsAutoSuppressErrors suppressErrors(this, aSuppressErrors);
1725 // Parse a color, and check that there's nothing else after it.
1726 bool colorParsed = ParseColor(aValue) && !GetToken(true);
1728 if (aSuppressErrors) {
1729 CLEAR_ERROR();
1730 } else {
1731 OUTPUT_ERROR();
1734 ReleaseScanner();
1735 return colorParsed;
1738 bool
1739 CSSParserImpl::ParseFontFamilyListString(const nsSubstring& aBuffer,
1740 nsIURI* aURI, // for error reporting
1741 uint32_t aLineNumber, // for error reporting
1742 nsCSSValue& aValue)
1744 nsCSSScanner scanner(aBuffer, aLineNumber);
1745 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1746 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1748 // Parse a font family list, and check that there's nothing else after it.
1749 bool familyParsed = ParseFamily(aValue) && !GetToken(true);
1750 OUTPUT_ERROR();
1751 ReleaseScanner();
1752 return familyParsed;
1755 nsresult
1756 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
1757 nsIURI* aURI, // for error reporting
1758 uint32_t aLineNumber, // for error reporting
1759 nsCSSSelectorList **aSelectorList)
1761 nsCSSScanner scanner(aSelectorString, aLineNumber);
1762 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1763 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1765 bool success = ParseSelectorList(*aSelectorList, char16_t(0));
1767 // We deliberately do not call OUTPUT_ERROR here, because all our
1768 // callers map a failure return to a JS exception, and if that JS
1769 // exception is caught, people don't want to see parser diagnostics;
1770 // see e.g. http://bugs.jquery.com/ticket/7535
1771 // It would be nice to be able to save the parser diagnostics into
1772 // the exception, so that if it _isn't_ caught we can report them
1773 // along with the usual uncaught-exception message, but we don't
1774 // have any way to do that at present; see bug 631621.
1775 CLEAR_ERROR();
1776 ReleaseScanner();
1778 if (success) {
1779 NS_ASSERTION(*aSelectorList, "Should have list!");
1780 return NS_OK;
1783 NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
1785 return NS_ERROR_DOM_SYNTAX_ERR;
1789 already_AddRefed<nsCSSKeyframeRule>
1790 CSSParserImpl::ParseKeyframeRule(const nsSubstring& aBuffer,
1791 nsIURI* aURI,
1792 uint32_t aLineNumber)
1794 nsCSSScanner scanner(aBuffer, aLineNumber);
1795 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1796 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1798 nsRefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule();
1799 if (GetToken(true)) {
1800 // extra garbage at the end
1801 result = nullptr;
1804 OUTPUT_ERROR();
1805 ReleaseScanner();
1807 return result.forget();
1810 bool
1811 CSSParserImpl::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
1812 nsIURI* aURI, // for error reporting
1813 uint32_t aLineNumber, // for error reporting
1814 InfallibleTArray<float>& aSelectorList)
1816 NS_ABORT_IF_FALSE(aSelectorList.IsEmpty(), "given list should start empty");
1818 nsCSSScanner scanner(aSelectorString, aLineNumber);
1819 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1820 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1822 bool success = ParseKeyframeSelectorList(aSelectorList) &&
1823 // must consume entire input string
1824 !GetToken(true);
1826 OUTPUT_ERROR();
1827 ReleaseScanner();
1829 if (success) {
1830 NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty");
1831 } else {
1832 aSelectorList.Clear();
1835 return success;
1838 bool
1839 CSSParserImpl::EvaluateSupportsDeclaration(const nsAString& aProperty,
1840 const nsAString& aValue,
1841 nsIURI* aDocURL,
1842 nsIURI* aBaseURL,
1843 nsIPrincipal* aDocPrincipal)
1845 nsCSSProperty propID = LookupEnabledProperty(aProperty);
1846 if (propID == eCSSProperty_UNKNOWN) {
1847 return false;
1850 nsCSSScanner scanner(aValue, 0);
1851 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
1852 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
1853 nsAutoSuppressErrors suppressErrors(this);
1855 bool parsedOK;
1857 if (propID == eCSSPropertyExtra_variable) {
1858 MOZ_ASSERT(Substring(aProperty, 0,
1859 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
1860 const nsDependentSubstring varName =
1861 Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH); // remove '--'
1862 CSSVariableDeclarations::Type variableType;
1863 nsString variableValue;
1864 parsedOK = ParseVariableDeclaration(&variableType, variableValue) &&
1865 !GetToken(true);
1866 } else {
1867 parsedOK = ParseProperty(propID) && !GetToken(true);
1869 mTempData.ClearProperty(propID);
1870 mTempData.AssertInitialState();
1873 CLEAR_ERROR();
1874 ReleaseScanner();
1876 return parsedOK;
1879 bool
1880 CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration,
1881 nsIURI* aDocURL,
1882 nsIURI* aBaseURL,
1883 nsIPrincipal* aDocPrincipal)
1885 nsCSSScanner scanner(aDeclaration, 0);
1886 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
1887 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
1888 nsAutoSuppressErrors suppressErrors(this);
1890 bool conditionMet;
1891 bool parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true);
1893 CLEAR_ERROR();
1894 ReleaseScanner();
1896 return parsedOK && conditionMet;
1899 bool
1900 CSSParserImpl::EnumerateVariableReferences(const nsAString& aPropertyValue,
1901 VariableEnumFunc aFunc,
1902 void* aData)
1904 nsCSSScanner scanner(aPropertyValue, 0);
1905 css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
1906 InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
1907 nsAutoSuppressErrors suppressErrors(this);
1909 CSSVariableDeclarations::Type type;
1910 bool dropBackslash;
1911 nsString impliedCharacters;
1912 bool result = ParseValueWithVariables(&type, &dropBackslash,
1913 impliedCharacters, aFunc, aData) &&
1914 !GetToken(true);
1916 ReleaseScanner();
1918 return result;
1921 static bool
1922 SeparatorRequiredBetweenTokens(nsCSSTokenSerializationType aToken1,
1923 nsCSSTokenSerializationType aToken2)
1925 // The two lines marked with (*) do not correspond to entries in
1926 // the table in the css-syntax spec but which we need to handle,
1927 // as we treat them as whole tokens.
1928 switch (aToken1) {
1929 case eCSSTokenSerialization_Ident:
1930 return aToken2 == eCSSTokenSerialization_Ident ||
1931 aToken2 == eCSSTokenSerialization_Function ||
1932 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1933 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1934 aToken2 == eCSSTokenSerialization_Number ||
1935 aToken2 == eCSSTokenSerialization_Percentage ||
1936 aToken2 == eCSSTokenSerialization_Dimension ||
1937 aToken2 == eCSSTokenSerialization_URange ||
1938 aToken2 == eCSSTokenSerialization_CDC ||
1939 aToken2 == eCSSTokenSerialization_Symbol_OpenParen;
1940 case eCSSTokenSerialization_AtKeyword_or_Hash:
1941 case eCSSTokenSerialization_Dimension:
1942 return aToken2 == eCSSTokenSerialization_Ident ||
1943 aToken2 == eCSSTokenSerialization_Function ||
1944 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1945 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1946 aToken2 == eCSSTokenSerialization_Number ||
1947 aToken2 == eCSSTokenSerialization_Percentage ||
1948 aToken2 == eCSSTokenSerialization_Dimension ||
1949 aToken2 == eCSSTokenSerialization_URange ||
1950 aToken2 == eCSSTokenSerialization_CDC;
1951 case eCSSTokenSerialization_Symbol_Hash:
1952 return aToken2 == eCSSTokenSerialization_Ident ||
1953 aToken2 == eCSSTokenSerialization_Function ||
1954 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1955 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1956 aToken2 == eCSSTokenSerialization_Number ||
1957 aToken2 == eCSSTokenSerialization_Percentage ||
1958 aToken2 == eCSSTokenSerialization_Dimension ||
1959 aToken2 == eCSSTokenSerialization_URange;
1960 case eCSSTokenSerialization_Symbol_Minus:
1961 case eCSSTokenSerialization_Number:
1962 return aToken2 == eCSSTokenSerialization_Ident ||
1963 aToken2 == eCSSTokenSerialization_Function ||
1964 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1965 aToken2 == eCSSTokenSerialization_Number ||
1966 aToken2 == eCSSTokenSerialization_Percentage ||
1967 aToken2 == eCSSTokenSerialization_Dimension ||
1968 aToken2 == eCSSTokenSerialization_URange;
1969 case eCSSTokenSerialization_Symbol_At:
1970 return aToken2 == eCSSTokenSerialization_Ident ||
1971 aToken2 == eCSSTokenSerialization_Function ||
1972 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1973 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1974 aToken2 == eCSSTokenSerialization_URange;
1975 case eCSSTokenSerialization_URange:
1976 return aToken2 == eCSSTokenSerialization_Ident ||
1977 aToken2 == eCSSTokenSerialization_Function ||
1978 aToken2 == eCSSTokenSerialization_Number ||
1979 aToken2 == eCSSTokenSerialization_Percentage ||
1980 aToken2 == eCSSTokenSerialization_Dimension ||
1981 aToken2 == eCSSTokenSerialization_Symbol_Question;
1982 case eCSSTokenSerialization_Symbol_Dot_or_Plus:
1983 return aToken2 == eCSSTokenSerialization_Number ||
1984 aToken2 == eCSSTokenSerialization_Percentage ||
1985 aToken2 == eCSSTokenSerialization_Dimension;
1986 case eCSSTokenSerialization_Symbol_Assorted:
1987 case eCSSTokenSerialization_Symbol_Asterisk:
1988 return aToken2 == eCSSTokenSerialization_Symbol_Equals;
1989 case eCSSTokenSerialization_Symbol_Bar:
1990 return aToken2 == eCSSTokenSerialization_Symbol_Equals ||
1991 aToken2 == eCSSTokenSerialization_Symbol_Bar ||
1992 aToken2 == eCSSTokenSerialization_DashMatch; // (*)
1993 case eCSSTokenSerialization_Symbol_Slash:
1994 return aToken2 == eCSSTokenSerialization_Symbol_Asterisk ||
1995 aToken2 == eCSSTokenSerialization_ContainsMatch; // (*)
1996 default:
1997 MOZ_ASSERT(aToken1 == eCSSTokenSerialization_Nothing ||
1998 aToken1 == eCSSTokenSerialization_Whitespace ||
1999 aToken1 == eCSSTokenSerialization_Percentage ||
2000 aToken1 == eCSSTokenSerialization_URL_or_BadURL ||
2001 aToken1 == eCSSTokenSerialization_Function ||
2002 aToken1 == eCSSTokenSerialization_CDC ||
2003 aToken1 == eCSSTokenSerialization_Symbol_OpenParen ||
2004 aToken1 == eCSSTokenSerialization_Symbol_Question ||
2005 aToken1 == eCSSTokenSerialization_Symbol_Assorted ||
2006 aToken1 == eCSSTokenSerialization_Symbol_Asterisk ||
2007 aToken1 == eCSSTokenSerialization_Symbol_Equals ||
2008 aToken1 == eCSSTokenSerialization_Symbol_Bar ||
2009 aToken1 == eCSSTokenSerialization_Symbol_Slash ||
2010 aToken1 == eCSSTokenSerialization_Other ||
2011 "unexpected nsCSSTokenSerializationType value");
2012 return false;
2017 * Appends aValue to aResult, possibly inserting an empty CSS
2018 * comment between the two to ensure that tokens from both strings
2019 * remain separated.
2021 static void
2022 AppendTokens(nsAString& aResult,
2023 nsCSSTokenSerializationType& aResultFirstToken,
2024 nsCSSTokenSerializationType& aResultLastToken,
2025 nsCSSTokenSerializationType aValueFirstToken,
2026 nsCSSTokenSerializationType aValueLastToken,
2027 const nsAString& aValue)
2029 if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
2030 aResult.AppendLiteral("/**/");
2032 aResult.Append(aValue);
2033 if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
2034 aResultFirstToken = aValueFirstToken;
2036 if (aValueLastToken != eCSSTokenSerialization_Nothing) {
2037 aResultLastToken = aValueLastToken;
2042 * Stops the given scanner recording, and appends the recorded result
2043 * to aResult, possibly inserting an empty CSS comment between the two to
2044 * ensure that tokens from both strings remain separated.
2046 static void
2047 StopRecordingAndAppendTokens(nsString& aResult,
2048 nsCSSTokenSerializationType& aResultFirstToken,
2049 nsCSSTokenSerializationType& aResultLastToken,
2050 nsCSSTokenSerializationType aValueFirstToken,
2051 nsCSSTokenSerializationType aValueLastToken,
2052 nsCSSScanner* aScanner)
2054 if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
2055 aResult.AppendLiteral("/**/");
2057 aScanner->StopRecording(aResult);
2058 if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
2059 aResultFirstToken = aValueFirstToken;
2061 if (aValueLastToken != eCSSTokenSerialization_Nothing) {
2062 aResultLastToken = aValueLastToken;
2066 bool
2067 CSSParserImpl::ResolveValueWithVariableReferencesRec(
2068 nsString& aResult,
2069 nsCSSTokenSerializationType& aResultFirstToken,
2070 nsCSSTokenSerializationType& aResultLastToken,
2071 const CSSVariableValues* aVariables)
2073 // This function assumes we are already recording, and will leave the scanner
2074 // recording when it returns.
2075 MOZ_ASSERT(mScanner->IsRecording());
2076 MOZ_ASSERT(aResult.IsEmpty());
2078 // Stack of closing characters for currently open constructs.
2079 nsAutoTArray<char16_t, 16> stack;
2081 // The resolved value for this ResolveValueWithVariableReferencesRec call.
2082 nsString value;
2084 // The length of the scanner's recording before the currently parsed token.
2085 // This is used so that when we encounter a "var(" token, we can strip
2086 // it off the end of the recording, regardless of how long the token was.
2087 // (With escapes, it could be longer than four characters.)
2088 uint32_t lengthBeforeVar = 0;
2090 // Tracking the type of token that appears at the start and end of |value|
2091 // and that appears at the start and end of the scanner recording. These are
2092 // used to determine whether we need to insert "/**/" when pasting token
2093 // streams together.
2094 nsCSSTokenSerializationType valueFirstToken = eCSSTokenSerialization_Nothing,
2095 valueLastToken = eCSSTokenSerialization_Nothing,
2096 recFirstToken = eCSSTokenSerialization_Nothing,
2097 recLastToken = eCSSTokenSerialization_Nothing;
2099 #define UPDATE_RECORDING_TOKENS(type) \
2100 if (recFirstToken == eCSSTokenSerialization_Nothing) { \
2101 recFirstToken = type; \
2103 recLastToken = type;
2105 while (GetToken(false)) {
2106 switch (mToken.mType) {
2107 case eCSSToken_Symbol: {
2108 nsCSSTokenSerializationType type = eCSSTokenSerialization_Other;
2109 if (mToken.mSymbol == '(') {
2110 stack.AppendElement(')');
2111 type = eCSSTokenSerialization_Symbol_OpenParen;
2112 } else if (mToken.mSymbol == '[') {
2113 stack.AppendElement(']');
2114 } else if (mToken.mSymbol == '{') {
2115 stack.AppendElement('}');
2116 } else if (mToken.mSymbol == ';') {
2117 if (stack.IsEmpty()) {
2118 // A ';' that is at the top level of the value or at the top level
2119 // of a variable reference's fallback is invalid.
2120 return false;
2122 } else if (mToken.mSymbol == '!') {
2123 if (stack.IsEmpty()) {
2124 // An '!' that is at the top level of the value or at the top level
2125 // of a variable reference's fallback is invalid.
2126 return false;
2128 } else if (mToken.mSymbol == ')' &&
2129 stack.IsEmpty()) {
2130 // We're closing a "var(".
2131 nsString finalTokens;
2132 mScanner->StopRecording(finalTokens);
2133 MOZ_ASSERT(finalTokens[finalTokens.Length() - 1] == ')');
2134 finalTokens.Truncate(finalTokens.Length() - 1);
2135 aResult.Append(value);
2137 AppendTokens(aResult, valueFirstToken, valueLastToken,
2138 recFirstToken, recLastToken, finalTokens);
2140 mScanner->StartRecording();
2141 UngetToken();
2142 aResultFirstToken = valueFirstToken;
2143 aResultLastToken = valueLastToken;
2144 return true;
2145 } else if (mToken.mSymbol == ')' ||
2146 mToken.mSymbol == ']' ||
2147 mToken.mSymbol == '}') {
2148 if (stack.IsEmpty() ||
2149 stack.LastElement() != mToken.mSymbol) {
2150 // A mismatched closing bracket is invalid.
2151 return false;
2153 stack.TruncateLength(stack.Length() - 1);
2154 } else if (mToken.mSymbol == '#') {
2155 type = eCSSTokenSerialization_Symbol_Hash;
2156 } else if (mToken.mSymbol == '@') {
2157 type = eCSSTokenSerialization_Symbol_At;
2158 } else if (mToken.mSymbol == '.' ||
2159 mToken.mSymbol == '+') {
2160 type = eCSSTokenSerialization_Symbol_Dot_or_Plus;
2161 } else if (mToken.mSymbol == '-') {
2162 type = eCSSTokenSerialization_Symbol_Minus;
2163 } else if (mToken.mSymbol == '?') {
2164 type = eCSSTokenSerialization_Symbol_Question;
2165 } else if (mToken.mSymbol == '$' ||
2166 mToken.mSymbol == '^' ||
2167 mToken.mSymbol == '~') {
2168 type = eCSSTokenSerialization_Symbol_Assorted;
2169 } else if (mToken.mSymbol == '=') {
2170 type = eCSSTokenSerialization_Symbol_Equals;
2171 } else if (mToken.mSymbol == '|') {
2172 type = eCSSTokenSerialization_Symbol_Bar;
2173 } else if (mToken.mSymbol == '/') {
2174 type = eCSSTokenSerialization_Symbol_Slash;
2175 } else if (mToken.mSymbol == '*') {
2176 type = eCSSTokenSerialization_Symbol_Asterisk;
2178 UPDATE_RECORDING_TOKENS(type);
2179 break;
2182 case eCSSToken_Function:
2183 if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
2184 // Save the tokens before the "var(" to our resolved value.
2185 nsString recording;
2186 mScanner->StopRecording(recording);
2187 recording.Truncate(lengthBeforeVar);
2188 AppendTokens(value, valueFirstToken, valueLastToken,
2189 recFirstToken, recLastToken, recording);
2190 recFirstToken = eCSSTokenSerialization_Nothing;
2191 recLastToken = eCSSTokenSerialization_Nothing;
2193 if (!GetToken(true) ||
2194 mToken.mType != eCSSToken_Ident ||
2195 !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
2196 // "var(" must be followed by an identifier, and it must be a
2197 // custom property name.
2198 return false;
2201 // Turn the custom property name into a variable name by removing the
2202 // '--' prefix.
2203 MOZ_ASSERT(Substring(mToken.mIdent, 0,
2204 CSS_CUSTOM_NAME_PREFIX_LENGTH).
2205 EqualsLiteral("--"));
2206 nsDependentString variableName(mToken.mIdent,
2207 CSS_CUSTOM_NAME_PREFIX_LENGTH);
2209 // Get the value of the identified variable. Note that we
2210 // check if the variable value is the empty string, as that means
2211 // that the variable was invalid at computed value time due to
2212 // unresolveable variable references or cycles.
2213 nsString variableValue;
2214 nsCSSTokenSerializationType varFirstToken, varLastToken;
2215 bool valid = aVariables->Get(variableName, variableValue,
2216 varFirstToken, varLastToken) &&
2217 !variableValue.IsEmpty();
2219 if (!GetToken(true) ||
2220 mToken.IsSymbol(')')) {
2221 mScanner->StartRecording();
2222 if (!valid) {
2223 // Invalid variable with no fallback.
2224 return false;
2226 // Valid variable with no fallback.
2227 AppendTokens(value, valueFirstToken, valueLastToken,
2228 varFirstToken, varLastToken, variableValue);
2229 } else if (mToken.IsSymbol(',')) {
2230 mScanner->StartRecording();
2231 if (!GetToken(false) ||
2232 mToken.IsSymbol(')')) {
2233 // Comma must be followed by at least one fallback token.
2234 return false;
2236 UngetToken();
2237 if (valid) {
2238 // Valid variable with ignored fallback.
2239 mScanner->StopRecording();
2240 AppendTokens(value, valueFirstToken, valueLastToken,
2241 varFirstToken, varLastToken, variableValue);
2242 bool ok = SkipBalancedContentUntil(')');
2243 mScanner->StartRecording();
2244 if (!ok) {
2245 return false;
2247 } else {
2248 nsString fallback;
2249 if (!ResolveValueWithVariableReferencesRec(fallback,
2250 varFirstToken,
2251 varLastToken,
2252 aVariables)) {
2253 // Fallback value had invalid tokens or an invalid variable reference
2254 // that itself had no fallback.
2255 return false;
2257 AppendTokens(value, valueFirstToken, valueLastToken,
2258 varFirstToken, varLastToken, fallback);
2259 // Now we're either at the pushed back ')' that finished the
2260 // fallback or at EOF.
2261 DebugOnly<bool> gotToken = GetToken(false);
2262 MOZ_ASSERT(!gotToken || mToken.IsSymbol(')'));
2264 } else {
2265 // Expected ',' or ')' after the variable name.
2266 mScanner->StartRecording();
2267 return false;
2269 } else {
2270 stack.AppendElement(')');
2271 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function);
2273 break;
2275 case eCSSToken_Bad_String:
2276 case eCSSToken_Bad_URL:
2277 return false;
2279 case eCSSToken_Whitespace:
2280 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace);
2281 break;
2283 case eCSSToken_AtKeyword:
2284 case eCSSToken_Hash:
2285 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash);
2286 break;
2288 case eCSSToken_Number:
2289 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number);
2290 break;
2292 case eCSSToken_Dimension:
2293 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension);
2294 break;
2296 case eCSSToken_Ident:
2297 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident);
2298 break;
2300 case eCSSToken_Percentage:
2301 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage);
2302 break;
2304 case eCSSToken_URange:
2305 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange);
2306 break;
2308 case eCSSToken_URL:
2309 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL);
2310 break;
2312 case eCSSToken_HTMLComment:
2313 if (mToken.mIdent[0] == '-') {
2314 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC);
2315 } else {
2316 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
2318 break;
2320 case eCSSToken_Dashmatch:
2321 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch);
2322 break;
2324 case eCSSToken_Containsmatch:
2325 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch);
2326 break;
2328 default:
2329 NS_NOTREACHED("unexpected token type");
2330 // fall through
2331 case eCSSToken_ID:
2332 case eCSSToken_String:
2333 case eCSSToken_Includes:
2334 case eCSSToken_Beginsmatch:
2335 case eCSSToken_Endsmatch:
2336 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
2337 break;
2340 lengthBeforeVar = mScanner->RecordingLength();
2343 #undef UPDATE_RECORDING_TOKENS
2345 aResult.Append(value);
2346 StopRecordingAndAppendTokens(aResult, valueFirstToken, valueLastToken,
2347 recFirstToken, recLastToken, mScanner);
2349 // Append any implicitly closed brackets.
2350 if (!stack.IsEmpty()) {
2351 do {
2352 aResult.Append(stack.LastElement());
2353 stack.TruncateLength(stack.Length() - 1);
2354 } while (!stack.IsEmpty());
2355 valueLastToken = eCSSTokenSerialization_Other;
2358 mScanner->StartRecording();
2359 aResultFirstToken = valueFirstToken;
2360 aResultLastToken = valueLastToken;
2361 return true;
2364 bool
2365 CSSParserImpl::ResolveValueWithVariableReferences(
2366 const CSSVariableValues* aVariables,
2367 nsString& aResult,
2368 nsCSSTokenSerializationType& aFirstToken,
2369 nsCSSTokenSerializationType& aLastToken)
2371 aResult.Truncate(0);
2373 // Start recording before we read the first token.
2374 mScanner->StartRecording();
2376 if (!GetToken(false)) {
2377 // Value was empty since we reached EOF.
2378 mScanner->StopRecording();
2379 return false;
2382 UngetToken();
2384 nsString value;
2385 nsCSSTokenSerializationType firstToken, lastToken;
2386 bool ok = ResolveValueWithVariableReferencesRec(value, firstToken, lastToken, aVariables) &&
2387 !GetToken(true);
2389 mScanner->StopRecording();
2391 if (ok) {
2392 aResult = value;
2393 aFirstToken = firstToken;
2394 aLastToken = lastToken;
2396 return ok;
2399 bool
2400 CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue,
2401 const CSSVariableValues* aVariables,
2402 nsString& aResult,
2403 nsCSSTokenSerializationType& aFirstToken,
2404 nsCSSTokenSerializationType& aLastToken)
2406 nsCSSScanner scanner(aPropertyValue, 0);
2408 // At this point, we know that aPropertyValue is syntactically correct
2409 // for a token stream that has variable references. We also won't be
2410 // interpreting any of the stream as we parse it, apart from expanding
2411 // var() references, so we don't need a base URL etc. or any useful
2412 // error reporting.
2413 css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
2414 InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
2416 bool valid = ResolveValueWithVariableReferences(aVariables, aResult,
2417 aFirstToken, aLastToken);
2419 ReleaseScanner();
2420 return valid;
2423 void
2424 CSSParserImpl::ParsePropertyWithVariableReferences(
2425 nsCSSProperty aPropertyID,
2426 nsCSSProperty aShorthandPropertyID,
2427 const nsAString& aValue,
2428 const CSSVariableValues* aVariables,
2429 nsRuleData* aRuleData,
2430 nsIURI* aDocURL,
2431 nsIURI* aBaseURL,
2432 nsIPrincipal* aDocPrincipal,
2433 CSSStyleSheet* aSheet,
2434 uint32_t aLineNumber,
2435 uint32_t aLineOffset)
2437 mTempData.AssertInitialState();
2439 bool valid;
2440 nsString expandedValue;
2442 // Resolve any variable references in the property value.
2444 nsCSSScanner scanner(aValue, 0);
2445 css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
2446 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2448 nsCSSTokenSerializationType firstToken, lastToken;
2449 valid = ResolveValueWithVariableReferences(aVariables, expandedValue,
2450 firstToken, lastToken);
2451 if (!valid) {
2452 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropertyID));
2453 REPORT_UNEXPECTED(PEInvalidVariableReference);
2454 REPORT_UNEXPECTED_P(PEValueParsingError, propName);
2455 if (nsCSSProps::IsInherited(aPropertyID)) {
2456 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
2457 } else {
2458 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
2460 OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
2462 ReleaseScanner();
2465 nsCSSProperty propertyToParse =
2466 aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID :
2467 aPropertyID;
2469 // Parse the property with that resolved value.
2470 if (valid) {
2471 nsCSSScanner scanner(expandedValue, 0);
2472 css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
2473 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2474 valid = ParseProperty(propertyToParse);
2475 if (valid && GetToken(true)) {
2476 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
2477 valid = false;
2479 if (!valid) {
2480 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(
2481 propertyToParse));
2482 REPORT_UNEXPECTED_P(PEValueWithVariablesParsingError, propName);
2483 if (nsCSSProps::IsInherited(aPropertyID)) {
2484 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
2485 } else {
2486 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
2488 OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
2490 ReleaseScanner();
2493 // If the property could not be parsed with the resolved value, then we
2494 // treat it as if the value were 'initial' or 'inherit', depending on whether
2495 // the property is an inherited property.
2496 if (!valid) {
2497 nsCSSValue defaultValue;
2498 if (nsCSSProps::IsInherited(aPropertyID)) {
2499 defaultValue.SetInheritValue();
2500 } else {
2501 defaultValue.SetInitialValue();
2503 mTempData.AddLonghandProperty(aPropertyID, defaultValue);
2506 // Copy the property value into the rule data.
2507 mTempData.MapRuleInfoInto(aPropertyID, aRuleData);
2509 mTempData.ClearProperty(propertyToParse);
2510 mTempData.AssertInitialState();
2513 bool
2514 CSSParserImpl::ParseCounterStyleName(const nsAString& aBuffer,
2515 nsIURI* aURL,
2516 nsAString& aName)
2518 nsCSSScanner scanner(aBuffer, 0);
2519 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURL);
2520 InitScanner(scanner, reporter, aURL, aURL, nullptr);
2522 bool success = ParseCounterStyleName(aName, true) && !GetToken(true);
2524 OUTPUT_ERROR();
2525 ReleaseScanner();
2527 return success;
2530 bool
2531 CSSParserImpl::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
2532 const nsAString& aBuffer,
2533 nsIURI* aSheetURL,
2534 nsIURI* aBaseURL,
2535 nsIPrincipal* aSheetPrincipal,
2536 nsCSSValue& aValue)
2538 nsCSSScanner scanner(aBuffer, 0);
2539 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
2540 InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
2542 bool success = ParseCounterDescriptorValue(aDescID, aValue) &&
2543 !GetToken(true);
2545 OUTPUT_ERROR();
2546 ReleaseScanner();
2548 return success;
2551 //----------------------------------------------------------------------
2553 bool
2554 CSSParserImpl::GetToken(bool aSkipWS)
2556 if (mHavePushBack) {
2557 mHavePushBack = false;
2558 if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) {
2559 return true;
2562 return mScanner->Next(mToken, aSkipWS);
2565 void
2566 CSSParserImpl::UngetToken()
2568 NS_PRECONDITION(!mHavePushBack, "double pushback");
2569 mHavePushBack = true;
2572 bool
2573 CSSParserImpl::GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum)
2575 // Peek at next token so that mScanner updates line and column vals
2576 if (!GetToken(aSkipWS)) {
2577 return false;
2579 UngetToken();
2580 // The scanner uses one-indexing for line numbers but zero-indexing
2581 // for column numbers.
2582 *linenum = mScanner->GetLineNumber();
2583 *colnum = 1 + mScanner->GetColumnNumber();
2584 return true;
2587 bool
2588 CSSParserImpl::ExpectSymbol(char16_t aSymbol,
2589 bool aSkipWS)
2591 if (!GetToken(aSkipWS)) {
2592 // CSS2.1 specifies that all "open constructs" are to be closed at
2593 // EOF. It simplifies higher layers if we claim to have found an
2594 // ), ], }, or ; if we encounter EOF while looking for one of them.
2595 // Do still issue a diagnostic, to aid debugging.
2596 if (aSymbol == ')' || aSymbol == ']' ||
2597 aSymbol == '}' || aSymbol == ';') {
2598 REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
2599 return true;
2601 else
2602 return false;
2604 if (mToken.IsSymbol(aSymbol)) {
2605 return true;
2607 UngetToken();
2608 return false;
2611 // Checks to see if we're at the end of a property. If an error occurs during
2612 // the check, does not signal a parse error.
2613 bool
2614 CSSParserImpl::CheckEndProperty()
2616 if (!GetToken(true)) {
2617 return true; // properties may end with eof
2619 if ((eCSSToken_Symbol == mToken.mType) &&
2620 ((';' == mToken.mSymbol) ||
2621 ('!' == mToken.mSymbol) ||
2622 ('}' == mToken.mSymbol) ||
2623 (')' == mToken.mSymbol))) {
2624 // XXX need to verify that ! is only followed by "important [;|}]
2625 // XXX this requires a multi-token pushback buffer
2626 UngetToken();
2627 return true;
2629 UngetToken();
2630 return false;
2633 // Checks if we're at the end of a property, raising an error if we're not.
2634 bool
2635 CSSParserImpl::ExpectEndProperty()
2637 if (CheckEndProperty())
2638 return true;
2640 // If we're here, we read something incorrect, so we should report it.
2641 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
2642 return false;
2645 // Parses the priority suffix on a property, which at present may be
2646 // either '!important' or nothing.
2647 CSSParserImpl::PriorityParsingStatus
2648 CSSParserImpl::ParsePriority()
2650 if (!GetToken(true)) {
2651 return ePriority_None; // properties may end with EOF
2653 if (!mToken.IsSymbol('!')) {
2654 UngetToken();
2655 return ePriority_None; // dunno what it is, but it's not a priority
2658 if (!GetToken(true)) {
2659 // EOF is not ok after !
2660 REPORT_UNEXPECTED_EOF(PEImportantEOF);
2661 return ePriority_Error;
2664 if (mToken.mType != eCSSToken_Ident ||
2665 !mToken.mIdent.LowerCaseEqualsLiteral("important")) {
2666 REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
2667 UngetToken();
2668 return ePriority_Error;
2671 return ePriority_Important;
2674 nsSubstring*
2675 CSSParserImpl::NextIdent()
2677 // XXX Error reporting?
2678 if (!GetToken(true)) {
2679 return nullptr;
2681 if (eCSSToken_Ident != mToken.mType) {
2682 UngetToken();
2683 return nullptr;
2685 return &mToken.mIdent;
2688 bool
2689 CSSParserImpl::SkipAtRule(bool aInsideBlock)
2691 for (;;) {
2692 if (!GetToken(true)) {
2693 REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2);
2694 return false;
2696 if (eCSSToken_Symbol == mToken.mType) {
2697 char16_t symbol = mToken.mSymbol;
2698 if (symbol == ';') {
2699 break;
2701 if (aInsideBlock && symbol == '}') {
2702 // The closing } doesn't belong to us.
2703 UngetToken();
2704 break;
2706 if (symbol == '{') {
2707 SkipUntil('}');
2708 break;
2709 } else if (symbol == '(') {
2710 SkipUntil(')');
2711 } else if (symbol == '[') {
2712 SkipUntil(']');
2714 } else if (eCSSToken_Function == mToken.mType ||
2715 eCSSToken_Bad_URL == mToken.mType) {
2716 SkipUntil(')');
2719 return true;
2722 bool
2723 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
2724 void* aData,
2725 bool aInAtRule)
2728 nsCSSSection newSection;
2729 bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*);
2731 if ((mSection <= eCSSSection_Charset) &&
2732 (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
2733 parseFunc = &CSSParserImpl::ParseCharsetRule;
2734 newSection = eCSSSection_Import; // only one charset allowed
2736 } else if ((mSection <= eCSSSection_Import) &&
2737 mToken.mIdent.LowerCaseEqualsLiteral("import")) {
2738 parseFunc = &CSSParserImpl::ParseImportRule;
2739 newSection = eCSSSection_Import;
2741 } else if ((mSection <= eCSSSection_NameSpace) &&
2742 mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
2743 parseFunc = &CSSParserImpl::ParseNameSpaceRule;
2744 newSection = eCSSSection_NameSpace;
2746 } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
2747 parseFunc = &CSSParserImpl::ParseMediaRule;
2748 newSection = eCSSSection_General;
2750 } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
2751 parseFunc = &CSSParserImpl::ParseMozDocumentRule;
2752 newSection = eCSSSection_General;
2754 } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
2755 parseFunc = &CSSParserImpl::ParseFontFaceRule;
2756 newSection = eCSSSection_General;
2758 } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values")) {
2759 parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule;
2760 newSection = eCSSSection_General;
2762 } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
2763 parseFunc = &CSSParserImpl::ParsePageRule;
2764 newSection = eCSSSection_General;
2766 } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation) &&
2767 mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) ||
2768 mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) {
2769 parseFunc = &CSSParserImpl::ParseKeyframesRule;
2770 newSection = eCSSSection_General;
2772 } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports")) {
2773 parseFunc = &CSSParserImpl::ParseSupportsRule;
2774 newSection = eCSSSection_General;
2776 } else if (mToken.mIdent.LowerCaseEqualsLiteral("counter-style")) {
2777 parseFunc = &CSSParserImpl::ParseCounterStyleRule;
2778 newSection = eCSSSection_General;
2780 } else {
2781 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
2782 REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
2783 OUTPUT_ERROR();
2785 // Skip over unsupported at rule, don't advance section
2786 return SkipAtRule(aInAtRule);
2789 // Inside of @-rules, only the rules that can occur anywhere
2790 // are allowed.
2791 bool unnestable = aInAtRule && newSection != eCSSSection_General;
2792 if (unnestable) {
2793 REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule);
2796 if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) {
2797 // Skip over invalid at rule, don't advance section
2798 OUTPUT_ERROR();
2799 return SkipAtRule(aInAtRule);
2802 // Nested @-rules don't affect the top-level rule chain requirement
2803 if (!aInAtRule) {
2804 mSection = newSection;
2807 return true;
2810 bool
2811 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
2812 void* aData)
2814 uint32_t linenum, colnum;
2815 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
2816 !GetToken(true)) {
2817 REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
2818 return false;
2821 if (eCSSToken_String != mToken.mType) {
2822 UngetToken();
2823 REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
2824 return false;
2827 nsAutoString charset = mToken.mIdent;
2829 if (!ExpectSymbol(';', true)) {
2830 return false;
2833 nsRefPtr<css::CharsetRule> rule = new css::CharsetRule(charset,
2834 linenum, colnum);
2835 (*aAppendFunc)(rule, aData);
2837 return true;
2840 bool
2841 CSSParserImpl::ParseURLOrString(nsString& aURL)
2843 if (!GetToken(true)) {
2844 return false;
2846 if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) {
2847 aURL = mToken.mIdent;
2848 return true;
2850 UngetToken();
2851 return false;
2854 bool
2855 CSSParserImpl::ParseMediaQuery(eMediaQueryType aQueryType,
2856 nsMediaQuery **aQuery,
2857 bool *aHitStop)
2859 *aQuery = nullptr;
2860 *aHitStop = false;
2861 bool inAtRule = aQueryType == eMediaQueryAtRule;
2862 // Attempt to parse a single condition and stop
2863 bool singleCondition = aQueryType == eMediaQuerySingleCondition;
2865 // "If the comma-separated list is the empty list it is assumed to
2866 // specify the media query 'all'." (css3-mediaqueries, section
2867 // "Media Queries")
2868 if (!GetToken(true)) {
2869 *aHitStop = true;
2870 // expected termination by EOF
2871 if (!inAtRule)
2872 return true;
2874 // unexpected termination by EOF
2875 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
2876 return true;
2879 if (eCSSToken_Symbol == mToken.mType && inAtRule &&
2880 (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) {
2881 *aHitStop = true;
2882 UngetToken();
2883 return true;
2885 UngetToken();
2887 nsMediaQuery* query = new nsMediaQuery;
2888 *aQuery = query;
2890 if (ExpectSymbol('(', true)) {
2891 // we got an expression without a media type
2892 UngetToken(); // so ParseMediaQueryExpression can handle it
2893 query->SetType(nsGkAtoms::all);
2894 query->SetTypeOmitted();
2895 // Just parse the first expression here.
2896 if (!ParseMediaQueryExpression(query)) {
2897 OUTPUT_ERROR();
2898 query->SetHadUnknownExpression();
2900 } else if (singleCondition) {
2901 // Since we are only trying to consume a single condition, which precludes
2902 // media types and not/only, this should be the same as reaching immediate
2903 // EOF (no condition to parse)
2904 *aHitStop = true;
2905 return true;
2906 } else {
2907 nsCOMPtr<nsIAtom> mediaType;
2908 bool gotNotOrOnly = false;
2909 for (;;) {
2910 if (!GetToken(true)) {
2911 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
2912 return false;
2914 if (eCSSToken_Ident != mToken.mType) {
2915 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
2916 UngetToken();
2917 return false;
2919 // case insensitive from CSS - must be lower cased
2920 nsContentUtils::ASCIIToLower(mToken.mIdent);
2921 mediaType = do_GetAtom(mToken.mIdent);
2922 if (!mediaType) {
2923 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
2925 if (!gotNotOrOnly && mediaType == nsGkAtoms::_not) {
2926 gotNotOrOnly = true;
2927 query->SetNegated();
2928 } else if (!gotNotOrOnly && mediaType == nsGkAtoms::only) {
2929 gotNotOrOnly = true;
2930 query->SetHasOnly();
2931 } else if (mediaType == nsGkAtoms::_not ||
2932 mediaType == nsGkAtoms::only ||
2933 mediaType == nsGkAtoms::_and ||
2934 mediaType == nsGkAtoms::_or) {
2935 REPORT_UNEXPECTED_TOKEN(PEGatherMediaReservedMediaType);
2936 UngetToken();
2937 return false;
2938 } else {
2939 // valid media type
2940 break;
2943 query->SetType(mediaType);
2946 for (;;) {
2947 if (!GetToken(true)) {
2948 *aHitStop = true;
2949 // expected termination by EOF
2950 if (!inAtRule)
2951 break;
2953 // unexpected termination by EOF
2954 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
2955 break;
2958 if (eCSSToken_Symbol == mToken.mType && inAtRule &&
2959 (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) {
2960 *aHitStop = true;
2961 UngetToken();
2962 break;
2964 if (!singleCondition &&
2965 eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
2966 // Done with the expressions for this query
2967 break;
2969 if (eCSSToken_Ident != mToken.mType ||
2970 !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
2971 if (singleCondition) {
2972 // We have a condition at this point -- if we're not chained to other
2973 // conditions with and/or, we're done.
2974 UngetToken();
2975 break;
2976 } else {
2977 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
2978 UngetToken();
2979 return false;
2982 if (!ParseMediaQueryExpression(query)) {
2983 OUTPUT_ERROR();
2984 query->SetHadUnknownExpression();
2987 return true;
2990 // Returns false only when there is a low-level error in the scanner
2991 // (out-of-memory).
2992 bool
2993 CSSParserImpl::GatherMedia(nsMediaList* aMedia,
2994 bool aInAtRule)
2996 eMediaQueryType type = aInAtRule ? eMediaQueryAtRule : eMediaQueryNormal;
2997 for (;;) {
2998 nsAutoPtr<nsMediaQuery> query;
2999 bool hitStop;
3000 if (!ParseMediaQuery(type, getter_Transfers(query), &hitStop)) {
3001 NS_ASSERTION(!hitStop, "should return true when hit stop");
3002 OUTPUT_ERROR();
3003 if (query) {
3004 query->SetHadUnknownExpression();
3006 if (aInAtRule) {
3007 const char16_t stopChars[] =
3008 { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) };
3009 SkipUntilOneOf(stopChars);
3010 } else {
3011 SkipUntil(',');
3013 // Rely on SkipUntilOneOf leaving mToken around as the last token read.
3014 if (mToken.mType == eCSSToken_Symbol && aInAtRule &&
3015 (mToken.mSymbol == '{' || mToken.mSymbol == ';' || mToken.mSymbol == '}')) {
3016 UngetToken();
3017 hitStop = true;
3020 if (query) {
3021 aMedia->AppendQuery(query);
3023 if (hitStop) {
3024 break;
3027 return true;
3030 bool
3031 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
3033 if (!ExpectSymbol('(', true)) {
3034 REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
3035 return false;
3037 if (! GetToken(true)) {
3038 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
3039 return false;
3041 if (eCSSToken_Ident != mToken.mType) {
3042 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
3043 UngetToken();
3044 SkipUntil(')');
3045 return false;
3048 nsMediaExpression *expr = aQuery->NewExpression();
3050 // case insensitive from CSS - must be lower cased
3051 nsContentUtils::ASCIIToLower(mToken.mIdent);
3052 const char16_t *featureString;
3053 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
3054 expr->mRange = nsMediaExpression::eMin;
3055 featureString = mToken.mIdent.get() + 4;
3056 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
3057 expr->mRange = nsMediaExpression::eMax;
3058 featureString = mToken.mIdent.get() + 4;
3059 } else {
3060 expr->mRange = nsMediaExpression::eEqual;
3061 featureString = mToken.mIdent.get();
3064 nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
3065 if (!mediaFeatureAtom) {
3066 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
3068 const nsMediaFeature *feature = nsMediaFeatures::features;
3069 for (; feature->mName; ++feature) {
3070 if (*(feature->mName) == mediaFeatureAtom) {
3071 break;
3074 if (!feature->mName ||
3075 (expr->mRange != nsMediaExpression::eEqual &&
3076 feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
3077 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
3078 SkipUntil(')');
3079 return false;
3081 expr->mFeature = feature;
3083 if (!GetToken(true) || mToken.IsSymbol(')')) {
3084 // Query expressions for any feature can be given without a value.
3085 // However, min/max prefixes are not allowed.
3086 if (expr->mRange != nsMediaExpression::eEqual) {
3087 REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
3088 return false;
3090 expr->mValue.Reset();
3091 return true;
3094 if (!mToken.IsSymbol(':')) {
3095 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
3096 UngetToken();
3097 SkipUntil(')');
3098 return false;
3101 bool rv = false;
3102 switch (feature->mValueType) {
3103 case nsMediaFeature::eLength:
3104 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_LENGTH, nullptr);
3105 break;
3106 case nsMediaFeature::eInteger:
3107 case nsMediaFeature::eBoolInteger:
3108 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_INTEGER, nullptr);
3109 // Enforce extra restrictions for eBoolInteger
3110 if (rv &&
3111 feature->mValueType == nsMediaFeature::eBoolInteger &&
3112 expr->mValue.GetIntValue() > 1)
3113 rv = false;
3114 break;
3115 case nsMediaFeature::eFloat:
3116 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_NUMBER, nullptr);
3117 break;
3118 case nsMediaFeature::eIntRatio:
3120 // Two integers separated by '/', with optional whitespace on
3121 // either side of the '/'.
3122 nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
3123 expr->mValue.SetArrayValue(a, eCSSUnit_Array);
3124 // We don't bother with ParseNonNegativeVariant since we have to
3125 // check for != 0 as well; no need to worry about the UngetToken
3126 // since we're throwing out up to the next ')' anyway.
3127 rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nullptr) &&
3128 a->Item(0).GetIntValue() > 0 &&
3129 ExpectSymbol('/', true) &&
3130 ParseVariant(a->Item(1), VARIANT_INTEGER, nullptr) &&
3131 a->Item(1).GetIntValue() > 0;
3133 break;
3134 case nsMediaFeature::eResolution:
3135 rv = GetToken(true);
3136 if (!rv)
3137 break;
3138 rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f;
3139 if (!rv) {
3140 UngetToken();
3141 break;
3143 // No worries about whether unitless zero is allowed, since the
3144 // value must be positive (and we checked that above).
3145 NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
3146 if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
3147 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
3148 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) {
3149 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
3150 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
3151 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
3152 } else {
3153 rv = false;
3155 break;
3156 case nsMediaFeature::eEnumerated:
3157 rv = ParseVariant(expr->mValue, VARIANT_KEYWORD,
3158 feature->mData.mKeywordTable);
3159 break;
3160 case nsMediaFeature::eIdent:
3161 rv = ParseVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr);
3162 break;
3164 if (!rv || !ExpectSymbol(')', true)) {
3165 REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
3166 SkipUntil(')');
3167 return false;
3170 return true;
3173 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
3174 bool
3175 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
3177 nsRefPtr<nsMediaList> media = new nsMediaList();
3179 uint32_t linenum, colnum;
3180 nsAutoString url;
3181 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3182 !ParseURLOrString(url)) {
3183 REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
3184 return false;
3187 if (!ExpectSymbol(';', true)) {
3188 if (!GatherMedia(media, true) ||
3189 !ExpectSymbol(';', true)) {
3190 REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
3191 // don't advance section, simply ignore invalid @import
3192 return false;
3195 // Safe to assert this, since we ensured that there is something
3196 // other than the ';' coming after the @import's url() token.
3197 NS_ASSERTION(media->Length() != 0, "media list must be nonempty");
3200 ProcessImport(url, media, aAppendFunc, aData, linenum, colnum);
3201 return true;
3204 void
3205 CSSParserImpl::ProcessImport(const nsString& aURLSpec,
3206 nsMediaList* aMedia,
3207 RuleAppendFunc aAppendFunc,
3208 void* aData,
3209 uint32_t aLineNumber,
3210 uint32_t aColumnNumber)
3212 nsRefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec,
3213 aLineNumber,
3214 aColumnNumber);
3215 (*aAppendFunc)(rule, aData);
3217 // Diagnose bad URIs even if we don't have a child loader.
3218 nsCOMPtr<nsIURI> url;
3219 // Charset will be deduced from mBaseURI, which is more or less correct.
3220 nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI);
3222 if (NS_FAILED(rv)) {
3223 if (rv == NS_ERROR_MALFORMED_URI) {
3224 // import url is bad
3225 REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
3226 OUTPUT_ERROR();
3228 return;
3231 if (mChildLoader) {
3232 mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
3236 // Parse the {} part of an @media or @-moz-document rule.
3237 bool
3238 CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
3239 RuleAppendFunc aAppendFunc,
3240 void* aData)
3242 // XXXbz this could use better error reporting throughout the method
3243 if (!ExpectSymbol('{', true)) {
3244 return false;
3247 // push rule on stack, loop over children
3248 PushGroup(aRule);
3249 nsCSSSection holdSection = mSection;
3250 mSection = eCSSSection_General;
3252 for (;;) {
3253 // Get next non-whitespace token
3254 if (! GetToken(true)) {
3255 REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2);
3256 break;
3258 if (mToken.IsSymbol('}')) { // done!
3259 UngetToken();
3260 break;
3262 if (eCSSToken_AtKeyword == mToken.mType) {
3263 // Parse for nested rules
3264 ParseAtRule(aAppendFunc, aData, true);
3265 continue;
3267 UngetToken();
3268 ParseRuleSet(AppendRuleToSheet, this, true);
3270 PopGroup();
3272 if (!ExpectSymbol('}', true)) {
3273 mSection = holdSection;
3274 return false;
3276 (*aAppendFunc)(aRule, aData);
3277 return true;
3280 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
3281 bool
3282 CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
3284 nsRefPtr<nsMediaList> media = new nsMediaList();
3285 uint32_t linenum, colnum;
3286 if (GetNextTokenLocation(true, &linenum, &colnum) &&
3287 GatherMedia(media, true)) {
3288 // XXXbz this could use better error reporting throughout the method
3289 nsRefPtr<css::MediaRule> rule = new css::MediaRule(linenum, colnum);
3290 // Append first, so when we do SetMedia() the rule
3291 // knows what its stylesheet is.
3292 if (ParseGroupRule(rule, aAppendFunc, aData)) {
3293 rule->SetMedia(media);
3294 return true;
3298 return false;
3301 // Parse a @-moz-document rule. This is like an @media rule, but instead
3302 // of a medium it has a nonempty list of items where each item is either
3303 // url(), url-prefix(), or domain().
3304 bool
3305 CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
3307 css::DocumentRule::URL *urls = nullptr;
3308 css::DocumentRule::URL **next = &urls;
3310 uint32_t linenum, colnum;
3311 if (!GetNextTokenLocation(true, &linenum, &colnum)) {
3312 return false;
3315 do {
3316 if (!GetToken(true)) {
3317 REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF);
3318 delete urls;
3319 return false;
3322 if (!(eCSSToken_URL == mToken.mType ||
3323 (eCSSToken_Function == mToken.mType &&
3324 (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
3325 mToken.mIdent.LowerCaseEqualsLiteral("domain") ||
3326 mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) {
3327 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc2);
3328 UngetToken();
3329 delete urls;
3330 return false;
3332 css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL;
3333 next = &cur->next;
3334 if (mToken.mType == eCSSToken_URL) {
3335 cur->func = css::DocumentRule::eURL;
3336 CopyUTF16toUTF8(mToken.mIdent, cur->url);
3337 } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) {
3338 // regexp() is different from url-prefix() and domain() (but
3339 // probably the way they *should* have been* in that it requires a
3340 // string argument, and doesn't try to behave like url().
3341 cur->func = css::DocumentRule::eRegExp;
3342 GetToken(true);
3343 // copy before we know it's valid (but before ExpectSymbol changes
3344 // mToken.mIdent)
3345 CopyUTF16toUTF8(mToken.mIdent, cur->url);
3346 if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) {
3347 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString);
3348 SkipUntil(')');
3349 delete urls;
3350 return false;
3352 } else {
3353 if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
3354 cur->func = css::DocumentRule::eURLPrefix;
3355 } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
3356 cur->func = css::DocumentRule::eDomain;
3359 NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point");
3360 mScanner->NextURL(mToken);
3361 if (mToken.mType != eCSSToken_URL) {
3362 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
3363 SkipUntil(')');
3364 delete urls;
3365 return false;
3368 // We could try to make the URL (as long as it's not domain())
3369 // canonical and absolute with NS_NewURI and GetSpec, but I'm
3370 // inclined to think we shouldn't.
3371 CopyUTF16toUTF8(mToken.mIdent, cur->url);
3373 } while (ExpectSymbol(',', true));
3375 nsRefPtr<css::DocumentRule> rule = new css::DocumentRule(linenum, colnum);
3376 rule->SetURLs(urls);
3378 return ParseGroupRule(rule, aAppendFunc, aData);
3381 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
3382 bool
3383 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
3385 uint32_t linenum, colnum;
3386 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3387 !GetToken(true)) {
3388 REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
3389 return false;
3392 nsAutoString prefix;
3393 nsAutoString url;
3395 if (eCSSToken_Ident == mToken.mType) {
3396 prefix = mToken.mIdent;
3397 // user-specified identifiers are case-sensitive (bug 416106)
3398 } else {
3399 UngetToken();
3402 if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) {
3403 if (mHavePushBack) {
3404 REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
3405 } else {
3406 REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
3408 return false;
3411 ProcessNameSpace(prefix, url, aAppendFunc, aData, linenum, colnum);
3412 return true;
3415 void
3416 CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
3417 const nsString& aURLSpec,
3418 RuleAppendFunc aAppendFunc,
3419 void* aData,
3420 uint32_t aLineNumber,
3421 uint32_t aColumnNumber)
3423 nsCOMPtr<nsIAtom> prefix;
3425 if (!aPrefix.IsEmpty()) {
3426 prefix = do_GetAtom(aPrefix);
3427 if (!prefix) {
3428 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
3432 nsRefPtr<css::NameSpaceRule> rule = new css::NameSpaceRule(prefix, aURLSpec,
3433 aLineNumber,
3434 aColumnNumber);
3435 (*aAppendFunc)(rule, aData);
3437 // If this was the first namespace rule encountered, it will trigger
3438 // creation of a namespace map.
3439 if (!mNameSpaceMap) {
3440 mNameSpaceMap = mSheet->GetNameSpaceMap();
3444 // font-face-rule: '@font-face' '{' font-description '}'
3445 // font-description: font-descriptor+
3446 bool
3447 CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
3449 uint32_t linenum, colnum;
3450 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3451 !ExpectSymbol('{', true)) {
3452 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart);
3453 return false;
3456 nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule(linenum, colnum));
3458 for (;;) {
3459 if (!GetToken(true)) {
3460 REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
3461 break;
3463 if (mToken.IsSymbol('}')) { // done!
3464 UngetToken();
3465 break;
3468 // ignore extra semicolons
3469 if (mToken.IsSymbol(';'))
3470 continue;
3472 if (!ParseFontDescriptor(rule)) {
3473 REPORT_UNEXPECTED(PEDeclSkipped);
3474 OUTPUT_ERROR();
3475 if (!SkipDeclaration(true))
3476 break;
3479 if (!ExpectSymbol('}', true)) {
3480 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd);
3481 return false;
3483 (*aAppendFunc)(rule, aData);
3484 return true;
3487 // font-descriptor: font-family-desc
3488 // | font-style-desc
3489 // | font-weight-desc
3490 // | font-stretch-desc
3491 // | font-src-desc
3492 // | unicode-range-desc
3494 // All font-*-desc productions follow the pattern
3495 // IDENT ':' value ';'
3497 // On entry to this function, mToken is the IDENT.
3499 bool
3500 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
3502 if (eCSSToken_Ident != mToken.mType) {
3503 REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
3504 return false;
3507 nsString descName = mToken.mIdent;
3508 if (!ExpectSymbol(':', true)) {
3509 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3510 OUTPUT_ERROR();
3511 return false;
3514 nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
3515 nsCSSValue value;
3517 if (descID == eCSSFontDesc_UNKNOWN) {
3518 if (NonMozillaVendorIdentifier(descName)) {
3519 // silently skip other vendors' extensions
3520 SkipDeclaration(true);
3521 return true;
3522 } else {
3523 REPORT_UNEXPECTED_P(PEUnknownFontDesc, descName);
3524 return false;
3528 if (!ParseFontDescriptorValue(descID, value)) {
3529 REPORT_UNEXPECTED_P(PEValueParsingError, descName);
3530 return false;
3533 if (!ExpectEndProperty())
3534 return false;
3536 aRule->SetDesc(descID, value);
3537 return true;
3540 // @font-feature-values <font-family># {
3541 // @<feature-type> {
3542 // <feature-ident> : <feature-index>+;
3543 // <feature-ident> : <feature-index>+;
3544 // ...
3545 // }
3546 // ...
3547 // }
3549 bool
3550 CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
3551 void* aData)
3553 uint32_t linenum, colnum;
3554 if (!GetNextTokenLocation(true, &linenum, &colnum)) {
3555 return false;
3558 nsRefPtr<nsCSSFontFeatureValuesRule>
3559 valuesRule(new nsCSSFontFeatureValuesRule(linenum, colnum));
3561 // parse family list
3562 nsCSSValue fontlistValue;
3564 if (!ParseFamily(fontlistValue) ||
3565 fontlistValue.GetUnit() != eCSSUnit_FontFamilyList)
3567 REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily);
3568 return false;
3571 // add family to rule
3572 const FontFamilyList* fontlist = fontlistValue.GetFontFamilyListValue();
3574 // family list has generic ==> parse error
3575 if (fontlist->HasGeneric()) {
3576 REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList);
3577 return false;
3580 valuesRule->SetFamilyList(*fontlist);
3582 // open brace
3583 if (!ExpectSymbol('{', true)) {
3584 REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart);
3585 return false;
3588 // list of sets of feature values, each set bound to a specific
3589 // feature-type (e.g. swash, annotation)
3590 for (;;) {
3591 if (!GetToken(true)) {
3592 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
3593 break;
3595 if (mToken.IsSymbol('}')) { // done!
3596 UngetToken();
3597 break;
3600 if (!ParseFontFeatureValueSet(valuesRule)) {
3601 if (!SkipAtRule(false)) {
3602 break;
3606 if (!ExpectSymbol('}', true)) {
3607 REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd);
3608 SkipUntil('}');
3609 return false;
3612 (*aAppendFunc)(valuesRule, aData);
3613 return true;
3616 #define NUMVALUES_NO_LIMIT 0xFFFF
3618 // parse a single value set containing name-value pairs for a single feature type
3619 // @<feature-type> { [ <feature-ident> : <feature-index>+ ; ]* }
3620 // Ex: @swash { flowing: 1; delicate: 2; }
3621 bool
3622 CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule
3623 *aFeatureValuesRule)
3625 // -- @keyword (e.g. swash, styleset)
3626 if (eCSSToken_AtKeyword != mToken.mType) {
3627 REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt);
3628 OUTPUT_ERROR();
3629 UngetToken();
3630 return false;
3633 // which font-specific variant of font-variant-alternates
3634 int32_t whichVariant;
3635 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
3636 if (keyword == eCSSKeyword_UNKNOWN ||
3637 !nsCSSProps::FindKeyword(keyword,
3638 nsCSSProps::kFontVariantAlternatesFuncsKTable,
3639 whichVariant))
3641 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
3642 REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue);
3643 OUTPUT_ERROR();
3645 UngetToken();
3646 return false;
3649 nsAutoString featureType(mToken.mIdent);
3651 // open brace
3652 if (!ExpectSymbol('{', true)) {
3653 REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart);
3654 return false;
3657 // styleset and character-variant can be multi-valued, otherwise single value
3658 int32_t limitNumValues;
3660 switch (keyword) {
3661 case eCSSKeyword_styleset:
3662 limitNumValues = NUMVALUES_NO_LIMIT;
3663 break;
3664 case eCSSKeyword_character_variant:
3665 limitNumValues = 2;
3666 break;
3667 default:
3668 limitNumValues = 1;
3669 break;
3672 // -- ident integer+ [, ident integer+]
3673 nsAutoTArray<gfxFontFeatureValueSet::ValueList, 5> values;
3675 // list of font-feature-values-declaration's
3676 for (;;) {
3677 nsAutoString valueId;
3679 if (!GetToken(true)) {
3680 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
3681 break;
3684 // ignore extra semicolons
3685 if (mToken.IsSymbol(';')) {
3686 continue;
3689 // close brace ==> done
3690 if (mToken.IsSymbol('}')) {
3691 break;
3694 // ident
3695 if (eCSSToken_Ident != mToken.mType) {
3696 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent);
3697 if (!SkipDeclaration(true)) {
3698 break;
3700 continue;
3703 valueId.Assign(mToken.mIdent);
3705 // colon
3706 if (!ExpectSymbol(':', true)) {
3707 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3708 OUTPUT_ERROR();
3709 if (!SkipDeclaration(true)) {
3710 break;
3712 continue;
3715 // value list
3716 nsAutoTArray<uint32_t,4> featureSelectors;
3718 nsCSSValue intValue;
3719 while (ParseNonNegativeVariant(intValue, VARIANT_INTEGER, nullptr)) {
3720 featureSelectors.AppendElement(uint32_t(intValue.GetIntValue()));
3723 int32_t numValues = featureSelectors.Length();
3725 if (numValues == 0) {
3726 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue);
3727 OUTPUT_ERROR();
3728 if (!SkipDeclaration(true)) {
3729 break;
3731 continue;
3734 if (numValues > limitNumValues) {
3735 REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType);
3736 OUTPUT_ERROR();
3737 if (!SkipDeclaration(true)) {
3738 break;
3740 continue;
3743 if (!GetToken(true)) {
3744 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
3745 gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
3746 values.AppendElement(v);
3747 break;
3750 // ';' or '}' to end definition
3751 if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) {
3752 REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing);
3753 OUTPUT_ERROR();
3754 if (!SkipDeclaration(true)) {
3755 break;
3757 continue;
3760 gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
3761 values.AppendElement(v);
3763 if (mToken.IsSymbol('}')) {
3764 break;
3768 aFeatureValuesRule->AddValueList(whichVariant, values);
3769 return true;
3772 bool
3773 CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData)
3775 uint32_t linenum, colnum;
3776 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3777 !GetToken(true)) {
3778 REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF);
3779 return false;
3782 if (mToken.mType != eCSSToken_Ident) {
3783 REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
3784 UngetToken();
3785 return false;
3787 nsString name(mToken.mIdent);
3789 if (!ExpectSymbol('{', true)) {
3790 REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace);
3791 return false;
3794 nsRefPtr<nsCSSKeyframesRule> rule = new nsCSSKeyframesRule(name,
3795 linenum, colnum);
3797 while (!ExpectSymbol('}', true)) {
3798 nsRefPtr<nsCSSKeyframeRule> kid = ParseKeyframeRule();
3799 if (kid) {
3800 rule->AppendStyleRule(kid);
3801 } else {
3802 OUTPUT_ERROR();
3803 SkipRuleSet(true);
3807 (*aAppendFunc)(rule, aData);
3808 return true;
3811 bool
3812 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
3814 uint32_t linenum, colnum;
3815 if (!GetNextTokenLocation(true, &linenum, &colnum)) {
3816 return false;
3819 // TODO: There can be page selectors after @page such as ":first", ":left".
3820 uint32_t parseFlags = eParseDeclaration_InBraces |
3821 eParseDeclaration_AllowImportant;
3823 // Forbid viewport units in @page rules. See bug 811391.
3824 NS_ABORT_IF_FALSE(mViewportUnitsEnabled,
3825 "Viewport units should be enabled outside of @page rules.");
3826 mViewportUnitsEnabled = false;
3827 nsAutoPtr<css::Declaration> declaration(
3828 ParseDeclarationBlock(parseFlags,
3829 eCSSContext_Page));
3830 mViewportUnitsEnabled = true;
3832 if (!declaration) {
3833 return false;
3836 // Takes ownership of declaration.
3837 nsRefPtr<nsCSSPageRule> rule = new nsCSSPageRule(Move(declaration),
3838 linenum, colnum);
3840 (*aAppendFunc)(rule, aData);
3841 return true;
3844 already_AddRefed<nsCSSKeyframeRule>
3845 CSSParserImpl::ParseKeyframeRule()
3847 InfallibleTArray<float> selectorList;
3848 uint32_t linenum, colnum;
3849 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3850 !ParseKeyframeSelectorList(selectorList)) {
3851 REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
3852 return nullptr;
3855 // Ignore !important in keyframe rules
3856 uint32_t parseFlags = eParseDeclaration_InBraces;
3857 nsAutoPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags));
3858 if (!declaration) {
3859 return nullptr;
3862 // Takes ownership of declaration, and steals contents of selectorList.
3863 nsRefPtr<nsCSSKeyframeRule> rule =
3864 new nsCSSKeyframeRule(selectorList, Move(declaration), linenum, colnum);
3865 return rule.forget();
3868 bool
3869 CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList)
3871 for (;;) {
3872 if (!GetToken(true)) {
3873 // The first time through the loop, this means we got an empty
3874 // list. Otherwise, it means we have a trailing comma.
3875 return false;
3877 float value;
3878 switch (mToken.mType) {
3879 case eCSSToken_Percentage:
3880 value = mToken.mNumber;
3881 break;
3882 case eCSSToken_Ident:
3883 if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
3884 value = 0.0f;
3885 break;
3887 if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
3888 value = 1.0f;
3889 break;
3891 // fall through
3892 default:
3893 UngetToken();
3894 // The first time through the loop, this means we got an empty
3895 // list. Otherwise, it means we have a trailing comma.
3896 return false;
3898 aSelectorList.AppendElement(value);
3899 if (!ExpectSymbol(',', true)) {
3900 return true;
3905 // supports_rule
3906 // : "@supports" supports_condition group_rule_body
3907 // ;
3908 bool
3909 CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData)
3911 bool conditionMet = false;
3912 nsString condition;
3914 mScanner->StartRecording();
3916 uint32_t linenum, colnum;
3917 if (!GetNextTokenLocation(true, &linenum, &colnum)) {
3918 return false;
3921 bool parsed = ParseSupportsCondition(conditionMet);
3923 if (!parsed) {
3924 mScanner->StopRecording();
3925 return false;
3928 if (!ExpectSymbol('{', true)) {
3929 REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart);
3930 mScanner->StopRecording();
3931 return false;
3934 UngetToken();
3935 mScanner->StopRecording(condition);
3937 // Remove the "{" that would follow the condition.
3938 if (condition.Length() != 0) {
3939 condition.Truncate(condition.Length() - 1);
3942 // Remove spaces from the start and end of the recorded supports condition.
3943 condition.Trim(" ", true, true, false);
3945 // Record whether we are in a failing @supports, so that property parse
3946 // errors don't get reported.
3947 nsAutoFailingSupportsRule failing(this, conditionMet);
3949 nsRefPtr<css::GroupRule> rule = new CSSSupportsRule(conditionMet, condition,
3950 linenum, colnum);
3951 return ParseGroupRule(rule, aAppendFunc, aProcessData);
3954 // supports_condition
3955 // : supports_condition_in_parens supports_condition_terms
3956 // | supports_condition_negation
3957 // ;
3958 bool
3959 CSSParserImpl::ParseSupportsCondition(bool& aConditionMet)
3961 mInSupportsCondition = true;
3963 if (!GetToken(true)) {
3964 REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2);
3965 return false;
3968 UngetToken();
3970 mScanner->ClearSeenBadToken();
3972 if (mToken.IsSymbol('(') ||
3973 mToken.mType == eCSSToken_Function ||
3974 mToken.mType == eCSSToken_URL ||
3975 mToken.mType == eCSSToken_Bad_URL) {
3976 bool result = ParseSupportsConditionInParens(aConditionMet) &&
3977 ParseSupportsConditionTerms(aConditionMet) &&
3978 !mScanner->SeenBadToken();
3979 mInSupportsCondition = false;
3980 return result;
3983 if (mToken.mType == eCSSToken_Ident &&
3984 mToken.mIdent.LowerCaseEqualsLiteral("not")) {
3985 bool result = ParseSupportsConditionNegation(aConditionMet) &&
3986 !mScanner->SeenBadToken();
3987 mInSupportsCondition = false;
3988 return result;
3991 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart);
3992 mInSupportsCondition = false;
3993 return false;
3996 // supports_condition_negation
3997 // : 'not' S+ supports_condition_in_parens
3998 // ;
3999 bool
4000 CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet)
4002 if (!GetToken(true)) {
4003 REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF);
4004 return false;
4007 if (mToken.mType != eCSSToken_Ident ||
4008 !mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4009 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot);
4010 return false;
4013 if (!RequireWhitespace()) {
4014 REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
4015 return false;
4018 if (ParseSupportsConditionInParens(aConditionMet)) {
4019 aConditionMet = !aConditionMet;
4020 return true;
4023 return false;
4026 // supports_condition_in_parens
4027 // : '(' S* supports_condition_in_parens_inside_parens ')' S*
4028 // | general_enclosed
4029 // ;
4030 bool
4031 CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet)
4033 if (!GetToken(true)) {
4034 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF);
4035 return false;
4038 if (mToken.mType == eCSSToken_URL) {
4039 aConditionMet = false;
4040 return true;
4043 if (mToken.mType == eCSSToken_Function ||
4044 mToken.mType == eCSSToken_Bad_URL) {
4045 if (!SkipUntil(')')) {
4046 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
4047 return false;
4049 aConditionMet = false;
4050 return true;
4053 if (!mToken.IsSymbol('(')) {
4054 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction);
4055 UngetToken();
4056 return false;
4059 if (!ParseSupportsConditionInParensInsideParens(aConditionMet)) {
4060 if (!SkipUntil(')')) {
4061 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
4062 return false;
4064 aConditionMet = false;
4065 return true;
4068 if (!(ExpectSymbol(')', true))) {
4069 SkipUntil(')');
4070 aConditionMet = false;
4071 return true;
4074 return true;
4077 // supports_condition_in_parens_inside_parens
4078 // : core_declaration
4079 // | supports_condition_negation
4080 // | supports_condition_in_parens supports_condition_terms
4081 // ;
4082 bool
4083 CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet)
4085 if (!GetToken(true)) {
4086 return false;
4089 if (mToken.mType == eCSSToken_Ident) {
4090 if (!mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4091 nsAutoString propertyName = mToken.mIdent;
4092 if (!ExpectSymbol(':', true)) {
4093 return false;
4096 nsCSSProperty propID = LookupEnabledProperty(propertyName);
4097 if (propID == eCSSProperty_UNKNOWN) {
4098 if (ExpectSymbol(')', true)) {
4099 UngetToken();
4100 return false;
4102 aConditionMet = false;
4103 SkipUntil(')');
4104 UngetToken();
4105 } else if (propID == eCSSPropertyExtra_variable) {
4106 if (ExpectSymbol(')', false)) {
4107 UngetToken();
4108 return false;
4110 CSSVariableDeclarations::Type variableType;
4111 nsString variableValue;
4112 aConditionMet =
4113 ParseVariableDeclaration(&variableType, variableValue) &&
4114 ParsePriority() != ePriority_Error;
4115 if (!aConditionMet) {
4116 SkipUntil(')');
4117 UngetToken();
4119 } else {
4120 if (ExpectSymbol(')', true)) {
4121 UngetToken();
4122 return false;
4124 aConditionMet = ParseProperty(propID) &&
4125 ParsePriority() != ePriority_Error;
4126 if (!aConditionMet) {
4127 SkipUntil(')');
4128 UngetToken();
4130 mTempData.ClearProperty(propID);
4131 mTempData.AssertInitialState();
4133 return true;
4136 UngetToken();
4137 return ParseSupportsConditionNegation(aConditionMet);
4140 UngetToken();
4141 return ParseSupportsConditionInParens(aConditionMet) &&
4142 ParseSupportsConditionTerms(aConditionMet);
4145 // supports_condition_terms
4146 // : S+ 'and' supports_condition_terms_after_operator('and')
4147 // | S+ 'or' supports_condition_terms_after_operator('or')
4148 // |
4149 // ;
4150 bool
4151 CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet)
4153 if (!RequireWhitespace() || !GetToken(false)) {
4154 return true;
4157 if (mToken.mType != eCSSToken_Ident) {
4158 UngetToken();
4159 return true;
4162 if (mToken.mIdent.LowerCaseEqualsLiteral("and")) {
4163 return ParseSupportsConditionTermsAfterOperator(aConditionMet, eAnd);
4166 if (mToken.mIdent.LowerCaseEqualsLiteral("or")) {
4167 return ParseSupportsConditionTermsAfterOperator(aConditionMet, eOr);
4170 UngetToken();
4171 return true;
4174 // supports_condition_terms_after_operator(operator)
4175 // : S+ supports_condition_in_parens ( <operator> supports_condition_in_parens )*
4176 // ;
4177 bool
4178 CSSParserImpl::ParseSupportsConditionTermsAfterOperator(
4179 bool& aConditionMet,
4180 CSSParserImpl::SupportsConditionTermOperator aOperator)
4182 if (!RequireWhitespace()) {
4183 REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
4184 return false;
4187 const char* token = aOperator == eAnd ? "and" : "or";
4188 for (;;) {
4189 bool termConditionMet = false;
4190 if (!ParseSupportsConditionInParens(termConditionMet)) {
4191 return false;
4193 aConditionMet = aOperator == eAnd ? aConditionMet && termConditionMet :
4194 aConditionMet || termConditionMet;
4196 if (!GetToken(true)) {
4197 return true;
4200 if (mToken.mType != eCSSToken_Ident ||
4201 !mToken.mIdent.LowerCaseEqualsASCII(token)) {
4202 UngetToken();
4203 return true;
4208 bool
4209 CSSParserImpl::ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aData)
4211 nsAutoString name;
4212 uint32_t linenum, colnum;
4213 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4214 !ParseCounterStyleName(name, true)) {
4215 REPORT_UNEXPECTED_TOKEN(PECounterStyleNotIdent);
4216 return false;
4219 if (!ExpectSymbol('{', true)) {
4220 REPORT_UNEXPECTED_TOKEN(PECounterStyleBadBlockStart);
4221 return false;
4224 nsRefPtr<nsCSSCounterStyleRule> rule = new nsCSSCounterStyleRule(name,
4225 linenum,
4226 colnum);
4227 for (;;) {
4228 if (!GetToken(true)) {
4229 REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
4230 break;
4232 if (mToken.IsSymbol('}')) {
4233 break;
4235 if (mToken.IsSymbol(';')) {
4236 continue;
4239 if (!ParseCounterDescriptor(rule)) {
4240 REPORT_UNEXPECTED(PEDeclSkipped);
4241 OUTPUT_ERROR();
4242 if (!SkipDeclaration(true)) {
4243 REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
4244 break;
4249 int32_t system = rule->GetSystem();
4250 bool isCorrect = false;
4251 switch (system) {
4252 case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
4253 case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
4254 case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
4255 case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
4256 case NS_STYLE_COUNTER_SYSTEM_FIXED: {
4257 // check whether symbols is set and the length is sufficient
4258 const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
4259 if (symbols.GetUnit() == eCSSUnit_List &&
4260 nsCSSCounterStyleRule::CheckDescValue(
4261 system, eCSSCounterDesc_Symbols, symbols)) {
4262 isCorrect = true;
4264 break;
4266 case NS_STYLE_COUNTER_SYSTEM_ADDITIVE: {
4267 // for additive system, additive-symbols must be set
4268 const nsCSSValue& symbols =
4269 rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
4270 if (symbols.GetUnit() == eCSSUnit_PairList) {
4271 isCorrect = true;
4273 break;
4275 case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
4276 // for extends system, symbols & additive-symbols must not be set
4277 const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
4278 const nsCSSValue& additiveSymbols =
4279 rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
4280 if (symbols.GetUnit() == eCSSUnit_Null &&
4281 additiveSymbols.GetUnit() == eCSSUnit_Null) {
4282 isCorrect = true;
4284 break;
4286 default:
4287 NS_NOTREACHED("unknown system");
4290 if (isCorrect) {
4291 (*aAppendFunc)(rule, aData);
4293 return true;
4296 bool
4297 CSSParserImpl::ParseCounterStyleName(nsAString& aName, bool aForDefinition)
4299 if (!GetToken(true)) {
4300 return false;
4303 if (mToken.mType != eCSSToken_Ident) {
4304 UngetToken();
4305 return false;
4308 static const nsCSSKeyword kReservedNames[] = {
4309 eCSSKeyword_none,
4310 eCSSKeyword_decimal,
4311 eCSSKeyword_UNKNOWN
4314 nsCSSValue value; // we don't actually care about the value
4315 if (!ParseCustomIdent(value, mToken.mIdent,
4316 aForDefinition ? kReservedNames : nullptr)) {
4317 REPORT_UNEXPECTED_TOKEN(PECounterStyleBadName);
4318 UngetToken();
4319 return false;
4322 aName = mToken.mIdent;
4323 if (nsCSSProps::IsPredefinedCounterStyle(aName)) {
4324 ToLowerCase(aName);
4326 return true;
4329 bool
4330 CSSParserImpl::ParseCounterDescriptor(nsCSSCounterStyleRule* aRule)
4332 if (eCSSToken_Ident != mToken.mType) {
4333 REPORT_UNEXPECTED_TOKEN(PECounterDescExpected);
4334 return false;
4337 nsString descName = mToken.mIdent;
4338 if (!ExpectSymbol(':', true)) {
4339 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
4340 OUTPUT_ERROR();
4341 return false;
4344 nsCSSCounterDesc descID = nsCSSProps::LookupCounterDesc(descName);
4345 nsCSSValue value;
4347 if (descID == eCSSCounterDesc_UNKNOWN) {
4348 REPORT_UNEXPECTED_P(PEUnknownCounterDesc, descName);
4349 return false;
4352 if (!ParseCounterDescriptorValue(descID, value)) {
4353 REPORT_UNEXPECTED_P(PEValueParsingError, descName);
4354 return false;
4357 if (!ExpectEndProperty()) {
4358 return false;
4361 aRule->SetDesc(descID, value);
4362 return true;
4365 bool
4366 CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
4367 nsCSSValue& aValue)
4369 // Should also include VARIANT_IMAGE, but it is not supported currently.
4370 // See bug 1024179.
4371 static const int32_t VARIANT_COUNTER_SYMBOL =
4372 VARIANT_STRING | VARIANT_IDENTIFIER;
4374 switch (aDescID) {
4375 case eCSSCounterDesc_System: {
4376 nsCSSValue system;
4377 if (!ParseEnum(system, nsCSSProps::kCounterSystemKTable)) {
4378 return false;
4380 switch (system.GetIntValue()) {
4381 case NS_STYLE_COUNTER_SYSTEM_FIXED: {
4382 nsCSSValue start;
4383 if (!ParseVariant(start, VARIANT_INTEGER, nullptr)) {
4384 start.SetIntValue(1, eCSSUnit_Integer);
4386 aValue.SetPairValue(system, start);
4387 return true;
4389 case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
4390 nsString name;
4391 if (!ParseCounterStyleName(name, false)) {
4392 REPORT_UNEXPECTED_TOKEN(PECounterExtendsNotIdent);
4393 return false;
4395 aValue.SetPairValue(system, nsCSSValue(name, eCSSUnit_Ident));
4396 return true;
4398 default:
4399 aValue = system;
4400 return true;
4404 case eCSSCounterDesc_Negative: {
4405 nsCSSValue first, second;
4406 if (!ParseVariant(first, VARIANT_COUNTER_SYMBOL, nullptr)) {
4407 return false;
4409 if (!ParseVariant(second, VARIANT_COUNTER_SYMBOL, nullptr)) {
4410 aValue = first;
4411 } else {
4412 aValue.SetPairValue(first, second);
4414 return true;
4417 case eCSSCounterDesc_Prefix:
4418 case eCSSCounterDesc_Suffix:
4419 return ParseVariant(aValue, VARIANT_COUNTER_SYMBOL, nullptr);
4421 case eCSSCounterDesc_Range: {
4422 if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
4423 return true;
4425 nsCSSValuePairList* item = aValue.SetPairListValue();
4426 for (;;) {
4427 nsCSSValuePair pair;
4428 if (!ParseCounterRange(pair)) {
4429 return false;
4431 item->mXValue = pair.mXValue;
4432 item->mYValue = pair.mYValue;
4433 if (!ExpectSymbol(',', true)) {
4434 return true;
4436 item->mNext = new nsCSSValuePairList;
4437 item = item->mNext;
4439 // should always return in the loop
4442 case eCSSCounterDesc_Pad: {
4443 nsCSSValue width, symbol;
4444 bool hasWidth = ParseNonNegativeInteger(width);
4445 if (!ParseVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
4446 (!hasWidth && !ParseNonNegativeInteger(width))) {
4447 return false;
4449 aValue.SetPairValue(width, symbol);
4450 return true;
4453 case eCSSCounterDesc_Fallback: {
4454 nsString name;
4455 if (!ParseCounterStyleName(name, false)) {
4456 return false;
4458 aValue.SetStringValue(name, eCSSUnit_Ident);
4459 return true;
4462 case eCSSCounterDesc_Symbols: {
4463 nsCSSValueList* item = nullptr;
4464 for (;;) {
4465 nsCSSValue value;
4466 if (!ParseVariant(value, VARIANT_COUNTER_SYMBOL, nullptr)) {
4467 return !!item;
4469 if (!item) {
4470 item = aValue.SetListValue();
4471 } else {
4472 item->mNext = new nsCSSValueList;
4473 item = item->mNext;
4475 item->mValue = value;
4477 // should always return in the loop
4480 case eCSSCounterDesc_AdditiveSymbols: {
4481 nsCSSValuePairList* item = nullptr;
4482 int32_t lastWeight = -1;
4483 for (;;) {
4484 nsCSSValue weight, symbol;
4485 bool hasWeight = ParseNonNegativeInteger(weight);
4486 if (!ParseVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
4487 (!hasWeight && !ParseNonNegativeInteger(weight))) {
4488 return false;
4490 if (lastWeight != -1 && weight.GetIntValue() >= lastWeight) {
4491 REPORT_UNEXPECTED(PECounterASWeight);
4492 return false;
4494 lastWeight = weight.GetIntValue();
4495 if (!item) {
4496 item = aValue.SetPairListValue();
4497 } else {
4498 item->mNext = new nsCSSValuePairList;
4499 item = item->mNext;
4501 item->mXValue = weight;
4502 item->mYValue = symbol;
4503 if (!ExpectSymbol(',', true)) {
4504 return true;
4507 // should always return in the loop
4510 case eCSSCounterDesc_SpeakAs: {
4511 if (ParseVariant(aValue, VARIANT_AUTO | VARIANT_KEYWORD,
4512 nsCSSProps::kCounterSpeakAsKTable)) {
4513 if (aValue.GetUnit() == eCSSUnit_Enumerated &&
4514 aValue.GetIntValue() == NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT) {
4515 // Currently spell-out is not supported, so it is explicitly
4516 // rejected here rather than parsed as a custom identifier.
4517 // See bug 1024178.
4518 return false;
4520 return true;
4522 nsString name;
4523 if (ParseCounterStyleName(name, false)) {
4524 aValue.SetStringValue(name, eCSSUnit_Ident);
4525 return true;
4527 return false;
4530 default:
4531 NS_NOTREACHED("unknown descriptor");
4532 return false;
4536 bool
4537 CSSParserImpl::ParseCounterRange(nsCSSValuePair& aPair)
4539 static const int32_t VARIANT_BOUND = VARIANT_INTEGER | VARIANT_KEYWORD;
4540 nsCSSValue lower, upper;
4541 if (!ParseVariant(lower, VARIANT_BOUND, nsCSSProps::kCounterRangeKTable) ||
4542 !ParseVariant(upper, VARIANT_BOUND, nsCSSProps::kCounterRangeKTable)) {
4543 return false;
4545 if (lower.GetUnit() != eCSSUnit_Enumerated &&
4546 upper.GetUnit() != eCSSUnit_Enumerated &&
4547 lower.GetIntValue() > upper.GetIntValue()) {
4548 return false;
4550 aPair = nsCSSValuePair(lower, upper);
4551 return true;
4554 bool
4555 CSSParserImpl::SkipUntil(char16_t aStopSymbol)
4557 nsCSSToken* tk = &mToken;
4558 nsAutoTArray<char16_t, 16> stack;
4559 stack.AppendElement(aStopSymbol);
4560 for (;;) {
4561 if (!GetToken(true)) {
4562 return false;
4564 if (eCSSToken_Symbol == tk->mType) {
4565 char16_t symbol = tk->mSymbol;
4566 uint32_t stackTopIndex = stack.Length() - 1;
4567 if (symbol == stack.ElementAt(stackTopIndex)) {
4568 stack.RemoveElementAt(stackTopIndex);
4569 if (stackTopIndex == 0) {
4570 return true;
4573 // Just handle out-of-memory by parsing incorrectly. It's
4574 // highly unlikely we're dealing with a legitimate style sheet
4575 // anyway.
4576 } else if ('{' == symbol) {
4577 stack.AppendElement('}');
4578 } else if ('[' == symbol) {
4579 stack.AppendElement(']');
4580 } else if ('(' == symbol) {
4581 stack.AppendElement(')');
4583 } else if (eCSSToken_Function == tk->mType ||
4584 eCSSToken_Bad_URL == tk->mType) {
4585 stack.AppendElement(')');
4590 bool
4591 CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol)
4593 nsCSSToken* tk = &mToken;
4594 nsAutoTArray<char16_t, 16> stack;
4595 stack.AppendElement(aStopSymbol);
4596 for (;;) {
4597 if (!GetToken(true)) {
4598 return true;
4600 if (eCSSToken_Symbol == tk->mType) {
4601 char16_t symbol = tk->mSymbol;
4602 uint32_t stackTopIndex = stack.Length() - 1;
4603 if (symbol == stack.ElementAt(stackTopIndex)) {
4604 stack.RemoveElementAt(stackTopIndex);
4605 if (stackTopIndex == 0) {
4606 return true;
4609 // Just handle out-of-memory by parsing incorrectly. It's
4610 // highly unlikely we're dealing with a legitimate style sheet
4611 // anyway.
4612 } else if ('{' == symbol) {
4613 stack.AppendElement('}');
4614 } else if ('[' == symbol) {
4615 stack.AppendElement(']');
4616 } else if ('(' == symbol) {
4617 stack.AppendElement(')');
4618 } else if (')' == symbol ||
4619 ']' == symbol ||
4620 '}' == symbol) {
4621 UngetToken();
4622 return false;
4624 } else if (eCSSToken_Function == tk->mType ||
4625 eCSSToken_Bad_URL == tk->mType) {
4626 stack.AppendElement(')');
4631 void
4632 CSSParserImpl::SkipUntilOneOf(const char16_t* aStopSymbolChars)
4634 nsCSSToken* tk = &mToken;
4635 nsDependentString stopSymbolChars(aStopSymbolChars);
4636 for (;;) {
4637 if (!GetToken(true)) {
4638 break;
4640 if (eCSSToken_Symbol == tk->mType) {
4641 char16_t symbol = tk->mSymbol;
4642 if (stopSymbolChars.FindChar(symbol) != -1) {
4643 break;
4644 } else if ('{' == symbol) {
4645 SkipUntil('}');
4646 } else if ('[' == symbol) {
4647 SkipUntil(']');
4648 } else if ('(' == symbol) {
4649 SkipUntil(')');
4651 } else if (eCSSToken_Function == tk->mType ||
4652 eCSSToken_Bad_URL == tk->mType) {
4653 SkipUntil(')');
4658 void
4659 CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars)
4661 uint32_t i = aStopSymbolChars.Length();
4662 while (i--) {
4663 SkipUntil(aStopSymbolChars[i]);
4667 bool
4668 CSSParserImpl::SkipDeclaration(bool aCheckForBraces)
4670 nsCSSToken* tk = &mToken;
4671 for (;;) {
4672 if (!GetToken(true)) {
4673 if (aCheckForBraces) {
4674 REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
4676 return false;
4678 if (eCSSToken_Symbol == tk->mType) {
4679 char16_t symbol = tk->mSymbol;
4680 if (';' == symbol) {
4681 break;
4683 if (aCheckForBraces) {
4684 if ('}' == symbol) {
4685 UngetToken();
4686 break;
4689 if ('{' == symbol) {
4690 SkipUntil('}');
4691 } else if ('(' == symbol) {
4692 SkipUntil(')');
4693 } else if ('[' == symbol) {
4694 SkipUntil(']');
4696 } else if (eCSSToken_Function == tk->mType ||
4697 eCSSToken_Bad_URL == tk->mType) {
4698 SkipUntil(')');
4701 return true;
4704 void
4705 CSSParserImpl::SkipRuleSet(bool aInsideBraces)
4707 nsCSSToken* tk = &mToken;
4708 for (;;) {
4709 if (!GetToken(true)) {
4710 REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
4711 break;
4713 if (eCSSToken_Symbol == tk->mType) {
4714 char16_t symbol = tk->mSymbol;
4715 if ('}' == symbol && aInsideBraces) {
4716 // leave block closer for higher-level grammar to consume
4717 UngetToken();
4718 break;
4719 } else if ('{' == symbol) {
4720 SkipUntil('}');
4721 break;
4722 } else if ('(' == symbol) {
4723 SkipUntil(')');
4724 } else if ('[' == symbol) {
4725 SkipUntil(']');
4727 } else if (eCSSToken_Function == tk->mType ||
4728 eCSSToken_Bad_URL == tk->mType) {
4729 SkipUntil(')');
4734 void
4735 CSSParserImpl::PushGroup(css::GroupRule* aRule)
4737 mGroupStack.AppendElement(aRule);
4740 void
4741 CSSParserImpl::PopGroup()
4743 uint32_t count = mGroupStack.Length();
4744 if (0 < count) {
4745 mGroupStack.RemoveElementAt(count - 1);
4749 void
4750 CSSParserImpl::AppendRule(css::Rule* aRule)
4752 uint32_t count = mGroupStack.Length();
4753 if (0 < count) {
4754 mGroupStack[count - 1]->AppendStyleRule(aRule);
4756 else {
4757 mSheet->AppendStyleRule(aRule);
4761 bool
4762 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
4763 bool aInsideBraces)
4765 // First get the list of selectors for the rule
4766 nsCSSSelectorList* slist = nullptr;
4767 uint32_t linenum, colnum;
4768 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4769 !ParseSelectorList(slist, char16_t('{'))) {
4770 REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
4771 OUTPUT_ERROR();
4772 SkipRuleSet(aInsideBraces);
4773 return false;
4775 NS_ASSERTION(nullptr != slist, "null selector list");
4776 CLEAR_ERROR();
4778 // Next parse the declaration block
4779 uint32_t parseFlags = eParseDeclaration_InBraces |
4780 eParseDeclaration_AllowImportant;
4781 css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
4782 if (nullptr == declaration) {
4783 delete slist;
4784 return false;
4787 #if 0
4788 slist->Dump();
4789 fputs("{\n", stdout);
4790 declaration->List();
4791 fputs("}\n", stdout);
4792 #endif
4794 // Translate the selector list and declaration block into style data
4796 nsRefPtr<css::StyleRule> rule = new css::StyleRule(slist, declaration,
4797 linenum, colnum);
4798 (*aAppendFunc)(rule, aData);
4800 return true;
4803 bool
4804 CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
4805 char16_t aStopChar)
4807 nsCSSSelectorList* list = nullptr;
4808 if (! ParseSelectorGroup(list)) {
4809 // must have at least one selector group
4810 aListHead = nullptr;
4811 return false;
4813 NS_ASSERTION(nullptr != list, "no selector list");
4814 aListHead = list;
4816 // After that there must either be a "," or a "{" (the latter if
4817 // StopChar is nonzero)
4818 nsCSSToken* tk = &mToken;
4819 for (;;) {
4820 if (! GetToken(true)) {
4821 if (aStopChar == char16_t(0)) {
4822 return true;
4825 REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
4826 break;
4829 if (eCSSToken_Symbol == tk->mType) {
4830 if (',' == tk->mSymbol) {
4831 nsCSSSelectorList* newList = nullptr;
4832 // Another selector group must follow
4833 if (! ParseSelectorGroup(newList)) {
4834 break;
4836 // add new list to the end of the selector list
4837 list->mNext = newList;
4838 list = newList;
4839 continue;
4840 } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) {
4841 UngetToken();
4842 return true;
4845 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
4846 UngetToken();
4847 break;
4850 delete aListHead;
4851 aListHead = nullptr;
4852 return false;
4855 static bool IsUniversalSelector(const nsCSSSelector& aSelector)
4857 return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
4858 (aSelector.mLowercaseTag == nullptr) &&
4859 (aSelector.mIDList == nullptr) &&
4860 (aSelector.mClassList == nullptr) &&
4861 (aSelector.mAttrList == nullptr) &&
4862 (aSelector.mNegations == nullptr) &&
4863 (aSelector.mPseudoClassList == nullptr));
4866 bool
4867 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
4869 char16_t combinator = 0;
4870 nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
4872 for (;;) {
4873 if (!ParseSelector(list, combinator)) {
4874 return false;
4877 // Look for a combinator.
4878 if (!GetToken(false)) {
4879 break; // EOF ok here
4882 combinator = char16_t(0);
4883 if (mToken.mType == eCSSToken_Whitespace) {
4884 if (!GetToken(true)) {
4885 break; // EOF ok here
4887 combinator = char16_t(' ');
4890 if (mToken.mType != eCSSToken_Symbol) {
4891 UngetToken(); // not a combinator
4892 } else {
4893 char16_t symbol = mToken.mSymbol;
4894 if (symbol == '+' || symbol == '>' || symbol == '~') {
4895 combinator = mToken.mSymbol;
4896 } else {
4897 UngetToken(); // not a combinator
4898 if (symbol == ',' || symbol == '{' || symbol == ')') {
4899 break; // end of selector group
4904 if (!combinator) {
4905 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
4906 return false;
4910 aList = list.forget();
4911 return true;
4914 #define SEL_MASK_NSPACE 0x01
4915 #define SEL_MASK_ELEM 0x02
4916 #define SEL_MASK_ID 0x04
4917 #define SEL_MASK_CLASS 0x08
4918 #define SEL_MASK_ATTRIB 0x10
4919 #define SEL_MASK_PCLASS 0x20
4920 #define SEL_MASK_PELEM 0x40
4923 // Parses an ID selector #name
4925 CSSParserImpl::nsSelectorParsingStatus
4926 CSSParserImpl::ParseIDSelector(int32_t& aDataMask,
4927 nsCSSSelector& aSelector)
4929 NS_ASSERTION(!mToken.mIdent.IsEmpty(),
4930 "Empty mIdent in eCSSToken_ID token?");
4931 aDataMask |= SEL_MASK_ID;
4932 aSelector.AddID(mToken.mIdent);
4933 return eSelectorParsingStatus_Continue;
4937 // Parses a class selector .name
4939 CSSParserImpl::nsSelectorParsingStatus
4940 CSSParserImpl::ParseClassSelector(int32_t& aDataMask,
4941 nsCSSSelector& aSelector)
4943 if (! GetToken(false)) { // get ident
4944 REPORT_UNEXPECTED_EOF(PEClassSelEOF);
4945 return eSelectorParsingStatus_Error;
4947 if (eCSSToken_Ident != mToken.mType) { // malformed selector
4948 REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
4949 UngetToken();
4950 return eSelectorParsingStatus_Error;
4952 aDataMask |= SEL_MASK_CLASS;
4954 aSelector.AddClass(mToken.mIdent);
4956 return eSelectorParsingStatus_Continue;
4960 // Parse a type element selector or a universal selector
4961 // namespace|type or namespace|* or *|* or *
4963 CSSParserImpl::nsSelectorParsingStatus
4964 CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
4965 nsCSSSelector& aSelector,
4966 bool aIsNegated)
4968 nsAutoString buffer;
4969 if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
4970 if (ExpectSymbol('|', false)) { // was namespace
4971 aDataMask |= SEL_MASK_NSPACE;
4972 aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
4974 if (! GetToken(false)) {
4975 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
4976 return eSelectorParsingStatus_Error;
4978 if (eCSSToken_Ident == mToken.mType) { // element name
4979 aDataMask |= SEL_MASK_ELEM;
4981 aSelector.SetTag(mToken.mIdent);
4983 else if (mToken.IsSymbol('*')) { // universal selector
4984 aDataMask |= SEL_MASK_ELEM;
4985 // don't set tag
4987 else {
4988 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
4989 UngetToken();
4990 return eSelectorParsingStatus_Error;
4993 else { // was universal element selector
4994 SetDefaultNamespaceOnSelector(aSelector);
4995 aDataMask |= SEL_MASK_ELEM;
4996 // don't set any tag in the selector
4998 if (! GetToken(false)) { // premature eof is ok (here!)
4999 return eSelectorParsingStatus_Done;
5002 else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
5003 buffer = mToken.mIdent; // hang on to ident
5005 if (ExpectSymbol('|', false)) { // was namespace
5006 aDataMask |= SEL_MASK_NSPACE;
5007 int32_t nameSpaceID = GetNamespaceIdForPrefix(buffer);
5008 if (nameSpaceID == kNameSpaceID_Unknown) {
5009 return eSelectorParsingStatus_Error;
5011 aSelector.SetNameSpace(nameSpaceID);
5013 if (! GetToken(false)) {
5014 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5015 return eSelectorParsingStatus_Error;
5017 if (eCSSToken_Ident == mToken.mType) { // element name
5018 aDataMask |= SEL_MASK_ELEM;
5019 aSelector.SetTag(mToken.mIdent);
5021 else if (mToken.IsSymbol('*')) { // universal selector
5022 aDataMask |= SEL_MASK_ELEM;
5023 // don't set tag
5025 else {
5026 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5027 UngetToken();
5028 return eSelectorParsingStatus_Error;
5031 else { // was element name
5032 SetDefaultNamespaceOnSelector(aSelector);
5033 aSelector.SetTag(buffer);
5035 aDataMask |= SEL_MASK_ELEM;
5037 if (! GetToken(false)) { // premature eof is ok (here!)
5038 return eSelectorParsingStatus_Done;
5041 else if (mToken.IsSymbol('|')) { // No namespace
5042 aDataMask |= SEL_MASK_NSPACE;
5043 aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
5045 // get mandatory tag
5046 if (! GetToken(false)) {
5047 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5048 return eSelectorParsingStatus_Error;
5050 if (eCSSToken_Ident == mToken.mType) { // element name
5051 aDataMask |= SEL_MASK_ELEM;
5052 aSelector.SetTag(mToken.mIdent);
5054 else if (mToken.IsSymbol('*')) { // universal selector
5055 aDataMask |= SEL_MASK_ELEM;
5056 // don't set tag
5058 else {
5059 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5060 UngetToken();
5061 return eSelectorParsingStatus_Error;
5063 if (! GetToken(false)) { // premature eof is ok (here!)
5064 return eSelectorParsingStatus_Done;
5067 else {
5068 SetDefaultNamespaceOnSelector(aSelector);
5071 if (aIsNegated) {
5072 // restore last token read in case of a negated type selector
5073 UngetToken();
5075 return eSelectorParsingStatus_Continue;
5079 // Parse attribute selectors [attr], [attr=value], [attr|=value],
5080 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
5082 CSSParserImpl::nsSelectorParsingStatus
5083 CSSParserImpl::ParseAttributeSelector(int32_t& aDataMask,
5084 nsCSSSelector& aSelector)
5086 if (! GetToken(true)) { // premature EOF
5087 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5088 return eSelectorParsingStatus_Error;
5091 int32_t nameSpaceID = kNameSpaceID_None;
5092 nsAutoString attr;
5093 if (mToken.IsSymbol('*')) { // wildcard namespace
5094 nameSpaceID = kNameSpaceID_Unknown;
5095 if (ExpectSymbol('|', false)) {
5096 if (! GetToken(false)) { // premature EOF
5097 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5098 return eSelectorParsingStatus_Error;
5100 if (eCSSToken_Ident == mToken.mType) { // attr name
5101 attr = mToken.mIdent;
5103 else {
5104 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5105 UngetToken();
5106 return eSelectorParsingStatus_Error;
5109 else {
5110 REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
5111 return eSelectorParsingStatus_Error;
5114 else if (mToken.IsSymbol('|')) { // NO namespace
5115 if (! GetToken(false)) { // premature EOF
5116 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5117 return eSelectorParsingStatus_Error;
5119 if (eCSSToken_Ident == mToken.mType) { // attr name
5120 attr = mToken.mIdent;
5122 else {
5123 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5124 UngetToken();
5125 return eSelectorParsingStatus_Error;
5128 else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
5129 attr = mToken.mIdent; // hang on to it
5130 if (ExpectSymbol('|', false)) { // was a namespace
5131 nameSpaceID = GetNamespaceIdForPrefix(attr);
5132 if (nameSpaceID == kNameSpaceID_Unknown) {
5133 return eSelectorParsingStatus_Error;
5135 if (! GetToken(false)) { // premature EOF
5136 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5137 return eSelectorParsingStatus_Error;
5139 if (eCSSToken_Ident == mToken.mType) { // attr name
5140 attr = mToken.mIdent;
5142 else {
5143 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5144 UngetToken();
5145 return eSelectorParsingStatus_Error;
5149 else { // malformed
5150 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
5151 UngetToken();
5152 return eSelectorParsingStatus_Error;
5155 if (! GetToken(true)) { // premature EOF
5156 REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
5157 return eSelectorParsingStatus_Error;
5159 if ((eCSSToken_Symbol == mToken.mType) ||
5160 (eCSSToken_Includes == mToken.mType) ||
5161 (eCSSToken_Dashmatch == mToken.mType) ||
5162 (eCSSToken_Beginsmatch == mToken.mType) ||
5163 (eCSSToken_Endsmatch == mToken.mType) ||
5164 (eCSSToken_Containsmatch == mToken.mType)) {
5165 uint8_t func;
5166 if (eCSSToken_Includes == mToken.mType) {
5167 func = NS_ATTR_FUNC_INCLUDES;
5169 else if (eCSSToken_Dashmatch == mToken.mType) {
5170 func = NS_ATTR_FUNC_DASHMATCH;
5172 else if (eCSSToken_Beginsmatch == mToken.mType) {
5173 func = NS_ATTR_FUNC_BEGINSMATCH;
5175 else if (eCSSToken_Endsmatch == mToken.mType) {
5176 func = NS_ATTR_FUNC_ENDSMATCH;
5178 else if (eCSSToken_Containsmatch == mToken.mType) {
5179 func = NS_ATTR_FUNC_CONTAINSMATCH;
5181 else if (']' == mToken.mSymbol) {
5182 aDataMask |= SEL_MASK_ATTRIB;
5183 aSelector.AddAttribute(nameSpaceID, attr);
5184 func = NS_ATTR_FUNC_SET;
5186 else if ('=' == mToken.mSymbol) {
5187 func = NS_ATTR_FUNC_EQUALS;
5189 else {
5190 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
5191 UngetToken(); // bad function
5192 return eSelectorParsingStatus_Error;
5194 if (NS_ATTR_FUNC_SET != func) { // get value
5195 if (! GetToken(true)) { // premature EOF
5196 REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
5197 return eSelectorParsingStatus_Error;
5199 if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
5200 nsAutoString value(mToken.mIdent);
5201 if (! GetToken(true)) { // premature EOF
5202 REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
5203 return eSelectorParsingStatus_Error;
5205 if (mToken.IsSymbol(']')) {
5206 bool isCaseSensitive = true;
5208 // For cases when this style sheet is applied to an HTML
5209 // element in an HTML document, and the attribute selector is
5210 // for a non-namespaced attribute, then check to see if it's
5211 // one of the known attributes whose VALUE is
5212 // case-insensitive.
5213 if (nameSpaceID == kNameSpaceID_None) {
5214 static const char* caseInsensitiveHTMLAttribute[] = {
5215 // list based on http://www.w3.org/TR/html4/
5216 "lang",
5217 "dir",
5218 "http-equiv",
5219 "text",
5220 "link",
5221 "vlink",
5222 "alink",
5223 "compact",
5224 "align",
5225 "frame",
5226 "rules",
5227 "valign",
5228 "scope",
5229 "axis",
5230 "nowrap",
5231 "hreflang",
5232 "rel",
5233 "rev",
5234 "charset",
5235 "codetype",
5236 "declare",
5237 "valuetype",
5238 "shape",
5239 "nohref",
5240 "media",
5241 "bgcolor",
5242 "clear",
5243 "color",
5244 "face",
5245 "noshade",
5246 "noresize",
5247 "scrolling",
5248 "target",
5249 "method",
5250 "enctype",
5251 "accept-charset",
5252 "accept",
5253 "checked",
5254 "multiple",
5255 "selected",
5256 "disabled",
5257 "readonly",
5258 "language",
5259 "defer",
5260 "type",
5261 // additional attributes not in HTML4
5262 "direction", // marquee
5263 nullptr
5265 short i = 0;
5266 const char* htmlAttr;
5267 while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
5268 if (attr.LowerCaseEqualsASCII(htmlAttr)) {
5269 isCaseSensitive = false;
5270 break;
5274 aDataMask |= SEL_MASK_ATTRIB;
5275 aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
5277 else {
5278 REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
5279 UngetToken();
5280 return eSelectorParsingStatus_Error;
5283 else {
5284 REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
5285 UngetToken();
5286 return eSelectorParsingStatus_Error;
5290 else {
5291 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
5292 UngetToken(); // bad dog, no biscut!
5293 return eSelectorParsingStatus_Error;
5295 return eSelectorParsingStatus_Continue;
5299 // Parse pseudo-classes and pseudo-elements
5301 CSSParserImpl::nsSelectorParsingStatus
5302 CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
5303 nsCSSSelector& aSelector,
5304 bool aIsNegated,
5305 nsIAtom** aPseudoElement,
5306 nsAtomList** aPseudoElementArgs,
5307 nsCSSPseudoElements::Type* aPseudoElementType)
5309 NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs),
5310 "expected location to store pseudo element");
5311 NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs),
5312 "negated selectors shouldn't have a place to store "
5313 "pseudo elements");
5314 if (! GetToken(false)) { // premature eof
5315 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
5316 return eSelectorParsingStatus_Error;
5319 // First, find out whether we are parsing a CSS3 pseudo-element
5320 bool parsingPseudoElement = false;
5321 if (mToken.IsSymbol(':')) {
5322 parsingPseudoElement = true;
5323 if (! GetToken(false)) { // premature eof
5324 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
5325 return eSelectorParsingStatus_Error;
5329 // Do some sanity-checking on the token
5330 if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
5331 // malformed selector
5332 REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
5333 UngetToken();
5334 return eSelectorParsingStatus_Error;
5337 // OK, now we know we have an mIdent. Atomize it. All the atoms, for
5338 // pseudo-classes as well as pseudo-elements, start with a single ':'.
5339 nsAutoString buffer;
5340 buffer.Append(char16_t(':'));
5341 buffer.Append(mToken.mIdent);
5342 nsContentUtils::ASCIIToLower(buffer);
5343 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
5344 if (!pseudo) {
5345 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
5348 // stash away some info about this pseudo so we only have to get it once.
5349 bool isTreePseudo = false;
5350 nsCSSPseudoElements::Type pseudoElementType =
5351 nsCSSPseudoElements::GetPseudoType(pseudo);
5352 nsCSSPseudoClasses::Type pseudoClassType =
5353 nsCSSPseudoClasses::GetPseudoType(pseudo);
5354 bool pseudoClassIsUserAction =
5355 nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
5357 if (!mUnsafeRulesEnabled &&
5358 pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount &&
5359 nsCSSPseudoElements::PseudoElementIsChromeOnly(pseudoElementType)) {
5360 // This pseudo-element is not exposed to content.
5361 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
5362 UngetToken();
5363 return eSelectorParsingStatus_Error;
5366 // We currently allow :-moz-placeholder and ::-moz-placeholder. We have to
5367 // be a bit stricter regarding the pseudo-element parsing rules.
5368 if (pseudoElementType == nsCSSPseudoElements::ePseudo_mozPlaceholder &&
5369 pseudoClassType == nsCSSPseudoClasses::ePseudoClass_mozPlaceholder) {
5370 if (parsingPseudoElement) {
5371 pseudoClassType = nsCSSPseudoClasses::ePseudoClass_NotPseudoClass;
5372 } else {
5373 pseudoElementType = nsCSSPseudoElements::ePseudo_NotPseudoElement;
5377 #ifdef MOZ_XUL
5378 isTreePseudo = (pseudoElementType == nsCSSPseudoElements::ePseudo_XULTree);
5379 // If a tree pseudo-element is using the function syntax, it will
5380 // get isTree set here and will pass the check below that only
5381 // allows functions if they are in our list of things allowed to be
5382 // functions. If it is _not_ using the function syntax, isTree will
5383 // be false, and it will still pass that check. So the tree
5384 // pseudo-elements are allowed to be either functions or not, as
5385 // desired.
5386 bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
5387 #endif
5388 bool isPseudoElement =
5389 (pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount);
5390 // anonymous boxes are only allowed if they're the tree boxes or we have
5391 // enabled unsafe rules
5392 bool isAnonBox = isTreePseudo ||
5393 (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox &&
5394 mUnsafeRulesEnabled);
5395 bool isPseudoClass =
5396 (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass);
5398 NS_ASSERTION(!isPseudoClass ||
5399 pseudoElementType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
5400 "Why is this atom both a pseudo-class and a pseudo-element?");
5401 NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1,
5402 "Shouldn't be more than one of these");
5404 if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
5405 // Not a pseudo-class, not a pseudo-element.... forget it
5406 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
5407 UngetToken();
5408 return eSelectorParsingStatus_Error;
5411 // If it's a function token, it better be on our "ok" list, and if the name
5412 // is that of a function pseudo it better be a function token
5413 if ((eCSSToken_Function == mToken.mType) !=
5415 #ifdef MOZ_XUL
5416 isTree ||
5417 #endif
5418 nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType ||
5419 nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
5420 nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
5421 nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
5422 // There are no other function pseudos
5423 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
5424 UngetToken();
5425 return eSelectorParsingStatus_Error;
5428 // If it starts with "::", it better be a pseudo-element
5429 if (parsingPseudoElement &&
5430 !isPseudoElement &&
5431 !isAnonBox) {
5432 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
5433 UngetToken();
5434 return eSelectorParsingStatus_Error;
5437 if (!parsingPseudoElement &&
5438 nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType) {
5439 if (aIsNegated) { // :not() can't be itself negated
5440 REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
5441 UngetToken();
5442 return eSelectorParsingStatus_Error;
5444 // CSS 3 Negation pseudo-class takes one simple selector as argument
5445 nsSelectorParsingStatus parsingStatus =
5446 ParseNegatedSimpleSelector(aDataMask, aSelector);
5447 if (eSelectorParsingStatus_Continue != parsingStatus) {
5448 return parsingStatus;
5451 else if (!parsingPseudoElement && isPseudoClass) {
5452 if (aSelector.IsPseudoElement()) {
5453 nsCSSPseudoElements::Type type = aSelector.PseudoType();
5454 if (!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
5455 // We only allow user action pseudo-classes on certain pseudo-elements.
5456 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
5457 UngetToken();
5458 return eSelectorParsingStatus_Error;
5460 if (!pseudoClassIsUserAction) {
5461 // CSS 4 Selectors says that pseudo-elements can only be followed by
5462 // a user action pseudo-class.
5463 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
5464 UngetToken();
5465 return eSelectorParsingStatus_Error;
5468 aDataMask |= SEL_MASK_PCLASS;
5469 if (eCSSToken_Function == mToken.mType) {
5470 nsSelectorParsingStatus parsingStatus;
5471 if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
5472 parsingStatus =
5473 ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
5475 else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) {
5476 parsingStatus =
5477 ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType);
5479 else {
5480 NS_ABORT_IF_FALSE(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
5481 "unexpected pseudo with function token");
5482 parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
5483 pseudoClassType);
5485 if (eSelectorParsingStatus_Continue != parsingStatus) {
5486 if (eSelectorParsingStatus_Error == parsingStatus) {
5487 SkipUntil(')');
5489 return parsingStatus;
5492 else {
5493 aSelector.AddPseudoClass(pseudoClassType);
5496 else if (isPseudoElement || isAnonBox) {
5497 // Pseudo-element. Make some more sanity checks.
5499 if (aIsNegated) { // pseudo-elements can't be negated
5500 REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
5501 UngetToken();
5502 return eSelectorParsingStatus_Error;
5504 // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
5505 // to have a single ':' on them. Others (CSS3+ pseudo-elements and
5506 // various -moz-* pseudo-elements) must have |parsingPseudoElement|
5507 // set.
5508 if (!parsingPseudoElement &&
5509 !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
5510 #ifdef MOZ_XUL
5511 && !isTreePseudo
5512 #endif
5514 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
5515 UngetToken();
5516 return eSelectorParsingStatus_Error;
5519 if (0 == (aDataMask & SEL_MASK_PELEM)) {
5520 aDataMask |= SEL_MASK_PELEM;
5521 NS_ADDREF(*aPseudoElement = pseudo);
5522 *aPseudoElementType = pseudoElementType;
5524 #ifdef MOZ_XUL
5525 if (isTree) {
5526 // We have encountered a pseudoelement of the form
5527 // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each
5528 // item in the list to the pseudoclass list. They will be pulled
5529 // from the list later along with the pseudo-element.
5530 if (!ParseTreePseudoElement(aPseudoElementArgs)) {
5531 return eSelectorParsingStatus_Error;
5534 #endif
5536 // Pseudo-elements can only be followed by user action pseudo-classes
5537 // or be the end of the selector. So the next non-whitespace token must
5538 // be ':', '{' or ',' or EOF.
5539 if (!GetToken(true)) { // premature eof is ok (here!)
5540 return eSelectorParsingStatus_Done;
5542 if (parsingPseudoElement && mToken.IsSymbol(':')) {
5543 UngetToken();
5544 return eSelectorParsingStatus_Continue;
5546 if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
5547 UngetToken();
5548 return eSelectorParsingStatus_Done;
5550 REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC);
5551 UngetToken();
5552 return eSelectorParsingStatus_Error;
5554 else { // multiple pseudo elements, not legal
5555 REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
5556 UngetToken();
5557 return eSelectorParsingStatus_Error;
5560 #ifdef DEBUG
5561 else {
5562 // We should never end up here. Indeed, if we ended up here, we know (from
5563 // the current if/else cascade) that !isPseudoElement and !isAnonBox. But
5564 // then due to our earlier check we know that isPseudoClass. Since we
5565 // didn't fall into the isPseudoClass case in this cascade, we must have
5566 // parsingPseudoElement. But we've already checked the
5567 // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
5568 // it's happened.
5569 NS_NOTREACHED("How did this happen?");
5571 #endif
5572 return eSelectorParsingStatus_Continue;
5576 // Parse the argument of a negation pseudo-class :not()
5578 CSSParserImpl::nsSelectorParsingStatus
5579 CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask,
5580 nsCSSSelector& aSelector)
5582 if (! GetToken(true)) { // premature eof
5583 REPORT_UNEXPECTED_EOF(PENegationEOF);
5584 return eSelectorParsingStatus_Error;
5587 if (mToken.IsSymbol(')')) {
5588 REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
5589 return eSelectorParsingStatus_Error;
5592 // Create a new nsCSSSelector and add it to the end of
5593 // aSelector.mNegations.
5594 // Given the current parsing rules, every selector in mNegations
5595 // contains only one simple selector (css3 definition) within it.
5596 // This could easily change in future versions of CSS, and the only
5597 // thing we need to change to support that is this parsing code and the
5598 // serialization code for nsCSSSelector.
5599 nsCSSSelector *newSel = new nsCSSSelector();
5600 nsCSSSelector* negations = &aSelector;
5601 while (negations->mNegations) {
5602 negations = negations->mNegations;
5604 negations->mNegations = newSel;
5606 nsSelectorParsingStatus parsingStatus;
5607 if (eCSSToken_ID == mToken.mType) { // #id
5608 parsingStatus = ParseIDSelector(aDataMask, *newSel);
5610 else if (mToken.IsSymbol('.')) { // .class
5611 parsingStatus = ParseClassSelector(aDataMask, *newSel);
5613 else if (mToken.IsSymbol(':')) { // :pseudo
5614 parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true,
5615 nullptr, nullptr, nullptr);
5617 else if (mToken.IsSymbol('[')) { // [attribute
5618 parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
5619 if (eSelectorParsingStatus_Error == parsingStatus) {
5620 // Skip forward to the matching ']'
5621 SkipUntil(']');
5624 else {
5625 // then it should be a type element or universal selector
5626 parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true);
5628 if (eSelectorParsingStatus_Error == parsingStatus) {
5629 REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
5630 SkipUntil(')');
5631 return parsingStatus;
5633 // close the parenthesis
5634 if (!ExpectSymbol(')', true)) {
5635 REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
5636 SkipUntil(')');
5637 return eSelectorParsingStatus_Error;
5640 NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown ||
5641 (!newSel->mIDList && !newSel->mClassList &&
5642 !newSel->mPseudoClassList && !newSel->mAttrList),
5643 "Need to fix the serialization code to deal with this");
5645 return eSelectorParsingStatus_Continue;
5649 // Parse the argument of a pseudo-class that has an ident arg
5651 CSSParserImpl::nsSelectorParsingStatus
5652 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
5653 nsCSSPseudoClasses::Type aType)
5655 if (! GetToken(true)) { // premature eof
5656 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5657 return eSelectorParsingStatus_Error;
5659 // We expect an identifier with a language abbreviation
5660 if (eCSSToken_Ident != mToken.mType) {
5661 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
5662 UngetToken();
5663 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5666 // -moz-locale-dir and -moz-dir can only have values of 'ltr' or 'rtl'.
5667 if (aType == nsCSSPseudoClasses::ePseudoClass_mozLocaleDir ||
5668 aType == nsCSSPseudoClasses::ePseudoClass_dir) {
5669 nsContentUtils::ASCIIToLower(mToken.mIdent); // case insensitive
5670 if (!mToken.mIdent.EqualsLiteral("ltr") &&
5671 !mToken.mIdent.EqualsLiteral("rtl")) {
5672 REPORT_UNEXPECTED_TOKEN(PEBadDirValue);
5673 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5677 // Add the pseudo with the language parameter
5678 aSelector.AddPseudoClass(aType, mToken.mIdent.get());
5680 // close the parenthesis
5681 if (!ExpectSymbol(')', true)) {
5682 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
5683 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5686 return eSelectorParsingStatus_Continue;
5689 CSSParserImpl::nsSelectorParsingStatus
5690 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
5691 nsCSSPseudoClasses::Type aType)
5693 int32_t numbers[2] = { 0, 0 };
5694 int32_t sign[2] = { 1, 1 };
5695 bool hasSign[2] = { false, false };
5696 bool lookForB = true;
5698 // Follow the whitespace rules as proposed in
5699 // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
5701 if (! GetToken(true)) {
5702 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5703 return eSelectorParsingStatus_Error;
5706 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
5707 hasSign[0] = true;
5708 if (mToken.IsSymbol('-')) {
5709 sign[0] = -1;
5711 if (! GetToken(false)) {
5712 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5713 return eSelectorParsingStatus_Error;
5717 if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
5718 // The CSS tokenization doesn't handle :nth-child() containing - well:
5719 // 2n-1 is a dimension
5720 // n-1 is an identifier
5721 // The easiest way to deal with that is to push everything from the
5722 // minus on back onto the scanner's pushback buffer.
5723 uint32_t truncAt = 0;
5724 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
5725 truncAt = 1;
5726 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-")) && !hasSign[0]) {
5727 truncAt = 2;
5729 if (truncAt != 0) {
5730 mScanner->Backup(mToken.mIdent.Length() - truncAt);
5731 mToken.mIdent.Truncate(truncAt);
5735 if (eCSSToken_Ident == mToken.mType) {
5736 if (mToken.mIdent.LowerCaseEqualsLiteral("odd") && !hasSign[0]) {
5737 numbers[0] = 2;
5738 numbers[1] = 1;
5739 lookForB = false;
5741 else if (mToken.mIdent.LowerCaseEqualsLiteral("even") && !hasSign[0]) {
5742 numbers[0] = 2;
5743 numbers[1] = 0;
5744 lookForB = false;
5746 else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
5747 numbers[0] = sign[0];
5749 else if (mToken.mIdent.LowerCaseEqualsLiteral("-n") && !hasSign[0]) {
5750 numbers[0] = -1;
5752 else {
5753 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5754 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5757 else if (eCSSToken_Number == mToken.mType) {
5758 if (!mToken.mIntegerValid) {
5759 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5760 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5762 // for +-an case
5763 if (mToken.mHasSign && hasSign[0]) {
5764 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5765 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5767 int32_t intValue = mToken.mInteger * sign[0];
5768 // for -a/**/n case
5769 if (! GetToken(false)) {
5770 numbers[1] = intValue;
5771 lookForB = false;
5773 else {
5774 if (eCSSToken_Ident == mToken.mType && mToken.mIdent.LowerCaseEqualsLiteral("n")) {
5775 numbers[0] = intValue;
5777 else if (eCSSToken_Ident == mToken.mType && StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
5778 numbers[0] = intValue;
5779 mScanner->Backup(mToken.mIdent.Length() - 1);
5781 else {
5782 UngetToken();
5783 numbers[1] = intValue;
5784 lookForB = false;
5788 else if (eCSSToken_Dimension == mToken.mType) {
5789 if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
5790 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5791 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5793 // for +-an case
5794 if ( mToken.mHasSign && hasSign[0] ) {
5795 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5796 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5798 numbers[0] = mToken.mInteger * sign[0];
5800 // XXX If it's a ')', is that valid? (as 0n+0)
5801 else {
5802 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5803 UngetToken();
5804 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5807 if (! GetToken(true)) {
5808 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5809 return eSelectorParsingStatus_Error;
5811 if (lookForB && !mToken.IsSymbol(')')) {
5812 // The '+' or '-' sign can optionally be separated by whitespace.
5813 // If it is separated by whitespace from what follows it, it appears
5814 // as a separate token rather than part of the number token.
5815 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
5816 hasSign[1] = true;
5817 if (mToken.IsSymbol('-')) {
5818 sign[1] = -1;
5820 if (! GetToken(true)) {
5821 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5822 return eSelectorParsingStatus_Error;
5825 if (eCSSToken_Number != mToken.mType ||
5826 !mToken.mIntegerValid || mToken.mHasSign == hasSign[1]) {
5827 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5828 UngetToken();
5829 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5831 numbers[1] = mToken.mInteger * sign[1];
5832 if (! GetToken(true)) {
5833 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5834 return eSelectorParsingStatus_Error;
5837 if (!mToken.IsSymbol(')')) {
5838 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
5839 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5841 aSelector.AddPseudoClass(aType, numbers);
5842 return eSelectorParsingStatus_Continue;
5846 // Parse the argument of a pseudo-class that has a selector list argument.
5847 // Such selector lists cannot contain combinators, but can contain
5848 // anything that goes between a pair of combinators.
5850 CSSParserImpl::nsSelectorParsingStatus
5851 CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
5852 nsCSSPseudoClasses::Type aType)
5854 nsAutoPtr<nsCSSSelectorList> slist;
5855 if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) {
5856 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5859 // Check that none of the selectors in the list have combinators or
5860 // pseudo-elements.
5861 for (nsCSSSelectorList *l = slist; l; l = l->mNext) {
5862 nsCSSSelector *s = l->mSelectors;
5863 if (s->mNext || s->IsPseudoElement()) {
5864 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5868 // Add the pseudo with the selector list parameter
5869 aSelector.AddPseudoClass(aType, slist.forget());
5871 // close the parenthesis
5872 if (!ExpectSymbol(')', true)) {
5873 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
5874 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5877 return eSelectorParsingStatus_Continue;
5882 * This is the format for selectors:
5883 * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
5885 bool
5886 CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
5887 char16_t aPrevCombinator)
5889 if (! GetToken(true)) {
5890 REPORT_UNEXPECTED_EOF(PESelectorEOF);
5891 return false;
5894 nsCSSSelector* selector = aList->AddSelector(aPrevCombinator);
5895 nsCOMPtr<nsIAtom> pseudoElement;
5896 nsAutoPtr<nsAtomList> pseudoElementArgs;
5897 nsCSSPseudoElements::Type pseudoElementType =
5898 nsCSSPseudoElements::ePseudo_NotPseudoElement;
5900 int32_t dataMask = 0;
5901 nsSelectorParsingStatus parsingStatus =
5902 ParseTypeOrUniversalSelector(dataMask, *selector, false);
5904 while (parsingStatus == eSelectorParsingStatus_Continue) {
5905 if (eCSSToken_ID == mToken.mType) { // #id
5906 parsingStatus = ParseIDSelector(dataMask, *selector);
5908 else if (mToken.IsSymbol('.')) { // .class
5909 parsingStatus = ParseClassSelector(dataMask, *selector);
5911 else if (mToken.IsSymbol(':')) { // :pseudo
5912 parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
5913 getter_AddRefs(pseudoElement),
5914 getter_Transfers(pseudoElementArgs),
5915 &pseudoElementType);
5916 if (pseudoElement &&
5917 pseudoElementType != nsCSSPseudoElements::ePseudo_AnonBox) {
5918 // Pseudo-elements other than anonymous boxes are represented with
5919 // a special ':' combinator.
5921 aList->mWeight += selector->CalcWeight();
5923 selector = aList->AddSelector(':');
5925 selector->mLowercaseTag.swap(pseudoElement);
5926 selector->mClassList = pseudoElementArgs.forget();
5927 selector->SetPseudoType(pseudoElementType);
5930 else if (mToken.IsSymbol('[')) { // [attribute
5931 parsingStatus = ParseAttributeSelector(dataMask, *selector);
5932 if (eSelectorParsingStatus_Error == parsingStatus) {
5933 SkipUntil(']');
5936 else { // not a selector token, we're done
5937 parsingStatus = eSelectorParsingStatus_Done;
5938 UngetToken();
5939 break;
5942 if (parsingStatus != eSelectorParsingStatus_Continue) {
5943 break;
5946 if (! GetToken(false)) { // premature eof is ok (here!)
5947 parsingStatus = eSelectorParsingStatus_Done;
5948 break;
5952 if (parsingStatus == eSelectorParsingStatus_Error) {
5953 return false;
5956 if (!dataMask) {
5957 if (selector->mNext) {
5958 REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
5959 } else {
5960 REPORT_UNEXPECTED(PESelectorGroupNoSelector);
5962 return false;
5965 if (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox) {
5966 // We got an anonymous box pseudo-element; it must be the only
5967 // thing in this selector group.
5968 if (selector->mNext || !IsUniversalSelector(*selector)) {
5969 REPORT_UNEXPECTED(PEAnonBoxNotAlone);
5970 return false;
5973 // Rewrite the current selector as this pseudo-element.
5974 // It does not contribute to selector weight.
5975 selector->mLowercaseTag.swap(pseudoElement);
5976 selector->mClassList = pseudoElementArgs.forget();
5977 selector->SetPseudoType(pseudoElementType);
5978 return true;
5981 aList->mWeight += selector->CalcWeight();
5983 return true;
5986 css::Declaration*
5987 CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext)
5989 bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
5991 if (checkForBraces) {
5992 if (!ExpectSymbol('{', true)) {
5993 REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
5994 OUTPUT_ERROR();
5995 return nullptr;
5998 css::Declaration* declaration = new css::Declaration();
5999 mData.AssertInitialState();
6000 if (declaration) {
6001 for (;;) {
6002 bool changed;
6003 if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) {
6004 if (!SkipDeclaration(checkForBraces)) {
6005 break;
6007 if (checkForBraces) {
6008 if (ExpectSymbol('}', true)) {
6009 break;
6012 // Since the skipped declaration didn't end the block we parse
6013 // the next declaration.
6016 declaration->CompressFrom(&mData);
6018 return declaration;
6021 bool
6022 CSSParserImpl::ParseColor(nsCSSValue& aValue)
6024 if (!GetToken(true)) {
6025 REPORT_UNEXPECTED_EOF(PEColorEOF);
6026 return false;
6029 nsCSSToken* tk = &mToken;
6030 nscolor rgba;
6031 switch (tk->mType) {
6032 case eCSSToken_ID:
6033 case eCSSToken_Hash:
6034 // #xxyyzz
6035 if (NS_HexToRGB(tk->mIdent, &rgba)) {
6036 MOZ_ASSERT(tk->mIdent.Length() == 3 || tk->mIdent.Length() == 6,
6037 "unexpected hex color length");
6038 nsCSSUnit unit = tk->mIdent.Length() == 3 ?
6039 eCSSUnit_ShortHexColor :
6040 eCSSUnit_HexColor;
6041 aValue.SetIntegerColorValue(rgba, unit);
6042 return true;
6044 break;
6046 case eCSSToken_Ident:
6047 if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
6048 aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
6049 return true;
6051 else {
6052 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
6053 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
6054 int32_t value;
6055 if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
6056 aValue.SetIntValue(value, eCSSUnit_EnumColor);
6057 return true;
6061 break;
6062 case eCSSToken_Function:
6063 if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
6064 // rgb ( component , component , component )
6065 if (GetToken(true)) {
6066 UngetToken();
6068 if (mToken.mType == eCSSToken_Number) {
6069 uint8_t r, g, b;
6070 if (ParseNumberColorComponent(r, ',') &&
6071 ParseNumberColorComponent(g, ',') &&
6072 ParseNumberColorComponent(b, ')')) {
6073 aValue.SetIntegerColorValue(NS_RGB(r, g, b), eCSSUnit_RGBColor);
6074 return true;
6076 } else {
6077 float r, g, b;
6078 if (ParsePercentageColorComponent(r, ',') &&
6079 ParsePercentageColorComponent(g, ',') &&
6080 ParsePercentageColorComponent(b, ')')) {
6081 aValue.SetFloatColorValue(r, g, b, 1.0f,
6082 eCSSUnit_PercentageRGBColor);
6083 return true;
6086 SkipUntil(')');
6087 return false;
6089 else if (mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
6090 // rgba ( component , component , component , opacity )
6091 if (GetToken(true)) {
6092 UngetToken();
6094 if (mToken.mType == eCSSToken_Number) {
6095 uint8_t r, g, b, a;
6096 if (ParseNumberColorComponent(r, ',') &&
6097 ParseNumberColorComponent(g, ',') &&
6098 ParseNumberColorComponent(b, ',') &&
6099 ParseColorOpacity(a)) {
6100 aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a),
6101 eCSSUnit_RGBAColor);
6102 return true;
6104 } else {
6105 float r, g, b, a;
6106 if (ParsePercentageColorComponent(r, ',') &&
6107 ParsePercentageColorComponent(g, ',') &&
6108 ParsePercentageColorComponent(b, ',') &&
6109 ParseColorOpacity(a)) {
6110 aValue.SetFloatColorValue(r, g, b, a, eCSSUnit_PercentageRGBAColor);
6111 return true;
6114 SkipUntil(')');
6115 return false;
6117 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
6118 // hsl ( hue , saturation , lightness )
6119 // "hue" is a number, "saturation" and "lightness" are percentages.
6120 float h, s, l;
6121 if (ParseHSLColor(h, s, l, ')')) {
6122 aValue.SetFloatColorValue(h, s, l, 1.0f, eCSSUnit_HSLColor);
6123 return true;
6125 SkipUntil(')');
6126 return false;
6128 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
6129 // hsla ( hue , saturation , lightness , opacity )
6130 // "hue" is a number, "saturation" and "lightness" are percentages,
6131 // "opacity" is a number.
6132 float h, s, l, a;
6133 if (ParseHSLColor(h, s, l, ',') &&
6134 ParseColorOpacity(a)) {
6135 aValue.SetFloatColorValue(h, s, l, a, eCSSUnit_HSLAColor);
6136 return true;
6138 SkipUntil(')');
6139 return false;
6141 break;
6142 default:
6143 break;
6146 // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
6147 if (mHashlessColorQuirk) {
6148 // - If the string starts with 'a-f', the nsCSSScanner builds the
6149 // token as a eCSSToken_Ident and we can parse the string as a
6150 // 'xxyyzz' RGB color.
6151 // - If it only contains '0-9' digits, the token is a
6152 // eCSSToken_Number and it must be converted back to a 6
6153 // characters string to be parsed as a RGB color.
6154 // - If it starts with '0-9' and contains any 'a-f', the token is a
6155 // eCSSToken_Dimension, the mNumber part must be converted back to
6156 // a string and the mIdent part must be appended to that string so
6157 // that the resulting string has 6 characters.
6158 // Note: This is a hack for Nav compatibility. Do not attempt to
6159 // simplify it by hacking into the ncCSSScanner. This would be very
6160 // bad.
6161 nsAutoString str;
6162 char buffer[20];
6163 switch (tk->mType) {
6164 case eCSSToken_Ident:
6165 str.Assign(tk->mIdent);
6166 break;
6168 case eCSSToken_Number:
6169 if (tk->mIntegerValid) {
6170 PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
6171 str.AssignWithConversion(buffer);
6173 break;
6175 case eCSSToken_Dimension:
6176 if (tk->mIdent.Length() <= 6) {
6177 PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
6178 nsAutoString temp;
6179 temp.AssignWithConversion(buffer);
6180 temp.Right(str, 6 - tk->mIdent.Length());
6181 str.Append(tk->mIdent);
6183 break;
6184 default:
6185 // There is a whole bunch of cases that are
6186 // not handled by this switch. Ignore them.
6187 break;
6189 if (NS_HexToRGB(str, &rgba)) {
6190 aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor);
6191 return true;
6195 // It's not a color
6196 REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
6197 UngetToken();
6198 return false;
6201 bool
6202 CSSParserImpl::ParseNumberColorComponent(uint8_t& aComponent, char aStop)
6204 if (!GetToken(true)) {
6205 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
6206 return false;
6209 if (mToken.mType != eCSSToken_Number || !mToken.mIntegerValid) {
6210 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
6211 UngetToken();
6212 return false;
6215 float value = mToken.mNumber;
6216 if (value < 0.0f) value = 0.0f;
6217 if (value > 255.0f) value = 255.0f;
6219 if (ExpectSymbol(aStop, true)) {
6220 aComponent = NSToIntRound(value);
6221 return true;
6223 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
6224 return false;
6227 bool
6228 CSSParserImpl::ParsePercentageColorComponent(float& aComponent, char aStop)
6230 if (!GetToken(true)) {
6231 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
6232 return false;
6235 if (mToken.mType != eCSSToken_Percentage) {
6236 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
6237 UngetToken();
6238 return false;
6241 float value = mToken.mNumber;
6242 if (value < 0.0f) value = 0.0f;
6243 if (value > 1.0f) value = 1.0f;
6245 if (ExpectSymbol(aStop, true)) {
6246 aComponent = value;
6247 return true;
6249 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
6250 return false;
6254 bool
6255 CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
6256 char aStop)
6258 float h, s, l;
6260 // Get the hue
6261 if (!GetToken(true)) {
6262 REPORT_UNEXPECTED_EOF(PEColorHueEOF);
6263 return false;
6265 if (mToken.mType != eCSSToken_Number) {
6266 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
6267 UngetToken();
6268 return false;
6270 h = mToken.mNumber;
6271 h /= 360.0f;
6272 // hue values are wraparound
6273 h = h - floor(h);
6275 if (!ExpectSymbol(',', true)) {
6276 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
6277 return false;
6280 // Get the saturation
6281 if (!GetToken(true)) {
6282 REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
6283 return false;
6285 if (mToken.mType != eCSSToken_Percentage) {
6286 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
6287 UngetToken();
6288 return false;
6290 s = mToken.mNumber;
6291 if (s < 0.0f) s = 0.0f;
6292 if (s > 1.0f) s = 1.0f;
6294 if (!ExpectSymbol(',', true)) {
6295 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
6296 return false;
6299 // Get the lightness
6300 if (!GetToken(true)) {
6301 REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
6302 return false;
6304 if (mToken.mType != eCSSToken_Percentage) {
6305 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
6306 UngetToken();
6307 return false;
6309 l = mToken.mNumber;
6310 if (l < 0.0f) l = 0.0f;
6311 if (l > 1.0f) l = 1.0f;
6313 if (ExpectSymbol(aStop, true)) {
6314 aHue = h;
6315 aSaturation = s;
6316 aLightness = l;
6317 return true;
6320 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
6321 return false;
6325 bool
6326 CSSParserImpl::ParseColorOpacity(uint8_t& aOpacity)
6328 float floatOpacity;
6329 if (!ParseColorOpacity(floatOpacity)) {
6330 return false;
6333 uint8_t value = nsStyleUtil::FloatToColorComponent(floatOpacity);
6334 // Need to compare to something slightly larger
6335 // than 0.5 due to floating point inaccuracies.
6336 NS_ASSERTION(fabs(255.0f*mToken.mNumber - value) <= 0.51f,
6337 "FloatToColorComponent did something weird");
6339 aOpacity = value;
6340 return true;
6343 bool
6344 CSSParserImpl::ParseColorOpacity(float& aOpacity)
6346 if (!GetToken(true)) {
6347 REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
6348 return false;
6351 if (mToken.mType != eCSSToken_Number) {
6352 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
6353 UngetToken();
6354 return false;
6357 if (!ExpectSymbol(')', true)) {
6358 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
6359 return false;
6362 if (mToken.mNumber < 0.0f) {
6363 mToken.mNumber = 0.0f;
6364 } else if (mToken.mNumber > 1.0f) {
6365 mToken.mNumber = 1.0f;
6368 aOpacity = mToken.mNumber;
6369 return true;
6372 #ifdef MOZ_XUL
6373 bool
6374 CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs)
6376 // The argument to a tree pseudo-element is a sequence of identifiers
6377 // that are either space- or comma-separated. (Was the intent to
6378 // allow only comma-separated? That's not what was done.)
6379 nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass
6381 while (!ExpectSymbol(')', true)) {
6382 if (!GetToken(true)) {
6383 return false;
6385 if (eCSSToken_Ident == mToken.mType) {
6386 fakeSelector.AddClass(mToken.mIdent);
6388 else if (!mToken.IsSymbol(',')) {
6389 UngetToken();
6390 SkipUntil(')');
6391 return false;
6394 *aPseudoElementArgs = fakeSelector.mClassList;
6395 fakeSelector.mClassList = nullptr;
6396 return true;
6398 #endif
6400 //----------------------------------------------------------------------
6402 bool
6403 CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
6404 uint32_t aFlags,
6405 bool aMustCallValueAppended,
6406 bool* aChanged,
6407 nsCSSContextType aContext)
6409 NS_PRECONDITION(aContext == eCSSContext_General ||
6410 aContext == eCSSContext_Page,
6411 "Must be page or general context");
6413 bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
6415 mTempData.AssertInitialState();
6417 // Get property name
6418 nsCSSToken* tk = &mToken;
6419 nsAutoString propertyName;
6420 for (;;) {
6421 if (!GetToken(true)) {
6422 if (checkForBraces) {
6423 REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
6425 return false;
6427 if (eCSSToken_Ident == tk->mType) {
6428 propertyName = tk->mIdent;
6429 // grab the ident before the ExpectSymbol trashes the token
6430 if (!ExpectSymbol(':', true)) {
6431 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
6432 REPORT_UNEXPECTED(PEDeclDropped);
6433 OUTPUT_ERROR();
6434 return false;
6436 break;
6438 if (tk->IsSymbol(';')) {
6439 // dangling semicolons are skipped
6440 continue;
6443 if (!tk->IsSymbol('}')) {
6444 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
6445 REPORT_UNEXPECTED(PEDeclSkipped);
6446 OUTPUT_ERROR();
6448 if (eCSSToken_AtKeyword == tk->mType) {
6449 SkipAtRule(checkForBraces);
6450 return true; // Not a declaration, but don't skip until ';'
6453 // Not a declaration...
6454 UngetToken();
6455 return false;
6458 // Don't report property parse errors if we're inside a failing @supports
6459 // rule.
6460 nsAutoSuppressErrors suppressErrors(this, mInFailingSupportsRule);
6462 // Information about a parsed non-custom property.
6463 nsCSSProperty propID;
6465 // Information about a parsed custom property.
6466 CSSVariableDeclarations::Type variableType;
6467 nsString variableValue;
6469 // Check if the property name is a custom property.
6470 bool customProperty = nsLayoutUtils::CSSVariablesEnabled() &&
6471 nsCSSProps::IsCustomPropertyName(propertyName) &&
6472 aContext == eCSSContext_General;
6474 if (customProperty) {
6475 if (!ParseVariableDeclaration(&variableType, variableValue)) {
6476 REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
6477 REPORT_UNEXPECTED(PEDeclDropped);
6478 OUTPUT_ERROR();
6479 return false;
6481 } else {
6482 // Map property name to its ID.
6483 propID = LookupEnabledProperty(propertyName);
6484 if (eCSSProperty_UNKNOWN == propID ||
6485 (aContext == eCSSContext_Page &&
6486 !nsCSSProps::PropHasFlags(propID,
6487 CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property
6488 if (!NonMozillaVendorIdentifier(propertyName)) {
6489 REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName);
6490 REPORT_UNEXPECTED(PEDeclDropped);
6491 OUTPUT_ERROR();
6493 return false;
6495 // Then parse the property.
6496 if (!ParseProperty(propID)) {
6497 // XXX Much better to put stuff in the value parsers instead...
6498 REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
6499 REPORT_UNEXPECTED(PEDeclDropped);
6500 OUTPUT_ERROR();
6501 mTempData.ClearProperty(propID);
6502 mTempData.AssertInitialState();
6503 return false;
6507 CLEAR_ERROR();
6509 // Look for "!important".
6510 PriorityParsingStatus status;
6511 if ((aFlags & eParseDeclaration_AllowImportant) != 0) {
6512 status = ParsePriority();
6513 } else {
6514 status = ePriority_None;
6517 // Look for a semicolon or close brace.
6518 if (status != ePriority_Error) {
6519 if (!GetToken(true)) {
6520 // EOF is always ok
6521 } else if (mToken.IsSymbol(';')) {
6522 // semicolon is always ok
6523 } else if (mToken.IsSymbol('}')) {
6524 // brace is ok if checkForBraces, but don't eat it
6525 UngetToken();
6526 if (!checkForBraces) {
6527 status = ePriority_Error;
6529 } else {
6530 UngetToken();
6531 status = ePriority_Error;
6535 if (status == ePriority_Error) {
6536 if (checkForBraces) {
6537 REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
6538 } else {
6539 REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
6541 REPORT_UNEXPECTED(PEDeclDropped);
6542 OUTPUT_ERROR();
6543 if (!customProperty) {
6544 mTempData.ClearProperty(propID);
6546 mTempData.AssertInitialState();
6547 return false;
6550 if (customProperty) {
6551 MOZ_ASSERT(Substring(propertyName, 0,
6552 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
6553 // remove '--'
6554 nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
6555 aDeclaration->AddVariableDeclaration(varName, variableType, variableValue,
6556 status == ePriority_Important, false);
6557 } else {
6558 *aChanged |= mData.TransferFromBlock(mTempData, propID,
6559 status == ePriority_Important,
6560 false, aMustCallValueAppended,
6561 aDeclaration);
6564 return true;
6567 static const nsCSSProperty kBorderTopIDs[] = {
6568 eCSSProperty_border_top_width,
6569 eCSSProperty_border_top_style,
6570 eCSSProperty_border_top_color
6572 static const nsCSSProperty kBorderRightIDs[] = {
6573 eCSSProperty_border_right_width_value,
6574 eCSSProperty_border_right_style_value,
6575 eCSSProperty_border_right_color_value,
6576 eCSSProperty_border_right_width,
6577 eCSSProperty_border_right_style,
6578 eCSSProperty_border_right_color
6580 static const nsCSSProperty kBorderBottomIDs[] = {
6581 eCSSProperty_border_bottom_width,
6582 eCSSProperty_border_bottom_style,
6583 eCSSProperty_border_bottom_color
6585 static const nsCSSProperty kBorderLeftIDs[] = {
6586 eCSSProperty_border_left_width_value,
6587 eCSSProperty_border_left_style_value,
6588 eCSSProperty_border_left_color_value,
6589 eCSSProperty_border_left_width,
6590 eCSSProperty_border_left_style,
6591 eCSSProperty_border_left_color
6593 static const nsCSSProperty kBorderStartIDs[] = {
6594 eCSSProperty_border_start_width_value,
6595 eCSSProperty_border_start_style_value,
6596 eCSSProperty_border_start_color_value,
6597 eCSSProperty_border_start_width,
6598 eCSSProperty_border_start_style,
6599 eCSSProperty_border_start_color
6601 static const nsCSSProperty kBorderEndIDs[] = {
6602 eCSSProperty_border_end_width_value,
6603 eCSSProperty_border_end_style_value,
6604 eCSSProperty_border_end_color_value,
6605 eCSSProperty_border_end_width,
6606 eCSSProperty_border_end_style,
6607 eCSSProperty_border_end_color
6609 static const nsCSSProperty kColumnRuleIDs[] = {
6610 eCSSProperty__moz_column_rule_width,
6611 eCSSProperty__moz_column_rule_style,
6612 eCSSProperty__moz_column_rule_color
6615 bool
6616 CSSParserImpl::ParseEnum(nsCSSValue& aValue,
6617 const KTableValue aKeywordTable[])
6619 nsSubstring* ident = NextIdent();
6620 if (nullptr == ident) {
6621 return false;
6623 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
6624 if (eCSSKeyword_UNKNOWN < keyword) {
6625 int32_t value;
6626 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
6627 aValue.SetIntValue(value, eCSSUnit_Enumerated);
6628 return true;
6632 // Put the unknown identifier back and return
6633 UngetToken();
6634 return false;
6638 struct UnitInfo {
6639 char name[6]; // needs to be long enough for the longest unit, with
6640 // terminating null.
6641 uint32_t length;
6642 nsCSSUnit unit;
6643 int32_t type;
6646 #define STR_WITH_LEN(_str) \
6647 _str, sizeof(_str) - 1
6649 const UnitInfo UnitData[] = {
6650 { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
6651 { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
6652 { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
6653 { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
6654 { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
6655 { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
6656 { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
6657 { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH },
6658 { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
6659 { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH },
6660 { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH },
6661 { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH },
6662 { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH },
6663 { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH },
6664 { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
6665 { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
6666 { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
6667 { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
6668 { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE },
6669 { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
6670 { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
6671 { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
6672 { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
6675 #undef STR_WITH_LEN
6677 bool
6678 CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
6679 int32_t aVariantMask,
6680 float aNumber,
6681 const nsString& aUnit)
6683 nsCSSUnit units;
6684 int32_t type = 0;
6685 if (!aUnit.IsEmpty()) {
6686 uint32_t i;
6687 for (i = 0; i < ArrayLength(UnitData); ++i) {
6688 if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
6689 UnitData[i].length)) {
6690 units = UnitData[i].unit;
6691 type = UnitData[i].type;
6692 break;
6696 if (i == ArrayLength(UnitData)) {
6697 // Unknown unit
6698 return false;
6701 if (!mViewportUnitsEnabled &&
6702 (eCSSUnit_ViewportWidth == units ||
6703 eCSSUnit_ViewportHeight == units ||
6704 eCSSUnit_ViewportMin == units ||
6705 eCSSUnit_ViewportMax == units)) {
6706 // Viewport units aren't allowed right now, probably because we're
6707 // inside an @page declaration. Fail.
6708 return false;
6710 } else {
6711 // Must be a zero number...
6712 NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
6713 if ((VARIANT_LENGTH & aVariantMask) != 0) {
6714 units = eCSSUnit_Pixel;
6715 type = VARIANT_LENGTH;
6717 else if ((VARIANT_ANGLE & aVariantMask) != 0) {
6718 NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE,
6719 "must have allowed zero angle");
6720 units = eCSSUnit_Degree;
6721 type = VARIANT_ANGLE;
6723 else {
6724 NS_ERROR("Variant mask does not include dimension; why were we called?");
6725 return false;
6728 if ((type & aVariantMask) != 0) {
6729 aValue.SetFloatValue(aNumber, units);
6730 return true;
6732 return false;
6735 // Note that this does include VARIANT_CALC, which is numeric. This is
6736 // because calc() parsing, as proposed, drops range restrictions inside
6737 // the calc() expression and clamps the result of the calculation to the
6738 // range.
6739 #define VARIANT_ALL_NONNUMERIC \
6740 VARIANT_KEYWORD | \
6741 VARIANT_COLOR | \
6742 VARIANT_URL | \
6743 VARIANT_STRING | \
6744 VARIANT_COUNTER | \
6745 VARIANT_ATTR | \
6746 VARIANT_IDENTIFIER | \
6747 VARIANT_IDENTIFIER_NO_INHERIT | \
6748 VARIANT_AUTO | \
6749 VARIANT_INHERIT | \
6750 VARIANT_NONE | \
6751 VARIANT_NORMAL | \
6752 VARIANT_SYSFONT | \
6753 VARIANT_GRADIENT | \
6754 VARIANT_TIMING_FUNCTION | \
6755 VARIANT_ALL | \
6756 VARIANT_CALC | \
6757 VARIANT_OPENTYPE_SVG_KEYWORD
6759 // Note that callers passing VARIANT_CALC in aVariantMask will get
6760 // full-range parsing inside the calc() expression, and the code that
6761 // computes the calc will be required to clamp the resulting value to an
6762 // appropriate range.
6763 bool
6764 CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue,
6765 int32_t aVariantMask,
6766 const KTableValue aKeywordTable[])
6768 // The variant mask must only contain non-numeric variants or the ones
6769 // that we specifically handle.
6770 NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
6771 VARIANT_NUMBER |
6772 VARIANT_LENGTH |
6773 VARIANT_PERCENT |
6774 VARIANT_INTEGER)) == 0,
6775 "need to update code below to handle additional variants");
6777 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
6778 if (eCSSUnit_Number == aValue.GetUnit() ||
6779 aValue.IsLengthUnit()){
6780 if (aValue.GetFloatValue() < 0) {
6781 UngetToken();
6782 return false;
6785 else if (aValue.GetUnit() == eCSSUnit_Percent) {
6786 if (aValue.GetPercentValue() < 0) {
6787 UngetToken();
6788 return false;
6790 } else if (aValue.GetUnit() == eCSSUnit_Integer) {
6791 if (aValue.GetIntValue() < 0) {
6792 UngetToken();
6793 return false;
6796 return true;
6798 return false;
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::ParseOneOrLargerVariant(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_INTEGER)) == 0,
6815 "need to update code below to handle additional variants");
6817 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
6818 if (aValue.GetUnit() == eCSSUnit_Integer) {
6819 if (aValue.GetIntValue() < 1) {
6820 UngetToken();
6821 return false;
6823 } else if (eCSSUnit_Number == aValue.GetUnit()) {
6824 if (aValue.GetFloatValue() < 1.0f) {
6825 UngetToken();
6826 return false;
6829 return true;
6831 return false;
6834 // Assigns to aValue iff it returns true.
6835 bool
6836 CSSParserImpl::ParseVariant(nsCSSValue& aValue,
6837 int32_t aVariantMask,
6838 const KTableValue aKeywordTable[])
6840 NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
6841 !(aVariantMask & VARIANT_NUMBER),
6842 "can't distinguish colors from numbers");
6843 NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
6844 !(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)),
6845 "can't distinguish colors from lengths");
6846 NS_ASSERTION(!(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)) ||
6847 !(aVariantMask & VARIANT_NUMBER),
6848 "can't distinguish lengths from numbers");
6849 NS_ABORT_IF_FALSE(!(aVariantMask & VARIANT_IDENTIFIER) ||
6850 !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT),
6851 "must not set both VARIANT_IDENTIFIER and "
6852 "VARIANT_IDENTIFIER_NO_INHERIT");
6854 if (!GetToken(true)) {
6855 return false;
6857 nsCSSToken* tk = &mToken;
6858 if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
6859 (eCSSToken_Ident == tk->mType)) {
6860 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
6861 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
6862 if ((aVariantMask & VARIANT_AUTO) != 0) {
6863 if (eCSSKeyword_auto == keyword) {
6864 aValue.SetAutoValue();
6865 return true;
6868 if ((aVariantMask & VARIANT_INHERIT) != 0) {
6869 // XXX Should we check IsParsingCompoundProperty, or do all
6870 // callers handle it? (Not all callers set it, though, since
6871 // they want the quirks that are disabled by setting it.)
6873 // IMPORTANT: If new keywords are added here,
6874 // they probably need to be added in ParseCustomIdent as well.
6875 if (eCSSKeyword_inherit == keyword) {
6876 aValue.SetInheritValue();
6877 return true;
6879 else if (eCSSKeyword_initial == keyword) {
6880 aValue.SetInitialValue();
6881 return true;
6883 else if (eCSSKeyword_unset == keyword &&
6884 nsLayoutUtils::UnsetValueEnabled()) {
6885 aValue.SetUnsetValue();
6886 return true;
6889 if ((aVariantMask & VARIANT_NONE) != 0) {
6890 if (eCSSKeyword_none == keyword) {
6891 aValue.SetNoneValue();
6892 return true;
6895 if ((aVariantMask & VARIANT_ALL) != 0) {
6896 if (eCSSKeyword_all == keyword) {
6897 aValue.SetAllValue();
6898 return true;
6901 if ((aVariantMask & VARIANT_NORMAL) != 0) {
6902 if (eCSSKeyword_normal == keyword) {
6903 aValue.SetNormalValue();
6904 return true;
6907 if ((aVariantMask & VARIANT_SYSFONT) != 0) {
6908 if (eCSSKeyword__moz_use_system_font == keyword &&
6909 !IsParsingCompoundProperty()) {
6910 aValue.SetSystemFontValue();
6911 return true;
6914 if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) {
6915 static bool sOpentypeSVGEnabled;
6916 static bool sOpentypeSVGEnabledCached = false;
6917 if (!sOpentypeSVGEnabledCached) {
6918 sOpentypeSVGEnabledCached = true;
6919 Preferences::AddBoolVarCache(&sOpentypeSVGEnabled,
6920 "gfx.font_rendering.opentype_svg.enabled");
6922 if (sOpentypeSVGEnabled) {
6923 aVariantMask |= VARIANT_KEYWORD;
6926 if ((aVariantMask & VARIANT_KEYWORD) != 0) {
6927 int32_t value;
6928 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
6929 aValue.SetIntValue(value, eCSSUnit_Enumerated);
6930 return true;
6935 // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or
6936 // VARIANT_ZERO_ANGLE.
6937 if (((aVariantMask & VARIANT_NUMBER) != 0) &&
6938 (eCSSToken_Number == tk->mType)) {
6939 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
6940 return true;
6942 if (((aVariantMask & VARIANT_INTEGER) != 0) &&
6943 (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
6944 aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
6945 return true;
6947 if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
6948 VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
6949 eCSSToken_Dimension == tk->mType) ||
6950 ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
6951 eCSSToken_Number == tk->mType &&
6952 tk->mNumber == 0.0f)) {
6953 if (((aVariantMask & VARIANT_POSITIVE_DIMENSION) != 0 &&
6954 tk->mNumber <= 0.0) ||
6955 ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 &&
6956 tk->mNumber < 0.0)) {
6957 UngetToken();
6958 return false;
6960 if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
6961 return true;
6963 // Put the token back; we didn't parse it, so we shouldn't consume it
6964 UngetToken();
6965 return false;
6967 if (((aVariantMask & VARIANT_PERCENT) != 0) &&
6968 (eCSSToken_Percentage == tk->mType)) {
6969 aValue.SetPercentValue(tk->mNumber);
6970 return true;
6972 if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px
6973 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
6974 (eCSSToken_Number == tk->mType)) {
6975 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
6976 return true;
6980 if (IsSVGMode() && !IsParsingCompoundProperty()) {
6981 // STANDARD: SVG Spec states that lengths and coordinates can be unitless
6982 // in which case they default to user-units (1 px = 1 user unit)
6983 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
6984 (eCSSToken_Number == tk->mType)) {
6985 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
6986 return true;
6990 if (((aVariantMask & VARIANT_URL) != 0) &&
6991 eCSSToken_URL == tk->mType) {
6992 SetValueToURL(aValue, tk->mIdent);
6993 return true;
6995 if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
6996 eCSSToken_Function == tk->mType) {
6997 // a generated gradient
6998 nsDependentString tmp(tk->mIdent, 0);
6999 bool isLegacy = false;
7000 if (StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) {
7001 tmp.Rebind(tmp, 5);
7002 isLegacy = true;
7004 bool isRepeating = false;
7005 if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) {
7006 tmp.Rebind(tmp, 10);
7007 isRepeating = true;
7010 if (tmp.LowerCaseEqualsLiteral("linear-gradient")) {
7011 return ParseLinearGradient(aValue, isRepeating, isLegacy);
7013 if (tmp.LowerCaseEqualsLiteral("radial-gradient")) {
7014 return ParseRadialGradient(aValue, isRepeating, isLegacy);
7017 if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
7018 eCSSToken_Function == tk->mType &&
7019 tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
7020 return ParseImageRect(aValue);
7022 if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
7023 eCSSToken_Function == tk->mType &&
7024 tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) {
7025 return ParseElement(aValue);
7027 if ((aVariantMask & VARIANT_COLOR) != 0) {
7028 if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
7029 (eCSSToken_ID == tk->mType) ||
7030 (eCSSToken_Hash == tk->mType) ||
7031 (eCSSToken_Ident == tk->mType) ||
7032 ((eCSSToken_Function == tk->mType) &&
7033 (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
7034 tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
7035 tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
7036 tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
7038 // Put token back so that parse color can get it
7039 UngetToken();
7040 if (ParseColor(aValue)) {
7041 return true;
7043 return false;
7046 if (((aVariantMask & VARIANT_STRING) != 0) &&
7047 (eCSSToken_String == tk->mType)) {
7048 nsAutoString buffer;
7049 buffer.Append(tk->mIdent);
7050 aValue.SetStringValue(buffer, eCSSUnit_String);
7051 return true;
7053 if (((aVariantMask &
7054 (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
7055 (eCSSToken_Ident == tk->mType) &&
7056 ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
7057 !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
7058 tk->mIdent.LowerCaseEqualsLiteral("initial") ||
7059 (tk->mIdent.LowerCaseEqualsLiteral("unset") &&
7060 nsLayoutUtils::UnsetValueEnabled())))) {
7061 aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
7062 return true;
7064 if (((aVariantMask & VARIANT_COUNTER) != 0) &&
7065 (eCSSToken_Function == tk->mType) &&
7066 (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
7067 tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
7068 return ParseCounter(aValue);
7070 if (((aVariantMask & VARIANT_ATTR) != 0) &&
7071 (eCSSToken_Function == tk->mType) &&
7072 tk->mIdent.LowerCaseEqualsLiteral("attr")) {
7073 if (!ParseAttr(aValue)) {
7074 SkipUntil(')');
7075 return false;
7077 return true;
7079 if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
7080 (eCSSToken_Function == tk->mType)) {
7081 if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
7082 if (!ParseTransitionTimingFunctionValues(aValue)) {
7083 SkipUntil(')');
7084 return false;
7086 return true;
7088 if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
7089 if (!ParseTransitionStepTimingFunctionValues(aValue)) {
7090 SkipUntil(')');
7091 return false;
7093 return true;
7096 if ((aVariantMask & VARIANT_CALC) &&
7097 (eCSSToken_Function == tk->mType) &&
7098 (tk->mIdent.LowerCaseEqualsLiteral("calc") ||
7099 tk->mIdent.LowerCaseEqualsLiteral("-moz-calc"))) {
7100 // calc() currently allows only lengths and percents inside it.
7101 return ParseCalc(aValue, aVariantMask & VARIANT_LP);
7104 UngetToken();
7105 return false;
7108 bool
7109 CSSParserImpl::ParseCustomIdent(nsCSSValue& aValue,
7110 const nsAutoString& aIdentValue,
7111 const nsCSSKeyword aExcludedKeywords[],
7112 const nsCSSProps::KTableValue aPropertyKTable[])
7114 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aIdentValue);
7115 if (keyword == eCSSKeyword_UNKNOWN) {
7116 // Fast path for identifiers that are not known CSS keywords:
7117 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
7118 return true;
7120 if (keyword == eCSSKeyword_inherit ||
7121 keyword == eCSSKeyword_initial ||
7122 keyword == eCSSKeyword_unset ||
7123 keyword == eCSSKeyword_default ||
7124 (aPropertyKTable &&
7125 nsCSSProps::FindIndexOfKeyword(keyword, aPropertyKTable) >= 0)) {
7126 return false;
7128 if (aExcludedKeywords) {
7129 for (uint32_t i = 0;; i++) {
7130 nsCSSKeyword excludedKeyword = aExcludedKeywords[i];
7131 if (excludedKeyword == eCSSKeyword_UNKNOWN) {
7132 break;
7134 if (excludedKeyword == keyword) {
7135 return false;
7139 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
7140 return true;
7143 bool
7144 CSSParserImpl::ParseCounter(nsCSSValue& aValue)
7146 nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
7147 eCSSUnit_Counter : eCSSUnit_Counters);
7149 // A non-iterative for loop to break out when an error occurs.
7150 for (;;) {
7151 if (!GetToken(true)) {
7152 break;
7154 if (eCSSToken_Ident != mToken.mType) {
7155 UngetToken();
7156 break;
7159 nsRefPtr<nsCSSValue::Array> val =
7160 nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
7162 val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
7164 if (eCSSUnit_Counters == unit) {
7165 // must have a comma and then a separator string
7166 if (!ExpectSymbol(',', true) || !GetToken(true)) {
7167 break;
7169 if (eCSSToken_String != mToken.mType) {
7170 UngetToken();
7171 break;
7173 val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
7176 // get optional type
7177 nsString type = NS_LITERAL_STRING("decimal");
7178 if (ExpectSymbol(',', true)) {
7179 if (!ParseCounterStyleName(type, false)) {
7180 break;
7184 int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1;
7185 val->Item(typeItem).SetStringValue(type, eCSSUnit_Ident);
7187 if (!ExpectSymbol(')', true)) {
7188 break;
7191 aValue.SetArrayValue(val, unit);
7192 return true;
7195 SkipUntil(')');
7196 return false;
7199 bool
7200 CSSParserImpl::ParseAttr(nsCSSValue& aValue)
7202 if (!GetToken(true)) {
7203 return false;
7206 nsAutoString attr;
7207 if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
7208 nsAutoString holdIdent(mToken.mIdent);
7209 if (ExpectSymbol('|', false)) { // namespace
7210 int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent);
7211 if (nameSpaceID == kNameSpaceID_Unknown) {
7212 return false;
7214 attr.AppendInt(nameSpaceID, 10);
7215 attr.Append(char16_t('|'));
7216 if (! GetToken(false)) {
7217 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
7218 return false;
7220 if (eCSSToken_Ident == mToken.mType) {
7221 attr.Append(mToken.mIdent);
7223 else {
7224 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
7225 UngetToken();
7226 return false;
7229 else { // no namespace
7230 attr = holdIdent;
7233 else if (mToken.IsSymbol('*')) { // namespace wildcard
7234 // Wildcard namespace makes no sense here and is not allowed
7235 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
7236 UngetToken();
7237 return false;
7239 else if (mToken.IsSymbol('|')) { // explicit NO namespace
7240 if (! GetToken(false)) {
7241 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
7242 return false;
7244 if (eCSSToken_Ident == mToken.mType) {
7245 attr.Append(mToken.mIdent);
7247 else {
7248 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
7249 UngetToken();
7250 return false;
7253 else {
7254 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
7255 UngetToken();
7256 return false;
7258 if (!ExpectSymbol(')', true)) {
7259 return false;
7261 aValue.SetStringValue(attr, eCSSUnit_Attr);
7262 return true;
7265 bool
7266 CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL)
7268 if (!mSheetPrincipal) {
7269 NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
7270 "origin principal");
7271 return false;
7274 nsRefPtr<nsStringBuffer> buffer(nsCSSValue::BufferFromString(aURL));
7276 // Note: urlVal retains its own reference to |buffer|.
7277 mozilla::css::URLValue *urlVal =
7278 new mozilla::css::URLValue(buffer, mBaseURI, mSheetURI, mSheetPrincipal);
7279 aValue.SetURLValue(urlVal);
7280 return true;
7284 * Parse the image-orientation property, which has the grammar:
7285 * <angle> flip? | flip | from-image
7287 bool
7288 CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue)
7290 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
7291 // 'inherit', 'initial' and 'unset' must be alone
7292 return true;
7295 // Check for an angle with optional 'flip'.
7296 nsCSSValue angle;
7297 if (ParseVariant(angle, VARIANT_ANGLE, nullptr)) {
7298 nsCSSValue flip;
7300 if (ParseVariant(flip, VARIANT_KEYWORD, nsCSSProps::kImageOrientationFlipKTable)) {
7301 nsRefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(2);
7302 array->Item(0) = angle;
7303 array->Item(1) = flip;
7304 aValue.SetArrayValue(array, eCSSUnit_Array);
7305 } else {
7306 aValue = angle;
7309 return true;
7312 // The remaining possibilities (bare 'flip' and 'from-image') are both
7313 // keywords, so we can handle them at the same time.
7314 nsCSSValue keyword;
7315 if (ParseVariant(keyword, VARIANT_KEYWORD, nsCSSProps::kImageOrientationKTable)) {
7316 aValue = keyword;
7317 return true;
7320 // All possibilities failed.
7321 return false;
7325 * Parse the arguments of -moz-image-rect() function.
7326 * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
7328 bool
7329 CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
7331 // A non-iterative for loop to break out when an error occurs.
7332 for (;;) {
7333 nsCSSValue newFunction;
7334 static const uint32_t kNumArgs = 5;
7335 nsCSSValue::Array* func =
7336 newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs);
7338 // func->Item(0) is reserved for the function name.
7339 nsCSSValue& url = func->Item(1);
7340 nsCSSValue& top = func->Item(2);
7341 nsCSSValue& right = func->Item(3);
7342 nsCSSValue& bottom = func->Item(4);
7343 nsCSSValue& left = func->Item(5);
7345 nsAutoString urlString;
7346 if (!ParseURLOrString(urlString) ||
7347 !SetValueToURL(url, urlString) ||
7348 !ExpectSymbol(',', true)) {
7349 break;
7352 static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT;
7353 if (!ParseNonNegativeVariant(top, VARIANT_SIDE, nullptr) ||
7354 !ExpectSymbol(',', true) ||
7355 !ParseNonNegativeVariant(right, VARIANT_SIDE, nullptr) ||
7356 !ExpectSymbol(',', true) ||
7357 !ParseNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) ||
7358 !ExpectSymbol(',', true) ||
7359 !ParseNonNegativeVariant(left, VARIANT_SIDE, nullptr) ||
7360 !ExpectSymbol(')', true))
7361 break;
7363 aImage = newFunction;
7364 return true;
7367 SkipUntil(')');
7368 return false;
7371 // <element>: -moz-element(# <element_id> )
7372 bool
7373 CSSParserImpl::ParseElement(nsCSSValue& aValue)
7375 // A non-iterative for loop to break out when an error occurs.
7376 for (;;) {
7377 if (!GetToken(true))
7378 break;
7380 if (mToken.mType == eCSSToken_ID) {
7381 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element);
7382 } else {
7383 UngetToken();
7384 break;
7387 if (!ExpectSymbol(')', true))
7388 break;
7390 return true;
7393 // If we detect a syntax error, we must match the opening parenthesis of the
7394 // function with the closing parenthesis and skip all the tokens in between.
7395 SkipUntil(')');
7396 return false;
7399 // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
7400 bool
7401 CSSParserImpl::ParseFlex()
7403 // First check for inherit / initial / unset
7404 nsCSSValue tmpVal;
7405 if (ParseVariant(tmpVal, VARIANT_INHERIT, nullptr)) {
7406 AppendValue(eCSSProperty_flex_grow, tmpVal);
7407 AppendValue(eCSSProperty_flex_shrink, tmpVal);
7408 AppendValue(eCSSProperty_flex_basis, tmpVal);
7409 return true;
7412 // Next, check for 'none' == '0 0 auto'
7413 if (ParseVariant(tmpVal, VARIANT_NONE, nullptr)) {
7414 AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number));
7415 AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number));
7416 AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto));
7417 return true;
7420 // OK, try parsing our value as individual per-subproperty components:
7421 // [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
7423 // Each subproperty has a default value that it takes when it's omitted in a
7424 // "flex" shorthand value. These default values are *only* for the shorthand
7425 // syntax -- they're distinct from the subproperties' own initial values. We
7426 // start with each subproperty at its default, as if we had "flex: 1 1 0%".
7427 nsCSSValue flexGrow(1.0f, eCSSUnit_Number);
7428 nsCSSValue flexShrink(1.0f, eCSSUnit_Number);
7429 nsCSSValue flexBasis(0.0f, eCSSUnit_Percent);
7431 // OVERVIEW OF PARSING STRATEGY:
7432 // =============================
7433 // a) Parse the first component as either flex-basis or flex-grow.
7434 // b) If it wasn't flex-grow, parse the _next_ component as flex-grow.
7435 // c) Now we've just parsed flex-grow -- so try parsing the next thing as
7436 // flex-shrink.
7437 // d) Finally: If we didn't get flex-basis at the beginning, try to parse
7438 // it now, at the end.
7440 // More details in each section below.
7442 uint32_t flexBasisVariantMask =
7443 (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT));
7445 // (a) Parse first component. It can be either be a 'flex-basis' value or a
7446 // 'flex-grow' value, so we use the flex-basis-specific variant mask, along
7447 // with VARIANT_NUMBER to accept 'flex-grow' values.
7449 // NOTE: if we encounter unitless 0 here, we *must* interpret it as a
7450 // 'flex-grow' value (a number), *not* as a 'flex-basis' value (a length).
7451 // Conveniently, that's the behavior this combined variant-mask gives us --
7452 // it'll treat unitless 0 as a number. The flexbox spec requires this:
7453 // "a unitless zero that is not already preceded by two flex factors must be
7454 // interpreted as a flex factor.
7455 if (!ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER,
7456 nsCSSProps::kWidthKTable)) {
7457 // First component was not a valid flex-basis or flex-grow value. Fail.
7458 return false;
7461 // Record what we just parsed as either flex-basis or flex-grow:
7462 bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number);
7463 (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal;
7465 // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow.
7466 bool doneParsing = false;
7467 if (wasFirstComponentFlexBasis) {
7468 if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) {
7469 flexGrow = tmpVal;
7470 } else {
7471 // Failed to parse anything after our flex-basis -- that's fine. We can
7472 // skip the remaining parsing.
7473 doneParsing = true;
7477 if (!doneParsing) {
7478 // (c) OK -- the last thing we parsed was flex-grow, so look for a
7479 // flex-shrink in the next position.
7480 if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) {
7481 flexShrink = tmpVal;
7484 // d) Finally: If we didn't get flex-basis at the beginning, try to parse
7485 // it now, at the end.
7487 // NOTE: If we encounter unitless 0 in this final position, we'll parse it
7488 // as a 'flex-basis' value. That's OK, because we know it must have
7489 // been "preceded by 2 flex factors" (justification below), which gets us
7490 // out of the spec's requirement of otherwise having to treat unitless 0
7491 // as a flex factor.
7493 // JUSTIFICATION: How do we know that a unitless 0 here must have been
7494 // preceded by 2 flex factors? Well, suppose we had a unitless 0 that
7495 // was preceded by only 1 flex factor. Then, we would have already
7496 // accepted this unitless 0 as the 'flex-shrink' value, up above (since
7497 // it's a valid flex-shrink value), and we'd have moved on to the next
7498 // token (if any). And of course, if we instead had a unitless 0 preceded
7499 // by *no* flex factors (if it were the first token), we would've already
7500 // parsed it in our very first call to ParseNonNegativeVariant(). So, any
7501 // unitless 0 encountered here *must* have been preceded by 2 flex factors.
7502 if (!wasFirstComponentFlexBasis &&
7503 ParseNonNegativeVariant(tmpVal, flexBasisVariantMask,
7504 nsCSSProps::kWidthKTable)) {
7505 flexBasis = tmpVal;
7509 AppendValue(eCSSProperty_flex_grow, flexGrow);
7510 AppendValue(eCSSProperty_flex_shrink, flexShrink);
7511 AppendValue(eCSSProperty_flex_basis, flexBasis);
7513 return true;
7516 // flex-flow: <flex-direction> || <flex-wrap>
7517 bool
7518 CSSParserImpl::ParseFlexFlow()
7520 static const nsCSSProperty kFlexFlowSubprops[] = {
7521 eCSSProperty_flex_direction,
7522 eCSSProperty_flex_wrap
7524 const size_t numProps = MOZ_ARRAY_LENGTH(kFlexFlowSubprops);
7525 nsCSSValue values[numProps];
7527 int32_t found = ParseChoice(values, kFlexFlowSubprops, numProps);
7529 // Bail if we didn't successfully parse anything
7530 if (found < 1) {
7531 return false;
7534 // If either property didn't get an explicit value, use its initial value.
7535 if ((found & 1) == 0) {
7536 values[0].SetIntValue(NS_STYLE_FLEX_DIRECTION_ROW, eCSSUnit_Enumerated);
7538 if ((found & 2) == 0) {
7539 values[1].SetIntValue(NS_STYLE_FLEX_WRAP_NOWRAP, eCSSUnit_Enumerated);
7542 // Store these values and declare success!
7543 for (size_t i = 0; i < numProps; i++) {
7544 AppendValue(kFlexFlowSubprops[i], values[i]);
7546 return true;
7549 bool
7550 CSSParserImpl::ParseGridAutoFlow()
7552 nsCSSValue value;
7553 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
7554 AppendValue(eCSSProperty_grid_auto_flow, value);
7555 return true;
7558 static const int32_t mask[] = {
7559 NS_STYLE_GRID_AUTO_FLOW_ROW | NS_STYLE_GRID_AUTO_FLOW_COLUMN,
7560 NS_STYLE_GRID_AUTO_FLOW_DENSE | NS_STYLE_GRID_AUTO_FLOW_STACK,
7561 MASK_END_VALUE
7563 if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) {
7564 return false;
7566 int32_t bitField = value.GetIntValue();
7568 // Requires one of these
7569 if (!(bitField & NS_STYLE_GRID_AUTO_FLOW_ROW ||
7570 bitField & NS_STYLE_GRID_AUTO_FLOW_COLUMN ||
7571 bitField & NS_STYLE_GRID_AUTO_FLOW_STACK)) {
7572 return false;
7575 // 'stack' without 'row' or 'column' defaults to 'stack row'
7576 if (bitField == NS_STYLE_GRID_AUTO_FLOW_STACK) {
7577 value.SetIntValue(bitField | NS_STYLE_GRID_AUTO_FLOW_ROW,
7578 eCSSUnit_Enumerated);
7581 AppendValue(eCSSProperty_grid_auto_flow, value);
7582 return true;
7585 CSSParseResult
7586 CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue)
7588 if (!ExpectSymbol('(', true)) {
7589 return CSSParseResult::NotFound;
7591 if (!GetToken(true) || mToken.IsSymbol(')')) {
7592 return CSSParseResult::Ok;
7594 // 'return' so far leaves aValue untouched, to represent an empty list.
7596 nsCSSValueList* item;
7597 if (aValue.GetUnit() == eCSSUnit_List) {
7598 // Find the end of an existing list.
7599 // The grid-template shorthand uses this, at most once for a given list.
7601 // NOTE: we could avoid this traversal by somehow keeping around
7602 // a pointer to the last item from the previous call.
7603 // It's not yet clear if this is worth the additional code complexity.
7604 item = aValue.GetListValue();
7605 while (item->mNext) {
7606 item = item->mNext;
7608 item->mNext = new nsCSSValueList;
7609 item = item->mNext;
7610 } else {
7611 MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "Unexpected unit");
7612 item = aValue.SetListValue();
7614 for (;;) {
7615 if (!(eCSSToken_Ident == mToken.mType &&
7616 ParseCustomIdent(item->mValue, mToken.mIdent))) {
7617 UngetToken();
7618 SkipUntil(')');
7619 return CSSParseResult::Error;
7621 if (!GetToken(true) || mToken.IsSymbol(')')) {
7622 return CSSParseResult::Ok;
7624 item->mNext = new nsCSSValueList;
7625 item = item->mNext;
7629 // Assuming the 'repeat(' function token has already been consumed,
7630 // parse the rest of repeat(<positive-integer>, <line-names>+)
7631 // Append to the linked list whose end is given by |aTailPtr|,
7632 // and updated |aTailPtr| to point to the new end of the list.
7633 bool
7634 CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr)
7636 if (!(GetToken(true) &&
7637 mToken.mType == eCSSToken_Number &&
7638 mToken.mIntegerValid &&
7639 mToken.mInteger > 0)) {
7640 SkipUntil(')');
7641 return false;
7643 int32_t repetitions = std::min(mToken.mInteger,
7644 GRID_TEMPLATE_MAX_REPETITIONS);
7645 if (!ExpectSymbol(',', true)) {
7646 SkipUntil(')');
7647 return false;
7650 // Parse at least one <line-names>
7651 nsCSSValueList* tail = *aTailPtr;
7652 do {
7653 tail->mNext = new nsCSSValueList;
7654 tail = tail->mNext;
7655 if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) {
7656 SkipUntil(')');
7657 return false;
7659 } while (!ExpectSymbol(')', true));
7660 nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext;
7661 nsCSSValueList* lastRepeatedItem = tail;
7663 // Our repeated items are already in the target list once,
7664 // so they need to be repeated |repetitions - 1| more times.
7665 MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
7666 while (--repetitions) {
7667 nsCSSValueList* repeatedItem = firstRepeatedItem;
7668 for (;;) {
7669 tail->mNext = new nsCSSValueList;
7670 tail = tail->mNext;
7671 tail->mValue = repeatedItem->mValue;
7672 if (repeatedItem == lastRepeatedItem) {
7673 break;
7675 repeatedItem = repeatedItem->mNext;
7678 *aTailPtr = tail;
7679 return true;
7682 // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>?
7683 bool
7684 CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
7686 nsCSSValueList* item = aValue.SetListValue();
7687 // This marker distinguishes the value from a <track-list>.
7688 item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID,
7689 eCSSUnit_Enumerated);
7690 for (;;) {
7691 // First try to parse repeat(<positive-integer>, <line-names>+)
7692 if (!GetToken(true)) {
7693 return true;
7695 if (mToken.mType == eCSSToken_Function &&
7696 mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
7697 if (!ParseGridLineNameListRepeat(&item)) {
7698 return false;
7700 } else {
7701 UngetToken();
7703 // This was not a repeat() function. Try to parse <line-names>.
7704 nsCSSValue lineNames;
7705 CSSParseResult result = ParseGridLineNames(lineNames);
7706 if (result == CSSParseResult::NotFound) {
7707 return true;
7709 if (result == CSSParseResult::Error) {
7710 return false;
7712 item->mNext = new nsCSSValueList;
7713 item = item->mNext;
7714 item->mValue = lineNames;
7719 // Parse a <track-breadth>
7720 bool
7721 CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
7723 if (ParseNonNegativeVariant(aValue,
7724 VARIANT_LPCALC | VARIANT_KEYWORD,
7725 nsCSSProps::kGridTrackBreadthKTable)) {
7726 return true;
7729 // Attempt to parse <flex> (a dimension with the "fr" unit)
7730 if (!GetToken(true)) {
7731 return false;
7733 if (!(eCSSToken_Dimension == mToken.mType &&
7734 mToken.mIdent.LowerCaseEqualsLiteral("fr") &&
7735 mToken.mNumber >= 0)) {
7736 UngetToken();
7737 return false;
7739 aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction);
7740 return true;
7743 // Parse a <track-size>
7744 CSSParseResult
7745 CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue)
7747 // Attempt to parse 'auto' or a single <track-breadth>
7748 if (ParseGridTrackBreadth(aValue) ||
7749 ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
7750 return CSSParseResult::Ok;
7753 // Attempt to parse a minmax() function
7754 if (!GetToken(true)) {
7755 return CSSParseResult::NotFound;
7757 if (!(eCSSToken_Function == mToken.mType &&
7758 mToken.mIdent.LowerCaseEqualsLiteral("minmax"))) {
7759 UngetToken();
7760 return CSSParseResult::NotFound;
7762 nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2);
7763 if (ParseGridTrackBreadth(func->Item(1)) &&
7764 ExpectSymbol(',', true) &&
7765 ParseGridTrackBreadth(func->Item(2)) &&
7766 ExpectSymbol(')', true)) {
7767 return CSSParseResult::Ok;
7769 SkipUntil(')');
7770 return CSSParseResult::Error;
7773 bool
7774 CSSParserImpl::ParseGridAutoColumnsRows(nsCSSProperty aPropID)
7776 nsCSSValue value;
7777 if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
7778 ParseGridTrackSize(value) == CSSParseResult::Ok) {
7779 AppendValue(aPropID, value);
7780 return true;
7782 return false;
7785 bool
7786 CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
7787 const nsCSSValue& aFirstLineNames)
7789 nsCSSValueList* firstLineNamesItem = aValue.SetListValue();
7790 firstLineNamesItem->mValue = aFirstLineNames;
7792 // This function is trying to parse <track-list>, which is
7793 // [ <line-names>? [ <track-size> | <repeat()> ] ]+ <line-names>?
7794 // and we're already past the first "<line-names>?".
7796 // Each iteration of the following loop attempts to parse either a
7797 // repeat() or a <track-size> expression, and then an (optional)
7798 // <line-names> expression.
7800 // The only successful exit point from this loop is the ::NotFound
7801 // case after ParseGridTrackSize(); i.e. we'll greedily parse
7802 // repeat()/<track-size> until we can't find one.
7803 nsCSSValueList* item = firstLineNamesItem;
7804 for (;;) {
7805 // First try to parse repeat()
7806 if (!GetToken(true)) {
7807 break;
7809 if (mToken.mType == eCSSToken_Function &&
7810 mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
7811 if (!ParseGridTrackListRepeat(&item)) {
7812 return false;
7814 } else {
7815 UngetToken();
7817 // This was not a repeat() function. Try to parse <track-size>.
7818 nsCSSValue trackSize;
7819 CSSParseResult result = ParseGridTrackSize(trackSize);
7820 if (result == CSSParseResult::Error) {
7821 return false;
7823 if (result == CSSParseResult::NotFound) {
7824 // What we've parsed so far is a valid <track-list>
7825 // (modulo the "at least one <track-size>" check below.)
7826 // Stop here.
7827 break;
7829 item->mNext = new nsCSSValueList;
7830 item = item->mNext;
7831 item->mValue = trackSize;
7833 item->mNext = new nsCSSValueList;
7834 item = item->mNext;
7836 if (ParseGridLineNames(item->mValue) == CSSParseResult::Error) {
7837 return false;
7841 // Require at least one <track-size>.
7842 if (item == firstLineNamesItem) {
7843 return false;
7846 MOZ_ASSERT(aValue.GetListValue() &&
7847 aValue.GetListValue()->mNext &&
7848 aValue.GetListValue()->mNext->mNext,
7849 "<track-list> should have a minimum length of 3");
7850 return true;
7853 // Takes ownership of |aSecond|
7854 static void
7855 ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond)
7857 if (aSecond.GetUnit() == eCSSUnit_Null) {
7858 // Nothing to do.
7859 return;
7861 if (aFirst.GetUnit() == eCSSUnit_Null) {
7862 // Empty or omitted <line-names>. Replace it.
7863 aFirst = aSecond;
7864 return;
7867 // Join the two <line-names> lists.
7868 nsCSSValueList* source = aSecond.GetListValue();
7869 nsCSSValueList* target = aFirst.GetListValue();
7870 // Find the end:
7871 while (target->mNext) {
7872 target = target->mNext;
7874 // Copy the first name. We can't take ownership of it
7875 // as it'll be destroyed when |aSecond| goes out of scope.
7876 target->mNext = new nsCSSValueList;
7877 target = target->mNext;
7878 target->mValue = source->mValue;
7879 // Move the rest of the linked list.
7880 target->mNext = source->mNext;
7881 source->mNext = nullptr;
7884 // Assuming the 'repeat(' function token has already been consumed,
7885 // parse the rest of
7886 // repeat( <positive-integer> ,
7887 // [ <line-names>? <track-size> ]+ <line-names>? )
7888 // Append to the linked list whose end is given by |aTailPtr|,
7889 // and updated |aTailPtr| to point to the new end of the list.
7890 bool
7891 CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
7893 if (!(GetToken(true) &&
7894 mToken.mType == eCSSToken_Number &&
7895 mToken.mIntegerValid &&
7896 mToken.mInteger > 0)) {
7897 SkipUntil(')');
7898 return false;
7900 int32_t repetitions = std::min(mToken.mInteger,
7901 GRID_TEMPLATE_MAX_REPETITIONS);
7902 if (!ExpectSymbol(',', true)) {
7903 SkipUntil(')');
7904 return false;
7907 // Parse [ <line-names>? <track-size> ]+ <line-names>?
7908 // but keep the first and last <line-names> separate
7909 // because they'll need to be joined.
7910 // http://dev.w3.org/csswg/css-grid/#repeat-notation
7911 nsCSSValue firstLineNames;
7912 nsCSSValue trackSize;
7913 nsCSSValue lastLineNames;
7914 // Optional
7915 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) {
7916 SkipUntil(')');
7917 return false;
7919 // Required
7920 if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
7921 SkipUntil(')');
7922 return false;
7924 // Use nsAutoPtr to free the list in case of early return.
7925 nsAutoPtr<nsCSSValueList> firstTrackSizeItemAuto(new nsCSSValueList);
7926 firstTrackSizeItemAuto->mValue = trackSize;
7928 nsCSSValueList* item = firstTrackSizeItemAuto;
7929 for (;;) {
7930 // Optional
7931 if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) {
7932 SkipUntil(')');
7933 return false;
7936 if (ExpectSymbol(')', true)) {
7937 break;
7940 // Required
7941 if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
7942 SkipUntil(')');
7943 return false;
7946 item->mNext = new nsCSSValueList;
7947 item = item->mNext;
7948 item->mValue = lastLineNames;
7949 // Do not append to this list at the next iteration.
7950 lastLineNames.Reset();
7952 item->mNext = new nsCSSValueList;
7953 item = item->mNext;
7954 item->mValue = trackSize;
7956 nsCSSValueList* lastTrackSizeItem = item;
7958 // [ <line-names>? <track-size> ]+ <line-names>? is now parsed into:
7959 // * firstLineNames: the first <line-names>
7960 // * a linked list of odd length >= 1, from firstTrackSizeItem
7961 // (the first <track-size>) to lastTrackSizeItem (the last),
7962 // with the <line-names> sublists in between
7963 // * lastLineNames: the last <line-names>
7966 // Join the last and first <line-names> (in that order.)
7967 // For example, repeat(3, (a) 100px (b) 200px (c)) results in
7968 // (a) 100px (b) 200px (c a) 100px (b) 200px (c a) 100px (b) 200px (c)
7969 // This is (c a).
7970 // Make deep copies: the originals will be moved.
7971 nsCSSValue joinerLineNames;
7973 nsCSSValueList* target = nullptr;
7974 if (lastLineNames.GetUnit() != eCSSUnit_Null) {
7975 target = joinerLineNames.SetListValue();
7976 nsCSSValueList* source = lastLineNames.GetListValue();
7977 for (;;) {
7978 target->mValue = source->mValue;
7979 source = source->mNext;
7980 if (!source) {
7981 break;
7983 target->mNext = new nsCSSValueList;
7984 target = target->mNext;
7988 if (firstLineNames.GetUnit() != eCSSUnit_Null) {
7989 if (target) {
7990 target->mNext = new nsCSSValueList;
7991 target = target->mNext;
7992 } else {
7993 target = joinerLineNames.SetListValue();
7995 nsCSSValueList* source = firstLineNames.GetListValue();
7996 for (;;) {
7997 target->mValue = source->mValue;
7998 source = source->mNext;
7999 if (!source) {
8000 break;
8002 target->mNext = new nsCSSValueList;
8003 target = target->mNext;
8008 // Join our first <line-names> with the one before repeat().
8009 // (a) repeat(1, (b) 20px) expands to (a b) 20px
8010 nsCSSValueList* previousItemBeforeRepeat = *aTailPtr;
8011 ConcatLineNames(previousItemBeforeRepeat->mValue, firstLineNames);
8013 // Move our linked list
8014 // (first to last <track-size>, with the <line-names> sublists in between).
8015 // This is the first repetition.
8016 NS_ASSERTION(previousItemBeforeRepeat->mNext == nullptr,
8017 "Expected the end of a linked list");
8018 previousItemBeforeRepeat->mNext = firstTrackSizeItemAuto.forget();
8019 nsCSSValueList* firstTrackSizeItem = previousItemBeforeRepeat->mNext;
8020 nsCSSValueList* tail = lastTrackSizeItem;
8022 // Repeat |repetitions - 1| more times:
8023 // * the joiner <line-names>
8024 // * the linked list
8025 // (first to last <track-size>, with the <line-names> sublists in between)
8026 MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
8027 while (--repetitions) {
8028 tail->mNext = new nsCSSValueList;
8029 tail = tail->mNext;
8030 tail->mValue = joinerLineNames;
8032 nsCSSValueList* repeatedItem = firstTrackSizeItem;
8033 for (;;) {
8034 tail->mNext = new nsCSSValueList;
8035 tail = tail->mNext;
8036 tail->mValue = repeatedItem->mValue;
8037 if (repeatedItem == lastTrackSizeItem) {
8038 break;
8040 repeatedItem = repeatedItem->mNext;
8044 // Finally, move our last <line-names>.
8045 // Any <line-names> immediately after repeat() will append to it.
8046 tail->mNext = new nsCSSValueList;
8047 tail = tail->mNext;
8048 tail->mValue = lastLineNames;
8050 *aTailPtr = tail;
8051 return true;
8054 bool
8055 CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSProperty aPropID)
8057 nsCSSValue value;
8058 if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
8059 AppendValue(aPropID, value);
8060 return true;
8063 nsSubstring* ident = NextIdent();
8064 if (ident) {
8065 if (ident->LowerCaseEqualsLiteral("subgrid")) {
8066 if (!ParseOptionalLineNameListAfterSubgrid(value)) {
8067 return false;
8069 AppendValue(aPropID, value);
8070 return true;
8072 UngetToken();
8075 nsCSSValue firstLineNames;
8076 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
8077 !ParseGridTrackListWithFirstLineNames(value, firstLineNames)) {
8078 return false;
8080 AppendValue(aPropID, value);
8081 return true;
8084 bool
8085 CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput,
8086 css::GridTemplateAreasValue* aAreas,
8087 nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices)
8089 aAreas->mTemplates.AppendElement(mToken.mIdent);
8091 nsCSSGridTemplateAreaScanner scanner(aInput);
8092 nsCSSGridTemplateAreaToken token;
8093 css::GridNamedArea* currentArea = nullptr;
8094 uint32_t row = aAreas->NRows();
8095 // Column numbers starts at 1, but we might not have any, eg
8096 // grid-template-areas:""; which will result in mNColumns == 0.
8097 uint32_t column = 0;
8098 while (scanner.Next(token)) {
8099 ++column;
8100 if (token.isTrash) {
8101 return false;
8103 if (currentArea) {
8104 if (token.mName == currentArea->mName) {
8105 if (currentArea->mRowStart == row) {
8106 // Next column in the first row of this named area.
8107 currentArea->mColumnEnd++;
8109 continue;
8111 // We're exiting |currentArea|, so currentArea is ending at |column|.
8112 // Make sure that this is consistent with currentArea on previous rows:
8113 if (currentArea->mColumnEnd != column) {
8114 NS_ASSERTION(currentArea->mRowStart != row,
8115 "Inconsistent column end for the first row of a named area.");
8116 // Not a rectangle
8117 return false;
8119 currentArea = nullptr;
8121 if (!token.mName.IsEmpty()) {
8122 // Named cell that doesn't have a cell with the same name on its left.
8124 // Check if this is the continuation of an existing named area:
8125 uint32_t index;
8126 if (aAreaIndices.Get(token.mName, &index)) {
8127 MOZ_ASSERT(index < aAreas->mNamedAreas.Length(),
8128 "Invalid aAreaIndices hash table");
8129 currentArea = &aAreas->mNamedAreas[index];
8130 if (currentArea->mColumnStart != column ||
8131 currentArea->mRowEnd != row) {
8132 // Existing named area, but not forming a rectangle
8133 return false;
8135 // Next row of an existing named area
8136 currentArea->mRowEnd++;
8137 } else {
8138 // New named area
8139 aAreaIndices.Put(token.mName, aAreas->mNamedAreas.Length());
8140 currentArea = aAreas->mNamedAreas.AppendElement();
8141 currentArea->mName = token.mName;
8142 // For column or row N (starting at 1),
8143 // the start line is N, the end line is N + 1
8144 currentArea->mColumnStart = column;
8145 currentArea->mColumnEnd = column + 1;
8146 currentArea->mRowStart = row;
8147 currentArea->mRowEnd = row + 1;
8151 if (currentArea && currentArea->mColumnEnd != column + 1) {
8152 NS_ASSERTION(currentArea->mRowStart != row,
8153 "Inconsistent column end for the first row of a named area.");
8154 // Not a rectangle
8155 return false;
8158 // On the first row, set the number of columns
8159 // that grid-template-areas contributes to the explicit grid.
8160 // On other rows, check that the number of columns is consistent
8161 // between rows.
8162 if (row == 1) {
8163 aAreas->mNColumns = column;
8164 } else if (aAreas->mNColumns != column) {
8165 return false;
8167 return true;
8170 bool
8171 CSSParserImpl::ParseGridTemplateAreas()
8173 nsCSSValue value;
8174 if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
8175 AppendValue(eCSSProperty_grid_template_areas, value);
8176 return true;
8179 nsRefPtr<css::GridTemplateAreasValue> areas =
8180 new css::GridTemplateAreasValue();
8181 nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
8182 for (;;) {
8183 if (!GetToken(true)) {
8184 break;
8186 if (eCSSToken_String != mToken.mType) {
8187 UngetToken();
8188 break;
8190 if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
8191 return false;
8195 if (areas->NRows() == 0) {
8196 return false;
8199 AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
8200 return true;
8203 bool
8204 CSSParserImpl::ParseGridTemplate()
8206 // none |
8207 // subgrid |
8208 // <'grid-template-columns'> / <'grid-template-rows'> |
8209 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
8210 nsCSSValue value;
8211 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
8212 AppendValue(eCSSProperty_grid_template_areas, value);
8213 AppendValue(eCSSProperty_grid_template_columns, value);
8214 AppendValue(eCSSProperty_grid_template_rows, value);
8215 return true;
8218 // TODO (bug 983175): add parsing for 'subgrid' by itself
8220 // 'none' can appear either by itself,
8221 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
8222 if (ParseVariant(value, VARIANT_NONE, nullptr)) {
8223 AppendValue(eCSSProperty_grid_template_columns, value);
8224 if (ExpectSymbol('/', true)) {
8225 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
8227 AppendValue(eCSSProperty_grid_template_areas, value);
8228 AppendValue(eCSSProperty_grid_template_rows, value);
8229 return true;
8232 // 'subgrid' can appear either by itself,
8233 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
8234 nsSubstring* ident = NextIdent();
8235 if (ident) {
8236 if (ident->LowerCaseEqualsLiteral("subgrid")) {
8237 if (!ParseOptionalLineNameListAfterSubgrid(value)) {
8238 return false;
8240 AppendValue(eCSSProperty_grid_template_columns, value);
8241 if (ExpectSymbol('/', true)) {
8242 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
8244 if (value.GetListValue()->mNext) {
8245 // Non-empty <line-name-list> after 'subgrid'.
8246 // This is only valid as part of <'grid-template-columns'>,
8247 // which must be followed by a slash.
8248 return false;
8250 // 'subgrid' by itself sets both grid-template-columns
8251 // and grid-template-rows.
8252 AppendValue(eCSSProperty_grid_template_rows, value);
8253 value.SetNoneValue();
8254 AppendValue(eCSSProperty_grid_template_areas, value);
8255 return true;
8257 UngetToken();
8260 // [ <line-names>? ] here is ambiguous:
8261 // it can be either the start of a <track-list>,
8262 // or the start of [ <line-names>? <string> <track-size>? <line-names>? ]+
8263 nsCSSValue firstLineNames;
8264 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
8265 !GetToken(true)) {
8266 return false;
8268 if (mToken.mType == eCSSToken_String) {
8269 // [ <track-list> / ]? was omitted
8270 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
8271 value.SetNoneValue();
8272 AppendValue(eCSSProperty_grid_template_columns, value);
8273 return ParseGridTemplateAfterString(firstLineNames);
8275 UngetToken();
8277 if (!(ParseGridTrackListWithFirstLineNames(value, firstLineNames) &&
8278 ExpectSymbol('/', true))) {
8279 return false;
8281 AppendValue(eCSSProperty_grid_template_columns, value);
8282 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ true);
8285 // Helper for parsing the 'grid-template' shorthand
8287 // NOTE: This parses the portion after the slash, for *one* of the
8288 // following types of expressions:
8289 // - <'grid-template-columns'> / <'grid-template-rows'>
8290 // - <track-list> / [ <line-names>? <string> <track-size>? <line-names>? ]+
8292 // We don't know which type of expression we've got until we've parsed the
8293 // second half, since the pre-slash part is ambiguous. The various return
8294 // clauses below are labeled with the type of expression they're completing.
8295 bool
8296 CSSParserImpl::ParseGridTemplateAfterSlash(bool aColumnsIsTrackList)
8298 nsCSSValue rowsValue;
8299 if (ParseVariant(rowsValue, VARIANT_NONE, nullptr)) {
8300 // <'grid-template-columns'> / <'grid-template-rows'>
8301 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
8302 nsCSSValue areasValue(eCSSUnit_None); // implied
8303 AppendValue(eCSSProperty_grid_template_areas, areasValue);
8304 return true;
8307 nsSubstring* ident = NextIdent();
8308 if (ident) {
8309 if (ident->LowerCaseEqualsLiteral("subgrid")) {
8310 if (!ParseOptionalLineNameListAfterSubgrid(rowsValue)) {
8311 return false;
8313 // <'grid-template-columns'> / <'grid-template-rows'>
8314 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
8315 nsCSSValue areasValue(eCSSUnit_None); // implied
8316 AppendValue(eCSSProperty_grid_template_areas, areasValue);
8317 return true;
8319 UngetToken();
8322 nsCSSValue firstLineNames;
8323 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
8324 !GetToken(true)) {
8325 return false;
8327 if (aColumnsIsTrackList && mToken.mType == eCSSToken_String) {
8328 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
8329 return ParseGridTemplateAfterString(firstLineNames);
8331 UngetToken();
8333 if (!ParseGridTrackListWithFirstLineNames(rowsValue, firstLineNames)) {
8334 return false;
8337 // <'grid-template-columns'> / <'grid-template-rows'>
8338 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
8339 nsCSSValue areasValue(eCSSUnit_None); // implied
8340 AppendValue(eCSSProperty_grid_template_areas, areasValue);
8341 return true;
8344 // Helper for parsing the 'grid-template' shorthand:
8345 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
8346 // with a <line-names>? already consumed, stored in |aFirstLineNames|,
8347 // and the current token a <string>
8348 bool
8349 CSSParserImpl::ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames)
8351 MOZ_ASSERT(mToken.mType == eCSSToken_String,
8352 "ParseGridTemplateAfterString called with a non-string token");
8354 nsCSSValue rowsValue;
8355 nsRefPtr<css::GridTemplateAreasValue> areas =
8356 new css::GridTemplateAreasValue();
8357 nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
8358 nsCSSValueList* rowsItem = rowsValue.SetListValue();
8359 rowsItem->mValue = aFirstLineNames;
8361 for (;;) {
8362 if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
8363 return false;
8366 rowsItem->mNext = new nsCSSValueList;
8367 rowsItem = rowsItem->mNext;
8368 CSSParseResult result = ParseGridTrackSize(rowsItem->mValue);
8369 if (result == CSSParseResult::Error) {
8370 return false;
8372 if (result == CSSParseResult::NotFound) {
8373 rowsItem->mValue.SetAutoValue();
8376 rowsItem->mNext = new nsCSSValueList;
8377 rowsItem = rowsItem->mNext;
8378 result = ParseGridLineNames(rowsItem->mValue);
8379 if (result == CSSParseResult::Error) {
8380 return false;
8382 if (result == CSSParseResult::Ok) {
8383 // Append to the same list as the previous call to ParseGridLineNames.
8384 result = ParseGridLineNames(rowsItem->mValue);
8385 if (result == CSSParseResult::Error) {
8386 return false;
8388 if (result == CSSParseResult::Ok) {
8389 // Parsed <line-name> twice.
8390 // The property value can not end here, we expect a string next.
8391 if (!GetToken(true)) {
8392 return false;
8394 if (eCSSToken_String != mToken.mType) {
8395 UngetToken();
8396 return false;
8398 continue;
8402 // Did not find a <line-names>.
8403 // Next, we expect either a string or the end of the property value.
8404 if (!GetToken(true)) {
8405 break;
8407 if (eCSSToken_String != mToken.mType) {
8408 UngetToken();
8409 break;
8413 AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
8414 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
8415 return true;
8418 // <'grid-template'> |
8419 // [ <'grid-auto-flow'> [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]? ]
8420 bool
8421 CSSParserImpl::ParseGrid()
8423 nsCSSValue value;
8424 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
8425 for (const nsCSSProperty* subprops =
8426 nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid);
8427 *subprops != eCSSProperty_UNKNOWN; ++subprops) {
8428 AppendValue(*subprops, value);
8430 return true;
8433 // An empty value is always invalid.
8434 if (!GetToken(true)) {
8435 return false;
8438 // The values starts with a <'grid-auto-flow'> if and only if
8439 // it starts with a 'stack', 'dense', 'column' or 'row' keyword.
8440 if (mToken.mType == eCSSToken_Ident) {
8441 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
8442 if (keyword == eCSSKeyword_stack ||
8443 keyword == eCSSKeyword_dense ||
8444 keyword == eCSSKeyword_column ||
8445 keyword == eCSSKeyword_row) {
8446 UngetToken();
8447 return ParseGridAutoFlow() && ParseGridShorthandAutoProps();
8450 UngetToken();
8452 // Set other subproperties to their initial values
8453 // and parse <'grid-template'>.
8454 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_ROW, eCSSUnit_Enumerated);
8455 AppendValue(eCSSProperty_grid_auto_flow, value);
8456 value.SetAutoValue();
8457 AppendValue(eCSSProperty_grid_auto_columns, value);
8458 AppendValue(eCSSProperty_grid_auto_rows, value);
8459 return ParseGridTemplate();
8462 // Parse [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]?
8463 // for the 'grid' shorthand.
8464 // Assumes that <'grid-auto-flow'> was already parsed by the caller.
8465 bool
8466 CSSParserImpl::ParseGridShorthandAutoProps()
8468 nsCSSValue autoColumnsValue;
8469 nsCSSValue autoRowsValue;
8470 CSSParseResult result = ParseGridTrackSize(autoColumnsValue);
8471 if (result == CSSParseResult::Error) {
8472 return false;
8474 if (result == CSSParseResult::NotFound) {
8475 autoColumnsValue.SetAutoValue();
8476 autoRowsValue.SetAutoValue();
8477 } else {
8478 if (!ExpectSymbol('/', true)) {
8479 autoRowsValue.SetAutoValue();
8480 } else if (ParseGridTrackSize(autoRowsValue) != CSSParseResult::Ok) {
8481 return false;
8484 AppendValue(eCSSProperty_grid_auto_columns, autoColumnsValue);
8485 AppendValue(eCSSProperty_grid_auto_rows, autoRowsValue);
8486 nsCSSValue templateValue(eCSSUnit_None); // Initial values
8487 AppendValue(eCSSProperty_grid_template_areas, templateValue);
8488 AppendValue(eCSSProperty_grid_template_columns, templateValue);
8489 AppendValue(eCSSProperty_grid_template_rows, templateValue);
8490 return true;
8493 // Parse a <grid-line>.
8494 // If successful, set aValue to eCSSUnit_Auto,
8495 // or a eCSSUnit_List containing, in that order:
8497 // * An optional eCSSUnit_Enumerated marking a "span" keyword.
8498 // * An optional eCSSUnit_Integer
8499 // * An optional eCSSUnit_Ident
8501 // At least one of eCSSUnit_Integer or eCSSUnit_Ident is present.
8502 bool
8503 CSSParserImpl::ParseGridLine(nsCSSValue& aValue)
8505 // <grid-line> =
8506 // auto |
8507 // <custom-ident> |
8508 // [ <integer> && <custom-ident>? ] |
8509 // [ span && [ <integer> || <custom-ident> ] ]
8511 // Syntactically, this simplifies to:
8513 // <grid-line> =
8514 // auto |
8515 // [ span? && [ <integer> || <custom-ident> ] ]
8517 if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
8518 return true;
8521 static const nsCSSKeyword kGridLineKeywords[] = {
8522 eCSSKeyword_span,
8523 eCSSKeyword_UNKNOWN // End-of-array marker
8525 bool hasSpan = false;
8526 bool hasIdent = false;
8527 Maybe<int32_t> integer;
8528 nsCSSValue ident;
8530 if (!GetToken(true)) {
8531 return false;
8533 if (mToken.mType == eCSSToken_Ident &&
8534 mToken.mIdent.LowerCaseEqualsLiteral("span")) {
8535 hasSpan = true;
8536 if (!GetToken(true)) {
8537 return false;
8541 do {
8542 if (!hasIdent &&
8543 mToken.mType == eCSSToken_Ident &&
8544 ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) {
8545 hasIdent = true;
8546 } else if (integer.isNothing() &&
8547 mToken.mType == eCSSToken_Number &&
8548 mToken.mIntegerValid &&
8549 mToken.mInteger != 0) {
8550 integer.emplace(mToken.mInteger);
8551 } else {
8552 UngetToken();
8553 break;
8555 } while (!(integer.isSome() && hasIdent) && GetToken(true));
8557 // Require at least one of <integer> or <custom-ident>
8558 if (!(integer.isSome() || hasIdent)) {
8559 return false;
8562 if (!hasSpan && GetToken(true)) {
8563 if (mToken.mType == eCSSToken_Ident &&
8564 mToken.mIdent.LowerCaseEqualsLiteral("span")) {
8565 hasSpan = true;
8566 } else {
8567 UngetToken();
8571 nsCSSValueList* item = aValue.SetListValue();
8572 if (hasSpan) {
8573 // Given "span", a negative <integer> is invalid.
8574 if (integer.isSome() && integer.ref() < 0) {
8575 return false;
8577 // '1' here is a dummy value.
8578 // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword.
8579 item->mValue.SetIntValue(1, eCSSUnit_Enumerated);
8580 item->mNext = new nsCSSValueList;
8581 item = item->mNext;
8583 if (integer.isSome()) {
8584 item->mValue.SetIntValue(integer.ref(), eCSSUnit_Integer);
8585 if (hasIdent) {
8586 item->mNext = new nsCSSValueList;
8587 item = item->mNext;
8590 if (hasIdent) {
8591 item->mValue = ident;
8593 return true;
8596 bool
8597 CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSProperty aPropID)
8599 nsCSSValue value;
8600 if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
8601 ParseGridLine(value)) {
8602 AppendValue(aPropID, value);
8603 return true;
8605 return false;
8608 // If |aFallback| is a List containing a single Ident, set |aValue| to that.
8609 // Otherwise, set |aValue| to Auto.
8610 // Used with |aFallback| from ParseGridLine()
8611 static void
8612 HandleGridLineFallback(const nsCSSValue& aFallback, nsCSSValue& aValue)
8614 if (aFallback.GetUnit() == eCSSUnit_List &&
8615 aFallback.GetListValue()->mValue.GetUnit() == eCSSUnit_Ident &&
8616 !aFallback.GetListValue()->mNext) {
8617 aValue = aFallback;
8618 } else {
8619 aValue.SetAutoValue();
8623 bool
8624 CSSParserImpl::ParseGridColumnRow(nsCSSProperty aStartPropID,
8625 nsCSSProperty aEndPropID)
8627 nsCSSValue value;
8628 nsCSSValue secondValue;
8629 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
8630 AppendValue(aStartPropID, value);
8631 AppendValue(aEndPropID, value);
8632 return true;
8635 if (!ParseGridLine(value)) {
8636 return false;
8638 if (GetToken(true)) {
8639 if (mToken.IsSymbol('/')) {
8640 if (ParseGridLine(secondValue)) {
8641 AppendValue(aStartPropID, value);
8642 AppendValue(aEndPropID, secondValue);
8643 return true;
8644 } else {
8645 return false;
8648 UngetToken();
8651 // A single <custom-ident> is repeated to both properties,
8652 // anything else sets the grid-{column,row}-end property to 'auto'.
8653 HandleGridLineFallback(value, secondValue);
8655 AppendValue(aStartPropID, value);
8656 AppendValue(aEndPropID, secondValue);
8657 return true;
8660 bool
8661 CSSParserImpl::ParseGridArea()
8663 nsCSSValue values[4];
8664 if (ParseVariant(values[0], VARIANT_INHERIT, nullptr)) {
8665 AppendValue(eCSSProperty_grid_row_start, values[0]);
8666 AppendValue(eCSSProperty_grid_column_start, values[0]);
8667 AppendValue(eCSSProperty_grid_row_end, values[0]);
8668 AppendValue(eCSSProperty_grid_column_end, values[0]);
8669 return true;
8672 int32_t i = 0;
8673 for (;;) {
8674 if (!ParseGridLine(values[i])) {
8675 return false;
8677 if (++i == 4 || !GetToken(true)) {
8678 break;
8680 if (!mToken.IsSymbol('/')) {
8681 UngetToken();
8682 break;
8686 MOZ_ASSERT(i >= 1, "should have parsed at least one grid-line (or returned)");
8687 if (i < 2) {
8688 HandleGridLineFallback(values[0], values[1]);
8690 if (i < 3) {
8691 HandleGridLineFallback(values[0], values[2]);
8693 if (i < 4) {
8694 HandleGridLineFallback(values[1], values[3]);
8697 AppendValue(eCSSProperty_grid_row_start, values[0]);
8698 AppendValue(eCSSProperty_grid_column_start, values[1]);
8699 AppendValue(eCSSProperty_grid_row_end, values[2]);
8700 AppendValue(eCSSProperty_grid_column_end, values[3]);
8701 return true;
8704 // <color-stop> : <color> [ <percentage> | <length> ]?
8705 bool
8706 CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
8708 nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
8709 if (!ParseVariant(stop->mColor, VARIANT_COLOR, nullptr)) {
8710 return false;
8713 // Stop positions do not have to fall between the starting-point and
8714 // ending-point, so we don't use ParseNonNegativeVariant.
8715 if (!ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr)) {
8716 stop->mLocation.SetNoneValue();
8718 return true;
8721 // <gradient>
8722 // : linear-gradient( <linear-gradient-line>? <color-stops> ')'
8723 // | radial-gradient( <radial-gradient-line>? <color-stops> ')'
8725 // <linear-gradient-line> : [ to [left | right] || [top | bottom] ] ,
8726 // | <legacy-gradient-line>
8727 // <radial-gradient-line> : [ <shape> || <size> ] [ at <position> ]? ,
8728 // | [ at <position> ] ,
8729 // | <legacy-gradient-line>? <legacy-shape-size>?
8730 // <shape> : circle | ellipse
8731 // <size> : closest-side | closest-corner | farthest-side | farthest-corner
8732 // | <length> | [<length> | <percentage>]{2}
8734 // <legacy-gradient-line> : [ <position> || <angle>] ,
8736 // <legacy-shape-size> : [ <shape> || <legacy-size> ] ,
8737 // <legacy-size> : closest-side | closest-corner | farthest-side
8738 // | farthest-corner | contain | cover
8740 // <color-stops> : <color-stop> , <color-stop> [, <color-stop>]*
8741 bool
8742 CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating,
8743 bool aIsLegacy)
8745 nsRefPtr<nsCSSValueGradient> cssGradient
8746 = new nsCSSValueGradient(false, aIsRepeating);
8748 if (!GetToken(true)) {
8749 return false;
8752 if (mToken.mType == eCSSToken_Ident &&
8753 mToken.mIdent.LowerCaseEqualsLiteral("to")) {
8755 // "to" syntax doesn't allow explicit "center"
8756 if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) {
8757 SkipUntil(')');
8758 return false;
8761 // [ to [left | right] || [top | bottom] ] ,
8762 const nsCSSValue& xValue = cssGradient->mBgPos.mXValue;
8763 const nsCSSValue& yValue = cssGradient->mBgPos.mYValue;
8764 if (xValue.GetUnit() != eCSSUnit_Enumerated ||
8765 !(xValue.GetIntValue() & (NS_STYLE_BG_POSITION_LEFT |
8766 NS_STYLE_BG_POSITION_CENTER |
8767 NS_STYLE_BG_POSITION_RIGHT)) ||
8768 yValue.GetUnit() != eCSSUnit_Enumerated ||
8769 !(yValue.GetIntValue() & (NS_STYLE_BG_POSITION_TOP |
8770 NS_STYLE_BG_POSITION_CENTER |
8771 NS_STYLE_BG_POSITION_BOTTOM))) {
8772 SkipUntil(')');
8773 return false;
8776 if (!ExpectSymbol(',', true)) {
8777 SkipUntil(')');
8778 return false;
8781 return ParseGradientColorStops(cssGradient, aValue);
8784 if (!aIsLegacy) {
8785 UngetToken();
8787 // <angle> ,
8788 if (ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) &&
8789 !ExpectSymbol(',', true)) {
8790 SkipUntil(')');
8791 return false;
8794 return ParseGradientColorStops(cssGradient, aValue);
8797 nsCSSTokenType ty = mToken.mType;
8798 nsString id = mToken.mIdent;
8799 UngetToken();
8801 // <legacy-gradient-line>
8802 bool haveGradientLine = IsLegacyGradientLine(ty, id);
8803 if (haveGradientLine) {
8804 cssGradient->mIsLegacySyntax = true;
8805 bool haveAngle =
8806 ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
8808 // if we got an angle, we might now have a comma, ending the gradient-line
8809 if (!haveAngle || !ExpectSymbol(',', true)) {
8810 if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
8811 SkipUntil(')');
8812 return false;
8815 if (!ExpectSymbol(',', true) &&
8816 // if we didn't already get an angle, we might have one now,
8817 // otherwise it's an error
8818 (haveAngle ||
8819 !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) ||
8820 // now we better have a comma
8821 !ExpectSymbol(',', true))) {
8822 SkipUntil(')');
8823 return false;
8828 return ParseGradientColorStops(cssGradient, aValue);
8831 bool
8832 CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating,
8833 bool aIsLegacy)
8835 nsRefPtr<nsCSSValueGradient> cssGradient
8836 = new nsCSSValueGradient(true, aIsRepeating);
8838 // [ <shape> || <size> ]
8839 bool haveShape =
8840 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
8841 nsCSSProps::kRadialGradientShapeKTable);
8843 bool haveSize = ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
8844 aIsLegacy ?
8845 nsCSSProps::kRadialGradientLegacySizeKTable :
8846 nsCSSProps::kRadialGradientSizeKTable);
8847 if (haveSize) {
8848 if (!haveShape) {
8849 // <size> <shape>
8850 haveShape = ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
8851 nsCSSProps::kRadialGradientShapeKTable);
8853 } else if (!aIsLegacy) {
8854 // Save RadialShape before parsing RadiusX because RadialShape and
8855 // RadiusX share the storage.
8856 int32_t shape =
8857 cssGradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated ?
8858 cssGradient->GetRadialShape().GetIntValue() : -1;
8859 // <length> | [<length> | <percentage>]{2}
8860 cssGradient->mIsExplicitSize = true;
8861 haveSize =
8862 ParseNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP, nullptr);
8863 if (!haveSize) {
8864 // It was not an explicit size after all.
8865 // Note that ParseNonNegativeVariant may have put something
8866 // invalid into our storage, but only in the case where it was
8867 // rejected only for being negative. Since this means the token
8868 // was a length or a percentage, we know it's not valid syntax
8869 // (which must be a comma, the 'at' keyword, or a color), so we
8870 // know this value will be dropped. This means it doesn't matter
8871 // that we have something invalid in our storage.
8872 cssGradient->mIsExplicitSize = false;
8873 } else {
8874 // vertical extent is optional
8875 bool haveYSize =
8876 ParseNonNegativeVariant(cssGradient->GetRadiusY(), VARIANT_LP, nullptr);
8877 if (!haveShape) {
8878 nsCSSValue shapeValue;
8879 haveShape = ParseVariant(shapeValue, VARIANT_KEYWORD,
8880 nsCSSProps::kRadialGradientShapeKTable);
8881 if (haveShape) {
8882 shape = shapeValue.GetIntValue();
8885 if (haveYSize
8886 ? shape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR
8887 : cssGradient->GetRadiusX().GetUnit() == eCSSUnit_Percent ||
8888 shape == NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL) {
8889 SkipUntil(')');
8890 return false;
8895 if ((haveShape || haveSize) && ExpectSymbol(',', true)) {
8896 // [ <shape> || <size> ] ,
8897 return ParseGradientColorStops(cssGradient, aValue);
8900 if (!GetToken(true)) {
8901 return false;
8904 if (!aIsLegacy) {
8905 if (mToken.mType == eCSSToken_Ident &&
8906 mToken.mIdent.LowerCaseEqualsLiteral("at")) {
8907 // [ <shape> || <size> ]? at <position> ,
8908 if (!ParseBoxPositionValues(cssGradient->mBgPos, false) ||
8909 !ExpectSymbol(',', true)) {
8910 SkipUntil(')');
8911 return false;
8914 return ParseGradientColorStops(cssGradient, aValue);
8917 // <color-stops> only
8918 UngetToken();
8919 return ParseGradientColorStops(cssGradient, aValue);
8921 MOZ_ASSERT(!cssGradient->mIsExplicitSize);
8923 nsCSSTokenType ty = mToken.mType;
8924 nsString id = mToken.mIdent;
8925 UngetToken();
8927 // <legacy-gradient-line>
8928 bool haveGradientLine = false;
8929 // if we already encountered a shape or size,
8930 // we can not have a gradient-line in legacy syntax
8931 if (!haveShape && !haveSize) {
8932 haveGradientLine = IsLegacyGradientLine(ty, id);
8934 if (haveGradientLine) {
8935 bool haveAngle =
8936 ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
8938 // if we got an angle, we might now have a comma, ending the gradient-line
8939 if (!haveAngle || !ExpectSymbol(',', true)) {
8940 if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
8941 SkipUntil(')');
8942 return false;
8945 if (!ExpectSymbol(',', true) &&
8946 // if we didn't already get an angle, we might have one now,
8947 // otherwise it's an error
8948 (haveAngle ||
8949 !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) ||
8950 // now we better have a comma
8951 !ExpectSymbol(',', true))) {
8952 SkipUntil(')');
8953 return false;
8957 if (cssGradient->mAngle.GetUnit() != eCSSUnit_None) {
8958 cssGradient->mIsLegacySyntax = true;
8962 // radial gradients might have a shape and size here for legacy syntax
8963 if (!haveShape && !haveSize) {
8964 haveShape =
8965 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
8966 nsCSSProps::kRadialGradientShapeKTable);
8967 haveSize =
8968 ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
8969 nsCSSProps::kRadialGradientLegacySizeKTable);
8971 // could be in either order
8972 if (!haveShape) {
8973 haveShape =
8974 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
8975 nsCSSProps::kRadialGradientShapeKTable);
8979 if ((haveShape || haveSize) && !ExpectSymbol(',', true)) {
8980 SkipUntil(')');
8981 return false;
8984 return ParseGradientColorStops(cssGradient, aValue);
8987 bool
8988 CSSParserImpl::IsLegacyGradientLine(const nsCSSTokenType& aType,
8989 const nsString& aId)
8991 // N.B. ParseBoxPositionValues is not guaranteed to put back
8992 // everything it scanned if it fails, so we must only call it
8993 // if there is no alternative to consuming a <box-position>.
8994 // ParseVariant, as used here, will either succeed and consume
8995 // a single token, or fail and consume none, so we can be more
8996 // cavalier about calling it.
8998 bool haveGradientLine = false;
8999 switch (aType) {
9000 case eCSSToken_Percentage:
9001 case eCSSToken_Number:
9002 case eCSSToken_Dimension:
9003 haveGradientLine = true;
9004 break;
9006 case eCSSToken_Function:
9007 if (aId.LowerCaseEqualsLiteral("calc") ||
9008 aId.LowerCaseEqualsLiteral("-moz-calc")) {
9009 haveGradientLine = true;
9010 break;
9012 // fall through
9013 case eCSSToken_ID:
9014 case eCSSToken_Hash:
9015 // this is a color
9016 break;
9018 case eCSSToken_Ident: {
9019 // This is only a gradient line if it's a box position keyword.
9020 nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(aId);
9021 int32_t junk;
9022 if (kw != eCSSKeyword_UNKNOWN &&
9023 nsCSSProps::FindKeyword(kw, nsCSSProps::kBackgroundPositionKTable,
9024 junk)) {
9025 haveGradientLine = true;
9027 break;
9030 default:
9031 // error
9032 break;
9035 return haveGradientLine;
9038 bool
9039 CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient,
9040 nsCSSValue& aValue)
9042 // At least two color stops are required
9043 if (!ParseColorStop(aGradient) ||
9044 !ExpectSymbol(',', true) ||
9045 !ParseColorStop(aGradient)) {
9046 SkipUntil(')');
9047 return false;
9050 // Additional color stops
9051 while (ExpectSymbol(',', true)) {
9052 if (!ParseColorStop(aGradient)) {
9053 SkipUntil(')');
9054 return false;
9058 if (!ExpectSymbol(')', true)) {
9059 SkipUntil(')');
9060 return false;
9063 aValue.SetGradientValue(aGradient);
9064 return true;
9067 int32_t
9068 CSSParserImpl::ParseChoice(nsCSSValue aValues[],
9069 const nsCSSProperty aPropIDs[], int32_t aNumIDs)
9071 int32_t found = 0;
9072 nsAutoParseCompoundProperty compound(this);
9074 int32_t loop;
9075 for (loop = 0; loop < aNumIDs; loop++) {
9076 // Try each property parser in order
9077 int32_t hadFound = found;
9078 int32_t index;
9079 for (index = 0; index < aNumIDs; index++) {
9080 int32_t bit = 1 << index;
9081 if ((found & bit) == 0) {
9082 if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) {
9083 found |= bit;
9084 // It's more efficient to break since it will reset |hadFound|
9085 // to |found|. Furthermore, ParseListStyle depends on our going
9086 // through the properties in order for each value..
9087 break;
9091 if (found == hadFound) { // found nothing new
9092 break;
9095 if (0 < found) {
9096 if (1 == found) { // only first property
9097 if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
9098 for (loop = 1; loop < aNumIDs; loop++) {
9099 aValues[loop].SetInheritValue();
9101 found = ((1 << aNumIDs) - 1);
9103 else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
9104 for (loop = 1; loop < aNumIDs; loop++) {
9105 aValues[loop].SetInitialValue();
9107 found = ((1 << aNumIDs) - 1);
9109 else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset
9110 for (loop = 1; loop < aNumIDs; loop++) {
9111 aValues[loop].SetUnsetValue();
9113 found = ((1 << aNumIDs) - 1);
9116 else { // more than one value, verify no inherits, initials or unsets
9117 for (loop = 0; loop < aNumIDs; loop++) {
9118 if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
9119 found = -1;
9120 break;
9122 else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
9123 found = -1;
9124 break;
9126 else if (eCSSUnit_Unset == aValues[loop].GetUnit()) {
9127 found = -1;
9128 break;
9133 return found;
9136 void
9137 CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue)
9139 mTempData.AddLonghandProperty(aPropID, aValue);
9143 * Parse a "box" property. Box properties have 1 to 4 values. When less
9144 * than 4 values are provided a standard mapping is used to replicate
9145 * existing values.
9147 bool
9148 CSSParserImpl::ParseBoxProperties(const nsCSSProperty aPropIDs[])
9150 // Get up to four values for the property
9151 int32_t count = 0;
9152 nsCSSRect result;
9153 NS_FOR_CSS_SIDES (index) {
9154 if (! ParseSingleValueProperty(result.*(nsCSSRect::sides[index]),
9155 aPropIDs[index])) {
9156 break;
9158 count++;
9160 if (count == 0) {
9161 return false;
9164 if (1 < count) { // verify no more than single inherit, initial or unset
9165 NS_FOR_CSS_SIDES (index) {
9166 nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
9167 if (eCSSUnit_Inherit == unit ||
9168 eCSSUnit_Initial == unit ||
9169 eCSSUnit_Unset == unit) {
9170 return false;
9175 // Provide missing values by replicating some of the values found
9176 switch (count) {
9177 case 1: // Make right == top
9178 result.mRight = result.mTop;
9179 case 2: // Make bottom == top
9180 result.mBottom = result.mTop;
9181 case 3: // Make left == right
9182 result.mLeft = result.mRight;
9185 NS_FOR_CSS_SIDES (index) {
9186 AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index]));
9188 return true;
9191 // Similar to ParseBoxProperties, except there is only one property
9192 // with the result as its value, not four. Requires values be nonnegative.
9193 bool
9194 CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask,
9195 /** outparam */ nsCSSValue& aValue)
9197 nsCSSRect& result = aValue.SetRectValue();
9199 int32_t count = 0;
9200 NS_FOR_CSS_SIDES (index) {
9201 if (!ParseNonNegativeVariant(result.*(nsCSSRect::sides[index]),
9202 aVariantMask, nullptr)) {
9203 break;
9205 count++;
9208 if (count == 0) {
9209 return false;
9212 // Provide missing values by replicating some of the values found
9213 switch (count) {
9214 case 1: // Make right == top
9215 result.mRight = result.mTop;
9216 case 2: // Make bottom == top
9217 result.mBottom = result.mTop;
9218 case 3: // Make left == right
9219 result.mLeft = result.mRight;
9222 return true;
9225 bool
9226 CSSParserImpl::ParseDirectionalBoxProperty(nsCSSProperty aProperty,
9227 int32_t aSourceType)
9229 const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty);
9230 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
9231 "not box property with physical vs. logical cascading");
9232 nsCSSValue value;
9233 if (!ParseSingleValueProperty(value, subprops[0])) {
9234 return false;
9237 AppendValue(subprops[0], value);
9238 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
9239 AppendValue(subprops[1], typeVal);
9240 AppendValue(subprops[2], typeVal);
9241 return true;
9244 bool
9245 CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID)
9247 nsCSSValue dimenX, dimenY;
9248 // required first value
9249 if (! ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr))
9250 return false;
9252 // optional second value (forbidden if first value is inherit/initial/unset)
9253 if (dimenX.GetUnit() != eCSSUnit_Inherit &&
9254 dimenX.GetUnit() != eCSSUnit_Initial &&
9255 dimenX.GetUnit() != eCSSUnit_Unset) {
9256 ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr);
9259 if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) {
9260 AppendValue(aPropID, dimenX);
9261 } else {
9262 nsCSSValue value;
9263 value.SetPairValue(dimenX, dimenY);
9264 AppendValue(aPropID, value);
9266 return true;
9269 bool
9270 CSSParserImpl::ParseBoxCornerRadii(const nsCSSProperty aPropIDs[])
9272 // Rectangles are used as scratch storage.
9273 // top => top-left, right => top-right,
9274 // bottom => bottom-right, left => bottom-left.
9275 nsCSSRect dimenX, dimenY;
9276 int32_t countX = 0, countY = 0;
9278 NS_FOR_CSS_SIDES (side) {
9279 if (! ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side],
9280 (side > 0 ? 0 : VARIANT_INHERIT) |
9281 VARIANT_LP | VARIANT_CALC,
9282 nullptr))
9283 break;
9284 countX++;
9286 if (countX == 0)
9287 return false;
9289 if (ExpectSymbol('/', true)) {
9290 NS_FOR_CSS_SIDES (side) {
9291 if (! ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side],
9292 VARIANT_LP | VARIANT_CALC, nullptr))
9293 break;
9294 countY++;
9296 if (countY == 0)
9297 return false;
9300 // if 'initial', 'inherit' or 'unset' was used, it must be the only value
9301 if (countX > 1 || countY > 0) {
9302 nsCSSUnit unit = dimenX.mTop.GetUnit();
9303 if (eCSSUnit_Inherit == unit ||
9304 eCSSUnit_Initial == unit ||
9305 eCSSUnit_Unset == unit)
9306 return false;
9309 // if we have no Y-values, use the X-values
9310 if (countY == 0) {
9311 dimenY = dimenX;
9312 countY = countX;
9315 // Provide missing values by replicating some of the values found
9316 switch (countX) {
9317 case 1: dimenX.mRight = dimenX.mTop; // top-right same as top-left, and
9318 case 2: dimenX.mBottom = dimenX.mTop; // bottom-right same as top-left, and
9319 case 3: dimenX.mLeft = dimenX.mRight; // bottom-left same as top-right
9322 switch (countY) {
9323 case 1: dimenY.mRight = dimenY.mTop; // top-right same as top-left, and
9324 case 2: dimenY.mBottom = dimenY.mTop; // bottom-right same as top-left, and
9325 case 3: dimenY.mLeft = dimenY.mRight; // bottom-left same as top-right
9328 NS_FOR_CSS_SIDES(side) {
9329 nsCSSValue& x = dimenX.*nsCSSRect::sides[side];
9330 nsCSSValue& y = dimenY.*nsCSSRect::sides[side];
9332 if (x == y) {
9333 AppendValue(aPropIDs[side], x);
9334 } else {
9335 nsCSSValue pair;
9336 pair.SetPairValue(x, y);
9337 AppendValue(aPropIDs[side], pair);
9340 return true;
9343 // These must be in CSS order (top,right,bottom,left) for indexing to work
9344 static const nsCSSProperty kBorderStyleIDs[] = {
9345 eCSSProperty_border_top_style,
9346 eCSSProperty_border_right_style_value,
9347 eCSSProperty_border_bottom_style,
9348 eCSSProperty_border_left_style_value
9350 static const nsCSSProperty kBorderWidthIDs[] = {
9351 eCSSProperty_border_top_width,
9352 eCSSProperty_border_right_width_value,
9353 eCSSProperty_border_bottom_width,
9354 eCSSProperty_border_left_width_value
9356 static const nsCSSProperty kBorderColorIDs[] = {
9357 eCSSProperty_border_top_color,
9358 eCSSProperty_border_right_color_value,
9359 eCSSProperty_border_bottom_color,
9360 eCSSProperty_border_left_color_value
9362 static const nsCSSProperty kBorderRadiusIDs[] = {
9363 eCSSProperty_border_top_left_radius,
9364 eCSSProperty_border_top_right_radius,
9365 eCSSProperty_border_bottom_right_radius,
9366 eCSSProperty_border_bottom_left_radius
9368 static const nsCSSProperty kOutlineRadiusIDs[] = {
9369 eCSSProperty__moz_outline_radius_topLeft,
9370 eCSSProperty__moz_outline_radius_topRight,
9371 eCSSProperty__moz_outline_radius_bottomRight,
9372 eCSSProperty__moz_outline_radius_bottomLeft
9375 void
9376 CSSParserImpl::SaveInputState(CSSParserInputState& aState)
9378 aState.mToken = mToken;
9379 aState.mHavePushBack = mHavePushBack;
9380 mScanner->SavePosition(aState.mPosition);
9383 void
9384 CSSParserImpl::RestoreSavedInputState(const CSSParserInputState& aState)
9386 mToken = aState.mToken;
9387 mHavePushBack = aState.mHavePushBack;
9388 mScanner->RestoreSavedPosition(aState.mPosition);
9391 bool
9392 CSSParserImpl::ParseProperty(nsCSSProperty aPropID)
9394 // Can't use AutoRestore<bool> because it's a bitfield.
9395 NS_ABORT_IF_FALSE(!mHashlessColorQuirk,
9396 "hashless color quirk should not be set");
9397 NS_ABORT_IF_FALSE(!mUnitlessLengthQuirk,
9398 "unitless length quirk should not be set");
9400 if (mNavQuirkMode) {
9401 mHashlessColorQuirk =
9402 nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_HASHLESS_COLOR_QUIRK);
9403 mUnitlessLengthQuirk =
9404 nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_UNITLESS_LENGTH_QUIRK);
9407 // Save the current input state so that we can restore it later if we
9408 // have to re-parse the property value as a variable-reference-containing
9409 // token stream.
9410 CSSParserInputState stateBeforeProperty;
9411 SaveInputState(stateBeforeProperty);
9412 mScanner->ClearSeenVariableReference();
9414 NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
9415 bool allowVariables = true;
9416 bool result;
9417 switch (nsCSSProps::PropertyParseType(aPropID)) {
9418 case CSS_PROPERTY_PARSE_INACCESSIBLE: {
9419 // The user can't use these
9420 REPORT_UNEXPECTED(PEInaccessibleProperty2);
9421 allowVariables = false;
9422 result = false;
9423 break;
9425 case CSS_PROPERTY_PARSE_FUNCTION: {
9426 result = ParsePropertyByFunction(aPropID);
9427 break;
9429 case CSS_PROPERTY_PARSE_VALUE: {
9430 result = false;
9431 nsCSSValue value;
9432 if (ParseSingleValueProperty(value, aPropID)) {
9433 AppendValue(aPropID, value);
9434 result = true;
9436 // XXX Report errors?
9437 break;
9439 case CSS_PROPERTY_PARSE_VALUE_LIST: {
9440 result = ParseValueList(aPropID);
9441 break;
9443 default: {
9444 result = false;
9445 allowVariables = false;
9446 NS_ABORT_IF_FALSE(false,
9447 "Property's flags field in nsCSSPropList.h is missing "
9448 "one of the CSS_PROPERTY_PARSE_* constants");
9449 break;
9453 if (result) {
9454 // We need to call ExpectEndProperty() to decide whether to reparse
9455 // with variables. This is needed because the property parsing may
9456 // have stopped upon finding a variable (e.g., 'margin: 1px var(a)')
9457 // in a way that future variable substitutions will be valid, or
9458 // because it parsed everything that's possible but we still want to
9459 // act as though the property contains variables even though we know
9460 // the substitution will never work (e.g., for 'margin: 1px 2px 3px
9461 // 4px 5px var(a)').
9463 // It would be nice to find a better solution here
9464 // (and for the SkipUntilOneOf below), though, that doesn't depend
9465 // on using what we don't accept for doing parsing correctly.
9466 if (!ExpectEndProperty()) {
9467 result = false;
9471 bool seenVariable = mScanner->SeenVariableReference() ||
9472 (stateBeforeProperty.mHavePushBack &&
9473 stateBeforeProperty.mToken.mType == eCSSToken_Function &&
9474 stateBeforeProperty.mToken.mIdent.LowerCaseEqualsLiteral("var"));
9475 bool parseAsTokenStream;
9477 if (!result && allowVariables) {
9478 parseAsTokenStream = true;
9479 if (!seenVariable) {
9480 // We might have stopped parsing the property before its end and before
9481 // finding a variable reference. Keep checking until the end of the
9482 // property.
9483 CSSParserInputState stateAtError;
9484 SaveInputState(stateAtError);
9486 const char16_t stopChars[] = { ';', '!', '}', ')', 0 };
9487 SkipUntilOneOf(stopChars);
9488 UngetToken();
9489 parseAsTokenStream = mScanner->SeenVariableReference();
9491 if (!parseAsTokenStream) {
9492 // If we parsed to the end of the propery and didn't find any variable
9493 // references, then the real position we want to report the error at
9494 // is |stateAtError|.
9495 RestoreSavedInputState(stateAtError);
9498 } else {
9499 parseAsTokenStream = false;
9502 if (parseAsTokenStream) {
9503 // Go back to the start of the property value and parse it to make sure
9504 // its variable references are syntactically valid and is otherwise
9505 // balanced.
9506 RestoreSavedInputState(stateBeforeProperty);
9508 if (!mInSupportsCondition) {
9509 mScanner->StartRecording();
9512 CSSVariableDeclarations::Type type;
9513 bool dropBackslash;
9514 nsString impliedCharacters;
9515 nsCSSValue value;
9516 if (ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
9517 nullptr, nullptr)) {
9518 MOZ_ASSERT(type == CSSVariableDeclarations::eTokenStream,
9519 "a non-custom property reparsed since it contained variable "
9520 "references should not have been 'initial' or 'inherit'");
9522 nsString propertyValue;
9524 if (!mInSupportsCondition) {
9525 // If we are in an @supports condition, we don't need to store the
9526 // actual token stream on the nsCSSValue.
9527 mScanner->StopRecording(propertyValue);
9528 if (dropBackslash) {
9529 MOZ_ASSERT(!propertyValue.IsEmpty() &&
9530 propertyValue[propertyValue.Length() - 1] == '\\');
9531 propertyValue.Truncate(propertyValue.Length() - 1);
9533 propertyValue.Append(impliedCharacters);
9536 if (mHavePushBack) {
9537 // If we came to the end of a property value that had a variable
9538 // reference and a token was pushed back, then it would have been
9539 // ended by '!', ')', ';', ']' or '}'. We should remove it from the
9540 // recorded property value.
9541 MOZ_ASSERT(mToken.IsSymbol('!') ||
9542 mToken.IsSymbol(')') ||
9543 mToken.IsSymbol(';') ||
9544 mToken.IsSymbol(']') ||
9545 mToken.IsSymbol('}'));
9546 if (!mInSupportsCondition) {
9547 MOZ_ASSERT(!propertyValue.IsEmpty());
9548 MOZ_ASSERT(propertyValue[propertyValue.Length() - 1] ==
9549 mToken.mSymbol);
9550 propertyValue.Truncate(propertyValue.Length() - 1);
9554 if (!mInSupportsCondition) {
9555 if (nsCSSProps::IsShorthand(aPropID)) {
9556 // If this is a shorthand property, we store the token stream on each
9557 // of its corresponding longhand properties.
9558 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
9559 nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
9560 tokenStream->mPropertyID = *p;
9561 tokenStream->mShorthandPropertyID = aPropID;
9562 tokenStream->mTokenStream = propertyValue;
9563 tokenStream->mBaseURI = mBaseURI;
9564 tokenStream->mSheetURI = mSheetURI;
9565 tokenStream->mSheetPrincipal = mSheetPrincipal;
9566 tokenStream->mSheet = mSheet;
9567 tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
9568 tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
9569 value.SetTokenStreamValue(tokenStream);
9570 AppendValue(*p, value);
9572 } else {
9573 nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
9574 tokenStream->mPropertyID = aPropID;
9575 tokenStream->mTokenStream = propertyValue;
9576 tokenStream->mBaseURI = mBaseURI;
9577 tokenStream->mSheetURI = mSheetURI;
9578 tokenStream->mSheetPrincipal = mSheetPrincipal;
9579 tokenStream->mSheet = mSheet;
9580 tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
9581 tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
9582 value.SetTokenStreamValue(tokenStream);
9583 AppendValue(aPropID, value);
9586 result = true;
9587 } else {
9588 if (!mInSupportsCondition) {
9589 mScanner->StopRecording();
9594 if (mNavQuirkMode) {
9595 mHashlessColorQuirk = false;
9596 mUnitlessLengthQuirk = false;
9599 return result;
9602 bool
9603 CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID)
9605 switch (aPropID) { // handle shorthand or multiple properties
9606 case eCSSProperty_background:
9607 return ParseBackground();
9608 case eCSSProperty_background_repeat:
9609 return ParseBackgroundRepeat();
9610 case eCSSProperty_background_position:
9611 return ParseBackgroundPosition();
9612 case eCSSProperty_background_size:
9613 return ParseBackgroundSize();
9614 case eCSSProperty_border:
9615 return ParseBorderSide(kBorderTopIDs, true);
9616 case eCSSProperty_border_color:
9617 return ParseBorderColor();
9618 case eCSSProperty_border_spacing:
9619 return ParseBorderSpacing();
9620 case eCSSProperty_border_style:
9621 return ParseBorderStyle();
9622 case eCSSProperty_border_bottom:
9623 return ParseBorderSide(kBorderBottomIDs, false);
9624 case eCSSProperty_border_end:
9625 return ParseDirectionalBorderSide(kBorderEndIDs,
9626 NS_BOXPROP_SOURCE_LOGICAL);
9627 case eCSSProperty_border_left:
9628 return ParseDirectionalBorderSide(kBorderLeftIDs,
9629 NS_BOXPROP_SOURCE_PHYSICAL);
9630 case eCSSProperty_border_right:
9631 return ParseDirectionalBorderSide(kBorderRightIDs,
9632 NS_BOXPROP_SOURCE_PHYSICAL);
9633 case eCSSProperty_border_start:
9634 return ParseDirectionalBorderSide(kBorderStartIDs,
9635 NS_BOXPROP_SOURCE_LOGICAL);
9636 case eCSSProperty_border_top:
9637 return ParseBorderSide(kBorderTopIDs, false);
9638 case eCSSProperty_border_bottom_colors:
9639 case eCSSProperty_border_left_colors:
9640 case eCSSProperty_border_right_colors:
9641 case eCSSProperty_border_top_colors:
9642 return ParseBorderColors(aPropID);
9643 case eCSSProperty_border_image_slice:
9644 return ParseBorderImageSlice(true, nullptr);
9645 case eCSSProperty_border_image_width:
9646 return ParseBorderImageWidth(true);
9647 case eCSSProperty_border_image_outset:
9648 return ParseBorderImageOutset(true);
9649 case eCSSProperty_border_image_repeat:
9650 return ParseBorderImageRepeat(true);
9651 case eCSSProperty_border_image:
9652 return ParseBorderImage();
9653 case eCSSProperty_border_width:
9654 return ParseBorderWidth();
9655 case eCSSProperty_border_end_color:
9656 return ParseDirectionalBoxProperty(eCSSProperty_border_end_color,
9657 NS_BOXPROP_SOURCE_LOGICAL);
9658 case eCSSProperty_border_left_color:
9659 return ParseDirectionalBoxProperty(eCSSProperty_border_left_color,
9660 NS_BOXPROP_SOURCE_PHYSICAL);
9661 case eCSSProperty_border_right_color:
9662 return ParseDirectionalBoxProperty(eCSSProperty_border_right_color,
9663 NS_BOXPROP_SOURCE_PHYSICAL);
9664 case eCSSProperty_border_start_color:
9665 return ParseDirectionalBoxProperty(eCSSProperty_border_start_color,
9666 NS_BOXPROP_SOURCE_LOGICAL);
9667 case eCSSProperty_border_end_width:
9668 return ParseDirectionalBoxProperty(eCSSProperty_border_end_width,
9669 NS_BOXPROP_SOURCE_LOGICAL);
9670 case eCSSProperty_border_left_width:
9671 return ParseDirectionalBoxProperty(eCSSProperty_border_left_width,
9672 NS_BOXPROP_SOURCE_PHYSICAL);
9673 case eCSSProperty_border_right_width:
9674 return ParseDirectionalBoxProperty(eCSSProperty_border_right_width,
9675 NS_BOXPROP_SOURCE_PHYSICAL);
9676 case eCSSProperty_border_start_width:
9677 return ParseDirectionalBoxProperty(eCSSProperty_border_start_width,
9678 NS_BOXPROP_SOURCE_LOGICAL);
9679 case eCSSProperty_border_end_style:
9680 return ParseDirectionalBoxProperty(eCSSProperty_border_end_style,
9681 NS_BOXPROP_SOURCE_LOGICAL);
9682 case eCSSProperty_border_left_style:
9683 return ParseDirectionalBoxProperty(eCSSProperty_border_left_style,
9684 NS_BOXPROP_SOURCE_PHYSICAL);
9685 case eCSSProperty_border_right_style:
9686 return ParseDirectionalBoxProperty(eCSSProperty_border_right_style,
9687 NS_BOXPROP_SOURCE_PHYSICAL);
9688 case eCSSProperty_border_start_style:
9689 return ParseDirectionalBoxProperty(eCSSProperty_border_start_style,
9690 NS_BOXPROP_SOURCE_LOGICAL);
9691 case eCSSProperty_border_radius:
9692 return ParseBoxCornerRadii(kBorderRadiusIDs);
9693 case eCSSProperty__moz_outline_radius:
9694 return ParseBoxCornerRadii(kOutlineRadiusIDs);
9696 case eCSSProperty_border_top_left_radius:
9697 case eCSSProperty_border_top_right_radius:
9698 case eCSSProperty_border_bottom_right_radius:
9699 case eCSSProperty_border_bottom_left_radius:
9700 case eCSSProperty__moz_outline_radius_topLeft:
9701 case eCSSProperty__moz_outline_radius_topRight:
9702 case eCSSProperty__moz_outline_radius_bottomRight:
9703 case eCSSProperty__moz_outline_radius_bottomLeft:
9704 return ParseBoxCornerRadius(aPropID);
9706 case eCSSProperty_box_shadow:
9707 case eCSSProperty_text_shadow:
9708 return ParseShadowList(aPropID);
9710 case eCSSProperty_clip:
9711 return ParseRect(eCSSProperty_clip);
9712 case eCSSProperty__moz_columns:
9713 return ParseColumns();
9714 case eCSSProperty__moz_column_rule:
9715 return ParseBorderSide(kColumnRuleIDs, false);
9716 case eCSSProperty_content:
9717 return ParseContent();
9718 case eCSSProperty_counter_increment:
9719 case eCSSProperty_counter_reset:
9720 return ParseCounterData(aPropID);
9721 case eCSSProperty_cursor:
9722 return ParseCursor();
9723 case eCSSProperty_filter:
9724 return ParseFilter();
9725 case eCSSProperty_flex:
9726 return ParseFlex();
9727 case eCSSProperty_flex_flow:
9728 return ParseFlexFlow();
9729 case eCSSProperty_font:
9730 return ParseFont();
9731 case eCSSProperty_font_variant:
9732 return ParseFontVariant();
9733 case eCSSProperty_grid_auto_flow:
9734 return ParseGridAutoFlow();
9735 case eCSSProperty_grid_auto_columns:
9736 case eCSSProperty_grid_auto_rows:
9737 return ParseGridAutoColumnsRows(aPropID);
9738 case eCSSProperty_grid_template_areas:
9739 return ParseGridTemplateAreas();
9740 case eCSSProperty_grid_template_columns:
9741 case eCSSProperty_grid_template_rows:
9742 return ParseGridTemplateColumnsRows(aPropID);
9743 case eCSSProperty_grid_template:
9744 return ParseGridTemplate();
9745 case eCSSProperty_grid:
9746 return ParseGrid();
9747 case eCSSProperty_grid_column_start:
9748 case eCSSProperty_grid_column_end:
9749 case eCSSProperty_grid_row_start:
9750 case eCSSProperty_grid_row_end:
9751 return ParseGridColumnRowStartEnd(aPropID);
9752 case eCSSProperty_grid_column:
9753 return ParseGridColumnRow(eCSSProperty_grid_column_start,
9754 eCSSProperty_grid_column_end);
9755 case eCSSProperty_grid_row:
9756 return ParseGridColumnRow(eCSSProperty_grid_row_start,
9757 eCSSProperty_grid_row_end);
9758 case eCSSProperty_grid_area:
9759 return ParseGridArea();
9760 case eCSSProperty_image_region:
9761 return ParseRect(eCSSProperty_image_region);
9762 case eCSSProperty_list_style:
9763 return ParseListStyle();
9764 case eCSSProperty_margin:
9765 return ParseMargin();
9766 case eCSSProperty_margin_end:
9767 return ParseDirectionalBoxProperty(eCSSProperty_margin_end,
9768 NS_BOXPROP_SOURCE_LOGICAL);
9769 case eCSSProperty_margin_left:
9770 return ParseDirectionalBoxProperty(eCSSProperty_margin_left,
9771 NS_BOXPROP_SOURCE_PHYSICAL);
9772 case eCSSProperty_margin_right:
9773 return ParseDirectionalBoxProperty(eCSSProperty_margin_right,
9774 NS_BOXPROP_SOURCE_PHYSICAL);
9775 case eCSSProperty_margin_start:
9776 return ParseDirectionalBoxProperty(eCSSProperty_margin_start,
9777 NS_BOXPROP_SOURCE_LOGICAL);
9778 case eCSSProperty_outline:
9779 return ParseOutline();
9780 case eCSSProperty_overflow:
9781 return ParseOverflow();
9782 case eCSSProperty_padding:
9783 return ParsePadding();
9784 case eCSSProperty_padding_end:
9785 return ParseDirectionalBoxProperty(eCSSProperty_padding_end,
9786 NS_BOXPROP_SOURCE_LOGICAL);
9787 case eCSSProperty_padding_left:
9788 return ParseDirectionalBoxProperty(eCSSProperty_padding_left,
9789 NS_BOXPROP_SOURCE_PHYSICAL);
9790 case eCSSProperty_padding_right:
9791 return ParseDirectionalBoxProperty(eCSSProperty_padding_right,
9792 NS_BOXPROP_SOURCE_PHYSICAL);
9793 case eCSSProperty_padding_start:
9794 return ParseDirectionalBoxProperty(eCSSProperty_padding_start,
9795 NS_BOXPROP_SOURCE_LOGICAL);
9796 case eCSSProperty_quotes:
9797 return ParseQuotes();
9798 case eCSSProperty_size:
9799 return ParseSize();
9800 case eCSSProperty_text_decoration:
9801 return ParseTextDecoration();
9802 case eCSSProperty_will_change:
9803 return ParseWillChange();
9804 case eCSSProperty_transform:
9805 return ParseTransform(false);
9806 case eCSSProperty__moz_transform:
9807 return ParseTransform(true);
9808 case eCSSProperty_transform_origin:
9809 return ParseTransformOrigin(false);
9810 case eCSSProperty_perspective_origin:
9811 return ParseTransformOrigin(true);
9812 case eCSSProperty_transition:
9813 return ParseTransition();
9814 case eCSSProperty_animation:
9815 return ParseAnimation();
9816 case eCSSProperty_transition_property:
9817 return ParseTransitionProperty();
9818 case eCSSProperty_fill:
9819 case eCSSProperty_stroke:
9820 return ParsePaint(aPropID);
9821 case eCSSProperty_stroke_dasharray:
9822 return ParseDasharray();
9823 case eCSSProperty_marker:
9824 return ParseMarker();
9825 case eCSSProperty_paint_order:
9826 return ParsePaintOrder();
9827 case eCSSProperty_all:
9828 return ParseAll();
9829 default:
9830 NS_ABORT_IF_FALSE(false, "should not be called");
9831 return false;
9835 // Bits used in determining which background position info we have
9836 #define BG_CENTER NS_STYLE_BG_POSITION_CENTER
9837 #define BG_TOP NS_STYLE_BG_POSITION_TOP
9838 #define BG_BOTTOM NS_STYLE_BG_POSITION_BOTTOM
9839 #define BG_LEFT NS_STYLE_BG_POSITION_LEFT
9840 #define BG_RIGHT NS_STYLE_BG_POSITION_RIGHT
9841 #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM)
9842 #define BG_TB (BG_TOP | BG_BOTTOM)
9843 #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT)
9844 #define BG_LR (BG_LEFT | BG_RIGHT)
9846 bool
9847 CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
9848 nsCSSProperty aPropID)
9850 if (aPropID == eCSSPropertyExtra_x_none_value) {
9851 return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nullptr);
9854 if (aPropID == eCSSPropertyExtra_x_auto_value) {
9855 return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nullptr);
9858 if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
9859 NS_ABORT_IF_FALSE(false, "not a single value property");
9860 return false;
9863 if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) {
9864 switch (aPropID) {
9865 case eCSSProperty_font_family:
9866 return ParseFamily(aValue);
9867 case eCSSProperty_font_synthesis:
9868 return ParseFontSynthesis(aValue);
9869 case eCSSProperty_font_variant_alternates:
9870 return ParseFontVariantAlternates(aValue);
9871 case eCSSProperty_font_variant_east_asian:
9872 return ParseFontVariantEastAsian(aValue);
9873 case eCSSProperty_font_variant_ligatures:
9874 return ParseFontVariantLigatures(aValue);
9875 case eCSSProperty_font_variant_numeric:
9876 return ParseFontVariantNumeric(aValue);
9877 case eCSSProperty_font_feature_settings:
9878 return ParseFontFeatureSettings(aValue);
9879 case eCSSProperty_font_weight:
9880 return ParseFontWeight(aValue);
9881 case eCSSProperty_image_orientation:
9882 return ParseImageOrientation(aValue);
9883 case eCSSProperty_list_style_type:
9884 return ParseListStyleType(aValue);
9885 case eCSSProperty_marks:
9886 return ParseMarks(aValue);
9887 case eCSSProperty_text_align:
9888 return ParseTextAlign(aValue);
9889 case eCSSProperty_text_align_last:
9890 return ParseTextAlignLast(aValue);
9891 case eCSSProperty_text_decoration_line:
9892 return ParseTextDecorationLine(aValue);
9893 case eCSSProperty_text_combine_upright:
9894 return ParseTextCombineUpright(aValue);
9895 case eCSSProperty_text_overflow:
9896 return ParseTextOverflow(aValue);
9897 case eCSSProperty_touch_action:
9898 return ParseTouchAction(aValue);
9899 default:
9900 NS_ABORT_IF_FALSE(false, "should not reach here");
9901 return false;
9905 uint32_t variant = nsCSSProps::ParserVariant(aPropID);
9906 if (variant == 0) {
9907 NS_ABORT_IF_FALSE(false, "not a single value property");
9908 return false;
9911 // We only allow 'script-level' when unsafe rules are enabled, because
9912 // otherwise it could interfere with rulenode optimizations if used in
9913 // a non-MathML-enabled document. We also only allow math-display when
9914 // unsafe rules are enabled.
9915 if (!mUnsafeRulesEnabled &&
9916 (aPropID == eCSSProperty_script_level ||
9917 aPropID == eCSSProperty_math_display))
9918 return false;
9920 const KTableValue *kwtable = nsCSSProps::kKeywordTableTable[aPropID];
9921 switch (nsCSSProps::ValueRestrictions(aPropID)) {
9922 default:
9923 NS_ABORT_IF_FALSE(false, "should not be reached");
9924 case 0:
9925 return ParseVariant(aValue, variant, kwtable);
9926 case CSS_PROPERTY_VALUE_NONNEGATIVE:
9927 return ParseNonNegativeVariant(aValue, variant, kwtable);
9928 case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
9929 return ParseOneOrLargerVariant(aValue, variant, kwtable);
9933 // font-descriptor: descriptor ':' value ';'
9934 // caller has advanced mToken to point at the descriptor
9935 bool
9936 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
9937 nsCSSValue& aValue)
9939 switch (aDescID) {
9940 // These four are similar to the properties of the same name,
9941 // possibly with more restrictions on the values they can take.
9942 case eCSSFontDesc_Family: {
9943 nsCSSValue value;
9944 if (!ParseFamily(value) ||
9945 value.GetUnit() != eCSSUnit_FontFamilyList)
9946 return false;
9948 // name can only be a single, non-generic name
9949 const FontFamilyList* f = value.GetFontFamilyListValue();
9950 const nsTArray<FontFamilyName>& fontlist = f->GetFontlist();
9952 if (fontlist.Length() != 1 || !fontlist[0].IsNamed()) {
9953 return false;
9956 aValue.SetStringValue(fontlist[0].mName, eCSSUnit_String);
9957 return true;
9960 case eCSSFontDesc_Style:
9961 // property is VARIANT_HMK|VARIANT_SYSFONT
9962 return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
9963 nsCSSProps::kFontStyleKTable);
9965 case eCSSFontDesc_Weight:
9966 return (ParseFontWeight(aValue) &&
9967 aValue.GetUnit() != eCSSUnit_Inherit &&
9968 aValue.GetUnit() != eCSSUnit_Initial &&
9969 aValue.GetUnit() != eCSSUnit_Unset &&
9970 (aValue.GetUnit() != eCSSUnit_Enumerated ||
9971 (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
9972 aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
9974 case eCSSFontDesc_Stretch:
9975 // property is VARIANT_HK|VARIANT_SYSFONT
9976 return ParseVariant(aValue, VARIANT_KEYWORD,
9977 nsCSSProps::kFontStretchKTable);
9979 // These two are unique to @font-face and have their own special grammar.
9980 case eCSSFontDesc_Src:
9981 return ParseFontSrc(aValue);
9983 case eCSSFontDesc_UnicodeRange:
9984 return ParseFontRanges(aValue);
9986 case eCSSFontDesc_FontFeatureSettings:
9987 return ParseFontFeatureSettings(aValue);
9989 case eCSSFontDesc_FontLanguageOverride:
9990 return ParseVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, nullptr);
9992 case eCSSFontDesc_UNKNOWN:
9993 case eCSSFontDesc_COUNT:
9994 NS_NOTREACHED("bad nsCSSFontDesc code");
9996 // explicitly do NOT have a default case to let the compiler
9997 // help find missing descriptors
9998 return false;
10001 void
10002 CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties)
10004 nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
10005 for (const nsCSSProperty *prop = aSourceProperties;
10006 *prop != eCSSProperty_UNKNOWN; ++prop) {
10007 AppendValue(*prop, physical);
10011 static nsCSSValue
10012 BoxPositionMaskToCSSValue(int32_t aMask, bool isX)
10014 int32_t val = NS_STYLE_BG_POSITION_CENTER;
10015 if (isX) {
10016 if (aMask & BG_LEFT) {
10017 val = NS_STYLE_BG_POSITION_LEFT;
10019 else if (aMask & BG_RIGHT) {
10020 val = NS_STYLE_BG_POSITION_RIGHT;
10023 else {
10024 if (aMask & BG_TOP) {
10025 val = NS_STYLE_BG_POSITION_TOP;
10027 else if (aMask & BG_BOTTOM) {
10028 val = NS_STYLE_BG_POSITION_BOTTOM;
10032 return nsCSSValue(val, eCSSUnit_Enumerated);
10035 bool
10036 CSSParserImpl::ParseBackground()
10038 nsAutoParseCompoundProperty compound(this);
10040 // background-color can only be set once, so it's not a list.
10041 nsCSSValue color;
10043 // Check first for inherit/initial/unset.
10044 if (ParseVariant(color, VARIANT_INHERIT, nullptr)) {
10045 // must be alone
10046 for (const nsCSSProperty* subprops =
10047 nsCSSProps::SubpropertyEntryFor(eCSSProperty_background);
10048 *subprops != eCSSProperty_UNKNOWN; ++subprops) {
10049 AppendValue(*subprops, color);
10051 return true;
10054 nsCSSValue image, repeat, attachment, clip, origin, position, size;
10055 BackgroundParseState state(color, image.SetListValue(),
10056 repeat.SetPairListValue(),
10057 attachment.SetListValue(), clip.SetListValue(),
10058 origin.SetListValue(), position.SetListValue(),
10059 size.SetPairListValue());
10061 for (;;) {
10062 if (!ParseBackgroundItem(state)) {
10063 return false;
10065 // If we saw a color, this must be the last item.
10066 if (color.GetUnit() != eCSSUnit_Null) {
10067 break;
10069 // If there's a comma, expect another item.
10070 if (!ExpectSymbol(',', true)) {
10071 break;
10073 // Chain another entry on all the lists.
10074 state.mImage->mNext = new nsCSSValueList;
10075 state.mImage = state.mImage->mNext;
10076 state.mRepeat->mNext = new nsCSSValuePairList;
10077 state.mRepeat = state.mRepeat->mNext;
10078 state.mAttachment->mNext = new nsCSSValueList;
10079 state.mAttachment = state.mAttachment->mNext;
10080 state.mClip->mNext = new nsCSSValueList;
10081 state.mClip = state.mClip->mNext;
10082 state.mOrigin->mNext = new nsCSSValueList;
10083 state.mOrigin = state.mOrigin->mNext;
10084 state.mPosition->mNext = new nsCSSValueList;
10085 state.mPosition = state.mPosition->mNext;
10086 state.mSize->mNext = new nsCSSValuePairList;
10087 state.mSize = state.mSize->mNext;
10090 // If we get to this point without seeing a color, provide a default.
10091 if (color.GetUnit() == eCSSUnit_Null) {
10092 color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor);
10095 AppendValue(eCSSProperty_background_image, image);
10096 AppendValue(eCSSProperty_background_repeat, repeat);
10097 AppendValue(eCSSProperty_background_attachment, attachment);
10098 AppendValue(eCSSProperty_background_clip, clip);
10099 AppendValue(eCSSProperty_background_origin, origin);
10100 AppendValue(eCSSProperty_background_position, position);
10101 AppendValue(eCSSProperty_background_size, size);
10102 AppendValue(eCSSProperty_background_color, color);
10103 return true;
10106 // Parse one item of the background shorthand property.
10107 bool
10108 CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState)
10111 // Fill in the values that the shorthand will set if we don't find
10112 // other values.
10113 aState.mImage->mValue.SetNoneValue();
10114 aState.mRepeat->mXValue.SetIntValue(NS_STYLE_BG_REPEAT_REPEAT,
10115 eCSSUnit_Enumerated);
10116 aState.mRepeat->mYValue.Reset();
10117 aState.mAttachment->mValue.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL,
10118 eCSSUnit_Enumerated);
10119 aState.mClip->mValue.SetIntValue(NS_STYLE_BG_CLIP_BORDER,
10120 eCSSUnit_Enumerated);
10121 aState.mOrigin->mValue.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING,
10122 eCSSUnit_Enumerated);
10123 nsRefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4);
10124 aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array);
10125 positionArr->Item(1).SetPercentValue(0.0f);
10126 positionArr->Item(3).SetPercentValue(0.0f);
10127 aState.mSize->mXValue.SetAutoValue();
10128 aState.mSize->mYValue.SetAutoValue();
10130 bool haveColor = false,
10131 haveImage = false,
10132 haveRepeat = false,
10133 haveAttach = false,
10134 havePositionAndSize = false,
10135 haveOrigin = false,
10136 haveSomething = false;
10138 while (GetToken(true)) {
10139 nsCSSTokenType tt = mToken.mType;
10140 UngetToken(); // ...but we'll still cheat and use mToken
10141 if (tt == eCSSToken_Symbol) {
10142 // ExpectEndProperty only looks for symbols, and nothing else will
10143 // show up as one.
10144 break;
10147 if (tt == eCSSToken_Ident) {
10148 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
10149 int32_t dummy;
10150 if (keyword == eCSSKeyword_inherit ||
10151 keyword == eCSSKeyword_initial ||
10152 keyword == eCSSKeyword_unset) {
10153 return false;
10154 } else if (keyword == eCSSKeyword_none) {
10155 if (haveImage)
10156 return false;
10157 haveImage = true;
10158 if (!ParseSingleValueProperty(aState.mImage->mValue,
10159 eCSSProperty_background_image)) {
10160 NS_NOTREACHED("should be able to parse");
10161 return false;
10163 } else if (nsCSSProps::FindKeyword(keyword,
10164 nsCSSProps::kBackgroundAttachmentKTable, dummy)) {
10165 if (haveAttach)
10166 return false;
10167 haveAttach = true;
10168 if (!ParseSingleValueProperty(aState.mAttachment->mValue,
10169 eCSSProperty_background_attachment)) {
10170 NS_NOTREACHED("should be able to parse");
10171 return false;
10173 } else if (nsCSSProps::FindKeyword(keyword,
10174 nsCSSProps::kBackgroundRepeatKTable, dummy)) {
10175 if (haveRepeat)
10176 return false;
10177 haveRepeat = true;
10178 nsCSSValuePair scratch;
10179 if (!ParseBackgroundRepeatValues(scratch)) {
10180 NS_NOTREACHED("should be able to parse");
10181 return false;
10183 aState.mRepeat->mXValue = scratch.mXValue;
10184 aState.mRepeat->mYValue = scratch.mYValue;
10185 } else if (nsCSSProps::FindKeyword(keyword,
10186 nsCSSProps::kBackgroundPositionKTable, dummy)) {
10187 if (havePositionAndSize)
10188 return false;
10189 havePositionAndSize = true;
10190 if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) {
10191 return false;
10193 if (ExpectSymbol('/', true)) {
10194 nsCSSValuePair scratch;
10195 if (!ParseBackgroundSizeValues(scratch)) {
10196 return false;
10198 aState.mSize->mXValue = scratch.mXValue;
10199 aState.mSize->mYValue = scratch.mYValue;
10201 } else if (nsCSSProps::FindKeyword(keyword,
10202 nsCSSProps::kBackgroundOriginKTable, dummy)) {
10203 if (haveOrigin)
10204 return false;
10205 haveOrigin = true;
10206 if (!ParseSingleValueProperty(aState.mOrigin->mValue,
10207 eCSSProperty_background_origin)) {
10208 NS_NOTREACHED("should be able to parse");
10209 return false;
10212 // The spec allows a second box value (for background-clip),
10213 // immediately following the first one (for background-origin).
10215 // 'background-clip' and 'background-origin' use the same keyword table
10216 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
10217 eCSSProperty_background_origin] ==
10218 nsCSSProps::kBackgroundOriginKTable);
10219 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
10220 eCSSProperty_background_clip] ==
10221 nsCSSProps::kBackgroundOriginKTable);
10222 static_assert(NS_STYLE_BG_CLIP_BORDER ==
10223 NS_STYLE_BG_ORIGIN_BORDER &&
10224 NS_STYLE_BG_CLIP_PADDING ==
10225 NS_STYLE_BG_ORIGIN_PADDING &&
10226 NS_STYLE_BG_CLIP_CONTENT ==
10227 NS_STYLE_BG_ORIGIN_CONTENT,
10228 "bg-clip and bg-origin style constants must agree");
10230 if (!ParseSingleValueProperty(aState.mClip->mValue,
10231 eCSSProperty_background_clip)) {
10232 // When exactly one <box> value is set, it is used for both
10233 // 'background-origin' and 'background-clip'.
10234 // See assertions above showing these values are compatible.
10235 aState.mClip->mValue = aState.mOrigin->mValue;
10237 } else {
10238 if (haveColor)
10239 return false;
10240 haveColor = true;
10241 if (!ParseSingleValueProperty(aState.mColor,
10242 eCSSProperty_background_color)) {
10243 return false;
10246 } else if (tt == eCSSToken_URL ||
10247 (tt == eCSSToken_Function &&
10248 (mToken.mIdent.LowerCaseEqualsLiteral("linear-gradient") ||
10249 mToken.mIdent.LowerCaseEqualsLiteral("radial-gradient") ||
10250 mToken.mIdent.LowerCaseEqualsLiteral("repeating-linear-gradient") ||
10251 mToken.mIdent.LowerCaseEqualsLiteral("repeating-radial-gradient") ||
10252 mToken.mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
10253 mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
10254 mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
10255 mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
10256 mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect") ||
10257 mToken.mIdent.LowerCaseEqualsLiteral("-moz-element")))) {
10258 if (haveImage)
10259 return false;
10260 haveImage = true;
10261 if (!ParseSingleValueProperty(aState.mImage->mValue,
10262 eCSSProperty_background_image)) {
10263 return false;
10265 } else if (tt == eCSSToken_Dimension ||
10266 tt == eCSSToken_Number ||
10267 tt == eCSSToken_Percentage ||
10268 (tt == eCSSToken_Function &&
10269 (mToken.mIdent.LowerCaseEqualsLiteral("calc") ||
10270 mToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")))) {
10271 if (havePositionAndSize)
10272 return false;
10273 havePositionAndSize = true;
10274 if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) {
10275 return false;
10277 if (ExpectSymbol('/', true)) {
10278 nsCSSValuePair scratch;
10279 if (!ParseBackgroundSizeValues(scratch)) {
10280 return false;
10282 aState.mSize->mXValue = scratch.mXValue;
10283 aState.mSize->mYValue = scratch.mYValue;
10285 } else {
10286 if (haveColor)
10287 return false;
10288 haveColor = true;
10289 // Note: This parses 'inherit', 'initial' and 'unset', but
10290 // we've already checked for them, so it's ok.
10291 if (!ParseSingleValueProperty(aState.mColor,
10292 eCSSProperty_background_color)) {
10293 return false;
10296 haveSomething = true;
10299 return haveSomething;
10302 // This function is very similar to ParseBackgroundPosition and
10303 // ParseBackgroundSize.
10304 bool
10305 CSSParserImpl::ParseValueList(nsCSSProperty aPropID)
10307 // aPropID is a single value prop-id
10308 nsCSSValue value;
10309 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10310 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10311 nsCSSValueList* item = value.SetListValue();
10312 for (;;) {
10313 if (!ParseSingleValueProperty(item->mValue, aPropID)) {
10314 return false;
10316 if (!ExpectSymbol(',', true)) {
10317 break;
10319 item->mNext = new nsCSSValueList;
10320 item = item->mNext;
10323 AppendValue(aPropID, value);
10324 return true;
10327 bool
10328 CSSParserImpl::ParseBackgroundRepeat()
10330 nsCSSValue value;
10331 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10332 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10333 nsCSSValuePair valuePair;
10334 if (!ParseBackgroundRepeatValues(valuePair)) {
10335 return false;
10337 nsCSSValuePairList* item = value.SetPairListValue();
10338 for (;;) {
10339 item->mXValue = valuePair.mXValue;
10340 item->mYValue = valuePair.mYValue;
10341 if (!ExpectSymbol(',', true)) {
10342 break;
10344 if (!ParseBackgroundRepeatValues(valuePair)) {
10345 return false;
10347 item->mNext = new nsCSSValuePairList;
10348 item = item->mNext;
10352 AppendValue(eCSSProperty_background_repeat, value);
10353 return true;
10356 bool
10357 CSSParserImpl::ParseBackgroundRepeatValues(nsCSSValuePair& aValue)
10359 nsCSSValue& xValue = aValue.mXValue;
10360 nsCSSValue& yValue = aValue.mYValue;
10362 if (ParseEnum(xValue, nsCSSProps::kBackgroundRepeatKTable)) {
10363 int32_t value = xValue.GetIntValue();
10364 // For single values set yValue as eCSSUnit_Null.
10365 if (value == NS_STYLE_BG_REPEAT_REPEAT_X ||
10366 value == NS_STYLE_BG_REPEAT_REPEAT_Y ||
10367 !ParseEnum(yValue, nsCSSProps::kBackgroundRepeatPartKTable)) {
10368 // the caller will fail cases like "repeat-x no-repeat"
10369 // by expecting a list separator or an end property.
10370 yValue.Reset();
10372 return true;
10375 return false;
10378 // This function is very similar to ParseBackgroundList and ParseBackgroundSize.
10379 bool
10380 CSSParserImpl::ParseBackgroundPosition()
10382 nsCSSValue value;
10383 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10384 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10385 nsCSSValue itemValue;
10386 if (!ParseBackgroundPositionValues(itemValue, false)) {
10387 return false;
10389 nsCSSValueList* item = value.SetListValue();
10390 for (;;) {
10391 item->mValue = itemValue;
10392 if (!ExpectSymbol(',', true)) {
10393 break;
10395 if (!ParseBackgroundPositionValues(itemValue, false)) {
10396 return false;
10398 item->mNext = new nsCSSValueList;
10399 item = item->mNext;
10402 AppendValue(eCSSProperty_background_position, value);
10403 return true;
10407 * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used
10408 * for parsing the CSS 2.1 background-position syntax (which has at
10409 * most two values). (Compare to the css3-background syntax which
10410 * takes up to four values.) Some current CSS specifications that
10411 * use background-position-like syntax still use this old syntax.
10413 * Parses two values that correspond to positions in a box. These can be
10414 * values corresponding to percentages of the box, raw offsets, or keywords
10415 * like "top," "left center," etc.
10417 * @param aOut The nsCSSValuePair in which to place the result.
10418 * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are
10419 * legal values
10420 * @param aAllowExplicitCenter If true, 'center' is a legal value
10421 * @return Whether or not the operation succeeded.
10423 bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
10424 bool aAcceptsInherit,
10425 bool aAllowExplicitCenter)
10427 // First try a percentage or a length value
10428 nsCSSValue &xValue = aOut.mXValue,
10429 &yValue = aOut.mYValue;
10430 int32_t variantMask =
10431 (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC;
10432 if (ParseVariant(xValue, variantMask, nullptr)) {
10433 if (eCSSUnit_Inherit == xValue.GetUnit() ||
10434 eCSSUnit_Initial == xValue.GetUnit() ||
10435 eCSSUnit_Unset == xValue.GetUnit()) { // both are inherit, initial or unset
10436 yValue = xValue;
10437 return true;
10439 // We have one percentage/length/calc. Get the optional second
10440 // percentage/length/calc/keyword.
10441 if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
10442 // We have two numbers
10443 return true;
10446 if (ParseEnum(yValue, nsCSSProps::kBackgroundPositionKTable)) {
10447 int32_t yVal = yValue.GetIntValue();
10448 if (!(yVal & BG_CTB)) {
10449 // The second keyword can only be 'center', 'top', or 'bottom'
10450 return false;
10452 yValue = BoxPositionMaskToCSSValue(yVal, false);
10453 return true;
10456 // If only one percentage or length value is given, it sets the
10457 // horizontal position only, and the vertical position will be 50%.
10458 yValue.SetPercentValue(0.5f);
10459 return true;
10462 // Now try keywords. We do this manually to allow for the first
10463 // appearance of "center" to apply to the either the x or y
10464 // position (it's ambiguous so we have to disambiguate). Each
10465 // allowed keyword value is assigned it's own bit. We don't allow
10466 // any duplicate keywords other than center. We try to get two
10467 // keywords but it's okay if there is only one.
10468 int32_t mask = 0;
10469 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
10470 int32_t bit = xValue.GetIntValue();
10471 mask |= bit;
10472 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
10473 bit = xValue.GetIntValue();
10474 if (mask & (bit & ~BG_CENTER)) {
10475 // Only the 'center' keyword can be duplicated.
10476 return false;
10478 mask |= bit;
10480 else {
10481 // Only one keyword. See if we have a length, percentage, or calc.
10482 if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
10483 if (!(mask & BG_CLR)) {
10484 // The first keyword can only be 'center', 'left', or 'right'
10485 return false;
10488 xValue = BoxPositionMaskToCSSValue(mask, true);
10489 return true;
10494 // Check for bad input. Bad input consists of no matching keywords,
10495 // or pairs of x keywords or pairs of y keywords.
10496 if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
10497 (mask == (BG_LEFT | BG_RIGHT)) ||
10498 (!aAllowExplicitCenter && (mask & BG_CENTER))) {
10499 return false;
10502 // Create style values
10503 xValue = BoxPositionMaskToCSSValue(mask, true);
10504 yValue = BoxPositionMaskToCSSValue(mask, false);
10505 return true;
10508 bool CSSParserImpl::ParseBackgroundPositionValues(nsCSSValue& aOut,
10509 bool aAcceptsInherit)
10511 // css3-background allows positions to be defined as offsets
10512 // from an edge. There can be 2 keywords and 2 offsets given. These
10513 // four 'values' are stored in an array in the following order:
10514 // [keyword offset keyword offset]. If a keyword or offset isn't
10515 // parsed the value of the corresponding array element is set
10516 // to eCSSUnit_Null by a call to nsCSSValue::Reset().
10517 if (aAcceptsInherit && ParseVariant(aOut, VARIANT_INHERIT, nullptr)) {
10518 return true;
10521 nsRefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(4);
10522 aOut.SetArrayValue(value, eCSSUnit_Array);
10524 // The following clarifies organisation of the array.
10525 nsCSSValue &xEdge = value->Item(0),
10526 &xOffset = value->Item(1),
10527 &yEdge = value->Item(2),
10528 &yOffset = value->Item(3);
10530 // Parse all the values into the array.
10531 uint32_t valueCount = 0;
10532 for (int32_t i = 0; i < 4; i++) {
10533 if (!ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD,
10534 nsCSSProps::kBackgroundPositionKTable)) {
10535 break;
10537 ++valueCount;
10540 switch (valueCount) {
10541 case 4:
10542 // "If three or four values are given, then each <percentage> or <length>
10543 // represents an offset and must be preceded by a keyword, which specifies
10544 // from which edge the offset is given."
10545 if (eCSSUnit_Enumerated != xEdge.GetUnit() ||
10546 BG_CENTER == xEdge.GetIntValue() ||
10547 eCSSUnit_Enumerated == xOffset.GetUnit() ||
10548 eCSSUnit_Enumerated != yEdge.GetUnit() ||
10549 BG_CENTER == yEdge.GetIntValue() ||
10550 eCSSUnit_Enumerated == yOffset.GetUnit()) {
10551 return false;
10553 break;
10554 case 3:
10555 // "If three or four values are given, then each <percentage> or<length>
10556 // represents an offset and must be preceded by a keyword, which specifies
10557 // from which edge the offset is given." ... "If three values are given,
10558 // the missing offset is assumed to be zero."
10559 if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) {
10560 // keyword offset keyword
10561 // Second value is non-keyword, thus first value must be a non-center
10562 // keyword.
10563 if (eCSSUnit_Enumerated != value->Item(0).GetUnit() ||
10564 BG_CENTER == value->Item(0).GetIntValue()) {
10565 return false;
10568 // Remaining value must be a keyword.
10569 if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
10570 return false;
10573 yOffset.Reset(); // Everything else is in the correct position.
10574 } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
10575 // keyword keyword offset
10576 // Third value is non-keyword, thus second value must be non-center
10577 // keyword.
10578 if (BG_CENTER == value->Item(1).GetIntValue()) {
10579 return false;
10582 // Remaining value must be a keyword.
10583 if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) {
10584 return false;
10587 // Move the values to the correct position in the array.
10588 value->Item(3) = value->Item(2); // yOffset
10589 value->Item(2) = value->Item(1); // yEdge
10590 value->Item(1).Reset(); // xOffset
10591 } else {
10592 return false;
10594 break;
10595 case 2:
10596 // "If two values are given and at least one value is not a keyword, then
10597 // the first value represents the horizontal position (or offset) and the
10598 // second represents the vertical position (or offset)"
10599 if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
10600 if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
10601 // keyword keyword
10602 value->Item(2) = value->Item(1); // move yEdge to correct position
10603 xOffset.Reset();
10604 yOffset.Reset();
10605 } else {
10606 // keyword offset
10607 // First value must represent horizontal position.
10608 if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) {
10609 return false;
10611 value->Item(3) = value->Item(1); // move yOffset to correct position
10612 xOffset.Reset();
10613 yEdge.Reset();
10615 } else {
10616 if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
10617 // offset keyword
10618 // Second value must represent vertical position.
10619 if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) {
10620 return false;
10622 value->Item(2) = value->Item(1); // move yEdge to correct position
10623 value->Item(1) = value->Item(0); // move xOffset to correct position
10624 xEdge.Reset();
10625 yOffset.Reset();
10626 } else {
10627 // offset offset
10628 value->Item(3) = value->Item(1); // move yOffset to correct position
10629 value->Item(1) = value->Item(0); // move xOffset to correct position
10630 xEdge.Reset();
10631 yEdge.Reset();
10634 break;
10635 case 1:
10636 // "If only one value is specified, the second value is assumed to be
10637 // center."
10638 if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
10639 xOffset.Reset();
10640 } else {
10641 value->Item(1) = value->Item(0); // move xOffset to correct position
10642 xEdge.Reset();
10644 yEdge.SetIntValue(NS_STYLE_BG_POSITION_CENTER, eCSSUnit_Enumerated);
10645 yOffset.Reset();
10646 break;
10647 default:
10648 return false;
10651 // For compatibility with CSS2.1 code the edges can be unspecified.
10652 // Unspecified edges are recorded as nullptr.
10653 NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() ||
10654 eCSSUnit_Null == xEdge.GetUnit()) &&
10655 (eCSSUnit_Enumerated == yEdge.GetUnit() ||
10656 eCSSUnit_Null == yEdge.GetUnit()) &&
10657 eCSSUnit_Enumerated != xOffset.GetUnit() &&
10658 eCSSUnit_Enumerated != yOffset.GetUnit(),
10659 "Unexpected units");
10661 // Keywords in first and second pairs can not both be vertical or
10662 // horizontal keywords. (eg. left right, bottom top). Additionally,
10663 // non-center keyword can not be duplicated (eg. left left).
10664 int32_t xEdgeEnum =
10665 xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0;
10666 int32_t yEdgeEnum =
10667 yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0;
10668 if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) ||
10669 (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) ||
10670 (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) {
10671 return false;
10674 // The values could be in an order that is different than expected.
10675 // eg. x contains vertical information, y contains horizontal information.
10676 // Swap if incorrect order.
10677 if (xEdgeEnum & (BG_TOP | BG_BOTTOM) ||
10678 yEdgeEnum & (BG_LEFT | BG_RIGHT)) {
10679 nsCSSValue swapEdge = xEdge;
10680 nsCSSValue swapOffset = xOffset;
10681 xEdge = yEdge;
10682 xOffset = yOffset;
10683 yEdge = swapEdge;
10684 yOffset = swapOffset;
10687 return true;
10690 // This function is very similar to ParseBackgroundList and
10691 // ParseBackgroundPosition.
10692 bool
10693 CSSParserImpl::ParseBackgroundSize()
10695 nsCSSValue value;
10696 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10697 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10698 nsCSSValuePair valuePair;
10699 if (!ParseBackgroundSizeValues(valuePair)) {
10700 return false;
10702 nsCSSValuePairList* item = value.SetPairListValue();
10703 for (;;) {
10704 item->mXValue = valuePair.mXValue;
10705 item->mYValue = valuePair.mYValue;
10706 if (!ExpectSymbol(',', true)) {
10707 break;
10709 if (!ParseBackgroundSizeValues(valuePair)) {
10710 return false;
10712 item->mNext = new nsCSSValuePairList;
10713 item = item->mNext;
10716 AppendValue(eCSSProperty_background_size, value);
10717 return true;
10721 * Parses two values that correspond to lengths for the background-size
10722 * property. These can be one or two lengths (or the 'auto' keyword) or
10723 * percentages corresponding to the element's dimensions or the single keywords
10724 * 'contain' or 'cover'. 'initial', 'inherit' and 'unset' must be handled by
10725 * the caller if desired.
10727 * @param aOut The nsCSSValuePair in which to place the result.
10728 * @return Whether or not the operation succeeded.
10730 #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC)
10731 bool CSSParserImpl::ParseBackgroundSizeValues(nsCSSValuePair &aOut)
10733 // First try a percentage or a length value
10734 nsCSSValue &xValue = aOut.mXValue,
10735 &yValue = aOut.mYValue;
10736 if (ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr)) {
10737 // We have one percentage/length/calc/auto. Get the optional second
10738 // percentage/length/calc/keyword.
10739 if (ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr)) {
10740 // We have a second percentage/length/calc/auto.
10741 return true;
10744 // If only one percentage or length value is given, it sets the
10745 // horizontal size only, and the vertical size will be as if by 'auto'.
10746 yValue.SetAutoValue();
10747 return true;
10750 // Now address 'contain' and 'cover'.
10751 if (!ParseEnum(xValue, nsCSSProps::kBackgroundSizeKTable))
10752 return false;
10753 yValue.Reset();
10754 return true;
10756 #undef BG_SIZE_VARIANT
10758 bool
10759 CSSParserImpl::ParseBorderColor()
10761 static const nsCSSProperty kBorderColorSources[] = {
10762 eCSSProperty_border_left_color_ltr_source,
10763 eCSSProperty_border_left_color_rtl_source,
10764 eCSSProperty_border_right_color_ltr_source,
10765 eCSSProperty_border_right_color_rtl_source,
10766 eCSSProperty_UNKNOWN
10769 // do this now, in case 4 values weren't specified
10770 InitBoxPropsAsPhysical(kBorderColorSources);
10771 return ParseBoxProperties(kBorderColorIDs);
10774 void
10775 CSSParserImpl::SetBorderImageInitialValues()
10777 // border-image-source: none
10778 nsCSSValue source;
10779 source.SetNoneValue();
10780 AppendValue(eCSSProperty_border_image_source, source);
10782 // border-image-slice: 100%
10783 nsCSSValue sliceBoxValue;
10784 nsCSSRect& sliceBox = sliceBoxValue.SetRectValue();
10785 sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent));
10786 nsCSSValue slice;
10787 nsCSSValueList* sliceList = slice.SetListValue();
10788 sliceList->mValue = sliceBoxValue;
10789 AppendValue(eCSSProperty_border_image_slice, slice);
10791 // border-image-width: 1
10792 nsCSSValue width;
10793 nsCSSRect& widthBox = width.SetRectValue();
10794 widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number));
10795 AppendValue(eCSSProperty_border_image_width, width);
10797 // border-image-outset: 0
10798 nsCSSValue outset;
10799 nsCSSRect& outsetBox = outset.SetRectValue();
10800 outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number));
10801 AppendValue(eCSSProperty_border_image_outset, outset);
10803 // border-image-repeat: repeat
10804 nsCSSValue repeat;
10805 nsCSSValuePair repeatPair;
10806 repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
10807 eCSSUnit_Enumerated));
10808 repeat.SetPairValue(&repeatPair);
10809 AppendValue(eCSSProperty_border_image_repeat, repeat);
10812 bool
10813 CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
10814 bool* aConsumedTokens)
10816 // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
10817 nsCSSValue value;
10819 if (aConsumedTokens) {
10820 *aConsumedTokens = true;
10823 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10824 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10825 // are done.
10826 AppendValue(eCSSProperty_border_image_slice, value);
10827 return true;
10830 // Try parsing "fill" value.
10831 nsCSSValue imageSliceFillValue;
10832 bool hasFill = ParseEnum(imageSliceFillValue,
10833 nsCSSProps::kBorderImageSliceKTable);
10835 // Parse the box dimensions.
10836 nsCSSValue imageSliceBoxValue;
10837 if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue)) {
10838 if (!hasFill && aConsumedTokens) {
10839 *aConsumedTokens = false;
10842 return false;
10845 // Try parsing "fill" keyword again if the first time failed because keyword
10846 // and slice dimensions can be in any order.
10847 if (!hasFill) {
10848 hasFill = ParseEnum(imageSliceFillValue,
10849 nsCSSProps::kBorderImageSliceKTable);
10852 nsCSSValueList* borderImageSlice = value.SetListValue();
10853 // Put the box value into the list.
10854 borderImageSlice->mValue = imageSliceBoxValue;
10856 if (hasFill) {
10857 // Put the "fill" value into the list.
10858 borderImageSlice->mNext = new nsCSSValueList;
10859 borderImageSlice->mNext->mValue = imageSliceFillValue;
10862 AppendValue(eCSSProperty_border_image_slice, value);
10863 return true;
10866 bool
10867 CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
10869 // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
10870 nsCSSValue value;
10872 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10873 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10874 // are done.
10875 AppendValue(eCSSProperty_border_image_width, value);
10876 return true;
10879 // Parse the box dimensions.
10880 if (!ParseGroupedBoxProperty(VARIANT_ALPN, value)) {
10881 return false;
10884 AppendValue(eCSSProperty_border_image_width, value);
10885 return true;
10888 bool
10889 CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
10891 // border-image-outset: initial | [<length>|<number>]{1,4}
10892 nsCSSValue value;
10894 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10895 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10896 // are done.
10897 AppendValue(eCSSProperty_border_image_outset, value);
10898 return true;
10901 // Parse the box dimensions.
10902 if (!ParseGroupedBoxProperty(VARIANT_LN, value)) {
10903 return false;
10906 AppendValue(eCSSProperty_border_image_outset, value);
10907 return true;
10910 bool
10911 CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit)
10913 nsCSSValue value;
10914 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10915 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10916 // are done.
10917 AppendValue(eCSSProperty_border_image_repeat, value);
10918 return true;
10921 nsCSSValuePair result;
10922 if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) {
10923 return false;
10926 // optional second keyword, defaults to first
10927 if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) {
10928 result.mYValue = result.mXValue;
10931 value.SetPairValue(&result);
10932 AppendValue(eCSSProperty_border_image_repeat, value);
10933 return true;
10936 bool
10937 CSSParserImpl::ParseBorderImage()
10939 nsAutoParseCompoundProperty compound(this);
10941 // border-image: inherit | initial |
10942 // <border-image-source> ||
10943 // <border-image-slice>
10944 // [ / <border-image-width> |
10945 // / <border-image-width>? / <border-image-outset> ]? ||
10946 // <border-image-repeat>
10948 nsCSSValue value;
10949 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10950 AppendValue(eCSSProperty_border_image_source, value);
10951 AppendValue(eCSSProperty_border_image_slice, value);
10952 AppendValue(eCSSProperty_border_image_width, value);
10953 AppendValue(eCSSProperty_border_image_outset, value);
10954 AppendValue(eCSSProperty_border_image_repeat, value);
10955 // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done.
10956 return true;
10959 // No empty property.
10960 if (CheckEndProperty()) {
10961 return false;
10964 // Shorthand properties are required to set everything they can.
10965 SetBorderImageInitialValues();
10967 bool foundSource = false;
10968 bool foundSliceWidthOutset = false;
10969 bool foundRepeat = false;
10971 // This loop is used to handle the parsing of border-image properties which
10972 // can appear in any order.
10973 nsCSSValue imageSourceValue;
10974 while (!CheckEndProperty()) {
10975 // <border-image-source>
10976 if (!foundSource && ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) {
10977 AppendValue(eCSSProperty_border_image_source, imageSourceValue);
10978 foundSource = true;
10979 continue;
10982 // <border-image-slice>
10983 // ParseBorderImageSlice is weird. It may consume tokens and then return
10984 // false, because it parses a property with two required components that
10985 // can appear in either order. Since the tokens that were consumed cannot
10986 // parse as anything else we care about, this isn't a problem.
10987 if (!foundSliceWidthOutset) {
10988 bool sliceConsumedTokens = false;
10989 if (ParseBorderImageSlice(false, &sliceConsumedTokens)) {
10990 foundSliceWidthOutset = true;
10992 // [ / <border-image-width>?
10993 if (ExpectSymbol('/', true)) {
10994 bool foundBorderImageWidth = ParseBorderImageWidth(false);
10996 // [ / <border-image-outset>
10997 if (ExpectSymbol('/', true)) {
10998 if (!ParseBorderImageOutset(false)) {
10999 return false;
11001 } else if (!foundBorderImageWidth) {
11002 // If this part has an trailing slash, the whole declaration is
11003 // invalid.
11004 return false;
11008 continue;
11009 } else {
11010 // If we consumed some tokens for <border-image-slice> but did not
11011 // successfully parse it, we have an error.
11012 if (sliceConsumedTokens) {
11013 return false;
11018 // <border-image-repeat>
11019 if (!foundRepeat && ParseBorderImageRepeat(false)) {
11020 foundRepeat = true;
11021 continue;
11024 return false;
11027 return true;
11030 bool
11031 CSSParserImpl::ParseBorderSpacing()
11033 nsCSSValue xValue, yValue;
11034 if (!ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr)) {
11035 return false;
11038 // If we have one length, get the optional second length.
11039 // set the second value equal to the first.
11040 if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) {
11041 ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC, nullptr);
11044 if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) {
11045 AppendValue(eCSSProperty_border_spacing, xValue);
11046 } else {
11047 nsCSSValue pair;
11048 pair.SetPairValue(xValue, yValue);
11049 AppendValue(eCSSProperty_border_spacing, pair);
11051 return true;
11054 bool
11055 CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[],
11056 bool aSetAllSides)
11058 const int32_t numProps = 3;
11059 nsCSSValue values[numProps];
11061 int32_t found = ParseChoice(values, aPropIDs, numProps);
11062 if (found < 1) {
11063 return false;
11066 if ((found & 1) == 0) { // Provide default border-width
11067 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
11069 if ((found & 2) == 0) { // Provide default border-style
11070 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
11072 if ((found & 4) == 0) { // text color will be used
11073 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
11076 if (aSetAllSides) {
11077 static const nsCSSProperty kBorderSources[] = {
11078 eCSSProperty_border_left_color_ltr_source,
11079 eCSSProperty_border_left_color_rtl_source,
11080 eCSSProperty_border_right_color_ltr_source,
11081 eCSSProperty_border_right_color_rtl_source,
11082 eCSSProperty_border_left_style_ltr_source,
11083 eCSSProperty_border_left_style_rtl_source,
11084 eCSSProperty_border_right_style_ltr_source,
11085 eCSSProperty_border_right_style_rtl_source,
11086 eCSSProperty_border_left_width_ltr_source,
11087 eCSSProperty_border_left_width_rtl_source,
11088 eCSSProperty_border_right_width_ltr_source,
11089 eCSSProperty_border_right_width_rtl_source,
11090 eCSSProperty_UNKNOWN
11093 InitBoxPropsAsPhysical(kBorderSources);
11095 // Parsing "border" shorthand; set all four sides to the same thing
11096 for (int32_t index = 0; index < 4; index++) {
11097 NS_ASSERTION(numProps == 3, "This code needs updating");
11098 AppendValue(kBorderWidthIDs[index], values[0]);
11099 AppendValue(kBorderStyleIDs[index], values[1]);
11100 AppendValue(kBorderColorIDs[index], values[2]);
11103 static const nsCSSProperty kBorderColorsProps[] = {
11104 eCSSProperty_border_top_colors,
11105 eCSSProperty_border_right_colors,
11106 eCSSProperty_border_bottom_colors,
11107 eCSSProperty_border_left_colors
11110 // Set the other properties that the border shorthand sets to their
11111 // initial values.
11112 nsCSSValue extraValue;
11113 switch (values[0].GetUnit()) {
11114 case eCSSUnit_Inherit:
11115 case eCSSUnit_Initial:
11116 case eCSSUnit_Unset:
11117 extraValue = values[0];
11118 // Set value of border-image properties to initial/inherit/unset
11119 AppendValue(eCSSProperty_border_image_source, extraValue);
11120 AppendValue(eCSSProperty_border_image_slice, extraValue);
11121 AppendValue(eCSSProperty_border_image_width, extraValue);
11122 AppendValue(eCSSProperty_border_image_outset, extraValue);
11123 AppendValue(eCSSProperty_border_image_repeat, extraValue);
11124 break;
11125 default:
11126 extraValue.SetNoneValue();
11127 SetBorderImageInitialValues();
11128 break;
11130 NS_FOR_CSS_SIDES(side) {
11131 AppendValue(kBorderColorsProps[side], extraValue);
11134 else {
11135 // Just set our one side
11136 for (int32_t index = 0; index < numProps; index++) {
11137 AppendValue(aPropIDs[index], values[index]);
11140 return true;
11143 bool
11144 CSSParserImpl::ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
11145 int32_t aSourceType)
11147 const int32_t numProps = 3;
11148 nsCSSValue values[numProps];
11150 int32_t found = ParseChoice(values, aPropIDs, numProps);
11151 if (found < 1) {
11152 return false;
11155 if ((found & 1) == 0) { // Provide default border-width
11156 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
11158 if ((found & 2) == 0) { // Provide default border-style
11159 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
11161 if ((found & 4) == 0) { // text color will be used
11162 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
11164 for (int32_t index = 0; index < numProps; index++) {
11165 const nsCSSProperty* subprops =
11166 nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]);
11167 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
11168 "not box property with physical vs. logical cascading");
11169 AppendValue(subprops[0], values[index]);
11170 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
11171 AppendValue(subprops[1], typeVal);
11172 AppendValue(subprops[2], typeVal);
11174 return true;
11177 bool
11178 CSSParserImpl::ParseBorderStyle()
11180 static const nsCSSProperty kBorderStyleSources[] = {
11181 eCSSProperty_border_left_style_ltr_source,
11182 eCSSProperty_border_left_style_rtl_source,
11183 eCSSProperty_border_right_style_ltr_source,
11184 eCSSProperty_border_right_style_rtl_source,
11185 eCSSProperty_UNKNOWN
11188 // do this now, in case 4 values weren't specified
11189 InitBoxPropsAsPhysical(kBorderStyleSources);
11190 return ParseBoxProperties(kBorderStyleIDs);
11193 bool
11194 CSSParserImpl::ParseBorderWidth()
11196 static const nsCSSProperty kBorderWidthSources[] = {
11197 eCSSProperty_border_left_width_ltr_source,
11198 eCSSProperty_border_left_width_rtl_source,
11199 eCSSProperty_border_right_width_ltr_source,
11200 eCSSProperty_border_right_width_rtl_source,
11201 eCSSProperty_UNKNOWN
11204 // do this now, in case 4 values weren't specified
11205 InitBoxPropsAsPhysical(kBorderWidthSources);
11206 return ParseBoxProperties(kBorderWidthIDs);
11209 bool
11210 CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty)
11212 nsCSSValue value;
11213 // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
11214 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
11215 nsCSSValueList *cur = value.SetListValue();
11216 for (;;) {
11217 if (!ParseVariant(cur->mValue, VARIANT_COLOR | VARIANT_KEYWORD,
11218 nsCSSProps::kBorderColorKTable)) {
11219 return false;
11221 if (CheckEndProperty()) {
11222 break;
11224 cur->mNext = new nsCSSValueList;
11225 cur = cur->mNext;
11228 AppendValue(aProperty, value);
11229 return true;
11232 // Parse the top level of a calc() expression.
11233 bool
11234 CSSParserImpl::ParseCalc(nsCSSValue &aValue, int32_t aVariantMask)
11236 // Parsing calc expressions requires, in a number of cases, looking
11237 // for a token that is *either* a value of the property or a number.
11238 // This can be done without lookahead when we assume that the property
11239 // values cannot themselves be numbers.
11240 NS_ASSERTION(!(aVariantMask & VARIANT_NUMBER), "unexpected variant mask");
11241 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
11243 bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk;
11244 mUnitlessLengthQuirk = false;
11246 // One-iteration loop so we can break to the error-handling case.
11247 do {
11248 // The toplevel of a calc() is always an nsCSSValue::Array of length 1.
11249 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
11251 if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask))
11252 break;
11254 if (!ExpectSymbol(')', true))
11255 break;
11257 aValue.SetArrayValue(arr, eCSSUnit_Calc);
11258 mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
11259 return true;
11260 } while (false);
11262 SkipUntil(')');
11263 mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
11264 return false;
11267 // We optimize away the <value-expression> production given that
11268 // ParseVariant consumes initial whitespace and we call
11269 // ExpectSymbol(')') with true for aSkipWS.
11270 // * If aVariantMask is VARIANT_NUMBER, this function parses the
11271 // <number-additive-expression> production.
11272 // * If aVariantMask does not contain VARIANT_NUMBER, this function
11273 // parses the <value-additive-expression> production.
11274 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
11275 // whichever one of the productions matches ***and modifies
11276 // aVariantMask*** to reflect which one it has parsed by either
11277 // removing VARIANT_NUMBER or removing all other bits.
11278 // It does so iteratively, but builds the correct recursive
11279 // data structure.
11280 bool
11281 CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue,
11282 int32_t& aVariantMask)
11284 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
11285 nsCSSValue *storage = &aValue;
11286 for (;;) {
11287 bool haveWS;
11288 if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS))
11289 return false;
11291 if (!haveWS || !GetToken(false))
11292 return true;
11293 nsCSSUnit unit;
11294 if (mToken.IsSymbol('+')) {
11295 unit = eCSSUnit_Calc_Plus;
11296 } else if (mToken.IsSymbol('-')) {
11297 unit = eCSSUnit_Calc_Minus;
11298 } else {
11299 UngetToken();
11300 return true;
11302 if (!RequireWhitespace())
11303 return false;
11305 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
11306 arr->Item(0) = aValue;
11307 storage = &arr->Item(1);
11308 aValue.SetArrayValue(arr, unit);
11312 struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
11313 public mozilla::css::CSSValueInputCalcOps
11315 result_type ComputeLeafValue(const nsCSSValue& aValue)
11317 NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
11318 return aValue.GetFloatValue();
11321 float ComputeNumber(const nsCSSValue& aValue)
11323 return mozilla::css::ComputeCalc(aValue, *this);
11327 // * If aVariantMask is VARIANT_NUMBER, this function parses the
11328 // <number-multiplicative-expression> production.
11329 // * If aVariantMask does not contain VARIANT_NUMBER, this function
11330 // parses the <value-multiplicative-expression> production.
11331 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
11332 // whichever one of the productions matches ***and modifies
11333 // aVariantMask*** to reflect which one it has parsed by either
11334 // removing VARIANT_NUMBER or removing all other bits.
11335 // It does so iteratively, but builds the correct recursive data
11336 // structure.
11337 // This function always consumes *trailing* whitespace when it returns
11338 // true; whether there was any such whitespace is returned in the
11339 // aHadFinalWS parameter.
11340 bool
11341 CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
11342 int32_t& aVariantMask,
11343 bool *aHadFinalWS)
11345 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
11346 bool gotValue = false; // already got the part with the unit
11347 bool afterDivision = false;
11349 nsCSSValue *storage = &aValue;
11350 for (;;) {
11351 int32_t variantMask;
11352 if (afterDivision || gotValue) {
11353 variantMask = VARIANT_NUMBER;
11354 } else {
11355 variantMask = aVariantMask | VARIANT_NUMBER;
11357 if (!ParseCalcTerm(*storage, variantMask))
11358 return false;
11359 NS_ABORT_IF_FALSE(variantMask != 0,
11360 "ParseCalcTerm did not set variantMask appropriately");
11361 NS_ABORT_IF_FALSE(!(variantMask & VARIANT_NUMBER) ||
11362 !(variantMask & ~int32_t(VARIANT_NUMBER)),
11363 "ParseCalcTerm did not set variantMask appropriately");
11365 if (variantMask & VARIANT_NUMBER) {
11366 // Simplify the value immediately so we can check for division by
11367 // zero.
11368 ReduceNumberCalcOps ops;
11369 float number = mozilla::css::ComputeCalc(*storage, ops);
11370 if (number == 0.0 && afterDivision)
11371 return false;
11372 storage->SetFloatValue(number, eCSSUnit_Number);
11373 } else {
11374 gotValue = true;
11376 if (storage != &aValue) {
11377 // Simplify any numbers in the Times_L position (which are
11378 // not simplified by the check above).
11379 NS_ABORT_IF_FALSE(storage == &aValue.GetArrayValue()->Item(1),
11380 "unexpected relationship to current storage");
11381 nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
11382 ReduceNumberCalcOps ops;
11383 float number = mozilla::css::ComputeCalc(leftValue, ops);
11384 leftValue.SetFloatValue(number, eCSSUnit_Number);
11388 bool hadWS = RequireWhitespace();
11389 if (!GetToken(false)) {
11390 *aHadFinalWS = hadWS;
11391 break;
11393 nsCSSUnit unit;
11394 if (mToken.IsSymbol('*')) {
11395 unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
11396 afterDivision = false;
11397 } else if (mToken.IsSymbol('/')) {
11398 unit = eCSSUnit_Calc_Divided;
11399 afterDivision = true;
11400 } else {
11401 UngetToken();
11402 *aHadFinalWS = hadWS;
11403 break;
11406 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
11407 arr->Item(0) = aValue;
11408 storage = &arr->Item(1);
11409 aValue.SetArrayValue(arr, unit);
11412 // Adjust aVariantMask (see comments above function) to reflect which
11413 // option we took.
11414 if (aVariantMask & VARIANT_NUMBER) {
11415 if (gotValue) {
11416 aVariantMask &= ~int32_t(VARIANT_NUMBER);
11417 } else {
11418 aVariantMask = VARIANT_NUMBER;
11420 } else {
11421 if (!gotValue) {
11422 // We had to find a value, but we didn't.
11423 return false;
11427 return true;
11430 // * If aVariantMask is VARIANT_NUMBER, this function parses the
11431 // <number-term> production.
11432 // * If aVariantMask does not contain VARIANT_NUMBER, this function
11433 // parses the <value-term> production.
11434 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
11435 // whichever one of the productions matches ***and modifies
11436 // aVariantMask*** to reflect which one it has parsed by either
11437 // removing VARIANT_NUMBER or removing all other bits.
11438 bool
11439 CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask)
11441 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
11442 if (!GetToken(true))
11443 return false;
11444 // Either an additive expression in parentheses...
11445 if (mToken.IsSymbol('(')) {
11446 if (!ParseCalcAdditiveExpression(aValue, aVariantMask) ||
11447 !ExpectSymbol(')', true)) {
11448 SkipUntil(')');
11449 return false;
11451 return true;
11453 // ... or just a value
11454 UngetToken();
11455 // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
11456 // always gets picked up
11457 if (!ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr)) {
11458 return false;
11460 // ...and do the VARIANT_NUMBER check ourselves.
11461 if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
11462 return false;
11464 // If we did the value parsing, we need to adjust aVariantMask to
11465 // reflect which option we took (see above).
11466 if (aVariantMask & VARIANT_NUMBER) {
11467 if (aValue.GetUnit() == eCSSUnit_Number) {
11468 aVariantMask = VARIANT_NUMBER;
11469 } else {
11470 aVariantMask &= ~int32_t(VARIANT_NUMBER);
11473 return true;
11476 // This function consumes all consecutive whitespace and returns whether
11477 // there was any.
11478 bool
11479 CSSParserImpl::RequireWhitespace()
11481 if (!GetToken(false))
11482 return false;
11483 if (mToken.mType != eCSSToken_Whitespace) {
11484 UngetToken();
11485 return false;
11487 // Skip any additional whitespace tokens.
11488 if (GetToken(true)) {
11489 UngetToken();
11491 return true;
11494 bool
11495 CSSParserImpl::ParseRect(nsCSSProperty aPropID)
11497 nsCSSValue val;
11498 if (ParseVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) {
11499 AppendValue(aPropID, val);
11500 return true;
11503 if (! GetToken(true)) {
11504 return false;
11507 if (mToken.mType == eCSSToken_Function &&
11508 mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
11509 nsCSSRect& rect = val.SetRectValue();
11510 bool useCommas;
11511 NS_FOR_CSS_SIDES(side) {
11512 if (! ParseVariant(rect.*(nsCSSRect::sides[side]),
11513 VARIANT_AL, nullptr)) {
11514 return false;
11516 if (side == 0) {
11517 useCommas = ExpectSymbol(',', true);
11518 } else if (useCommas && side < 3) {
11519 // Skip optional commas between elements, but only if the first
11520 // separator was a comma.
11521 if (!ExpectSymbol(',', true)) {
11522 return false;
11526 if (!ExpectSymbol(')', true)) {
11527 return false;
11529 } else {
11530 UngetToken();
11531 return false;
11534 AppendValue(aPropID, val);
11535 return true;
11538 bool
11539 CSSParserImpl::ParseColumns()
11541 // We use a similar "fake value" hack to ParseListStyle, because
11542 // "auto" is acceptable for both column-count and column-width.
11543 // If the fake "auto" value is found, and one of the real values isn't,
11544 // that means the fake auto value is meant for the real value we didn't
11545 // find.
11546 static const nsCSSProperty columnIDs[] = {
11547 eCSSPropertyExtra_x_auto_value,
11548 eCSSProperty__moz_column_count,
11549 eCSSProperty__moz_column_width
11551 const int32_t numProps = MOZ_ARRAY_LENGTH(columnIDs);
11553 nsCSSValue values[numProps];
11554 int32_t found = ParseChoice(values, columnIDs, numProps);
11555 if (found < 1) {
11556 return false;
11558 if ((found & (1|2|4)) == (1|2|4) &&
11559 values[0].GetUnit() == eCSSUnit_Auto) {
11560 // We filled all 3 values, which is invalid
11561 return false;
11564 if ((found & 2) == 0) {
11565 // Provide auto column-count
11566 values[1].SetAutoValue();
11568 if ((found & 4) == 0) {
11569 // Provide auto column-width
11570 values[2].SetAutoValue();
11573 // Start at index 1 to skip the fake auto value.
11574 for (int32_t index = 1; index < numProps; index++) {
11575 AppendValue(columnIDs[index], values[index]);
11577 return true;
11580 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
11581 VARIANT_KEYWORD)
11582 bool
11583 CSSParserImpl::ParseContent()
11585 // We need to divide the 'content' keywords into two classes for
11586 // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable.
11587 static const KTableValue kContentListKWs[] = {
11588 eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE,
11589 eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE,
11590 eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE,
11591 eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE,
11592 eCSSKeyword_UNKNOWN,-1
11595 static const KTableValue kContentSolitaryKWs[] = {
11596 eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT,
11597 eCSSKeyword_UNKNOWN,-1
11600 // Verify that these two lists add up to the size of
11601 // nsCSSProps::kContentKTable.
11602 NS_ABORT_IF_FALSE(nsCSSProps::kContentKTable[
11603 ArrayLength(kContentListKWs) +
11604 ArrayLength(kContentSolitaryKWs) - 4] ==
11605 eCSSKeyword_UNKNOWN &&
11606 nsCSSProps::kContentKTable[
11607 ArrayLength(kContentListKWs) +
11608 ArrayLength(kContentSolitaryKWs) - 3] == -1,
11609 "content keyword tables out of sync");
11611 nsCSSValue value;
11612 // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must
11613 // be alone
11614 if (!ParseVariant(value, VARIANT_HMK | VARIANT_NONE,
11615 kContentSolitaryKWs)) {
11616 nsCSSValueList* cur = value.SetListValue();
11617 for (;;) {
11618 if (!ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs)) {
11619 return false;
11621 if (CheckEndProperty()) {
11622 break;
11624 cur->mNext = new nsCSSValueList;
11625 cur = cur->mNext;
11628 AppendValue(eCSSProperty_content, value);
11629 return true;
11632 bool
11633 CSSParserImpl::ParseCounterData(nsCSSProperty aPropID)
11635 static const nsCSSKeyword kCounterDataKTable[] = {
11636 eCSSKeyword_none,
11637 eCSSKeyword_UNKNOWN
11639 nsCSSValue value;
11640 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
11641 if (!GetToken(true)) {
11642 return false;
11644 if (mToken.mType != eCSSToken_Ident) {
11645 UngetToken();
11646 return false;
11649 nsCSSValuePairList *cur = value.SetPairListValue();
11650 for (;;) {
11651 if (!ParseCustomIdent(cur->mXValue, mToken.mIdent, kCounterDataKTable)) {
11652 return false;
11654 if (!GetToken(true)) {
11655 break;
11657 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
11658 cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
11659 } else {
11660 UngetToken();
11662 if (!GetToken(true)) {
11663 break;
11665 if (mToken.mType != eCSSToken_Ident) {
11666 UngetToken();
11667 break;
11669 cur->mNext = new nsCSSValuePairList;
11670 cur = cur->mNext;
11673 AppendValue(aPropID, value);
11674 return true;
11677 bool
11678 CSSParserImpl::ParseCursor()
11680 nsCSSValue value;
11681 // 'inherit', 'initial' and 'unset' must be alone
11682 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
11683 nsCSSValueList* cur = value.SetListValue();
11684 for (;;) {
11685 if (!ParseVariant(cur->mValue, VARIANT_UK, nsCSSProps::kCursorKTable)) {
11686 return false;
11688 if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last
11689 break;
11692 // We have a URL, so make a value array with three values.
11693 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
11694 val->Item(0) = cur->mValue;
11696 // Parse optional x and y position of cursor hotspot (css3-ui).
11697 if (ParseVariant(val->Item(1), VARIANT_NUMBER, nullptr)) {
11698 // If we have one number, we must have two.
11699 if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nullptr)) {
11700 return false;
11703 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
11705 if (!ExpectSymbol(',', true)) { // url must not be last
11706 return false;
11708 cur->mNext = new nsCSSValueList;
11709 cur = cur->mNext;
11712 AppendValue(eCSSProperty_cursor, value);
11713 return true;
11717 bool
11718 CSSParserImpl::ParseFont()
11720 static const nsCSSProperty fontIDs[] = {
11721 eCSSProperty_font_style,
11722 eCSSProperty_font_variant_caps,
11723 eCSSProperty_font_weight
11726 nsCSSValue family;
11727 if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
11728 if (eCSSUnit_Inherit == family.GetUnit() ||
11729 eCSSUnit_Initial == family.GetUnit() ||
11730 eCSSUnit_Unset == family.GetUnit()) {
11731 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
11732 AppendValue(eCSSProperty_font_family, family);
11733 AppendValue(eCSSProperty_font_style, family);
11734 AppendValue(eCSSProperty_font_weight, family);
11735 AppendValue(eCSSProperty_font_size, family);
11736 AppendValue(eCSSProperty_line_height, family);
11737 AppendValue(eCSSProperty_font_stretch, family);
11738 AppendValue(eCSSProperty_font_size_adjust, family);
11739 AppendValue(eCSSProperty_font_feature_settings, family);
11740 AppendValue(eCSSProperty_font_language_override, family);
11741 AppendValue(eCSSProperty_font_kerning, family);
11742 AppendValue(eCSSProperty_font_synthesis, family);
11743 AppendValue(eCSSProperty_font_variant_alternates, family);
11744 AppendValue(eCSSProperty_font_variant_caps, family);
11745 AppendValue(eCSSProperty_font_variant_east_asian, family);
11746 AppendValue(eCSSProperty_font_variant_ligatures, family);
11747 AppendValue(eCSSProperty_font_variant_numeric, family);
11748 AppendValue(eCSSProperty_font_variant_position, family);
11750 else {
11751 AppendValue(eCSSProperty__x_system_font, family);
11752 nsCSSValue systemFont(eCSSUnit_System_Font);
11753 AppendValue(eCSSProperty_font_family, systemFont);
11754 AppendValue(eCSSProperty_font_style, systemFont);
11755 AppendValue(eCSSProperty_font_weight, systemFont);
11756 AppendValue(eCSSProperty_font_size, systemFont);
11757 AppendValue(eCSSProperty_line_height, systemFont);
11758 AppendValue(eCSSProperty_font_stretch, systemFont);
11759 AppendValue(eCSSProperty_font_size_adjust, systemFont);
11760 AppendValue(eCSSProperty_font_feature_settings, systemFont);
11761 AppendValue(eCSSProperty_font_language_override, systemFont);
11762 AppendValue(eCSSProperty_font_kerning, systemFont);
11763 AppendValue(eCSSProperty_font_synthesis, systemFont);
11764 AppendValue(eCSSProperty_font_variant_alternates, systemFont);
11765 AppendValue(eCSSProperty_font_variant_caps, systemFont);
11766 AppendValue(eCSSProperty_font_variant_east_asian, systemFont);
11767 AppendValue(eCSSProperty_font_variant_ligatures, systemFont);
11768 AppendValue(eCSSProperty_font_variant_numeric, systemFont);
11769 AppendValue(eCSSProperty_font_variant_position, systemFont);
11771 return true;
11774 // Get optional font-style, font-variant and font-weight (in any order)
11775 const int32_t numProps = 3;
11776 nsCSSValue values[numProps];
11777 int32_t found = ParseChoice(values, fontIDs, numProps);
11778 if (found < 0 ||
11779 eCSSUnit_Inherit == values[0].GetUnit() ||
11780 eCSSUnit_Initial == values[0].GetUnit() ||
11781 eCSSUnit_Unset == values[0].GetUnit()) { // illegal data
11782 return false;
11784 if ((found & 1) == 0) {
11785 // Provide default font-style
11786 values[0].SetIntValue(NS_FONT_STYLE_NORMAL, eCSSUnit_Enumerated);
11788 if ((found & 2) == 0) {
11789 // Provide default font-variant
11790 values[1].SetNormalValue();
11791 } else {
11792 if (values[1].GetUnit() == eCSSUnit_Enumerated &&
11793 values[1].GetIntValue() != NS_FONT_VARIANT_CAPS_SMALLCAPS) {
11794 // only normal or small-caps is allowed in font shorthand
11795 // this also assumes other values for font-variant-caps never overlap
11796 // possible values for style or weight
11797 return false;
11800 if ((found & 4) == 0) {
11801 // Provide default font-weight
11802 values[2].SetIntValue(NS_FONT_WEIGHT_NORMAL, eCSSUnit_Enumerated);
11805 // Get mandatory font-size
11806 nsCSSValue size;
11807 if (! ParseNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP,
11808 nsCSSProps::kFontSizeKTable)) {
11809 return false;
11812 // Get optional "/" line-height
11813 nsCSSValue lineHeight;
11814 if (ExpectSymbol('/', true)) {
11815 if (! ParseNonNegativeVariant(lineHeight,
11816 VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
11817 nullptr)) {
11818 return false;
11821 else {
11822 lineHeight.SetNormalValue();
11825 // Get final mandatory font-family
11826 nsAutoParseCompoundProperty compound(this);
11827 if (ParseFamily(family)) {
11828 if (eCSSUnit_Inherit != family.GetUnit() &&
11829 eCSSUnit_Initial != family.GetUnit() &&
11830 eCSSUnit_Unset != family.GetUnit()) {
11831 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
11832 AppendValue(eCSSProperty_font_family, family);
11833 AppendValue(eCSSProperty_font_style, values[0]);
11834 AppendValue(eCSSProperty_font_variant_caps, values[1]);
11835 AppendValue(eCSSProperty_font_weight, values[2]);
11836 AppendValue(eCSSProperty_font_size, size);
11837 AppendValue(eCSSProperty_line_height, lineHeight);
11838 AppendValue(eCSSProperty_font_stretch,
11839 nsCSSValue(NS_FONT_STRETCH_NORMAL, eCSSUnit_Enumerated));
11840 AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
11841 AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal));
11842 AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal));
11843 AppendValue(eCSSProperty_font_kerning,
11844 nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated));
11845 AppendValue(eCSSProperty_font_synthesis,
11846 nsCSSValue(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE,
11847 eCSSUnit_Enumerated));
11848 AppendValue(eCSSProperty_font_variant_alternates,
11849 nsCSSValue(eCSSUnit_Normal));
11850 AppendValue(eCSSProperty_font_variant_east_asian,
11851 nsCSSValue(eCSSUnit_Normal));
11852 AppendValue(eCSSProperty_font_variant_ligatures,
11853 nsCSSValue(eCSSUnit_Normal));
11854 AppendValue(eCSSProperty_font_variant_numeric,
11855 nsCSSValue(eCSSUnit_Normal));
11856 AppendValue(eCSSProperty_font_variant_position,
11857 nsCSSValue(eCSSUnit_Normal));
11858 return true;
11861 return false;
11864 bool
11865 CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue)
11867 if (!ParseVariant(aValue, VARIANT_HK | VARIANT_NONE,
11868 nsCSSProps::kFontSynthesisKTable)) {
11869 return false;
11872 // first value 'none' ==> done
11873 if (eCSSUnit_None == aValue.GetUnit() ||
11874 eCSSUnit_Initial == aValue.GetUnit() ||
11875 eCSSUnit_Inherit == aValue.GetUnit() ||
11876 eCSSUnit_Unset == aValue.GetUnit())
11878 return true;
11881 // look for a second value
11882 int32_t intValue = aValue.GetIntValue();
11883 nsCSSValue nextValue;
11885 if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) {
11886 int32_t nextIntValue = nextValue.GetIntValue();
11887 if (nextIntValue & intValue) {
11888 return false;
11890 aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
11893 return true;
11896 // font-variant-alternates allows for a combination of multiple
11897 // simple enumerated values and functional values. Functional values have
11898 // parameter lists with one or more idents which are later resolved
11899 // based on values defined in @font-feature-value rules.
11901 // font-variant-alternates: swash(flowing) historical-forms styleset(alt-g, alt-m);
11903 // So for this the nsCSSValue is set to a pair value, with one
11904 // value for a bitmask of both simple and functional property values
11905 // and another value containing a ValuePairList with lists of idents
11906 // for each functional property value.
11908 // pairValue
11909 // o intValue
11910 // NS_FONT_VARIANT_ALTERNATES_SWASH |
11911 // NS_FONT_VARIANT_ALTERNATES_STYLESET
11912 // o valuePairList, each element with
11913 // - intValue - indicates which alternate
11914 // - string or valueList of strings
11916 // Note: when only 'historical-forms' is specified, there are no
11917 // functional values to store, in which case the valuePairList is a
11918 // single element dummy list. In all other cases, the length of the
11919 // list will match the number of functional values.
11921 #define MAX_ALLOWED_FEATURES 512
11923 static uint16_t
11924 MaxElementsForAlternateType(nsCSSKeyword keyword)
11926 uint16_t maxElems = 1;
11927 if (keyword == eCSSKeyword_styleset ||
11928 keyword == eCSSKeyword_character_variant) {
11929 maxElems = MAX_ALLOWED_FEATURES;
11931 return maxElems;
11934 bool
11935 CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature,
11936 nsCSSValue& aValue)
11938 if (!GetToken(true)) {
11939 return false;
11942 bool isIdent = (mToken.mType == eCSSToken_Ident);
11943 if (mToken.mType != eCSSToken_Function && !isIdent) {
11944 UngetToken();
11945 return false;
11948 // ident ==> simple enumerated prop val (e.g. historical-forms)
11949 // function ==> e.g. swash(flowing) styleset(alt-g, alt-m)
11951 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
11952 if (!(eCSSKeyword_UNKNOWN < keyword &&
11953 nsCSSProps::FindKeyword(keyword,
11954 (isIdent ?
11955 nsCSSProps::kFontVariantAlternatesKTable :
11956 nsCSSProps::kFontVariantAlternatesFuncsKTable),
11957 aWhichFeature)))
11959 // failed, pop token
11960 UngetToken();
11961 return false;
11964 if (isIdent) {
11965 aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated);
11966 return true;
11969 return ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER,
11970 1, MaxElementsForAlternateType(keyword), aValue);
11973 bool
11974 CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue)
11976 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
11977 return true;
11980 // iterate through parameters
11981 nsCSSValue listValue;
11982 int32_t feature, featureFlags = 0;
11984 // if no functional values, this may be a list with a single, unused element
11985 listValue.SetListValue();
11987 nsCSSValueList* list = nullptr;
11988 nsCSSValue value;
11989 while (ParseSingleAlternate(feature, value)) {
11991 // check to make sure value not already set
11992 if (feature == 0 ||
11993 feature & featureFlags) {
11994 return false;
11997 featureFlags |= feature;
11999 // if function, need to add to the list of functions
12000 if (value.GetUnit() == eCSSUnit_Function) {
12001 if (!list) {
12002 list = listValue.GetListValue();
12003 } else {
12004 list->mNext = new nsCSSValueList;
12005 list = list->mNext;
12007 list->mValue = value;
12011 if (featureFlags == 0) {
12012 // ParseSingleAlternate failed the first time through the loop.
12013 return false;
12016 nsCSSValue featureValue;
12017 featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated);
12018 aValue.SetPairValue(featureValue, listValue);
12020 return true;
12023 bool
12024 CSSParserImpl::MergeBitmaskValue(int32_t aNewValue,
12025 const int32_t aMasks[],
12026 int32_t& aMergedValue)
12028 // check to make sure value not already set
12029 if (aNewValue & aMergedValue) {
12030 return false;
12033 const int32_t *m = aMasks;
12034 int32_t c = 0;
12036 while (*m != MASK_END_VALUE) {
12037 if (*m & aNewValue) {
12038 c = aMergedValue & *m;
12039 break;
12041 m++;
12044 if (c) {
12045 return false;
12048 aMergedValue |= aNewValue;
12049 return true;
12052 // aMasks - array of masks for mutually-exclusive property values,
12053 // e.g. proportial-nums, tabular-nums
12055 bool
12056 CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue,
12057 const KTableValue aKeywordTable[],
12058 const int32_t aMasks[])
12060 // Parse at least one keyword
12061 if (!ParseEnum(aValue, aKeywordTable)) {
12062 return false;
12065 // look for more values
12066 nsCSSValue nextValue;
12067 int32_t mergedValue = aValue.GetIntValue();
12069 while (ParseEnum(nextValue, aKeywordTable))
12071 if (!MergeBitmaskValue(nextValue.GetIntValue(), aMasks, mergedValue)) {
12072 return false;
12076 aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated);
12078 return true;
12081 static const int32_t maskEastAsian[] = {
12082 NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK,
12083 NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK,
12084 MASK_END_VALUE
12087 bool
12088 CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue)
12090 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
12091 return true;
12094 NS_ASSERTION(maskEastAsian[ArrayLength(maskEastAsian) - 1] ==
12095 MASK_END_VALUE,
12096 "incorrectly terminated array");
12098 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable,
12099 maskEastAsian);
12102 static const int32_t maskLigatures[] = {
12103 NS_FONT_VARIANT_LIGATURES_COMMON_MASK,
12104 NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK,
12105 NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK,
12106 NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK,
12107 MASK_END_VALUE
12110 bool
12111 CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue)
12113 if (ParseVariant(aValue,
12114 VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE,
12115 nullptr)) {
12116 return true;
12119 NS_ASSERTION(maskLigatures[ArrayLength(maskLigatures) - 1] ==
12120 MASK_END_VALUE,
12121 "incorrectly terminated array");
12123 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable,
12124 maskLigatures);
12127 static const int32_t maskNumeric[] = {
12128 NS_FONT_VARIANT_NUMERIC_FIGURE_MASK,
12129 NS_FONT_VARIANT_NUMERIC_SPACING_MASK,
12130 NS_FONT_VARIANT_NUMERIC_FRACTION_MASK,
12131 MASK_END_VALUE
12134 bool
12135 CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue)
12137 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
12138 return true;
12141 NS_ASSERTION(maskNumeric[ArrayLength(maskNumeric) - 1] ==
12142 MASK_END_VALUE,
12143 "incorrectly terminated array");
12145 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable,
12146 maskNumeric);
12149 bool
12150 CSSParserImpl::ParseFontVariant()
12152 // parse single values - normal/inherit/none
12153 nsCSSValue value;
12154 nsCSSValue normal(eCSSUnit_Normal);
12156 if (ParseVariant(value,
12157 VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE,
12158 nullptr)) {
12159 AppendValue(eCSSProperty_font_variant_ligatures, value);
12160 if (eCSSUnit_None == value.GetUnit()) {
12161 // 'none' applies the value 'normal' to all properties other
12162 // than 'font-variant-ligatures'
12163 value.SetNormalValue();
12165 AppendValue(eCSSProperty_font_variant_alternates, value);
12166 AppendValue(eCSSProperty_font_variant_caps, value);
12167 AppendValue(eCSSProperty_font_variant_east_asian, value);
12168 AppendValue(eCSSProperty_font_variant_numeric, value);
12169 AppendValue(eCSSProperty_font_variant_position, value);
12170 return true;
12173 // set each of the individual subproperties
12174 int32_t altFeatures = 0, capsFeatures = 0, eastAsianFeatures = 0,
12175 ligFeatures = 0, numericFeatures = 0, posFeatures = 0;
12176 nsCSSValue altListValue;
12177 nsCSSValueList* altList = nullptr;
12179 // if no functional values, this may be a list with a single, unused element
12180 altListValue.SetListValue();
12182 bool foundValid = false; // found at least one proper value
12183 while (GetToken(true)) {
12184 // only an ident or a function at this point
12185 bool isFunction = (mToken.mType == eCSSToken_Function);
12186 if (mToken.mType != eCSSToken_Ident && !isFunction) {
12187 UngetToken();
12188 break;
12191 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
12192 if (keyword == eCSSKeyword_UNKNOWN) {
12193 UngetToken();
12194 return false;
12197 int32_t feature;
12199 // function? ==> font-variant-alternates
12200 if (isFunction) {
12201 if (!nsCSSProps::FindKeyword(keyword,
12202 nsCSSProps::kFontVariantAlternatesFuncsKTable,
12203 feature) ||
12204 (feature & altFeatures)) {
12205 UngetToken();
12206 return false;
12209 altFeatures |= feature;
12210 nsCSSValue funcValue;
12211 if (!ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER, 1,
12212 MaxElementsForAlternateType(keyword), funcValue) ||
12213 funcValue.GetUnit() != eCSSUnit_Function) {
12214 UngetToken();
12215 return false;
12218 if (!altList) {
12219 altList = altListValue.GetListValue();
12220 } else {
12221 altList->mNext = new nsCSSValueList;
12222 altList = altList->mNext;
12224 altList->mValue = funcValue;
12225 } else if (nsCSSProps::FindKeyword(keyword,
12226 nsCSSProps::kFontVariantCapsKTable,
12227 feature)) {
12228 if (capsFeatures != 0) {
12229 // multiple values for font-variant-caps
12230 UngetToken();
12231 return false;
12233 capsFeatures = feature;
12234 } else if (nsCSSProps::FindKeyword(keyword,
12235 nsCSSProps::kFontVariantAlternatesKTable,
12236 feature)) {
12237 if (feature & altFeatures) {
12238 // same value repeated
12239 UngetToken();
12240 return false;
12242 altFeatures |= feature;
12243 } else if (nsCSSProps::FindKeyword(keyword,
12244 nsCSSProps::kFontVariantEastAsianKTable,
12245 feature)) {
12246 if (!MergeBitmaskValue(feature, maskEastAsian, eastAsianFeatures)) {
12247 // multiple mutually exclusive values
12248 UngetToken();
12249 return false;
12251 } else if (nsCSSProps::FindKeyword(keyword,
12252 nsCSSProps::kFontVariantLigaturesKTable,
12253 feature)) {
12254 if (keyword == eCSSKeyword_none ||
12255 !MergeBitmaskValue(feature, maskLigatures, ligFeatures)) {
12256 // none or multiple mutually exclusive values
12257 UngetToken();
12258 return false;
12260 } else if (nsCSSProps::FindKeyword(keyword,
12261 nsCSSProps::kFontVariantNumericKTable,
12262 feature)) {
12263 if (!MergeBitmaskValue(feature, maskNumeric, numericFeatures)) {
12264 // multiple mutually exclusive values
12265 UngetToken();
12266 return false;
12268 } else if (nsCSSProps::FindKeyword(keyword,
12269 nsCSSProps::kFontVariantPositionKTable,
12270 feature)) {
12271 if (posFeatures != 0) {
12272 // multiple values for font-variant-caps
12273 UngetToken();
12274 return false;
12276 posFeatures = feature;
12277 } else {
12278 // bogus keyword, bail...
12279 UngetToken();
12280 return false;
12283 foundValid = true;
12286 if (!foundValid) {
12287 return false;
12290 if (altFeatures) {
12291 nsCSSValue featureValue;
12292 featureValue.SetIntValue(altFeatures, eCSSUnit_Enumerated);
12293 value.SetPairValue(featureValue, altListValue);
12294 AppendValue(eCSSProperty_font_variant_alternates, value);
12295 } else {
12296 AppendValue(eCSSProperty_font_variant_alternates, normal);
12299 if (capsFeatures) {
12300 value.SetIntValue(capsFeatures, eCSSUnit_Enumerated);
12301 AppendValue(eCSSProperty_font_variant_caps, value);
12302 } else {
12303 AppendValue(eCSSProperty_font_variant_caps, normal);
12306 if (eastAsianFeatures) {
12307 value.SetIntValue(eastAsianFeatures, eCSSUnit_Enumerated);
12308 AppendValue(eCSSProperty_font_variant_east_asian, value);
12309 } else {
12310 AppendValue(eCSSProperty_font_variant_east_asian, normal);
12313 if (ligFeatures) {
12314 value.SetIntValue(ligFeatures, eCSSUnit_Enumerated);
12315 AppendValue(eCSSProperty_font_variant_ligatures, value);
12316 } else {
12317 AppendValue(eCSSProperty_font_variant_ligatures, normal);
12320 if (numericFeatures) {
12321 value.SetIntValue(numericFeatures, eCSSUnit_Enumerated);
12322 AppendValue(eCSSProperty_font_variant_numeric, value);
12323 } else {
12324 AppendValue(eCSSProperty_font_variant_numeric, normal);
12327 if (posFeatures) {
12328 value.SetIntValue(posFeatures, eCSSUnit_Enumerated);
12329 AppendValue(eCSSProperty_font_variant_position, value);
12330 } else {
12331 AppendValue(eCSSProperty_font_variant_position, normal);
12334 return true;
12337 bool
12338 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
12340 if (ParseVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT,
12341 nsCSSProps::kFontWeightKTable)) {
12342 if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
12343 int32_t intValue = aValue.GetIntValue();
12344 if ((100 <= intValue) &&
12345 (intValue <= 900) &&
12346 (0 == (intValue % 100))) {
12347 return true;
12348 } else {
12349 UngetToken();
12350 return false;
12353 return true;
12355 return false;
12358 bool
12359 CSSParserImpl::ParseOneFamily(nsAString& aFamily,
12360 bool& aOneKeyword,
12361 bool& aQuoted)
12363 if (!GetToken(true))
12364 return false;
12366 nsCSSToken* tk = &mToken;
12368 aOneKeyword = false;
12369 aQuoted = false;
12370 if (eCSSToken_Ident == tk->mType) {
12371 aOneKeyword = true;
12372 aFamily.Append(tk->mIdent);
12373 for (;;) {
12374 if (!GetToken(false))
12375 break;
12377 if (eCSSToken_Ident == tk->mType) {
12378 aOneKeyword = false;
12379 // We had at least another keyword before.
12380 // "If a sequence of identifiers is given as a font family name,
12381 // the computed value is the name converted to a string by joining
12382 // all the identifiers in the sequence by single spaces."
12383 // -- CSS 2.1, section 15.3
12384 // Whitespace tokens do not actually matter,
12385 // identifier tokens can be separated by comments.
12386 aFamily.Append(char16_t(' '));
12387 aFamily.Append(tk->mIdent);
12388 } else if (eCSSToken_Whitespace != tk->mType) {
12389 UngetToken();
12390 break;
12393 return true;
12395 } else if (eCSSToken_String == tk->mType) {
12396 aQuoted = true;
12397 aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
12398 return true;
12400 } else {
12401 UngetToken();
12402 return false;
12407 static bool
12408 AppendGeneric(nsCSSKeyword aKeyword, FontFamilyList *aFamilyList)
12410 switch (aKeyword) {
12411 case eCSSKeyword_serif:
12412 aFamilyList->Append(FontFamilyName(eFamily_serif));
12413 return true;
12414 case eCSSKeyword_sans_serif:
12415 aFamilyList->Append(FontFamilyName(eFamily_sans_serif));
12416 return true;
12417 case eCSSKeyword_monospace:
12418 aFamilyList->Append(FontFamilyName(eFamily_monospace));
12419 return true;
12420 case eCSSKeyword_cursive:
12421 aFamilyList->Append(FontFamilyName(eFamily_cursive));
12422 return true;
12423 case eCSSKeyword_fantasy:
12424 aFamilyList->Append(FontFamilyName(eFamily_fantasy));
12425 return true;
12426 case eCSSKeyword__moz_fixed:
12427 aFamilyList->Append(FontFamilyName(eFamily_moz_fixed));
12428 return true;
12429 default:
12430 break;
12433 return false;
12436 bool
12437 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
12439 nsRefPtr<css::FontFamilyListRefCnt> familyList =
12440 new css::FontFamilyListRefCnt();
12441 nsAutoString family;
12442 bool single, quoted;
12444 // keywords only have meaning in the first position
12445 if (!ParseOneFamily(family, single, quoted))
12446 return false;
12448 // check for keywords, but only when keywords appear by themselves
12449 // i.e. not in compounds such as font-family: default blah;
12450 bool foundGeneric = false;
12451 if (single) {
12452 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
12453 switch (keyword) {
12454 case eCSSKeyword_inherit:
12455 aValue.SetInheritValue();
12456 return true;
12457 case eCSSKeyword_default:
12458 // 605231 - don't parse unquoted 'default' reserved keyword
12459 return false;
12460 case eCSSKeyword_initial:
12461 aValue.SetInitialValue();
12462 return true;
12463 case eCSSKeyword_unset:
12464 if (nsLayoutUtils::UnsetValueEnabled()) {
12465 aValue.SetUnsetValue();
12466 return true;
12468 break;
12469 case eCSSKeyword__moz_use_system_font:
12470 if (!IsParsingCompoundProperty()) {
12471 aValue.SetSystemFontValue();
12472 return true;
12474 break;
12475 default:
12476 foundGeneric = AppendGeneric(keyword, familyList);
12480 if (!foundGeneric) {
12481 familyList->Append(
12482 FontFamilyName(family, (quoted ? eQuotedName : eUnquotedName)));
12485 for (;;) {
12486 if (!ExpectSymbol(',', true))
12487 break;
12489 nsAutoString nextFamily;
12490 if (!ParseOneFamily(nextFamily, single, quoted))
12491 return false;
12493 // at this point unquoted keywords are not allowed
12494 // as font family names but can appear within names
12495 foundGeneric = false;
12496 if (single) {
12497 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily);
12498 switch (keyword) {
12499 case eCSSKeyword_inherit:
12500 case eCSSKeyword_initial:
12501 case eCSSKeyword_default:
12502 case eCSSKeyword__moz_use_system_font:
12503 return false;
12504 case eCSSKeyword_unset:
12505 if (nsLayoutUtils::UnsetValueEnabled()) {
12506 return false;
12508 break;
12509 default:
12510 foundGeneric = AppendGeneric(keyword, familyList);
12511 break;
12515 if (!foundGeneric) {
12516 familyList->Append(
12517 FontFamilyName(nextFamily, (quoted ? eQuotedName : eUnquotedName)));
12521 if (familyList->IsEmpty()) {
12522 return false;
12525 aValue.SetFontFamilyListValue(familyList);
12526 return true;
12529 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
12530 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
12531 // local-src: 'local(' ( string | ident ) ')'
12533 bool
12534 CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
12536 // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>?
12537 InfallibleTArray<nsCSSValue> values;
12538 nsCSSValue cur;
12539 for (;;) {
12540 if (!GetToken(true))
12541 break;
12543 if (mToken.mType == eCSSToken_URL) {
12544 SetValueToURL(cur, mToken.mIdent);
12545 values.AppendElement(cur);
12546 if (!ParseFontSrcFormat(values))
12547 return false;
12549 } else if (mToken.mType == eCSSToken_Function &&
12550 mToken.mIdent.LowerCaseEqualsLiteral("local")) {
12551 // css3-fonts does not specify a formal grammar for local().
12552 // The text permits both unquoted identifiers and quoted
12553 // strings. We resolve this ambiguity in the spec by
12554 // assuming that the appropriate production is a single
12555 // <family-name>, possibly surrounded by whitespace.
12557 nsAutoString family;
12558 bool single, quoted;
12559 if (!ParseOneFamily(family, single, quoted)) {
12560 SkipUntil(')');
12561 return false;
12563 if (!ExpectSymbol(')', true)) {
12564 SkipUntil(')');
12565 return false;
12568 // reject generics
12569 if (single) {
12570 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
12571 switch (keyword) {
12572 case eCSSKeyword_serif:
12573 case eCSSKeyword_sans_serif:
12574 case eCSSKeyword_monospace:
12575 case eCSSKeyword_cursive:
12576 case eCSSKeyword_fantasy:
12577 case eCSSKeyword__moz_fixed:
12578 return false;
12579 default:
12580 break;
12584 cur.SetStringValue(family, eCSSUnit_Local_Font);
12585 values.AppendElement(cur);
12586 } else {
12587 // We don't know what to do with this token; unget it and error out
12588 UngetToken();
12589 return false;
12592 if (!ExpectSymbol(',', true))
12593 break;
12596 if (values.Length() == 0)
12597 return false;
12599 nsRefPtr<nsCSSValue::Array> srcVals
12600 = nsCSSValue::Array::Create(values.Length());
12602 uint32_t i;
12603 for (i = 0; i < values.Length(); i++)
12604 srcVals->Item(i) = values[i];
12605 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
12606 return true;
12609 bool
12610 CSSParserImpl::ParseFontSrcFormat(InfallibleTArray<nsCSSValue> & values)
12612 if (!GetToken(true))
12613 return true; // EOF harmless here
12614 if (mToken.mType != eCSSToken_Function ||
12615 !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
12616 UngetToken();
12617 return true;
12620 do {
12621 if (!GetToken(true))
12622 return false; // EOF - no need for SkipUntil
12624 if (mToken.mType != eCSSToken_String) {
12625 UngetToken();
12626 SkipUntil(')');
12627 return false;
12630 nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
12631 values.AppendElement(cur);
12632 } while (ExpectSymbol(',', true));
12634 if (!ExpectSymbol(')', true)) {
12635 SkipUntil(')');
12636 return false;
12639 return true;
12642 // font-ranges: urange ( ',' urange )*
12643 bool
12644 CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
12646 InfallibleTArray<uint32_t> ranges;
12647 for (;;) {
12648 if (!GetToken(true))
12649 break;
12651 if (mToken.mType != eCSSToken_URange) {
12652 UngetToken();
12653 break;
12656 // An invalid range token is a parsing error, causing the entire
12657 // descriptor to be ignored.
12658 if (!mToken.mIntegerValid)
12659 return false;
12661 uint32_t low = mToken.mInteger;
12662 uint32_t high = mToken.mInteger2;
12664 // A range that descends, or a range that is entirely outside the
12665 // current range of Unicode (U+0-10FFFF) is ignored, but does not
12666 // invalidate the descriptor. A range that straddles the high end
12667 // is clipped.
12668 if (low <= 0x10FFFF && low <= high) {
12669 if (high > 0x10FFFF)
12670 high = 0x10FFFF;
12672 ranges.AppendElement(low);
12673 ranges.AppendElement(high);
12675 if (!ExpectSymbol(',', true))
12676 break;
12679 if (ranges.Length() == 0)
12680 return false;
12682 nsRefPtr<nsCSSValue::Array> srcVals
12683 = nsCSSValue::Array::Create(ranges.Length());
12685 for (uint32_t i = 0; i < ranges.Length(); i++)
12686 srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer);
12687 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
12688 return true;
12691 // font-feature-settings: normal | <feature-tag-value> [, <feature-tag-value>]*
12692 // <feature-tag-value> = <string> [ <integer> | on | off ]?
12694 // minimum - "tagx", "tagy", "tagz"
12695 // edge error case - "tagx" on 1, "tagx" "tagy", "tagx" -1, "tagx" big
12697 // pair value is always x = string, y = int
12699 // font feature tags must be four ASCII characters
12700 #define FEATURE_TAG_LENGTH 4
12702 static bool
12703 ValidFontFeatureTag(const nsString& aTag)
12705 if (aTag.Length() != FEATURE_TAG_LENGTH) {
12706 return false;
12708 uint32_t i;
12709 for (i = 0; i < FEATURE_TAG_LENGTH; i++) {
12710 uint32_t ch = aTag[i];
12711 if (ch < 0x20 || ch > 0x7e) {
12712 return false;
12715 return true;
12718 bool
12719 CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue)
12721 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
12722 return true;
12725 nsCSSValuePairList *cur = aValue.SetPairListValue();
12726 for (;;) {
12727 // feature tag
12728 if (!GetToken(true)) {
12729 return false;
12732 if (mToken.mType != eCSSToken_String ||
12733 !ValidFontFeatureTag(mToken.mIdent)) {
12734 UngetToken();
12735 return false;
12737 cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
12739 if (!GetToken(true)) {
12740 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
12741 break;
12744 // optional value or on/off keyword
12745 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid &&
12746 mToken.mInteger >= 0) {
12747 cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
12748 } else if (mToken.mType == eCSSToken_Ident &&
12749 mToken.mIdent.LowerCaseEqualsLiteral("on")) {
12750 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
12751 } else if (mToken.mType == eCSSToken_Ident &&
12752 mToken.mIdent.LowerCaseEqualsLiteral("off")) {
12753 cur->mYValue.SetIntValue(0, eCSSUnit_Integer);
12754 } else {
12755 // something other than value/on/off, set default value
12756 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
12757 UngetToken();
12760 if (!ExpectSymbol(',', true)) {
12761 break;
12764 cur->mNext = new nsCSSValuePairList;
12765 cur = cur->mNext;
12768 return true;
12771 bool
12772 CSSParserImpl::ParseListStyle()
12774 // 'list-style' can accept 'none' for two different subproperties,
12775 // 'list-style-type' and 'list-style-image'. In order to accept
12776 // 'none' as the value of either but still allow another value for
12777 // either, we need to ensure that the first 'none' we find gets
12778 // allocated to a dummy property instead. Since parse function for
12779 // 'list-style-type' could accept values for 'list-style-position',
12780 // we put position in front of type.
12781 static const nsCSSProperty listStyleIDs[] = {
12782 eCSSPropertyExtra_x_none_value,
12783 eCSSProperty_list_style_position,
12784 eCSSProperty_list_style_type,
12785 eCSSProperty_list_style_image
12788 nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)];
12789 int32_t found =
12790 ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs));
12791 if (found < 1) {
12792 return false;
12795 if ((found & (1|4|8)) == (1|4|8)) {
12796 if (values[0].GetUnit() == eCSSUnit_None) {
12797 // We found a 'none' plus another value for both of
12798 // 'list-style-type' and 'list-style-image'. This is a parse
12799 // error, since the 'none' has to count for at least one of them.
12800 return false;
12801 } else {
12802 NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] &&
12803 values[0] == values[2] && values[0] == values[3],
12804 "should be a special value");
12808 if ((found & 2) == 0) {
12809 values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
12810 eCSSUnit_Enumerated);
12812 if ((found & 4) == 0) {
12813 // Provide default values
12814 nsString type = (found & 1) ?
12815 NS_LITERAL_STRING("none") : NS_LITERAL_STRING("disc");
12816 values[2].SetStringValue(type, eCSSUnit_Ident);
12818 if ((found & 8) == 0) {
12819 values[3].SetNoneValue();
12822 // Start at 1 to avoid appending fake value.
12823 for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) {
12824 AppendValue(listStyleIDs[index], values[index]);
12826 return true;
12829 bool
12830 CSSParserImpl::ParseListStyleType(nsCSSValue& aValue)
12832 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
12833 return true;
12836 nsString name;
12837 if (ParseCounterStyleName(name, false)) {
12838 aValue.SetStringValue(name, eCSSUnit_Ident);
12839 return true;
12841 return false;
12844 bool
12845 CSSParserImpl::ParseMargin()
12847 static const nsCSSProperty kMarginSideIDs[] = {
12848 eCSSProperty_margin_top,
12849 eCSSProperty_margin_right_value,
12850 eCSSProperty_margin_bottom,
12851 eCSSProperty_margin_left_value
12853 static const nsCSSProperty kMarginSources[] = {
12854 eCSSProperty_margin_left_ltr_source,
12855 eCSSProperty_margin_left_rtl_source,
12856 eCSSProperty_margin_right_ltr_source,
12857 eCSSProperty_margin_right_rtl_source,
12858 eCSSProperty_UNKNOWN
12861 // do this now, in case 4 values weren't specified
12862 InitBoxPropsAsPhysical(kMarginSources);
12863 return ParseBoxProperties(kMarginSideIDs);
12866 bool
12867 CSSParserImpl::ParseMarks(nsCSSValue& aValue)
12869 if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPageMarksKTable)) {
12870 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
12871 if (NS_STYLE_PAGE_MARKS_NONE != aValue.GetIntValue() &&
12872 false == CheckEndProperty()) {
12873 nsCSSValue second;
12874 if (ParseEnum(second, nsCSSProps::kPageMarksKTable)) {
12875 // 'none' keyword in conjuction with others is not allowed
12876 if (NS_STYLE_PAGE_MARKS_NONE != second.GetIntValue()) {
12877 aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(),
12878 eCSSUnit_Enumerated);
12879 return true;
12882 return false;
12885 return true;
12887 return false;
12890 bool
12891 CSSParserImpl::ParseOutline()
12893 const int32_t numProps = 3;
12894 static const nsCSSProperty kOutlineIDs[] = {
12895 eCSSProperty_outline_color,
12896 eCSSProperty_outline_style,
12897 eCSSProperty_outline_width
12900 nsCSSValue values[numProps];
12901 int32_t found = ParseChoice(values, kOutlineIDs, numProps);
12902 if (found < 1) {
12903 return false;
12906 // Provide default values
12907 if ((found & 1) == 0) { // Provide default outline-color
12908 values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
12910 if ((found & 2) == 0) { // Provide default outline-style
12911 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
12913 if ((found & 4) == 0) { // Provide default outline-width
12914 values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
12917 int32_t index;
12918 for (index = 0; index < numProps; index++) {
12919 AppendValue(kOutlineIDs[index], values[index]);
12921 return true;
12924 bool
12925 CSSParserImpl::ParseOverflow()
12927 nsCSSValue overflow;
12928 if (!ParseVariant(overflow, VARIANT_HK, nsCSSProps::kOverflowKTable)) {
12929 return false;
12932 nsCSSValue overflowX(overflow);
12933 nsCSSValue overflowY(overflow);
12934 if (eCSSUnit_Enumerated == overflow.GetUnit())
12935 switch(overflow.GetIntValue()) {
12936 case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
12937 overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
12938 overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
12939 break;
12940 case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
12941 overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
12942 overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
12943 break;
12945 AppendValue(eCSSProperty_overflow_x, overflowX);
12946 AppendValue(eCSSProperty_overflow_y, overflowY);
12947 return true;
12950 bool
12951 CSSParserImpl::ParsePadding()
12953 static const nsCSSProperty kPaddingSideIDs[] = {
12954 eCSSProperty_padding_top,
12955 eCSSProperty_padding_right_value,
12956 eCSSProperty_padding_bottom,
12957 eCSSProperty_padding_left_value
12959 static const nsCSSProperty kPaddingSources[] = {
12960 eCSSProperty_padding_left_ltr_source,
12961 eCSSProperty_padding_left_rtl_source,
12962 eCSSProperty_padding_right_ltr_source,
12963 eCSSProperty_padding_right_rtl_source,
12964 eCSSProperty_UNKNOWN
12967 // do this now, in case 4 values weren't specified
12968 InitBoxPropsAsPhysical(kPaddingSources);
12969 return ParseBoxProperties(kPaddingSideIDs);
12972 bool
12973 CSSParserImpl::ParseQuotes()
12975 nsCSSValue value;
12976 if (!ParseVariant(value, VARIANT_HOS, nullptr)) {
12977 return false;
12979 if (value.GetUnit() == eCSSUnit_String) {
12980 nsCSSValue open = value;
12981 nsCSSValuePairList* quotes = value.SetPairListValue();
12982 for (;;) {
12983 quotes->mXValue = open;
12984 // get mandatory close
12985 if (!ParseVariant(quotes->mYValue, VARIANT_STRING, nullptr)) {
12986 return false;
12988 // look for another open
12989 if (!ParseVariant(open, VARIANT_STRING, nullptr)) {
12990 break;
12992 quotes->mNext = new nsCSSValuePairList;
12993 quotes = quotes->mNext;
12996 AppendValue(eCSSProperty_quotes, value);
12997 return true;
13000 bool
13001 CSSParserImpl::ParseSize()
13003 nsCSSValue width, height;
13004 if (!ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
13005 return false;
13007 if (width.IsLengthUnit()) {
13008 ParseVariant(height, VARIANT_LENGTH, nullptr);
13011 if (width == height || height.GetUnit() == eCSSUnit_Null) {
13012 AppendValue(eCSSProperty_size, width);
13013 } else {
13014 nsCSSValue pair;
13015 pair.SetPairValue(width, height);
13016 AppendValue(eCSSProperty_size, pair);
13018 return true;
13021 bool
13022 CSSParserImpl::ParseTextDecoration()
13024 enum {
13025 eDecorationNone = NS_STYLE_TEXT_DECORATION_LINE_NONE,
13026 eDecorationUnderline = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
13027 eDecorationOverline = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE,
13028 eDecorationLineThrough = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
13029 eDecorationBlink = NS_STYLE_TEXT_DECORATION_LINE_BLINK,
13030 eDecorationPrefAnchors = NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS
13032 static_assert((eDecorationNone ^ eDecorationUnderline ^
13033 eDecorationOverline ^ eDecorationLineThrough ^
13034 eDecorationBlink ^ eDecorationPrefAnchors) ==
13035 (eDecorationNone | eDecorationUnderline |
13036 eDecorationOverline | eDecorationLineThrough |
13037 eDecorationBlink | eDecorationPrefAnchors),
13038 "text decoration constants need to be bitmasks");
13040 static const KTableValue kTextDecorationKTable[] = {
13041 eCSSKeyword_none, eDecorationNone,
13042 eCSSKeyword_underline, eDecorationUnderline,
13043 eCSSKeyword_overline, eDecorationOverline,
13044 eCSSKeyword_line_through, eDecorationLineThrough,
13045 eCSSKeyword_blink, eDecorationBlink,
13046 eCSSKeyword__moz_anchor_decoration, eDecorationPrefAnchors,
13047 eCSSKeyword_UNKNOWN,-1
13050 nsCSSValue value;
13051 if (!ParseVariant(value, VARIANT_HK, kTextDecorationKTable)) {
13052 return false;
13055 nsCSSValue line, style, color;
13056 switch (value.GetUnit()) {
13057 case eCSSUnit_Enumerated: {
13058 // We shouldn't accept decoration line style and color via
13059 // text-decoration.
13060 color.SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR,
13061 eCSSUnit_Enumerated);
13062 style.SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
13063 eCSSUnit_Enumerated);
13065 int32_t intValue = value.GetIntValue();
13066 if (intValue == eDecorationNone) {
13067 line.SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE,
13068 eCSSUnit_Enumerated);
13069 break;
13072 // look for more keywords
13073 nsCSSValue keyword;
13074 int32_t index;
13075 for (index = 0; index < 3; index++) {
13076 if (!ParseEnum(keyword, kTextDecorationKTable)) {
13077 break;
13079 int32_t newValue = keyword.GetIntValue();
13080 if (newValue == eDecorationNone || newValue & intValue) {
13081 // 'none' keyword in conjuction with others is not allowed, and
13082 // duplicate keyword is not allowed.
13083 return false;
13085 intValue |= newValue;
13088 line.SetIntValue(intValue, eCSSUnit_Enumerated);
13089 break;
13091 default:
13092 line = color = style = value;
13093 break;
13096 AppendValue(eCSSProperty_text_decoration_line, line);
13097 AppendValue(eCSSProperty_text_decoration_color, color);
13098 AppendValue(eCSSProperty_text_decoration_style, style);
13100 return true;
13103 bool
13104 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableValue aTable[])
13106 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
13107 // 'inherit', 'initial' and 'unset' must be alone
13108 return true;
13111 nsCSSValue left;
13112 if (!ParseVariant(left, VARIANT_KEYWORD, aTable)) {
13113 return false;
13116 if (!nsLayoutUtils::IsTextAlignTrueValueEnabled()) {
13117 aValue = left;
13118 return true;
13121 nsCSSValue right;
13122 if (ParseVariant(right, VARIANT_KEYWORD, aTable)) {
13123 // 'true' must be combined with some other value than 'true'.
13124 if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE &&
13125 right.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
13126 return false;
13128 aValue.SetPairValue(left, right);
13129 } else {
13130 // Single value 'true' is not allowed.
13131 if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
13132 return false;
13134 aValue = left;
13136 return true;
13139 bool
13140 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue)
13142 return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable);
13145 bool
13146 CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue)
13148 return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable);
13151 bool
13152 CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
13154 if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTextDecorationLineKTable)) {
13155 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
13156 int32_t intValue = aValue.GetIntValue();
13157 if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
13158 // look for more keywords
13159 nsCSSValue keyword;
13160 int32_t index;
13161 for (index = 0; index < 3; index++) {
13162 if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) {
13163 int32_t newValue = keyword.GetIntValue();
13164 if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE ||
13165 newValue & intValue) {
13166 // 'none' keyword in conjuction with others is not allowed, and
13167 // duplicate keyword is not allowed.
13168 return false;
13170 intValue |= newValue;
13172 else {
13173 break;
13176 aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
13179 return true;
13181 return false;
13184 bool
13185 CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
13187 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
13188 // 'inherit', 'initial' and 'unset' must be alone
13189 return true;
13192 nsCSSValue left;
13193 if (!ParseVariant(left, VARIANT_KEYWORD | VARIANT_STRING,
13194 nsCSSProps::kTextOverflowKTable))
13195 return false;
13197 nsCSSValue right;
13198 if (ParseVariant(right, VARIANT_KEYWORD | VARIANT_STRING,
13199 nsCSSProps::kTextOverflowKTable))
13200 aValue.SetPairValue(left, right);
13201 else {
13202 aValue = left;
13204 return true;
13207 bool
13208 CSSParserImpl::ParseTouchAction(nsCSSValue& aValue)
13210 // Avaliable values of property touch-action:
13211 // auto | none | [pan-x || pan-y] | manipulation
13213 if (!ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTouchActionKTable)) {
13214 return false;
13217 // Auto and None keywords aren't allowed in conjunction with others.
13218 // Also inherit, initial and unset values are available.
13219 if (eCSSUnit_Enumerated != aValue.GetUnit()) {
13220 return true;
13223 int32_t intValue = aValue.GetIntValue();
13224 nsCSSValue nextValue;
13225 if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) {
13226 int32_t nextIntValue = nextValue.GetIntValue();
13228 // duplicates aren't allowed.
13229 if (nextIntValue & intValue) {
13230 return false;
13233 // Auto and None and Manipulation is not allowed in conjunction with others.
13234 if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE |
13235 NS_STYLE_TOUCH_ACTION_AUTO |
13236 NS_STYLE_TOUCH_ACTION_MANIPULATION)) {
13237 return false;
13240 aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
13243 return true;
13246 bool
13247 CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue)
13249 if (!ParseVariant(aValue, VARIANT_HK,
13250 nsCSSProps::kTextCombineUprightKTable)) {
13251 return false;
13254 // if 'digits', need to check for an explicit number [2, 3, 4]
13255 if (eCSSUnit_Enumerated == aValue.GetUnit() &&
13256 aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) {
13257 if (!GetToken(true)) {
13258 return true;
13260 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
13261 switch (mToken.mInteger) {
13262 case 2: // already set, nothing to do
13263 break;
13264 case 3:
13265 aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3,
13266 eCSSUnit_Enumerated);
13267 break;
13268 case 4:
13269 aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4,
13270 eCSSUnit_Enumerated);
13271 break;
13272 default:
13273 // invalid digits value
13274 return false;
13276 } else {
13277 UngetToken();
13280 return true;
13283 ///////////////////////////////////////////////////////
13284 // transform Parsing Implementation
13286 /* Reads a function list of arguments and consumes the closing parenthesis.
13287 * Do not call this function directly; it's meant to be called from
13288 * ParseFunction.
13290 bool
13291 CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[],
13292 int32_t aVariantMaskAll,
13293 uint16_t aMinElems,
13294 uint16_t aMaxElems,
13295 InfallibleTArray<nsCSSValue> &aOutput)
13297 NS_ASSERTION((aVariantMask && !aVariantMaskAll) ||
13298 (!aVariantMask && aVariantMaskAll),
13299 "only one of the two variant mask parameters can be set");
13301 for (uint16_t index = 0; index < aMaxElems; ++index) {
13302 nsCSSValue newValue;
13303 int32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index];
13304 if (!ParseVariant(newValue, m, nullptr)) {
13305 break;
13308 aOutput.AppendElement(newValue);
13310 if (ExpectSymbol(',', true)) {
13311 // Move on to the next argument if we see a comma.
13312 continue;
13315 if (ExpectSymbol(')', true)) {
13316 // Make sure we've read enough symbols if we see a closing parenthesis.
13317 return (index + 1) >= aMinElems;
13320 // Only a comma or a closing parenthesis is valid after an argument.
13321 break;
13324 // If we're here, we've hit an error without seeing a closing parenthesis or
13325 // we've read too many elements without seeing a closing parenthesis.
13326 SkipUntil(')');
13327 return false;
13330 /* Parses a function [ input of the form (a [, b]*) ] and stores it
13331 * as an nsCSSValue that holds a function of the form
13332 * function-name arg1 arg2 ... argN
13334 * On error, the return value is false.
13336 * @param aFunction The name of the function that we're reading.
13337 * @param aAllowedTypes An array of values corresponding to the legal
13338 * types for each element in the function. The zeroth element in the
13339 * array corresponds to the first function parameter, etc. The length
13340 * of this array _must_ be greater than or equal to aMaxElems or the
13341 * behavior is undefined. If not null, aAllowTypesAll must be 0.
13342 * @param aAllowedTypesAll If set, every element tested for these types
13343 * @param aMinElems Minimum number of elements to read. Reading fewer than
13344 * this many elements will result in the function failing.
13345 * @param aMaxElems Maximum number of elements to read. Reading more than
13346 * this many elements will result in the function failing.
13347 * @param aValue (out) The value that was parsed.
13349 bool
13350 CSSParserImpl::ParseFunction(nsCSSKeyword aFunction,
13351 const int32_t aAllowedTypes[],
13352 int32_t aAllowedTypesAll,
13353 uint16_t aMinElems, uint16_t aMaxElems,
13354 nsCSSValue &aValue)
13356 NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) ||
13357 (!aAllowedTypes && aAllowedTypesAll),
13358 "only one of the two allowed type parameter can be set");
13359 typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
13361 /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
13362 * elements stored in the the nsCSSValue::Array.
13364 static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
13366 /* Read in a list of values as an array, failing if we can't or if
13367 * it's out of bounds.
13369 * We reserve 16 entries in the foundValues array in order to avoid
13370 * having to resize the array dynamically when parsing some well-formed
13371 * functions. The number 16 is coming from the number of arguments that
13372 * matrix3d() accepts.
13374 AutoInfallibleTArray<nsCSSValue, 16> foundValues;
13375 if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems,
13376 aMaxElems, foundValues)) {
13377 return false;
13381 * In case the user has given us more than 2^16 - 2 arguments,
13382 * we'll truncate them at 2^16 - 2 arguments.
13384 uint16_t numArgs = std::min(foundValues.Length(), MAX_ALLOWED_ELEMS);
13385 nsRefPtr<nsCSSValue::Array> convertedArray =
13386 aValue.InitFunction(aFunction, numArgs);
13388 /* Copy things over. */
13389 for (uint16_t index = 0; index < numArgs; ++index)
13390 convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
13392 /* Return it! */
13393 return true;
13397 * Given a token, determines the minimum and maximum number of function
13398 * parameters to read, along with the mask that should be used to read
13399 * those function parameters. If the token isn't a transform function,
13400 * returns an error.
13402 * @param aToken The token identifying the function.
13403 * @param aMinElems [out] The minimum number of elements to read.
13404 * @param aMaxElems [out] The maximum number of elements to read
13405 * @param aVariantMask [out] The variant mask to use during parsing
13406 * @return Whether the information was loaded successfully.
13408 static bool GetFunctionParseInformation(nsCSSKeyword aToken,
13409 bool aIsPrefixed,
13410 uint16_t &aMinElems,
13411 uint16_t &aMaxElems,
13412 const int32_t *& aVariantMask)
13414 /* These types represent the common variant masks that will be used to
13415 * parse out the individual functions. The order in the enumeration
13416 * must match the order in which the masks are declared.
13418 enum { eLengthPercentCalc,
13419 eLengthCalc,
13420 eTwoLengthPercentCalcs,
13421 eTwoLengthPercentCalcsOneLengthCalc,
13422 eAngle,
13423 eTwoAngles,
13424 eNumber,
13425 ePositiveLength,
13426 eTwoNumbers,
13427 eThreeNumbers,
13428 eThreeNumbersOneAngle,
13429 eMatrix,
13430 eMatrixPrefixed,
13431 eMatrix3d,
13432 eMatrix3dPrefixed,
13433 eNumVariantMasks };
13434 static const int32_t kMaxElemsPerFunction = 16;
13435 static const int32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
13436 {VARIANT_LPCALC},
13437 {VARIANT_LENGTH|VARIANT_CALC},
13438 {VARIANT_LPCALC, VARIANT_LPCALC},
13439 {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LENGTH|VARIANT_CALC},
13440 {VARIANT_ANGLE_OR_ZERO},
13441 {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
13442 {VARIANT_NUMBER},
13443 {VARIANT_LENGTH|VARIANT_POSITIVE_DIMENSION},
13444 {VARIANT_NUMBER, VARIANT_NUMBER},
13445 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
13446 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
13447 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13448 VARIANT_NUMBER, VARIANT_NUMBER},
13449 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13450 VARIANT_LPNCALC, VARIANT_LPNCALC},
13451 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13452 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13453 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13454 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
13455 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13456 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13457 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
13458 VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
13460 #ifdef DEBUG
13461 static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
13462 {1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 6, 16, 16};
13463 #endif
13465 int32_t variantIndex = eNumVariantMasks;
13467 switch (aToken) {
13468 case eCSSKeyword_translatex:
13469 case eCSSKeyword_translatey:
13470 /* Exactly one length or percent. */
13471 variantIndex = eLengthPercentCalc;
13472 aMinElems = 1U;
13473 aMaxElems = 1U;
13474 break;
13475 case eCSSKeyword_translatez:
13476 /* Exactly one length */
13477 variantIndex = eLengthCalc;
13478 aMinElems = 1U;
13479 aMaxElems = 1U;
13480 break;
13481 case eCSSKeyword_translate3d:
13482 /* Exactly two lengthds or percents and a number */
13483 variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
13484 aMinElems = 3U;
13485 aMaxElems = 3U;
13486 break;
13487 case eCSSKeyword_scalez:
13488 case eCSSKeyword_scalex:
13489 case eCSSKeyword_scaley:
13490 /* Exactly one scale factor. */
13491 variantIndex = eNumber;
13492 aMinElems = 1U;
13493 aMaxElems = 1U;
13494 break;
13495 case eCSSKeyword_scale3d:
13496 /* Exactly three scale factors. */
13497 variantIndex = eThreeNumbers;
13498 aMinElems = 3U;
13499 aMaxElems = 3U;
13500 break;
13501 case eCSSKeyword_rotatex:
13502 case eCSSKeyword_rotatey:
13503 case eCSSKeyword_rotate:
13504 case eCSSKeyword_rotatez:
13505 /* Exactly one angle. */
13506 variantIndex = eAngle;
13507 aMinElems = 1U;
13508 aMaxElems = 1U;
13509 break;
13510 case eCSSKeyword_rotate3d:
13511 variantIndex = eThreeNumbersOneAngle;
13512 aMinElems = 4U;
13513 aMaxElems = 4U;
13514 break;
13515 case eCSSKeyword_translate:
13516 /* One or two lengths or percents. */
13517 variantIndex = eTwoLengthPercentCalcs;
13518 aMinElems = 1U;
13519 aMaxElems = 2U;
13520 break;
13521 case eCSSKeyword_skew:
13522 /* Exactly one or two angles. */
13523 variantIndex = eTwoAngles;
13524 aMinElems = 1U;
13525 aMaxElems = 2U;
13526 break;
13527 case eCSSKeyword_scale:
13528 /* One or two scale factors. */
13529 variantIndex = eTwoNumbers;
13530 aMinElems = 1U;
13531 aMaxElems = 2U;
13532 break;
13533 case eCSSKeyword_skewx:
13534 /* Exactly one angle. */
13535 variantIndex = eAngle;
13536 aMinElems = 1U;
13537 aMaxElems = 1U;
13538 break;
13539 case eCSSKeyword_skewy:
13540 /* Exactly one angle. */
13541 variantIndex = eAngle;
13542 aMinElems = 1U;
13543 aMaxElems = 1U;
13544 break;
13545 case eCSSKeyword_matrix:
13546 /* Six values, all numbers. */
13547 variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
13548 aMinElems = 6U;
13549 aMaxElems = 6U;
13550 break;
13551 case eCSSKeyword_matrix3d:
13552 /* 16 matrix values, all numbers */
13553 variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
13554 aMinElems = 16U;
13555 aMaxElems = 16U;
13556 break;
13557 case eCSSKeyword_perspective:
13558 /* Exactly one scale number. */
13559 variantIndex = ePositiveLength;
13560 aMinElems = 1U;
13561 aMaxElems = 1U;
13562 break;
13563 default:
13564 /* Oh dear, we didn't match. Report an error. */
13565 return false;
13568 NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
13569 NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
13570 NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
13571 NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
13572 NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
13573 #ifdef DEBUG
13574 NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
13575 "Invalid aMaxElems for this variant mask.");
13576 #endif
13578 // Convert the index into a mask.
13579 aVariantMask = kVariantMasks[variantIndex];
13581 return true;
13584 bool CSSParserImpl::ParseWillChange()
13586 nsCSSValue listValue;
13587 nsCSSValueList* currentListValue = listValue.SetListValue();
13588 bool first = true;
13589 for (;;) {
13590 const uint32_t variantMask = VARIANT_IDENTIFIER |
13591 VARIANT_INHERIT |
13592 VARIANT_NONE |
13593 VARIANT_ALL |
13594 VARIANT_AUTO;
13595 nsCSSValue value;
13596 if (!ParseVariant(value, variantMask, nullptr)) {
13597 return false;
13600 if (value.GetUnit() == eCSSUnit_None ||
13601 value.GetUnit() == eCSSUnit_All)
13603 return false;
13606 if (value.GetUnit() != eCSSUnit_Ident) {
13607 if (first) {
13608 AppendValue(eCSSProperty_will_change, value);
13609 return true;
13610 } else {
13611 return false;
13615 nsString str;
13616 value.GetStringValue(str);
13617 if (str.LowerCaseEqualsLiteral("default")) {
13618 return false;
13621 currentListValue->mValue = value;
13623 if (!ExpectSymbol(',', true)) {
13624 break;
13626 currentListValue->mNext = new nsCSSValueList;
13627 currentListValue = currentListValue->mNext;
13628 first = false;
13631 AppendValue(eCSSProperty_will_change, listValue);
13632 return true;
13635 /* Reads a single transform function from the tokenizer stream, reporting an
13636 * error if something goes wrong.
13638 bool
13639 CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue)
13641 if (!GetToken(true))
13642 return false;
13644 if (mToken.mType != eCSSToken_Function) {
13645 UngetToken();
13646 return false;
13649 const int32_t* variantMask;
13650 uint16_t minElems, maxElems;
13651 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
13653 if (!GetFunctionParseInformation(keyword, aIsPrefixed,
13654 minElems, maxElems, variantMask))
13655 return false;
13657 return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue);
13660 /* Parses a transform property list by continuously reading in properties
13661 * and constructing a matrix from it.
13663 bool CSSParserImpl::ParseTransform(bool aIsPrefixed)
13665 nsCSSValue value;
13666 // 'inherit', 'initial', 'unset' and 'none' must be alone
13667 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
13668 nsCSSValueSharedList* list = new nsCSSValueSharedList;
13669 value.SetSharedListValue(list);
13670 list->mHead = new nsCSSValueList;
13671 nsCSSValueList* cur = list->mHead;
13672 for (;;) {
13673 if (!ParseSingleTransform(aIsPrefixed, cur->mValue)) {
13674 return false;
13676 if (CheckEndProperty()) {
13677 break;
13679 cur->mNext = new nsCSSValueList;
13680 cur = cur->mNext;
13683 AppendValue(eCSSProperty_transform, value);
13684 return true;
13687 bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
13689 nsCSSValuePair position;
13690 if (!ParseBoxPositionValues(position, true))
13691 return false;
13693 nsCSSProperty prop = eCSSProperty_transform_origin;
13694 if (aPerspective) {
13695 prop = eCSSProperty_perspective_origin;
13698 // Unlike many other uses of pairs, this position should always be stored
13699 // as a pair, even if the values are the same, so it always serializes as
13700 // a pair, and to keep the computation code simple.
13701 if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
13702 position.mXValue.GetUnit() == eCSSUnit_Initial ||
13703 position.mXValue.GetUnit() == eCSSUnit_Unset) {
13704 NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
13705 "inherit/initial/unset only half?");
13706 AppendValue(prop, position.mXValue);
13707 } else {
13708 nsCSSValue value;
13709 if (aPerspective) {
13710 value.SetPairValue(position.mXValue, position.mYValue);
13711 } else {
13712 nsCSSValue depth;
13713 if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr)) {
13714 depth.SetFloatValue(0.0f, eCSSUnit_Pixel);
13716 value.SetTripletValue(position.mXValue, position.mYValue, depth);
13719 AppendValue(prop, value);
13721 return true;
13725 * Reads a drop-shadow value. At the moment the Filter Effects specification
13726 * just expects one shadow item. Should this ever change to a list of shadow
13727 * items, use ParseShadowList instead.
13729 bool
13730 CSSParserImpl::ParseDropShadow(nsCSSValue* aValue)
13732 // Use nsCSSValueList to reuse the shadow resolving code in
13733 // nsRuleNode and nsComputedDOMStyle.
13734 nsCSSValue shadow;
13735 nsCSSValueList* cur = shadow.SetListValue();
13736 if (!ParseShadowItem(cur->mValue, false))
13737 return false;
13739 if (!ExpectSymbol(')', true))
13740 return false;
13742 nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1);
13744 // Copy things over.
13745 dropShadow->Item(1) = shadow;
13747 return true;
13751 * Reads a single url or filter function from the tokenizer stream, reporting an
13752 * error if something goes wrong.
13754 bool
13755 CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
13757 if (ParseVariant(*aValue, VARIANT_URL, nullptr)) {
13758 return true;
13761 if (!nsLayoutUtils::CSSFiltersEnabled()) {
13762 // With CSS Filters disabled, we should only accept an SVG reference filter.
13763 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
13764 return false;
13767 if (!GetToken(true)) {
13768 REPORT_UNEXPECTED_EOF(PEFilterEOF);
13769 return false;
13772 if (mToken.mType != eCSSToken_Function) {
13773 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
13774 UngetToken();
13775 return false;
13778 nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
13779 // Parse drop-shadow independently of the other filter functions
13780 // because of its more complex characteristics.
13781 if (functionName == eCSSKeyword_drop_shadow) {
13782 if (ParseDropShadow(aValue)) {
13783 return true;
13784 } else {
13785 // Unrecognized filter function.
13786 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
13787 SkipUntil(')');
13788 return false;
13792 // Set up the parsing rules based on the filter function.
13793 int32_t variantMask = VARIANT_PN;
13794 bool rejectNegativeArgument = true;
13795 bool clampArgumentToOne = false;
13796 switch (functionName) {
13797 case eCSSKeyword_blur:
13798 variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
13799 // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
13800 rejectNegativeArgument = false;
13801 break;
13802 case eCSSKeyword_brightness:
13803 case eCSSKeyword_contrast:
13804 case eCSSKeyword_saturate:
13805 break;
13806 case eCSSKeyword_grayscale:
13807 case eCSSKeyword_invert:
13808 case eCSSKeyword_sepia:
13809 case eCSSKeyword_opacity:
13810 clampArgumentToOne = true;
13811 break;
13812 case eCSSKeyword_hue_rotate:
13813 variantMask = VARIANT_ANGLE;
13814 rejectNegativeArgument = false;
13815 break;
13816 default:
13817 // Unrecognized filter function.
13818 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
13819 SkipUntil(')');
13820 return false;
13823 // Parse the function.
13824 uint16_t minElems = 1U;
13825 uint16_t maxElems = 1U;
13826 uint32_t allVariants = 0;
13827 if (!ParseFunction(functionName, &variantMask, allVariants,
13828 minElems, maxElems, *aValue)) {
13829 REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError);
13830 return false;
13833 // Get the first and only argument to the filter function.
13834 NS_ABORT_IF_FALSE(aValue->GetUnit() == eCSSUnit_Function,
13835 "expected a filter function");
13836 NS_ABORT_IF_FALSE(aValue->UnitHasArrayValue(),
13837 "filter function should be an array");
13838 NS_ABORT_IF_FALSE(aValue->GetArrayValue()->Count() == 2,
13839 "filter function should have exactly one argument");
13840 nsCSSValue& arg = aValue->GetArrayValue()->Item(1);
13842 if (rejectNegativeArgument &&
13843 ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) ||
13844 (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) {
13845 REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
13846 return false;
13849 if (clampArgumentToOne) {
13850 if (arg.GetUnit() == eCSSUnit_Number &&
13851 arg.GetFloatValue() > 1.0f) {
13852 arg.SetFloatValue(1.0f, arg.GetUnit());
13853 } else if (arg.GetUnit() == eCSSUnit_Percent &&
13854 arg.GetPercentValue() > 1.0f) {
13855 arg.SetPercentValue(1.0f);
13859 return true;
13863 * Parses a filter property value by continuously reading in urls and/or filter
13864 * functions and constructing a list.
13866 * When CSS Filters are enabled, the filter property accepts one or more SVG
13867 * reference filters and/or CSS filter functions.
13868 * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%);
13870 * When CSS Filters are disabled, the filter property only accepts one SVG
13871 * reference filter.
13872 * e.g. filter: url(#my-filter);
13874 bool
13875 CSSParserImpl::ParseFilter()
13877 nsCSSValue value;
13878 // 'inherit', 'initial', 'unset' and 'none' must be alone
13879 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
13880 nsCSSValueList* cur = value.SetListValue();
13881 while (cur) {
13882 if (!ParseSingleFilter(&cur->mValue)) {
13883 return false;
13885 if (CheckEndProperty()) {
13886 break;
13888 if (!nsLayoutUtils::CSSFiltersEnabled()) {
13889 // With CSS Filters disabled, we should only accept one SVG reference
13890 // filter.
13891 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
13892 return false;
13894 cur->mNext = new nsCSSValueList;
13895 cur = cur->mNext;
13898 AppendValue(eCSSProperty_filter, value);
13899 return true;
13902 bool
13903 CSSParserImpl::ParseTransitionProperty()
13905 nsCSSValue value;
13906 // 'inherit', 'initial', 'unset' and 'none' must be alone
13907 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
13908 // Accept a list of arbitrary identifiers. They should be
13909 // CSS properties, but we want to accept any so that we
13910 // accept properties that we don't know about yet, e.g.
13911 // transition-property: invalid-property, left, opacity;
13912 nsCSSValueList* cur = value.SetListValue();
13913 for (;;) {
13914 if (!ParseVariant(cur->mValue, VARIANT_IDENTIFIER | VARIANT_ALL, nullptr)) {
13915 return false;
13917 if (cur->mValue.GetUnit() == eCSSUnit_Ident) {
13918 nsDependentString str(cur->mValue.GetStringBufferValue());
13919 // Exclude 'none', 'inherit', 'initial' and 'unset' according to the
13920 // same rules as for 'counter-reset' in CSS 2.1.
13921 if (str.LowerCaseEqualsLiteral("none") ||
13922 str.LowerCaseEqualsLiteral("inherit") ||
13923 str.LowerCaseEqualsLiteral("initial") ||
13924 (str.LowerCaseEqualsLiteral("unset") &&
13925 nsLayoutUtils::UnsetValueEnabled())) {
13926 return false;
13929 if (!ExpectSymbol(',', true)) {
13930 break;
13932 cur->mNext = new nsCSSValueList;
13933 cur = cur->mNext;
13936 AppendValue(eCSSProperty_transition_property, value);
13937 return true;
13940 bool
13941 CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue)
13943 NS_ASSERTION(!mHavePushBack &&
13944 mToken.mType == eCSSToken_Function &&
13945 mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"),
13946 "unexpected initial state");
13948 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(4);
13950 float x1, x2, y1, y2;
13951 if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) ||
13952 !ParseTransitionTimingFunctionValueComponent(y1, ',', false) ||
13953 !ParseTransitionTimingFunctionValueComponent(x2, ',', true) ||
13954 !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) {
13955 return false;
13958 val->Item(0).SetFloatValue(x1, eCSSUnit_Number);
13959 val->Item(1).SetFloatValue(y1, eCSSUnit_Number);
13960 val->Item(2).SetFloatValue(x2, eCSSUnit_Number);
13961 val->Item(3).SetFloatValue(y2, eCSSUnit_Number);
13963 aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier);
13965 return true;
13968 bool
13969 CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent,
13970 char aStop,
13971 bool aCheckRange)
13973 if (!GetToken(true)) {
13974 return false;
13976 nsCSSToken* tk = &mToken;
13977 if (tk->mType == eCSSToken_Number) {
13978 float num = tk->mNumber;
13979 if (aCheckRange && (num < 0.0 || num > 1.0)) {
13980 return false;
13982 aComponent = num;
13983 if (ExpectSymbol(aStop, true)) {
13984 return true;
13987 return false;
13990 bool
13991 CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
13993 NS_ASSERTION(!mHavePushBack &&
13994 mToken.mType == eCSSToken_Function &&
13995 mToken.mIdent.LowerCaseEqualsLiteral("steps"),
13996 "unexpected initial state");
13998 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
14000 if (!ParseOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, nullptr)) {
14001 return false;
14004 int32_t type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
14005 if (ExpectSymbol(',', true)) {
14006 if (!GetToken(true)) {
14007 return false;
14009 type = -1;
14010 if (mToken.mType == eCSSToken_Ident) {
14011 if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
14012 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
14013 } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) {
14014 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
14017 if (type == -1) {
14018 UngetToken();
14019 return false;
14022 val->Item(1).SetIntValue(type, eCSSUnit_Enumerated);
14024 if (!ExpectSymbol(')', true)) {
14025 return false;
14028 aValue.SetArrayValue(val, eCSSUnit_Steps);
14029 return true;
14032 static nsCSSValueList*
14033 AppendValueToList(nsCSSValue& aContainer,
14034 nsCSSValueList* aTail,
14035 const nsCSSValue& aValue)
14037 nsCSSValueList* entry;
14038 if (aContainer.GetUnit() == eCSSUnit_Null) {
14039 NS_ABORT_IF_FALSE(!aTail, "should not have an entry");
14040 entry = aContainer.SetListValue();
14041 } else {
14042 NS_ABORT_IF_FALSE(!aTail->mNext, "should not have a next entry");
14043 NS_ABORT_IF_FALSE(aContainer.GetUnit() == eCSSUnit_List, "not a list");
14044 entry = new nsCSSValueList;
14045 aTail->mNext = entry;
14047 entry->mValue = aValue;
14048 return entry;
14051 CSSParserImpl::ParseAnimationOrTransitionShorthandResult
14052 CSSParserImpl::ParseAnimationOrTransitionShorthand(
14053 const nsCSSProperty* aProperties,
14054 const nsCSSValue* aInitialValues,
14055 nsCSSValue* aValues,
14056 size_t aNumProperties)
14058 nsCSSValue tempValue;
14059 // first see if 'inherit', 'initial' or 'unset' is specified. If one is,
14060 // it can be the only thing specified, so don't attempt to parse any
14061 // additional properties
14062 if (ParseVariant(tempValue, VARIANT_INHERIT, nullptr)) {
14063 for (uint32_t i = 0; i < aNumProperties; ++i) {
14064 AppendValue(aProperties[i], tempValue);
14066 return eParseAnimationOrTransitionShorthand_Inherit;
14069 static const size_t maxNumProperties = 8;
14070 NS_ABORT_IF_FALSE(aNumProperties <= maxNumProperties,
14071 "can't handle this many properties");
14072 nsCSSValueList *cur[maxNumProperties];
14073 bool parsedProperty[maxNumProperties];
14075 for (size_t i = 0; i < aNumProperties; ++i) {
14076 cur[i] = nullptr;
14078 bool atEOP = false; // at end of property?
14079 for (;;) { // loop over comma-separated transitions or animations
14080 // whether a particular subproperty was specified for this
14081 // transition or animation
14082 bool haveAnyProperty = false;
14083 for (size_t i = 0; i < aNumProperties; ++i) {
14084 parsedProperty[i] = false;
14086 for (;;) { // loop over values within a transition or animation
14087 bool foundProperty = false;
14088 // check to see if we're at the end of one full transition or
14089 // animation definition (either because we hit a comma or because
14090 // we hit the end of the property definition)
14091 if (ExpectSymbol(',', true))
14092 break;
14093 if (CheckEndProperty()) {
14094 atEOP = true;
14095 break;
14098 // else, try to parse the next transition or animation sub-property
14099 for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) {
14100 if (!parsedProperty[i]) {
14101 // if we haven't found this property yet, try to parse it
14102 if (ParseSingleValueProperty(tempValue, aProperties[i])) {
14103 parsedProperty[i] = true;
14104 cur[i] = AppendValueToList(aValues[i], cur[i], tempValue);
14105 foundProperty = true;
14106 haveAnyProperty = true;
14107 break; // out of inner loop; continue looking for next sub-property
14111 if (!foundProperty) {
14112 // We're not at a ',' or at the end of the property, but we couldn't
14113 // parse any of the sub-properties, so the declaration is invalid.
14114 return eParseAnimationOrTransitionShorthand_Error;
14118 if (!haveAnyProperty) {
14119 // Got an empty item.
14120 return eParseAnimationOrTransitionShorthand_Error;
14123 // We hit the end of the property or the end of one transition
14124 // or animation definition, add its components to the list.
14125 for (uint32_t i = 0; i < aNumProperties; ++i) {
14126 // If all of the subproperties were not explicitly specified, fill
14127 // in the missing ones with initial values.
14128 if (!parsedProperty[i]) {
14129 cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]);
14133 if (atEOP)
14134 break;
14135 // else we just hit a ',' so continue parsing the next compound transition
14138 return eParseAnimationOrTransitionShorthand_Values;
14141 bool
14142 CSSParserImpl::ParseTransition()
14144 static const nsCSSProperty kTransitionProperties[] = {
14145 eCSSProperty_transition_duration,
14146 eCSSProperty_transition_timing_function,
14147 // Must check 'transition-delay' after 'transition-duration', since
14148 // that's our assumption about what the spec means for the shorthand
14149 // syntax (the first time given is the duration, and the second
14150 // given is the delay).
14151 eCSSProperty_transition_delay,
14152 // Must check 'transition-property' after
14153 // 'transition-timing-function' since 'transition-property' accepts
14154 // any keyword.
14155 eCSSProperty_transition_property
14157 static const uint32_t numProps = MOZ_ARRAY_LENGTH(kTransitionProperties);
14158 // this is a shorthand property that accepts -property, -delay,
14159 // -duration, and -timing-function with some components missing.
14160 // there can be multiple transitions, separated with commas
14162 nsCSSValue initialValues[numProps];
14163 initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
14164 initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
14165 eCSSUnit_Enumerated);
14166 initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
14167 initialValues[3].SetAllValue();
14169 nsCSSValue values[numProps];
14171 ParseAnimationOrTransitionShorthandResult spres =
14172 ParseAnimationOrTransitionShorthand(kTransitionProperties,
14173 initialValues, values, numProps);
14174 if (spres != eParseAnimationOrTransitionShorthand_Values) {
14175 return spres != eParseAnimationOrTransitionShorthand_Error;
14178 // Make two checks on the list for 'transition-property':
14179 // + If there is more than one item, then none of the items can be
14180 // 'none'.
14181 // + None of the items can be 'inherit', 'initial' or 'unset'.
14183 NS_ABORT_IF_FALSE(kTransitionProperties[3] ==
14184 eCSSProperty_transition_property,
14185 "array index mismatch");
14186 nsCSSValueList *l = values[3].GetListValue();
14187 bool multipleItems = !!l->mNext;
14188 do {
14189 const nsCSSValue& val = l->mValue;
14190 if (val.GetUnit() == eCSSUnit_None) {
14191 if (multipleItems) {
14192 // This is a syntax error.
14193 return false;
14196 // Unbox a solitary 'none'.
14197 values[3].SetNoneValue();
14198 break;
14200 if (val.GetUnit() == eCSSUnit_Ident) {
14201 nsDependentString str(val.GetStringBufferValue());
14202 if (str.EqualsLiteral("inherit") ||
14203 str.EqualsLiteral("initial") ||
14204 (str.EqualsLiteral("unset") &&
14205 nsLayoutUtils::UnsetValueEnabled())) {
14206 return false;
14209 } while ((l = l->mNext));
14212 // Save all parsed transition sub-properties in mTempData
14213 for (uint32_t i = 0; i < numProps; ++i) {
14214 AppendValue(kTransitionProperties[i], values[i]);
14216 return true;
14219 bool
14220 CSSParserImpl::ParseAnimation()
14222 static const nsCSSProperty kAnimationProperties[] = {
14223 eCSSProperty_animation_duration,
14224 eCSSProperty_animation_timing_function,
14225 // Must check 'animation-delay' after 'animation-duration', since
14226 // that's our assumption about what the spec means for the shorthand
14227 // syntax (the first time given is the duration, and the second
14228 // given is the delay).
14229 eCSSProperty_animation_delay,
14230 eCSSProperty_animation_direction,
14231 eCSSProperty_animation_fill_mode,
14232 eCSSProperty_animation_iteration_count,
14233 eCSSProperty_animation_play_state,
14234 // Must check 'animation-name' after 'animation-timing-function',
14235 // 'animation-direction', 'animation-fill-mode',
14236 // 'animation-iteration-count', and 'animation-play-state' since
14237 // 'animation-name' accepts any keyword.
14238 eCSSProperty_animation_name
14240 static const uint32_t numProps = MOZ_ARRAY_LENGTH(kAnimationProperties);
14241 // this is a shorthand property that accepts -property, -delay,
14242 // -duration, and -timing-function with some components missing.
14243 // there can be multiple animations, separated with commas
14245 nsCSSValue initialValues[numProps];
14246 initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
14247 initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
14248 eCSSUnit_Enumerated);
14249 initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
14250 initialValues[3].SetIntValue(NS_STYLE_ANIMATION_DIRECTION_NORMAL, eCSSUnit_Enumerated);
14251 initialValues[4].SetIntValue(NS_STYLE_ANIMATION_FILL_MODE_NONE, eCSSUnit_Enumerated);
14252 initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
14253 initialValues[6].SetIntValue(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING, eCSSUnit_Enumerated);
14254 initialValues[7].SetNoneValue();
14256 nsCSSValue values[numProps];
14258 ParseAnimationOrTransitionShorthandResult spres =
14259 ParseAnimationOrTransitionShorthand(kAnimationProperties,
14260 initialValues, values, numProps);
14261 if (spres != eParseAnimationOrTransitionShorthand_Values) {
14262 return spres != eParseAnimationOrTransitionShorthand_Error;
14265 // Save all parsed animation sub-properties in mTempData
14266 for (uint32_t i = 0; i < numProps; ++i) {
14267 AppendValue(kAnimationProperties[i], values[i]);
14269 return true;
14272 bool
14273 CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow)
14275 // A shadow list item is an array, with entries in this sequence:
14276 enum {
14277 IndexX,
14278 IndexY,
14279 IndexRadius,
14280 IndexSpread, // only for box-shadow
14281 IndexColor,
14282 IndexInset // only for box-shadow
14285 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6);
14287 if (aIsBoxShadow) {
14288 // Optional inset keyword (ignore errors)
14289 ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
14290 nsCSSProps::kBoxShadowTypeKTable);
14293 nsCSSValue xOrColor;
14294 bool haveColor = false;
14295 if (!ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC,
14296 nullptr)) {
14297 return false;
14299 if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) {
14300 val->Item(IndexX) = xOrColor;
14301 } else {
14302 // Must be a color (as string or color value)
14303 NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident ||
14304 xOrColor.GetUnit() == eCSSUnit_EnumColor ||
14305 xOrColor.IsNumericColorUnit(),
14306 "Must be a color value");
14307 val->Item(IndexColor) = xOrColor;
14308 haveColor = true;
14310 // X coordinate mandatory after color
14311 if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC,
14312 nullptr)) {
14313 return false;
14317 // Y coordinate; mandatory
14318 if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC,
14319 nullptr)) {
14320 return false;
14323 // Optional radius. Ignore errors except if they pass a negative
14324 // value which we must reject. If we use ParseNonNegativeVariant
14325 // we can't tell the difference between an unspecified radius
14326 // and a negative radius.
14327 if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC,
14328 nullptr) &&
14329 val->Item(IndexRadius).IsLengthUnit() &&
14330 val->Item(IndexRadius).GetFloatValue() < 0) {
14331 return false;
14334 if (aIsBoxShadow) {
14335 // Optional spread
14336 ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC, nullptr);
14339 if (!haveColor) {
14340 // Optional color
14341 ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr);
14344 if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) {
14345 // Optional inset keyword
14346 ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
14347 nsCSSProps::kBoxShadowTypeKTable);
14350 aValue.SetArrayValue(val, eCSSUnit_Array);
14351 return true;
14354 bool
14355 CSSParserImpl::ParseShadowList(nsCSSProperty aProperty)
14357 nsAutoParseCompoundProperty compound(this);
14358 bool isBoxShadow = aProperty == eCSSProperty_box_shadow;
14360 nsCSSValue value;
14361 // 'inherit', 'initial', 'unset' and 'none' must be alone
14362 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
14363 nsCSSValueList* cur = value.SetListValue();
14364 for (;;) {
14365 if (!ParseShadowItem(cur->mValue, isBoxShadow)) {
14366 return false;
14368 if (!ExpectSymbol(',', true)) {
14369 break;
14371 cur->mNext = new nsCSSValueList;
14372 cur = cur->mNext;
14375 AppendValue(aProperty, value);
14376 return true;
14379 int32_t
14380 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix)
14382 NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
14384 int32_t nameSpaceID = kNameSpaceID_Unknown;
14385 if (mNameSpaceMap) {
14386 // user-specified identifiers are case-sensitive (bug 416106)
14387 nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
14388 if (!prefix) {
14389 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
14391 nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
14393 // else no declared namespaces
14395 if (nameSpaceID == kNameSpaceID_Unknown) { // unknown prefix, dump it
14396 REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, aPrefix);
14399 return nameSpaceID;
14402 void
14403 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
14405 if (mNameSpaceMap) {
14406 aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nullptr));
14407 } else {
14408 aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
14412 bool
14413 CSSParserImpl::ParsePaint(nsCSSProperty aPropID)
14415 nsCSSValue x, y;
14417 if (!ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL |
14418 VARIANT_OPENTYPE_SVG_KEYWORD,
14419 nsCSSProps::kContextPatternKTable)) {
14420 return false;
14423 bool canHaveFallback = x.GetUnit() == eCSSUnit_URL ||
14424 x.GetUnit() == eCSSUnit_Enumerated;
14425 if (canHaveFallback) {
14426 if (!ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr))
14427 y.SetNoneValue();
14430 if (!canHaveFallback) {
14431 AppendValue(aPropID, x);
14432 } else {
14433 nsCSSValue val;
14434 val.SetPairValue(x, y);
14435 AppendValue(aPropID, val);
14437 return true;
14440 bool
14441 CSSParserImpl::ParseDasharray()
14443 nsCSSValue value;
14445 // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
14446 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE |
14447 VARIANT_OPENTYPE_SVG_KEYWORD,
14448 nsCSSProps::kStrokeContextValueKTable)) {
14449 nsCSSValueList *cur = value.SetListValue();
14450 for (;;) {
14451 if (!ParseNonNegativeVariant(cur->mValue, VARIANT_LPN, nullptr)) {
14452 return false;
14454 if (CheckEndProperty()) {
14455 break;
14457 // skip optional commas between elements
14458 (void)ExpectSymbol(',', true);
14460 cur->mNext = new nsCSSValueList;
14461 cur = cur->mNext;
14464 AppendValue(eCSSProperty_stroke_dasharray, value);
14465 return true;
14468 bool
14469 CSSParserImpl::ParseMarker()
14471 nsCSSValue marker;
14472 if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) {
14473 AppendValue(eCSSProperty_marker_end, marker);
14474 AppendValue(eCSSProperty_marker_mid, marker);
14475 AppendValue(eCSSProperty_marker_start, marker);
14476 return true;
14478 return false;
14481 bool
14482 CSSParserImpl::ParsePaintOrder()
14484 static_assert
14485 ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) > NS_STYLE_PAINT_ORDER_LAST_VALUE,
14486 "bitfield width insufficient for paint-order constants");
14488 static const KTableValue kPaintOrderKTable[] = {
14489 eCSSKeyword_normal, NS_STYLE_PAINT_ORDER_NORMAL,
14490 eCSSKeyword_fill, NS_STYLE_PAINT_ORDER_FILL,
14491 eCSSKeyword_stroke, NS_STYLE_PAINT_ORDER_STROKE,
14492 eCSSKeyword_markers, NS_STYLE_PAINT_ORDER_MARKERS,
14493 eCSSKeyword_UNKNOWN,-1
14496 static_assert(MOZ_ARRAY_LENGTH(kPaintOrderKTable) ==
14497 2 * (NS_STYLE_PAINT_ORDER_LAST_VALUE + 2),
14498 "missing paint-order values in kPaintOrderKTable");
14500 nsCSSValue value;
14501 if (!ParseVariant(value, VARIANT_HK, kPaintOrderKTable)) {
14502 return false;
14505 uint32_t seen = 0;
14506 uint32_t order = 0;
14507 uint32_t position = 0;
14509 // Ensure that even cast to a signed int32_t when stored in CSSValue,
14510 // we have enough space for the entire paint-order value.
14511 static_assert
14512 (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE < 32,
14513 "seen and order not big enough");
14515 if (value.GetUnit() == eCSSUnit_Enumerated) {
14516 uint32_t component = static_cast<uint32_t>(value.GetIntValue());
14517 if (component != NS_STYLE_PAINT_ORDER_NORMAL) {
14518 bool parsedOK = true;
14519 for (;;) {
14520 if (seen & (1 << component)) {
14521 // Already seen this component.
14522 UngetToken();
14523 parsedOK = false;
14524 break;
14526 seen |= (1 << component);
14527 order |= (component << position);
14528 position += NS_STYLE_PAINT_ORDER_BITWIDTH;
14529 if (!ParseEnum(value, kPaintOrderKTable)) {
14530 break;
14532 component = value.GetIntValue();
14533 if (component == NS_STYLE_PAINT_ORDER_NORMAL) {
14534 // Can't have "normal" in the middle of the list of paint components.
14535 UngetToken();
14536 parsedOK = false;
14537 break;
14541 // Fill in the remaining paint-order components in the order of their
14542 // constant values.
14543 if (parsedOK) {
14544 for (component = 1;
14545 component <= NS_STYLE_PAINT_ORDER_LAST_VALUE;
14546 component++) {
14547 if (!(seen & (1 << component))) {
14548 order |= (component << position);
14549 position += NS_STYLE_PAINT_ORDER_BITWIDTH;
14555 static_assert(NS_STYLE_PAINT_ORDER_NORMAL == 0,
14556 "unexpected value for NS_STYLE_PAINT_ORDER_NORMAL");
14557 value.SetIntValue(static_cast<int32_t>(order), eCSSUnit_Enumerated);
14560 AppendValue(eCSSProperty_paint_order, value);
14561 return true;
14564 bool
14565 CSSParserImpl::BackslashDropped()
14567 return mScanner->GetEOFCharacters() &
14568 nsCSSScanner::eEOFCharacters_DropBackslash;
14571 void
14572 CSSParserImpl::AppendImpliedEOFCharacters(nsAString& aResult)
14574 nsCSSScanner::AppendImpliedEOFCharacters(mScanner->GetEOFCharacters(),
14575 aResult);
14578 bool
14579 CSSParserImpl::ParseAll()
14581 nsCSSValue value;
14582 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
14583 return false;
14586 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all) {
14587 AppendValue(*p, value);
14589 return true;
14592 bool
14593 CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
14594 nsString& aValue)
14596 CSSVariableDeclarations::Type type;
14597 nsString variableValue;
14598 bool dropBackslash;
14599 nsString impliedCharacters;
14601 // Record the token stream while parsing a variable value.
14602 if (!mInSupportsCondition) {
14603 mScanner->StartRecording();
14605 if (!ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
14606 nullptr, nullptr)) {
14607 if (!mInSupportsCondition) {
14608 mScanner->StopRecording();
14610 return false;
14613 if (!mInSupportsCondition) {
14614 if (type == CSSVariableDeclarations::eTokenStream) {
14615 // This was indeed a token stream value, so store it in variableValue.
14616 mScanner->StopRecording(variableValue);
14617 if (dropBackslash) {
14618 MOZ_ASSERT(!variableValue.IsEmpty() &&
14619 variableValue[variableValue.Length() - 1] == '\\');
14620 variableValue.Truncate(variableValue.Length() - 1);
14622 variableValue.Append(impliedCharacters);
14623 } else {
14624 // This was either 'inherit' or 'initial'; we don't need the recorded
14625 // input.
14626 mScanner->StopRecording();
14630 if (mHavePushBack && type == CSSVariableDeclarations::eTokenStream) {
14631 // If we came to the end of a valid variable declaration and a token was
14632 // pushed back, then it would have been ended by '!', ')', ';', ']' or '}'.
14633 // We need to remove it from the recorded variable value.
14634 MOZ_ASSERT(mToken.IsSymbol('!') ||
14635 mToken.IsSymbol(')') ||
14636 mToken.IsSymbol(';') ||
14637 mToken.IsSymbol(']') ||
14638 mToken.IsSymbol('}'));
14639 if (!mInSupportsCondition) {
14640 MOZ_ASSERT(!variableValue.IsEmpty());
14641 MOZ_ASSERT(variableValue[variableValue.Length() - 1] == mToken.mSymbol);
14642 variableValue.Truncate(variableValue.Length() - 1);
14646 *aType = type;
14647 aValue = variableValue;
14648 return true;
14651 bool
14652 CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
14653 bool* aDropBackslash,
14654 nsString& aImpliedCharacters,
14655 void (*aFunc)(const nsAString&, void*),
14656 void* aData)
14658 // A property value is invalid if it contains variable references and also:
14660 // * has unbalanced parens, brackets or braces
14661 // * has any BAD_STRING or BAD_URL tokens
14662 // * has any ';' or '!' tokens at the top level of a variable reference's
14663 // fallback
14665 // If the property is a custom property (i.e. a variable declaration), then
14666 // it is also invalid if it consists of no tokens, such as:
14668 // --invalid:;
14670 // Note that is valid for a custom property to have a value that consists
14671 // solely of white space, such as:
14673 // --valid: ;
14675 // Stack of closing characters for currently open constructs.
14676 StopSymbolCharStack stack;
14678 // Indexes into ')' characters in |stack| that correspond to "var(". This
14679 // is used to stop parsing when we encounter a '!' or ';' at the top level
14680 // of a variable reference's fallback.
14681 nsAutoTArray<uint32_t, 16> references;
14683 if (!GetToken(false)) {
14684 // Variable value was empty since we reached EOF.
14685 REPORT_UNEXPECTED_EOF(PEVariableEOF);
14686 return false;
14689 if (mToken.mType == eCSSToken_Symbol &&
14690 (mToken.mSymbol == '!' ||
14691 mToken.mSymbol == ')' ||
14692 mToken.mSymbol == ';' ||
14693 mToken.mSymbol == ']' ||
14694 mToken.mSymbol == '}')) {
14695 // Variable value was empty since we reached the end of the construct.
14696 UngetToken();
14697 REPORT_UNEXPECTED_TOKEN(PEVariableEmpty);
14698 return false;
14701 if (mToken.mType == eCSSToken_Whitespace) {
14702 if (!GetToken(true)) {
14703 // Variable value was white space only. This is valid.
14704 MOZ_ASSERT(!BackslashDropped());
14705 *aType = CSSVariableDeclarations::eTokenStream;
14706 *aDropBackslash = false;
14707 AppendImpliedEOFCharacters(aImpliedCharacters);
14708 return true;
14712 // Look for 'initial', 'inherit' or 'unset' as the first non-white space
14713 // token.
14714 CSSVariableDeclarations::Type type = CSSVariableDeclarations::eTokenStream;
14715 if (mToken.mType == eCSSToken_Ident) {
14716 if (mToken.mIdent.LowerCaseEqualsLiteral("initial")) {
14717 type = CSSVariableDeclarations::eInitial;
14718 } else if (mToken.mIdent.LowerCaseEqualsLiteral("inherit")) {
14719 type = CSSVariableDeclarations::eInherit;
14720 } else if (mToken.mIdent.LowerCaseEqualsLiteral("unset")) {
14721 type = CSSVariableDeclarations::eUnset;
14725 if (type != CSSVariableDeclarations::eTokenStream) {
14726 if (!GetToken(true)) {
14727 // Variable value was 'initial' or 'inherit' followed by EOF.
14728 MOZ_ASSERT(!BackslashDropped());
14729 *aType = type;
14730 *aDropBackslash = false;
14731 AppendImpliedEOFCharacters(aImpliedCharacters);
14732 return true;
14734 UngetToken();
14735 if (mToken.mType == eCSSToken_Symbol &&
14736 (mToken.mSymbol == '!' ||
14737 mToken.mSymbol == ')' ||
14738 mToken.mSymbol == ';' ||
14739 mToken.mSymbol == ']' ||
14740 mToken.mSymbol == '}')) {
14741 // Variable value was 'initial' or 'inherit' followed by the end
14742 // of the declaration.
14743 MOZ_ASSERT(!BackslashDropped());
14744 *aType = type;
14745 *aDropBackslash = false;
14746 return true;
14750 do {
14751 switch (mToken.mType) {
14752 case eCSSToken_Symbol:
14753 if (mToken.mSymbol == '(') {
14754 stack.AppendElement(')');
14755 } else if (mToken.mSymbol == '[') {
14756 stack.AppendElement(']');
14757 } else if (mToken.mSymbol == '{') {
14758 stack.AppendElement('}');
14759 } else if (mToken.mSymbol == ';' ||
14760 mToken.mSymbol == '!') {
14761 if (stack.IsEmpty()) {
14762 UngetToken();
14763 MOZ_ASSERT(!BackslashDropped());
14764 *aType = CSSVariableDeclarations::eTokenStream;
14765 *aDropBackslash = false;
14766 return true;
14767 } else if (!references.IsEmpty() &&
14768 references.LastElement() == stack.Length() - 1) {
14769 REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback);
14770 SkipUntilAllOf(stack);
14771 return false;
14773 } else if (mToken.mSymbol == ')' ||
14774 mToken.mSymbol == ']' ||
14775 mToken.mSymbol == '}') {
14776 for (;;) {
14777 if (stack.IsEmpty()) {
14778 UngetToken();
14779 MOZ_ASSERT(!BackslashDropped());
14780 *aType = CSSVariableDeclarations::eTokenStream;
14781 *aDropBackslash = false;
14782 return true;
14784 char16_t c = stack.LastElement();
14785 stack.TruncateLength(stack.Length() - 1);
14786 if (!references.IsEmpty() &&
14787 references.LastElement() == stack.Length()) {
14788 references.TruncateLength(references.Length() - 1);
14790 if (mToken.mSymbol == c) {
14791 break;
14795 break;
14797 case eCSSToken_Function:
14798 if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
14799 if (!GetToken(true)) {
14800 // EOF directly after "var(".
14801 REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF);
14802 return false;
14804 if (mToken.mType != eCSSToken_Ident ||
14805 !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
14806 // There must be an identifier directly after the "var(" and
14807 // it must be a custom property name.
14808 UngetToken();
14809 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName);
14810 SkipUntil(')');
14811 SkipUntilAllOf(stack);
14812 return false;
14814 if (aFunc) {
14815 MOZ_ASSERT(Substring(mToken.mIdent, 0,
14816 CSS_CUSTOM_NAME_PREFIX_LENGTH).
14817 EqualsLiteral("--"));
14818 // remove '--'
14819 const nsDependentSubstring varName =
14820 Substring(mToken.mIdent, CSS_CUSTOM_NAME_PREFIX_LENGTH);
14821 aFunc(varName, aData);
14823 if (!GetToken(true)) {
14824 // EOF right after "var(<ident>".
14825 stack.AppendElement(')');
14826 } else if (mToken.IsSymbol(',')) {
14827 // Variable reference with fallback.
14828 if (!GetToken(false) || mToken.IsSymbol(')')) {
14829 // Comma must be followed by at least one fallback token.
14830 REPORT_UNEXPECTED(PEExpectedVariableFallback);
14831 SkipUntilAllOf(stack);
14832 return false;
14834 UngetToken();
14835 references.AppendElement(stack.Length());
14836 stack.AppendElement(')');
14837 } else if (mToken.IsSymbol(')')) {
14838 // Correctly closed variable reference.
14839 } else {
14840 // Malformed variable reference.
14841 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen);
14842 SkipUntil(')');
14843 SkipUntilAllOf(stack);
14844 return false;
14846 } else {
14847 stack.AppendElement(')');
14849 break;
14851 case eCSSToken_Bad_String:
14852 SkipUntilAllOf(stack);
14853 return false;
14855 case eCSSToken_Bad_URL:
14856 SkipUntil(')');
14857 SkipUntilAllOf(stack);
14858 return false;
14860 default:
14861 break;
14863 } while (GetToken(true));
14865 // Append any implied closing characters.
14866 *aDropBackslash = BackslashDropped();
14867 AppendImpliedEOFCharacters(aImpliedCharacters);
14868 uint32_t i = stack.Length();
14869 while (i--) {
14870 aImpliedCharacters.Append(stack[i]);
14873 *aType = type;
14874 return true;
14877 bool
14878 CSSParserImpl::IsValueValidForProperty(const nsCSSProperty aPropID,
14879 const nsAString& aPropValue)
14881 mData.AssertInitialState();
14882 mTempData.AssertInitialState();
14884 nsCSSScanner scanner(aPropValue, 0);
14885 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr);
14886 InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
14888 nsAutoSuppressErrors suppressErrors(this);
14890 mSection = eCSSSection_General;
14891 scanner.SetSVGMode(false);
14893 // Check for unknown properties
14894 if (eCSSProperty_UNKNOWN == aPropID) {
14895 ReleaseScanner();
14896 return false;
14899 // Check that the property and value parse successfully
14900 bool parsedOK = ParseProperty(aPropID);
14902 // Check for priority
14903 parsedOK = parsedOK && ParsePriority() != ePriority_Error;
14905 // We should now be at EOF
14906 parsedOK = parsedOK && !GetToken(true);
14908 mTempData.ClearProperty(aPropID);
14909 mTempData.AssertInitialState();
14910 mData.AssertInitialState();
14912 CLEAR_ERROR();
14913 ReleaseScanner();
14915 return parsedOK;
14918 } // anonymous namespace
14920 // Recycling of parser implementation objects
14922 static CSSParserImpl* gFreeList = nullptr;
14924 nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
14925 CSSStyleSheet* aSheet)
14927 CSSParserImpl *impl = gFreeList;
14928 if (impl) {
14929 gFreeList = impl->mNextFree;
14930 impl->mNextFree = nullptr;
14931 } else {
14932 impl = new CSSParserImpl();
14935 if (aLoader) {
14936 impl->SetChildLoader(aLoader);
14937 impl->SetQuirkMode(aLoader->GetCompatibilityMode() ==
14938 eCompatibility_NavQuirks);
14940 if (aSheet) {
14941 impl->SetStyleSheet(aSheet);
14944 mImpl = static_cast<void*>(impl);
14947 nsCSSParser::~nsCSSParser()
14949 CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl);
14950 impl->Reset();
14951 impl->mNextFree = gFreeList;
14952 gFreeList = impl;
14955 /* static */ void
14956 nsCSSParser::Shutdown()
14958 CSSParserImpl *tofree = gFreeList;
14959 CSSParserImpl *next;
14960 while (tofree)
14962 next = tofree->mNextFree;
14963 delete tofree;
14964 tofree = next;
14968 // Wrapper methods
14970 nsresult
14971 nsCSSParser::SetStyleSheet(CSSStyleSheet* aSheet)
14973 return static_cast<CSSParserImpl*>(mImpl)->
14974 SetStyleSheet(aSheet);
14977 nsresult
14978 nsCSSParser::SetQuirkMode(bool aQuirkMode)
14980 return static_cast<CSSParserImpl*>(mImpl)->
14981 SetQuirkMode(aQuirkMode);
14984 nsresult
14985 nsCSSParser::SetChildLoader(mozilla::css::Loader* aChildLoader)
14987 return static_cast<CSSParserImpl*>(mImpl)->
14988 SetChildLoader(aChildLoader);
14991 nsresult
14992 nsCSSParser::ParseSheet(const nsAString& aInput,
14993 nsIURI* aSheetURI,
14994 nsIURI* aBaseURI,
14995 nsIPrincipal* aSheetPrincipal,
14996 uint32_t aLineNumber,
14997 bool aAllowUnsafeRules)
14999 return static_cast<CSSParserImpl*>(mImpl)->
15000 ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
15001 aAllowUnsafeRules);
15004 nsresult
15005 nsCSSParser::ParseStyleAttribute(const nsAString& aAttributeValue,
15006 nsIURI* aDocURI,
15007 nsIURI* aBaseURI,
15008 nsIPrincipal* aNodePrincipal,
15009 css::StyleRule** aResult)
15011 return static_cast<CSSParserImpl*>(mImpl)->
15012 ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI,
15013 aNodePrincipal, aResult);
15016 nsresult
15017 nsCSSParser::ParseDeclarations(const nsAString& aBuffer,
15018 nsIURI* aSheetURI,
15019 nsIURI* aBaseURI,
15020 nsIPrincipal* aSheetPrincipal,
15021 css::Declaration* aDeclaration,
15022 bool* aChanged)
15024 return static_cast<CSSParserImpl*>(mImpl)->
15025 ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal,
15026 aDeclaration, aChanged);
15029 nsresult
15030 nsCSSParser::ParseRule(const nsAString& aRule,
15031 nsIURI* aSheetURI,
15032 nsIURI* aBaseURI,
15033 nsIPrincipal* aSheetPrincipal,
15034 css::Rule** aResult)
15036 return static_cast<CSSParserImpl*>(mImpl)->
15037 ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult);
15040 nsresult
15041 nsCSSParser::ParseProperty(const nsCSSProperty aPropID,
15042 const nsAString& aPropValue,
15043 nsIURI* aSheetURI,
15044 nsIURI* aBaseURI,
15045 nsIPrincipal* aSheetPrincipal,
15046 css::Declaration* aDeclaration,
15047 bool* aChanged,
15048 bool aIsImportant,
15049 bool aIsSVGMode)
15051 return static_cast<CSSParserImpl*>(mImpl)->
15052 ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
15053 aSheetPrincipal, aDeclaration, aChanged,
15054 aIsImportant, aIsSVGMode);
15057 nsresult
15058 nsCSSParser::ParseVariable(const nsAString& aVariableName,
15059 const nsAString& aPropValue,
15060 nsIURI* aSheetURI,
15061 nsIURI* aBaseURI,
15062 nsIPrincipal* aSheetPrincipal,
15063 css::Declaration* aDeclaration,
15064 bool* aChanged,
15065 bool aIsImportant)
15067 return static_cast<CSSParserImpl*>(mImpl)->
15068 ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI,
15069 aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
15072 void
15073 nsCSSParser::ParseMediaList(const nsSubstring& aBuffer,
15074 nsIURI* aURI,
15075 uint32_t aLineNumber,
15076 nsMediaList* aMediaList,
15077 bool aHTMLMode)
15079 static_cast<CSSParserImpl*>(mImpl)->
15080 ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode);
15083 bool
15084 nsCSSParser::ParseSourceSizeList(const nsAString& aBuffer,
15085 nsIURI* aURI,
15086 uint32_t aLineNumber,
15087 InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
15088 InfallibleTArray<nsCSSValue>& aValues,
15089 bool aHTMLMode)
15091 return static_cast<CSSParserImpl*>(mImpl)->
15092 ParseSourceSizeList(aBuffer, aURI, aLineNumber, aQueries, aValues,
15093 aHTMLMode);
15096 bool
15097 nsCSSParser::ParseFontFamilyListString(const nsSubstring& aBuffer,
15098 nsIURI* aURI,
15099 uint32_t aLineNumber,
15100 nsCSSValue& aValue)
15102 return static_cast<CSSParserImpl*>(mImpl)->
15103 ParseFontFamilyListString(aBuffer, aURI, aLineNumber, aValue);
15106 bool
15107 nsCSSParser::ParseColorString(const nsSubstring& aBuffer,
15108 nsIURI* aURI,
15109 uint32_t aLineNumber,
15110 nsCSSValue& aValue,
15111 bool aSuppressErrors /* false */)
15113 return static_cast<CSSParserImpl*>(mImpl)->
15114 ParseColorString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors);
15117 nsresult
15118 nsCSSParser::ParseSelectorString(const nsSubstring& aSelectorString,
15119 nsIURI* aURI,
15120 uint32_t aLineNumber,
15121 nsCSSSelectorList** aSelectorList)
15123 return static_cast<CSSParserImpl*>(mImpl)->
15124 ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList);
15127 already_AddRefed<nsCSSKeyframeRule>
15128 nsCSSParser::ParseKeyframeRule(const nsSubstring& aBuffer,
15129 nsIURI* aURI,
15130 uint32_t aLineNumber)
15132 return static_cast<CSSParserImpl*>(mImpl)->
15133 ParseKeyframeRule(aBuffer, aURI, aLineNumber);
15136 bool
15137 nsCSSParser::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
15138 nsIURI* aURI,
15139 uint32_t aLineNumber,
15140 InfallibleTArray<float>& aSelectorList)
15142 return static_cast<CSSParserImpl*>(mImpl)->
15143 ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber,
15144 aSelectorList);
15147 bool
15148 nsCSSParser::EvaluateSupportsDeclaration(const nsAString& aProperty,
15149 const nsAString& aValue,
15150 nsIURI* aDocURL,
15151 nsIURI* aBaseURL,
15152 nsIPrincipal* aDocPrincipal)
15154 return static_cast<CSSParserImpl*>(mImpl)->
15155 EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL,
15156 aDocPrincipal);
15159 bool
15160 nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition,
15161 nsIURI* aDocURL,
15162 nsIURI* aBaseURL,
15163 nsIPrincipal* aDocPrincipal)
15165 return static_cast<CSSParserImpl*>(mImpl)->
15166 EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL, aDocPrincipal);
15169 bool
15170 nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue,
15171 VariableEnumFunc aFunc,
15172 void* aData)
15174 return static_cast<CSSParserImpl*>(mImpl)->
15175 EnumerateVariableReferences(aPropertyValue, aFunc, aData);
15178 bool
15179 nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue,
15180 const CSSVariableValues* aVariables,
15181 nsString& aResult,
15182 nsCSSTokenSerializationType& aFirstToken,
15183 nsCSSTokenSerializationType& aLastToken)
15185 return static_cast<CSSParserImpl*>(mImpl)->
15186 ResolveVariableValue(aPropertyValue, aVariables,
15187 aResult, aFirstToken, aLastToken);
15190 void
15191 nsCSSParser::ParsePropertyWithVariableReferences(
15192 nsCSSProperty aPropertyID,
15193 nsCSSProperty aShorthandPropertyID,
15194 const nsAString& aValue,
15195 const CSSVariableValues* aVariables,
15196 nsRuleData* aRuleData,
15197 nsIURI* aDocURL,
15198 nsIURI* aBaseURL,
15199 nsIPrincipal* aDocPrincipal,
15200 CSSStyleSheet* aSheet,
15201 uint32_t aLineNumber,
15202 uint32_t aLineOffset)
15204 static_cast<CSSParserImpl*>(mImpl)->
15205 ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID,
15206 aValue, aVariables, aRuleData, aDocURL,
15207 aBaseURL, aDocPrincipal, aSheet,
15208 aLineNumber, aLineOffset);
15211 bool
15212 nsCSSParser::ParseCounterStyleName(const nsAString& aBuffer,
15213 nsIURI* aURL,
15214 nsAString& aName)
15216 return static_cast<CSSParserImpl*>(mImpl)->
15217 ParseCounterStyleName(aBuffer, aURL, aName);
15220 bool
15221 nsCSSParser::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
15222 const nsAString& aBuffer,
15223 nsIURI* aSheetURL,
15224 nsIURI* aBaseURL,
15225 nsIPrincipal* aSheetPrincipal,
15226 nsCSSValue& aValue)
15228 return static_cast<CSSParserImpl*>(mImpl)->
15229 ParseCounterDescriptor(aDescID, aBuffer,
15230 aSheetURL, aBaseURL, aSheetPrincipal, aValue);
15233 bool
15234 nsCSSParser::IsValueValidForProperty(const nsCSSProperty aPropID,
15235 const nsAString& aPropValue)
15237 return static_cast<CSSParserImpl*>(mImpl)->
15238 IsValueValidForProperty(aPropID, aPropValue);