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"
25 #include "mozilla/CSSStyleSheet.h"
26 #include "mozilla/css/Declaration.h"
27 #include "nsStyleConsts.h"
28 #include "nsNetUtil.h"
33 #include "nsCSSPseudoClasses.h"
34 #include "nsCSSPseudoElements.h"
35 #include "nsNameSpaceManager.h"
36 #include "nsXMLNameSpaceMap.h"
38 #include "nsIMediaList.h"
39 #include "nsStyleUtil.h"
40 #include "nsIPrincipal.h"
42 #include "nsContentUtils.h"
43 #include "nsAutoPtr.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
;
58 nsCSSProps::kParserVariantTable
[eCSSProperty_COUNT_no_shorthands
] = {
59 #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \
60 stylestruct_, stylestructoffset_, animtype_) \
62 #include "nsCSSPropList.h"
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:
78 // Did not find what we were looking for, but did not consume any token:
80 // Unexpected token or token value, too late for UngetToken() to be enough:
82 MOZ_END_ENUM_CLASS(CSSParseResult
)
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
;
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
{
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.
114 nsresult
ParseSheet(const nsAString
& aInput
,
117 nsIPrincipal
* aSheetPrincipal
,
118 uint32_t aLineNumber
,
119 bool aAllowUnsafeRules
);
121 nsresult
ParseStyleAttribute(const nsAString
& aAttributeValue
,
124 nsIPrincipal
* aNodePrincipal
,
125 css::StyleRule
** aResult
);
127 nsresult
ParseDeclarations(const nsAString
& aBuffer
,
130 nsIPrincipal
* aSheetPrincipal
,
131 css::Declaration
* aDeclaration
,
134 nsresult
ParseRule(const nsAString
& aRule
,
137 nsIPrincipal
* aSheetPrincipal
,
138 css::Rule
** aResult
);
140 nsresult
ParseProperty(const nsCSSProperty aPropID
,
141 const nsAString
& aPropValue
,
144 nsIPrincipal
* aSheetPrincipal
,
145 css::Declaration
* aDeclaration
,
150 void ParseMediaList(const nsSubstring
& aBuffer
,
151 nsIURI
* aURL
, // for error reporting
152 uint32_t aLineNumber
, // for error reporting
153 nsMediaList
* aMediaList
,
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
,
163 nsresult
ParseVariable(const nsAString
& aVariableName
,
164 const nsAString
& aPropValue
,
167 nsIPrincipal
* aSheetPrincipal
,
168 css::Declaration
* aDeclaration
,
172 bool ParseFontFamilyListString(const nsSubstring
& aBuffer
,
173 nsIURI
* aURL
, // for error reporting
174 uint32_t aLineNumber
, // for error reporting
177 bool ParseColorString(const nsSubstring
& aBuffer
,
178 nsIURI
* aURL
, // for error reporting
179 uint32_t aLineNumber
, // for error reporting
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
,
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
,
202 nsIPrincipal
* aDocPrincipal
);
204 bool EvaluateSupportsCondition(const nsAString
& aCondition
,
207 nsIPrincipal
* aDocPrincipal
);
209 bool ParseCounterStyleName(const nsAString
& aBuffer
,
213 bool ParseCounterDescriptor(nsCSSCounterDesc aDescID
,
214 const nsAString
& aBuffer
,
217 nsIPrincipal
* aSheetPrincipal
,
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
,
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
247 * @param aResult Out parameter that gets the resolved value.
248 * @param aFirstToken Out parameter that gets the type of the first token in
250 * @param aLastToken Out parameter that gets the type of the last token in
252 * @return Whether aResult could be parsed successfully and variable reference
253 * substitution succeeded.
255 bool ResolveVariableValue(const nsAString
& aPropertyValue
,
256 const CSSVariableValues
* aVariables
,
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
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
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
,
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
);
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
{
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);
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
{
353 nsAutoFailingSupportsRule(CSSParserImpl
* aParser
,
356 mOriginalValue(aParser
->mInFailingSupportsRule
)
359 mParser
->mInFailingSupportsRule
= true;
363 ~nsAutoFailingSupportsRule()
365 mParser
->mInFailingSupportsRule
= mOriginalValue
;
369 CSSParserImpl
* mParser
;
374 * Auto class to set aParser->mSuppressErrors to the specified value
375 * and restore it to its original value later.
377 class nsAutoSuppressErrors
{
379 explicit nsAutoSuppressErrors(CSSParserImpl
* aParser
,
380 bool aSuppressErrors
= true)
382 mOriginalValue(aParser
->mSuppressErrors
)
384 mParser
->mSuppressErrors
= aSuppressErrors
;
387 ~nsAutoSuppressErrors()
389 mParser
->mSuppressErrors
= mOriginalValue
;
393 CSSParserImpl
* mParser
;
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
);
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
);
447 bool ParseRuleSet(RuleAppendFunc aAppendFunc
, void* aProcessData
,
448 bool aInsideBraces
= false);
449 bool ParseAtRule(RuleAppendFunc aAppendFunc
, void* aProcessData
,
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
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
,
466 bool ParseMediaQueryExpression(nsMediaQuery
* aQuery
);
467 void ProcessImport(const nsString
& aURLSpec
,
469 RuleAppendFunc aAppendFunc
,
471 uint32_t aLineNumber
,
472 uint32_t aColumnNumber
);
473 bool ParseGroupRule(css::GroupRule
* aRule
, RuleAppendFunc aAppendFunc
,
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
,
481 uint32_t aLineNumber
, uint32_t aColumnNumber
);
483 bool ParseFontFaceRule(RuleAppendFunc aAppendFunc
, void* aProcessData
);
484 bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc
,
486 bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule
*aRule
);
487 bool ParseFontDescriptor(nsCSSFontFaceRule
* aRule
);
488 bool ParseFontDescriptorValue(nsCSSFontDesc aDescID
,
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(
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
,
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
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
,
528 nsCSSTokenSerializationType
& aResultFirstToken
,
529 nsCSSTokenSerializationType
& aResultLastToken
);
530 // Helper function for ResolveValueWithVariableReferences.
531 bool ResolveValueWithVariableReferencesRec(
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
,
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
,
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
,
587 bool ParseSelectorGroup(nsCSSSelectorList
*& aListHead
);
588 bool ParseSelector(nsCSSSelectorList
* aList
, char16_t aPrevCombinator
);
591 eParseDeclaration_InBraces
= 1 << 0,
592 eParseDeclaration_AllowImportant
= 1 << 1
594 enum nsCSSContextType
{
599 css::Declaration
* ParseDeclarationBlock(uint32_t aFlags
,
600 nsCSSContextType aContext
= eCSSContext_General
);
601 bool ParseDeclaration(css::Declaration
* aDeclaration
,
603 bool aMustCallValueAppended
,
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
{
617 PriorityParsingStatus
ParsePriority();
620 bool ParseTreePseudoElement(nsAtomList
**aPseudoElementArgs
);
623 void InitBoxPropsAsPhysical(const nsCSSProperty
*aSourceProperties
);
625 // Property specific parsing routines
626 bool ParseBackground();
628 struct BackgroundParseState
{
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
[],
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
,
689 bool ParseCalcTerm(nsCSSValue
& aValue
, int32_t& aVariantMask
);
690 bool RequireWhitespace();
692 // For "flex" shorthand property, defined in CSS Flexbox spec
694 // For "flex-flow" shorthand property, defined in CSS Flexbox spec
695 bool ParseFlexFlow();
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
);
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
);
754 bool ParseCounterData(nsCSSProperty aPropID
);
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
);
779 bool ParseMarks(nsCSSValue
& aValue
);
780 bool ParseTransform(bool aIsPrefixed
);
782 bool ParseOverflow();
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
,
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
,
813 size_t aNumProperties
);
814 bool ParseTransition();
815 bool ParseAnimation();
816 bool ParseWillChange();
818 bool ParsePaint(nsCSSProperty aPropID
);
819 bool ParseDasharray();
821 bool ParsePaintOrder();
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
,
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
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*),
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
,
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
,
889 // ParseColorOpacity will enforce that the color ends with a ')'
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
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
,
937 bool ParseRadialGradient(nsCSSValue
& aValue
, bool aIsRepeating
,
939 bool IsLegacyGradientLine(const nsCSSTokenType
& aType
,
940 const nsString
& aId
);
941 bool ParseGradientColorStops(nsCSSValueGradient
* aGradient
,
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
,
960 InfallibleTArray
<nsCSSValue
>& aOutput
);
962 /* Functions for transform-origin/perspective-origin Parsing */
963 bool ParseTransformOrigin(bool aPerspective
);
965 /* Functions for filter parsing */
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
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).
1008 eCSSSection_Charset
,
1010 eCSSSection_NameSpace
,
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
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
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'
1072 nsCSSExpandedDataBlock mTempData
;
1074 // All data from successfully parsed properties are placed into |mData|.
1075 nsCSSExpandedDataBlock mData
;
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()
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),
1144 CSSParserImpl::~CSSParserImpl()
1146 mData
.AssertInitialState();
1147 mTempData
.AssertInitialState();
1151 CSSParserImpl::SetStyleSheet(CSSStyleSheet
* aSheet
)
1153 if (aSheet
!= mSheet
) {
1154 // Switch to using the new sheet, if any
1155 mGroupStack
.Clear();
1158 mNameSpaceMap
= mSheet
->GetNameSpaceMap();
1160 mNameSpaceMap
= nullptr;
1162 } else if (mSheet
) {
1163 mNameSpaceMap
= mSheet
->GetNameSpaceMap();
1170 CSSParserImpl::SetQuirkMode(bool aQuirkMode
)
1172 mNavQuirkMode
= aQuirkMode
;
1177 CSSParserImpl::SetChildLoader(mozilla::css::Loader
* aChildLoader
)
1179 mChildLoader
= aChildLoader
; // not ref counted, it owns us
1184 CSSParserImpl::Reset()
1186 NS_ASSERTION(!mScanner
, "resetting with scanner active");
1187 SetStyleSheet(nullptr);
1188 SetQuirkMode(false);
1189 SetChildLoader(nullptr);
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;
1213 CSSParserImpl::ReleaseScanner()
1216 mReporter
= nullptr;
1218 mSheetURI
= nullptr;
1219 mSheetPrincipal
= nullptr;
1223 CSSParserImpl::ParseSheet(const nsAString
& aInput
,
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
);
1237 nsIURI
* uri
= mSheet
->GetSheetURI();
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
,
1244 "Sheet principal does not match passed principal");
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);
1255 switch (lastRule
->GetType()) {
1256 case css::Rule::CHARSET_RULE
:
1257 case css::Rule::IMPORT_RULE
:
1258 mSection
= eCSSSection_Import
;
1260 case css::Rule::NAMESPACE_RULE
:
1261 mSection
= eCSSSection_NameSpace
;
1264 mSection
= eCSSSection_General
;
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
;
1280 // Get next non-whitespace token
1281 if (!GetToken(true)) {
1285 if (eCSSToken_HTMLComment
== tk
->mType
) {
1286 continue; // legal here only
1288 if (eCSSToken_AtKeyword
== tk
->mType
) {
1289 ParseAtRule(AppendRuleToSheet
, this, false);
1293 if (ParseRuleSet(AppendRuleToSheet
, this)) {
1294 mSection
= eCSSSection_General
;
1299 mUnsafeRulesEnabled
= false;
1300 mIsChromeOrCertifiedApp
= false;
1302 // XXX check for low level errors
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.
1311 NonMozillaVendorIdentifier(const nsAString
& ident
)
1313 return (ident
.First() == char16_t('-') &&
1314 !StringBeginsWith(ident
, NS_LITERAL_STRING("-moz-"))) ||
1315 ident
.First() == char16_t('_');
1320 CSSParserImpl::ParseStyleAttribute(const nsAString
& aAttributeValue
,
1323 nsIPrincipal
* aNodePrincipal
,
1324 css::StyleRule
** aResult
)
1326 NS_PRECONDITION(aNodePrincipal
, "Must have principal here!");
1327 NS_PRECONDITION(aBaseURI
, "need base URI");
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
);
1340 // Create a style rule for the declaration
1341 NS_ADDREF(*aResult
= new css::StyleRule(nullptr, declaration
, 0, 0));
1348 // XXX check for low level errors
1353 CSSParserImpl::ParseDeclarations(const nsAString
& aBuffer
,
1356 nsIPrincipal
* aSheetPrincipal
,
1357 css::Declaration
* aDeclaration
,
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...
1376 // If we cleared the old decl, then we want to be calling
1377 // ValueAppended as we parse.
1378 if (!ParseDeclaration(aDeclaration
, eParseDeclaration_AllowImportant
,
1380 if (!SkipDeclaration(false)) {
1386 aDeclaration
->CompressFrom(&mData
);
1392 CSSParserImpl::ParseRule(const nsAString
& aRule
,
1395 nsIPrincipal
* aSheetPrincipal
,
1396 css::Rule
** aResult
)
1398 NS_PRECONDITION(aSheetPrincipal
, "Must have principal here!");
1399 NS_PRECONDITION(aBaseURI
, "need base URI");
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
);
1415 rv
= NS_ERROR_DOM_SYNTAX_ERR
;
1417 if (eCSSToken_AtKeyword
== tk
->mType
) {
1418 // FIXME: perhaps aInsideBlock should be true when we are?
1419 ParseAtRule(AssignRuleToPointer
, aResult
, false);
1422 ParseRuleSet(AssignRuleToPointer
, aResult
);
1425 if (*aResult
&& GetToken(true)) {
1426 // garbage after rule
1427 REPORT_UNEXPECTED_TOKEN(PERuleTrailing
);
1428 NS_RELEASE(*aResult
);
1432 rv
= NS_ERROR_DOM_SYNTAX_ERR
;
1443 #pragma optimize( "", off )
1444 #pragma warning( push )
1445 #pragma warning( disable : 4748 )
1449 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID
,
1450 const nsAString
& aPropValue
,
1453 nsIPrincipal
* aSheetPrincipal
,
1454 css::Declaration
* aDeclaration
,
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
);
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
);
1489 bool parsedOK
= ParseProperty(aPropID
);
1490 // We should now be at EOF
1491 if (parsedOK
&& GetToken(true)) {
1492 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue
);
1497 NS_ConvertASCIItoUTF16
propName(nsCSSProps::GetStringValue(aPropID
));
1498 REPORT_UNEXPECTED_P(PEValueParsingError
, propName
);
1499 REPORT_UNEXPECTED(PEDeclDropped
);
1501 mTempData
.ClearProperty(aPropID
);
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
,
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
);
1521 mTempData
.AssertInitialState();
1528 CSSParserImpl::ParseVariable(const nsAString
& aVariableName
,
1529 const nsAString
& aPropValue
,
1532 nsIPrincipal
* aSheetPrincipal
,
1533 css::Declaration
* aDeclaration
,
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
;
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
);
1566 REPORT_UNEXPECTED_P(PEValueParsingError
, NS_LITERAL_STRING("--") +
1568 REPORT_UNEXPECTED(PEDeclDropped
);
1572 aDeclaration
->AddVariableDeclaration(aVariableName
, variableType
,
1573 variableValue
, aIsImportant
, true);
1577 mTempData
.AssertInitialState();
1584 #pragma warning( pop )
1585 #pragma optimize( "", on )
1589 CSSParserImpl::ParseMediaList(const nsSubstring
& aBuffer
,
1590 nsIURI
* aURI
, // for error reporting
1591 uint32_t aLineNumber
, // for error reporting
1592 nsMediaList
* aMediaList
,
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
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");
1625 mHTMLMediaMode
= false;
1628 // <source-size-list> = <source-size>#?
1629 // <source-size> = <media-condition>? <length>
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
,
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;
1651 nsAutoPtr
<nsMediaQuery
> query
;
1655 if (!ParseMediaQuery(eMediaQuerySingleCondition
, getter_Transfers(query
),
1657 NS_ASSERTION(!hitStop
, "should return true when hit stop");
1663 REPORT_UNEXPECTED_EOF(PEParseSourceSizeListEOF
);
1664 NS_ASSERTION(hitStop
,
1665 "should return hitStop or an error if returning no query");
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
1674 query
->SetNegated();
1677 if (!ParseNonNegativeVariant(value
, VARIANT_LPCALC
, nullptr)) {
1682 aQueries
.AppendElement(query
.forget());
1683 aValues
.AppendElement(value
);
1685 if (!GetToken(true)) {
1690 if (eCSSToken_Symbol
!= mToken
.mType
|| mToken
.mSymbol
!= ',') {
1691 REPORT_UNEXPECTED_TOKEN(PEParseSourceSizeListNotComma
);
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
1707 mHTMLMediaMode
= false;
1713 CSSParserImpl::ParseColorString(const nsSubstring
& aBuffer
,
1714 nsIURI
* aURI
, // for error reporting
1715 uint32_t aLineNumber
, // for error reporting
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
) {
1739 CSSParserImpl::ParseFontFamilyListString(const nsSubstring
& aBuffer
,
1740 nsIURI
* aURI
, // for error reporting
1741 uint32_t aLineNumber
, // for error reporting
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);
1752 return familyParsed
;
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.
1779 NS_ASSERTION(*aSelectorList
, "Should have list!");
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
,
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
1807 return result
.forget();
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
1830 NS_ASSERTION(!aSelectorList
.IsEmpty(), "should not be empty");
1832 aSelectorList
.Clear();
1839 CSSParserImpl::EvaluateSupportsDeclaration(const nsAString
& aProperty
,
1840 const nsAString
& aValue
,
1843 nsIPrincipal
* aDocPrincipal
)
1845 nsCSSProperty propID
= LookupEnabledProperty(aProperty
);
1846 if (propID
== eCSSProperty_UNKNOWN
) {
1850 nsCSSScanner
scanner(aValue
, 0);
1851 css::ErrorReporter
reporter(scanner
, mSheet
, mChildLoader
, aDocURL
);
1852 InitScanner(scanner
, reporter
, aDocURL
, aBaseURL
, aDocPrincipal
);
1853 nsAutoSuppressErrors
suppressErrors(this);
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
) &&
1867 parsedOK
= ParseProperty(propID
) && !GetToken(true);
1869 mTempData
.ClearProperty(propID
);
1870 mTempData
.AssertInitialState();
1880 CSSParserImpl::EvaluateSupportsCondition(const nsAString
& aDeclaration
,
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);
1891 bool parsedOK
= ParseSupportsCondition(conditionMet
) && !GetToken(true);
1896 return parsedOK
&& conditionMet
;
1900 CSSParserImpl::EnumerateVariableReferences(const nsAString
& aPropertyValue
,
1901 VariableEnumFunc aFunc
,
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
;
1911 nsString impliedCharacters
;
1912 bool result
= ParseValueWithVariables(&type
, &dropBackslash
,
1913 impliedCharacters
, aFunc
, aData
) &&
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.
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
; // (*)
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");
2017 * Appends aValue to aResult, possibly inserting an empty CSS
2018 * comment between the two to ensure that tokens from both strings
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.
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
;
2067 CSSParserImpl::ResolveValueWithVariableReferencesRec(
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.
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.
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.
2128 } else if (mToken
.mSymbol
== ')' &&
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();
2142 aResultFirstToken
= valueFirstToken
;
2143 aResultLastToken
= valueLastToken
;
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.
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
);
2182 case eCSSToken_Function
:
2183 if (mToken
.mIdent
.LowerCaseEqualsLiteral("var")) {
2184 // Save the tokens before the "var(" to our resolved value.
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.
2201 // Turn the custom property name into a variable name by removing the
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();
2223 // Invalid variable with no fallback.
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.
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();
2249 if (!ResolveValueWithVariableReferencesRec(fallback
,
2253 // Fallback value had invalid tokens or an invalid variable reference
2254 // that itself had no fallback.
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(')'));
2265 // Expected ',' or ')' after the variable name.
2266 mScanner
->StartRecording();
2270 stack
.AppendElement(')');
2271 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function
);
2275 case eCSSToken_Bad_String
:
2276 case eCSSToken_Bad_URL
:
2279 case eCSSToken_Whitespace
:
2280 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace
);
2283 case eCSSToken_AtKeyword
:
2284 case eCSSToken_Hash
:
2285 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash
);
2288 case eCSSToken_Number
:
2289 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number
);
2292 case eCSSToken_Dimension
:
2293 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension
);
2296 case eCSSToken_Ident
:
2297 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident
);
2300 case eCSSToken_Percentage
:
2301 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage
);
2304 case eCSSToken_URange
:
2305 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange
);
2309 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL
);
2312 case eCSSToken_HTMLComment
:
2313 if (mToken
.mIdent
[0] == '-') {
2314 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC
);
2316 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other
);
2320 case eCSSToken_Dashmatch
:
2321 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch
);
2324 case eCSSToken_Containsmatch
:
2325 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch
);
2329 NS_NOTREACHED("unexpected token type");
2332 case eCSSToken_String
:
2333 case eCSSToken_Includes
:
2334 case eCSSToken_Beginsmatch
:
2335 case eCSSToken_Endsmatch
:
2336 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other
);
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()) {
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
;
2365 CSSParserImpl::ResolveValueWithVariableReferences(
2366 const CSSVariableValues
* aVariables
,
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();
2385 nsCSSTokenSerializationType firstToken
, lastToken
;
2386 bool ok
= ResolveValueWithVariableReferencesRec(value
, firstToken
, lastToken
, aVariables
) &&
2389 mScanner
->StopRecording();
2393 aFirstToken
= firstToken
;
2394 aLastToken
= lastToken
;
2400 CSSParserImpl::ResolveVariableValue(const nsAString
& aPropertyValue
,
2401 const CSSVariableValues
* aVariables
,
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
2413 css::ErrorReporter
reporter(scanner
, nullptr, nullptr, nullptr);
2414 InitScanner(scanner
, reporter
, nullptr, nullptr, nullptr);
2416 bool valid
= ResolveValueWithVariableReferences(aVariables
, aResult
,
2417 aFirstToken
, aLastToken
);
2424 CSSParserImpl::ParsePropertyWithVariableReferences(
2425 nsCSSProperty aPropertyID
,
2426 nsCSSProperty aShorthandPropertyID
,
2427 const nsAString
& aValue
,
2428 const CSSVariableValues
* aVariables
,
2429 nsRuleData
* aRuleData
,
2432 nsIPrincipal
* aDocPrincipal
,
2433 CSSStyleSheet
* aSheet
,
2434 uint32_t aLineNumber
,
2435 uint32_t aLineOffset
)
2437 mTempData
.AssertInitialState();
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
);
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
);
2458 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial
);
2460 OUTPUT_ERROR_WITH_POSITION(aLineNumber
, aLineOffset
);
2465 nsCSSProperty propertyToParse
=
2466 aShorthandPropertyID
!= eCSSProperty_UNKNOWN
? aShorthandPropertyID
:
2469 // Parse the property with that resolved value.
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
);
2480 NS_ConvertASCIItoUTF16
propName(nsCSSProps::GetStringValue(
2482 REPORT_UNEXPECTED_P(PEValueWithVariablesParsingError
, propName
);
2483 if (nsCSSProps::IsInherited(aPropertyID
)) {
2484 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit
);
2486 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial
);
2488 OUTPUT_ERROR_WITH_POSITION(aLineNumber
, aLineOffset
);
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.
2497 nsCSSValue defaultValue
;
2498 if (nsCSSProps::IsInherited(aPropertyID
)) {
2499 defaultValue
.SetInheritValue();
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();
2514 CSSParserImpl::ParseCounterStyleName(const nsAString
& aBuffer
,
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);
2531 CSSParserImpl::ParseCounterDescriptor(nsCSSCounterDesc aDescID
,
2532 const nsAString
& aBuffer
,
2535 nsIPrincipal
* aSheetPrincipal
,
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
) &&
2551 //----------------------------------------------------------------------
2554 CSSParserImpl::GetToken(bool aSkipWS
)
2556 if (mHavePushBack
) {
2557 mHavePushBack
= false;
2558 if (!aSkipWS
|| mToken
.mType
!= eCSSToken_Whitespace
) {
2562 return mScanner
->Next(mToken
, aSkipWS
);
2566 CSSParserImpl::UngetToken()
2568 NS_PRECONDITION(!mHavePushBack
, "double pushback");
2569 mHavePushBack
= true;
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
)) {
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();
2588 CSSParserImpl::ExpectSymbol(char16_t aSymbol
,
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
);
2604 if (mToken
.IsSymbol(aSymbol
)) {
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.
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
2633 // Checks if we're at the end of a property, raising an error if we're not.
2635 CSSParserImpl::ExpectEndProperty()
2637 if (CheckEndProperty())
2640 // If we're here, we read something incorrect, so we should report it.
2641 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue
);
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('!')) {
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
);
2668 return ePriority_Error
;
2671 return ePriority_Important
;
2675 CSSParserImpl::NextIdent()
2677 // XXX Error reporting?
2678 if (!GetToken(true)) {
2681 if (eCSSToken_Ident
!= mToken
.mType
) {
2685 return &mToken
.mIdent
;
2689 CSSParserImpl::SkipAtRule(bool aInsideBlock
)
2692 if (!GetToken(true)) {
2693 REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2
);
2696 if (eCSSToken_Symbol
== mToken
.mType
) {
2697 char16_t symbol
= mToken
.mSymbol
;
2698 if (symbol
== ';') {
2701 if (aInsideBlock
&& symbol
== '}') {
2702 // The closing } doesn't belong to us.
2706 if (symbol
== '{') {
2709 } else if (symbol
== '(') {
2711 } else if (symbol
== '[') {
2714 } else if (eCSSToken_Function
== mToken
.mType
||
2715 eCSSToken_Bad_URL
== mToken
.mType
) {
2723 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc
,
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
;
2781 if (!NonMozillaVendorIdentifier(mToken
.mIdent
)) {
2782 REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule
);
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
2791 bool unnestable
= aInAtRule
&& newSection
!= eCSSSection_General
;
2793 REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule
);
2796 if (unnestable
|| !(this->*parseFunc
)(aAppendFunc
, aData
)) {
2797 // Skip over invalid at rule, don't advance section
2799 return SkipAtRule(aInAtRule
);
2802 // Nested @-rules don't affect the top-level rule chain requirement
2804 mSection
= newSection
;
2811 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc
,
2814 uint32_t linenum
, colnum
;
2815 if (!GetNextTokenLocation(true, &linenum
, &colnum
) ||
2817 REPORT_UNEXPECTED_EOF(PECharsetRuleEOF
);
2821 if (eCSSToken_String
!= mToken
.mType
) {
2823 REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString
);
2827 nsAutoString charset
= mToken
.mIdent
;
2829 if (!ExpectSymbol(';', true)) {
2833 nsRefPtr
<css::CharsetRule
> rule
= new css::CharsetRule(charset
,
2835 (*aAppendFunc
)(rule
, aData
);
2841 CSSParserImpl::ParseURLOrString(nsString
& aURL
)
2843 if (!GetToken(true)) {
2846 if (eCSSToken_String
== mToken
.mType
|| eCSSToken_URL
== mToken
.mType
) {
2847 aURL
= mToken
.mIdent
;
2855 CSSParserImpl::ParseMediaQuery(eMediaQueryType aQueryType
,
2856 nsMediaQuery
**aQuery
,
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
2868 if (!GetToken(true)) {
2870 // expected termination by EOF
2874 // unexpected termination by EOF
2875 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF
);
2879 if (eCSSToken_Symbol
== mToken
.mType
&& inAtRule
&&
2880 (mToken
.mSymbol
== ';' || mToken
.mSymbol
== '{' || mToken
.mSymbol
== '}' )) {
2887 nsMediaQuery
* query
= new nsMediaQuery
;
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
)) {
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)
2907 nsCOMPtr
<nsIAtom
> mediaType
;
2908 bool gotNotOrOnly
= false;
2910 if (!GetToken(true)) {
2911 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF
);
2914 if (eCSSToken_Ident
!= mToken
.mType
) {
2915 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent
);
2919 // case insensitive from CSS - must be lower cased
2920 nsContentUtils::ASCIIToLower(mToken
.mIdent
);
2921 mediaType
= do_GetAtom(mToken
.mIdent
);
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
);
2943 query
->SetType(mediaType
);
2947 if (!GetToken(true)) {
2949 // expected termination by EOF
2953 // unexpected termination by EOF
2954 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF
);
2958 if (eCSSToken_Symbol
== mToken
.mType
&& inAtRule
&&
2959 (mToken
.mSymbol
== ';' || mToken
.mSymbol
== '{' || mToken
.mSymbol
== '}')) {
2964 if (!singleCondition
&&
2965 eCSSToken_Symbol
== mToken
.mType
&& mToken
.mSymbol
== ',') {
2966 // Done with the expressions for this query
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.
2977 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma
);
2982 if (!ParseMediaQueryExpression(query
)) {
2984 query
->SetHadUnknownExpression();
2990 // Returns false only when there is a low-level error in the scanner
2993 CSSParserImpl::GatherMedia(nsMediaList
* aMedia
,
2996 eMediaQueryType type
= aInAtRule
? eMediaQueryAtRule
: eMediaQueryNormal
;
2998 nsAutoPtr
<nsMediaQuery
> query
;
3000 if (!ParseMediaQuery(type
, getter_Transfers(query
), &hitStop
)) {
3001 NS_ASSERTION(!hitStop
, "should return true when hit stop");
3004 query
->SetHadUnknownExpression();
3007 const char16_t stopChars
[] =
3008 { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) };
3009 SkipUntilOneOf(stopChars
);
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
== '}')) {
3021 aMedia
->AppendQuery(query
);
3031 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery
* aQuery
)
3033 if (!ExpectSymbol('(', true)) {
3034 REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart
);
3037 if (! GetToken(true)) {
3038 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF
);
3041 if (eCSSToken_Ident
!= mToken
.mType
) {
3042 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName
);
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;
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
) {
3074 if (!feature
->mName
||
3075 (expr
->mRange
!= nsMediaExpression::eEqual
&&
3076 feature
->mRangeType
!= nsMediaFeature::eMinMaxAllowed
)) {
3077 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName
);
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
);
3090 expr
->mValue
.Reset();
3094 if (!mToken
.IsSymbol(':')) {
3095 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd
);
3102 switch (feature
->mValueType
) {
3103 case nsMediaFeature::eLength
:
3104 rv
= ParseNonNegativeVariant(expr
->mValue
, VARIANT_LENGTH
, nullptr);
3106 case nsMediaFeature::eInteger
:
3107 case nsMediaFeature::eBoolInteger
:
3108 rv
= ParseNonNegativeVariant(expr
->mValue
, VARIANT_INTEGER
, nullptr);
3109 // Enforce extra restrictions for eBoolInteger
3111 feature
->mValueType
== nsMediaFeature::eBoolInteger
&&
3112 expr
->mValue
.GetIntValue() > 1)
3115 case nsMediaFeature::eFloat
:
3116 rv
= ParseNonNegativeVariant(expr
->mValue
, VARIANT_NUMBER
, nullptr);
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;
3134 case nsMediaFeature::eResolution
:
3135 rv
= GetToken(true);
3138 rv
= mToken
.mType
== eCSSToken_Dimension
&& mToken
.mNumber
> 0.0f
;
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
);
3156 case nsMediaFeature::eEnumerated
:
3157 rv
= ParseVariant(expr
->mValue
, VARIANT_KEYWORD
,
3158 feature
->mData
.mKeywordTable
);
3160 case nsMediaFeature::eIdent
:
3161 rv
= ParseVariant(expr
->mValue
, VARIANT_IDENTIFIER
, nullptr);
3164 if (!rv
|| !ExpectSymbol(')', true)) {
3165 REPORT_UNEXPECTED(PEMQExpectedFeatureValue
);
3173 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
3175 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc
, void* aData
)
3177 nsRefPtr
<nsMediaList
> media
= new nsMediaList();
3179 uint32_t linenum
, colnum
;
3181 if (!GetNextTokenLocation(true, &linenum
, &colnum
) ||
3182 !ParseURLOrString(url
)) {
3183 REPORT_UNEXPECTED_TOKEN(PEImportNotURI
);
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
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
);
3205 CSSParserImpl::ProcessImport(const nsString
& aURLSpec
,
3206 nsMediaList
* aMedia
,
3207 RuleAppendFunc aAppendFunc
,
3209 uint32_t aLineNumber
,
3210 uint32_t aColumnNumber
)
3212 nsRefPtr
<css::ImportRule
> rule
= new css::ImportRule(aMedia
, aURLSpec
,
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
);
3232 mChildLoader
->LoadChildSheet(mSheet
, url
, aMedia
, rule
);
3236 // Parse the {} part of an @media or @-moz-document rule.
3238 CSSParserImpl::ParseGroupRule(css::GroupRule
* aRule
,
3239 RuleAppendFunc aAppendFunc
,
3242 // XXXbz this could use better error reporting throughout the method
3243 if (!ExpectSymbol('{', true)) {
3247 // push rule on stack, loop over children
3249 nsCSSSection holdSection
= mSection
;
3250 mSection
= eCSSSection_General
;
3253 // Get next non-whitespace token
3254 if (! GetToken(true)) {
3255 REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2
);
3258 if (mToken
.IsSymbol('}')) { // done!
3262 if (eCSSToken_AtKeyword
== mToken
.mType
) {
3263 // Parse for nested rules
3264 ParseAtRule(aAppendFunc
, aData
, true);
3268 ParseRuleSet(AppendRuleToSheet
, this, true);
3272 if (!ExpectSymbol('}', true)) {
3273 mSection
= holdSection
;
3276 (*aAppendFunc
)(aRule
, aData
);
3280 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
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
);
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().
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
)) {
3316 if (!GetToken(true)) {
3317 REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF
);
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
);
3332 css::DocumentRule::URL
*cur
= *next
= new css::DocumentRule::URL
;
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
;
3343 // copy before we know it's valid (but before ExpectSymbol changes
3345 CopyUTF16toUTF8(mToken
.mIdent
, cur
->url
);
3346 if (eCSSToken_String
!= mToken
.mType
|| !ExpectSymbol(')', true)) {
3347 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString
);
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
);
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;"
3383 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc
, void* aData
)
3385 uint32_t linenum
, colnum
;
3386 if (!GetNextTokenLocation(true, &linenum
, &colnum
) ||
3388 REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF
);
3392 nsAutoString prefix
;
3395 if (eCSSToken_Ident
== mToken
.mType
) {
3396 prefix
= mToken
.mIdent
;
3397 // user-specified identifiers are case-sensitive (bug 416106)
3402 if (!ParseURLOrString(url
) || !ExpectSymbol(';', true)) {
3403 if (mHavePushBack
) {
3404 REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected
);
3406 REPORT_UNEXPECTED_EOF(PEAtNSURIEOF
);
3411 ProcessNameSpace(prefix
, url
, aAppendFunc
, aData
, linenum
, colnum
);
3416 CSSParserImpl::ProcessNameSpace(const nsString
& aPrefix
,
3417 const nsString
& aURLSpec
,
3418 RuleAppendFunc aAppendFunc
,
3420 uint32_t aLineNumber
,
3421 uint32_t aColumnNumber
)
3423 nsCOMPtr
<nsIAtom
> prefix
;
3425 if (!aPrefix
.IsEmpty()) {
3426 prefix
= do_GetAtom(aPrefix
);
3428 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
3432 nsRefPtr
<css::NameSpaceRule
> rule
= new css::NameSpaceRule(prefix
, aURLSpec
,
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+
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
);
3456 nsRefPtr
<nsCSSFontFaceRule
> rule(new nsCSSFontFaceRule(linenum
, colnum
));
3459 if (!GetToken(true)) {
3460 REPORT_UNEXPECTED_EOF(PEFontFaceEOF
);
3463 if (mToken
.IsSymbol('}')) { // done!
3468 // ignore extra semicolons
3469 if (mToken
.IsSymbol(';'))
3472 if (!ParseFontDescriptor(rule
)) {
3473 REPORT_UNEXPECTED(PEDeclSkipped
);
3475 if (!SkipDeclaration(true))
3479 if (!ExpectSymbol('}', true)) {
3480 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd
);
3483 (*aAppendFunc
)(rule
, aData
);
3487 // font-descriptor: font-family-desc
3488 // | font-style-desc
3489 // | font-weight-desc
3490 // | font-stretch-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.
3500 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule
* aRule
)
3502 if (eCSSToken_Ident
!= mToken
.mType
) {
3503 REPORT_UNEXPECTED_TOKEN(PEFontDescExpected
);
3507 nsString descName
= mToken
.mIdent
;
3508 if (!ExpectSymbol(':', true)) {
3509 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon
);
3514 nsCSSFontDesc descID
= nsCSSProps::LookupFontDesc(descName
);
3517 if (descID
== eCSSFontDesc_UNKNOWN
) {
3518 if (NonMozillaVendorIdentifier(descName
)) {
3519 // silently skip other vendors' extensions
3520 SkipDeclaration(true);
3523 REPORT_UNEXPECTED_P(PEUnknownFontDesc
, descName
);
3528 if (!ParseFontDescriptorValue(descID
, value
)) {
3529 REPORT_UNEXPECTED_P(PEValueParsingError
, descName
);
3533 if (!ExpectEndProperty())
3536 aRule
->SetDesc(descID
, value
);
3540 // @font-feature-values <font-family># {
3541 // @<feature-type> {
3542 // <feature-ident> : <feature-index>+;
3543 // <feature-ident> : <feature-index>+;
3550 CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc
,
3553 uint32_t linenum
, colnum
;
3554 if (!GetNextTokenLocation(true, &linenum
, &colnum
)) {
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
);
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
);
3580 valuesRule
->SetFamilyList(*fontlist
);
3583 if (!ExpectSymbol('{', true)) {
3584 REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart
);
3588 // list of sets of feature values, each set bound to a specific
3589 // feature-type (e.g. swash, annotation)
3591 if (!GetToken(true)) {
3592 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF
);
3595 if (mToken
.IsSymbol('}')) { // done!
3600 if (!ParseFontFeatureValueSet(valuesRule
)) {
3601 if (!SkipAtRule(false)) {
3606 if (!ExpectSymbol('}', true)) {
3607 REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd
);
3612 (*aAppendFunc
)(valuesRule
, aData
);
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; }
3622 CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule
3623 *aFeatureValuesRule
)
3625 // -- @keyword (e.g. swash, styleset)
3626 if (eCSSToken_AtKeyword
!= mToken
.mType
) {
3627 REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt
);
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
,
3641 if (!NonMozillaVendorIdentifier(mToken
.mIdent
)) {
3642 REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue
);
3649 nsAutoString
featureType(mToken
.mIdent
);
3652 if (!ExpectSymbol('{', true)) {
3653 REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart
);
3657 // styleset and character-variant can be multi-valued, otherwise single value
3658 int32_t limitNumValues
;
3661 case eCSSKeyword_styleset
:
3662 limitNumValues
= NUMVALUES_NO_LIMIT
;
3664 case eCSSKeyword_character_variant
:
3672 // -- ident integer+ [, ident integer+]
3673 nsAutoTArray
<gfxFontFeatureValueSet::ValueList
, 5> values
;
3675 // list of font-feature-values-declaration's
3677 nsAutoString valueId
;
3679 if (!GetToken(true)) {
3680 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF
);
3684 // ignore extra semicolons
3685 if (mToken
.IsSymbol(';')) {
3689 // close brace ==> done
3690 if (mToken
.IsSymbol('}')) {
3695 if (eCSSToken_Ident
!= mToken
.mType
) {
3696 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent
);
3697 if (!SkipDeclaration(true)) {
3703 valueId
.Assign(mToken
.mIdent
);
3706 if (!ExpectSymbol(':', true)) {
3707 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon
);
3709 if (!SkipDeclaration(true)) {
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
);
3728 if (!SkipDeclaration(true)) {
3734 if (numValues
> limitNumValues
) {
3735 REPORT_UNEXPECTED_P(PEFFVTooManyValues
, featureType
);
3737 if (!SkipDeclaration(true)) {
3743 if (!GetToken(true)) {
3744 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF
);
3745 gfxFontFeatureValueSet::ValueList
v(valueId
, featureSelectors
);
3746 values
.AppendElement(v
);
3750 // ';' or '}' to end definition
3751 if (!mToken
.IsSymbol(';') && !mToken
.IsSymbol('}')) {
3752 REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing
);
3754 if (!SkipDeclaration(true)) {
3760 gfxFontFeatureValueSet::ValueList
v(valueId
, featureSelectors
);
3761 values
.AppendElement(v
);
3763 if (mToken
.IsSymbol('}')) {
3768 aFeatureValuesRule
->AddValueList(whichVariant
, values
);
3773 CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc
, void* aData
)
3775 uint32_t linenum
, colnum
;
3776 if (!GetNextTokenLocation(true, &linenum
, &colnum
) ||
3778 REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF
);
3782 if (mToken
.mType
!= eCSSToken_Ident
) {
3783 REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName
);
3787 nsString
name(mToken
.mIdent
);
3789 if (!ExpectSymbol('{', true)) {
3790 REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace
);
3794 nsRefPtr
<nsCSSKeyframesRule
> rule
= new nsCSSKeyframesRule(name
,
3797 while (!ExpectSymbol('}', true)) {
3798 nsRefPtr
<nsCSSKeyframeRule
> kid
= ParseKeyframeRule();
3800 rule
->AppendStyleRule(kid
);
3807 (*aAppendFunc
)(rule
, aData
);
3812 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc
, void* aData
)
3814 uint32_t linenum
, colnum
;
3815 if (!GetNextTokenLocation(true, &linenum
, &colnum
)) {
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
,
3830 mViewportUnitsEnabled
= true;
3836 // Takes ownership of declaration.
3837 nsRefPtr
<nsCSSPageRule
> rule
= new nsCSSPageRule(Move(declaration
),
3840 (*aAppendFunc
)(rule
, aData
);
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
);
3855 // Ignore !important in keyframe rules
3856 uint32_t parseFlags
= eParseDeclaration_InBraces
;
3857 nsAutoPtr
<css::Declaration
> declaration(ParseDeclarationBlock(parseFlags
));
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();
3869 CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray
<float>& aSelectorList
)
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.
3878 switch (mToken
.mType
) {
3879 case eCSSToken_Percentage
:
3880 value
= mToken
.mNumber
;
3882 case eCSSToken_Ident
:
3883 if (mToken
.mIdent
.LowerCaseEqualsLiteral("from")) {
3887 if (mToken
.mIdent
.LowerCaseEqualsLiteral("to")) {
3894 // The first time through the loop, this means we got an empty
3895 // list. Otherwise, it means we have a trailing comma.
3898 aSelectorList
.AppendElement(value
);
3899 if (!ExpectSymbol(',', true)) {
3906 // : "@supports" supports_condition group_rule_body
3909 CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc
, void* aProcessData
)
3911 bool conditionMet
= false;
3914 mScanner
->StartRecording();
3916 uint32_t linenum
, colnum
;
3917 if (!GetNextTokenLocation(true, &linenum
, &colnum
)) {
3921 bool parsed
= ParseSupportsCondition(conditionMet
);
3924 mScanner
->StopRecording();
3928 if (!ExpectSymbol('{', true)) {
3929 REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart
);
3930 mScanner
->StopRecording();
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
,
3951 return ParseGroupRule(rule
, aAppendFunc
, aProcessData
);
3954 // supports_condition
3955 // : supports_condition_in_parens supports_condition_terms
3956 // | supports_condition_negation
3959 CSSParserImpl::ParseSupportsCondition(bool& aConditionMet
)
3961 mInSupportsCondition
= true;
3963 if (!GetToken(true)) {
3964 REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2
);
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;
3983 if (mToken
.mType
== eCSSToken_Ident
&&
3984 mToken
.mIdent
.LowerCaseEqualsLiteral("not")) {
3985 bool result
= ParseSupportsConditionNegation(aConditionMet
) &&
3986 !mScanner
->SeenBadToken();
3987 mInSupportsCondition
= false;
3991 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart
);
3992 mInSupportsCondition
= false;
3996 // supports_condition_negation
3997 // : 'not' S+ supports_condition_in_parens
4000 CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet
)
4002 if (!GetToken(true)) {
4003 REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF
);
4007 if (mToken
.mType
!= eCSSToken_Ident
||
4008 !mToken
.mIdent
.LowerCaseEqualsLiteral("not")) {
4009 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot
);
4013 if (!RequireWhitespace()) {
4014 REPORT_UNEXPECTED(PESupportsWhitespaceRequired
);
4018 if (ParseSupportsConditionInParens(aConditionMet
)) {
4019 aConditionMet
= !aConditionMet
;
4026 // supports_condition_in_parens
4027 // : '(' S* supports_condition_in_parens_inside_parens ')' S*
4028 // | general_enclosed
4031 CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet
)
4033 if (!GetToken(true)) {
4034 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF
);
4038 if (mToken
.mType
== eCSSToken_URL
) {
4039 aConditionMet
= false;
4043 if (mToken
.mType
== eCSSToken_Function
||
4044 mToken
.mType
== eCSSToken_Bad_URL
) {
4045 if (!SkipUntil(')')) {
4046 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF
);
4049 aConditionMet
= false;
4053 if (!mToken
.IsSymbol('(')) {
4054 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction
);
4059 if (!ParseSupportsConditionInParensInsideParens(aConditionMet
)) {
4060 if (!SkipUntil(')')) {
4061 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF
);
4064 aConditionMet
= false;
4068 if (!(ExpectSymbol(')', true))) {
4070 aConditionMet
= false;
4077 // supports_condition_in_parens_inside_parens
4078 // : core_declaration
4079 // | supports_condition_negation
4080 // | supports_condition_in_parens supports_condition_terms
4083 CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet
)
4085 if (!GetToken(true)) {
4089 if (mToken
.mType
== eCSSToken_Ident
) {
4090 if (!mToken
.mIdent
.LowerCaseEqualsLiteral("not")) {
4091 nsAutoString propertyName
= mToken
.mIdent
;
4092 if (!ExpectSymbol(':', true)) {
4096 nsCSSProperty propID
= LookupEnabledProperty(propertyName
);
4097 if (propID
== eCSSProperty_UNKNOWN
) {
4098 if (ExpectSymbol(')', true)) {
4102 aConditionMet
= false;
4105 } else if (propID
== eCSSPropertyExtra_variable
) {
4106 if (ExpectSymbol(')', false)) {
4110 CSSVariableDeclarations::Type variableType
;
4111 nsString variableValue
;
4113 ParseVariableDeclaration(&variableType
, variableValue
) &&
4114 ParsePriority() != ePriority_Error
;
4115 if (!aConditionMet
) {
4120 if (ExpectSymbol(')', true)) {
4124 aConditionMet
= ParseProperty(propID
) &&
4125 ParsePriority() != ePriority_Error
;
4126 if (!aConditionMet
) {
4130 mTempData
.ClearProperty(propID
);
4131 mTempData
.AssertInitialState();
4137 return ParseSupportsConditionNegation(aConditionMet
);
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')
4151 CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet
)
4153 if (!RequireWhitespace() || !GetToken(false)) {
4157 if (mToken
.mType
!= eCSSToken_Ident
) {
4162 if (mToken
.mIdent
.LowerCaseEqualsLiteral("and")) {
4163 return ParseSupportsConditionTermsAfterOperator(aConditionMet
, eAnd
);
4166 if (mToken
.mIdent
.LowerCaseEqualsLiteral("or")) {
4167 return ParseSupportsConditionTermsAfterOperator(aConditionMet
, eOr
);
4174 // supports_condition_terms_after_operator(operator)
4175 // : S+ supports_condition_in_parens ( <operator> supports_condition_in_parens )*
4178 CSSParserImpl::ParseSupportsConditionTermsAfterOperator(
4179 bool& aConditionMet
,
4180 CSSParserImpl::SupportsConditionTermOperator aOperator
)
4182 if (!RequireWhitespace()) {
4183 REPORT_UNEXPECTED(PESupportsWhitespaceRequired
);
4187 const char* token
= aOperator
== eAnd
? "and" : "or";
4189 bool termConditionMet
= false;
4190 if (!ParseSupportsConditionInParens(termConditionMet
)) {
4193 aConditionMet
= aOperator
== eAnd
? aConditionMet
&& termConditionMet
:
4194 aConditionMet
|| termConditionMet
;
4196 if (!GetToken(true)) {
4200 if (mToken
.mType
!= eCSSToken_Ident
||
4201 !mToken
.mIdent
.LowerCaseEqualsASCII(token
)) {
4209 CSSParserImpl::ParseCounterStyleRule(RuleAppendFunc aAppendFunc
, void* aData
)
4212 uint32_t linenum
, colnum
;
4213 if (!GetNextTokenLocation(true, &linenum
, &colnum
) ||
4214 !ParseCounterStyleName(name
, true)) {
4215 REPORT_UNEXPECTED_TOKEN(PECounterStyleNotIdent
);
4219 if (!ExpectSymbol('{', true)) {
4220 REPORT_UNEXPECTED_TOKEN(PECounterStyleBadBlockStart
);
4224 nsRefPtr
<nsCSSCounterStyleRule
> rule
= new nsCSSCounterStyleRule(name
,
4228 if (!GetToken(true)) {
4229 REPORT_UNEXPECTED_EOF(PECounterStyleEOF
);
4232 if (mToken
.IsSymbol('}')) {
4235 if (mToken
.IsSymbol(';')) {
4239 if (!ParseCounterDescriptor(rule
)) {
4240 REPORT_UNEXPECTED(PEDeclSkipped
);
4242 if (!SkipDeclaration(true)) {
4243 REPORT_UNEXPECTED_EOF(PECounterStyleEOF
);
4249 int32_t system
= rule
->GetSystem();
4250 bool isCorrect
= false;
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
)) {
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
) {
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
) {
4287 NS_NOTREACHED("unknown system");
4291 (*aAppendFunc
)(rule
, aData
);
4297 CSSParserImpl::ParseCounterStyleName(nsAString
& aName
, bool aForDefinition
)
4299 if (!GetToken(true)) {
4303 if (mToken
.mType
!= eCSSToken_Ident
) {
4308 static const nsCSSKeyword kReservedNames
[] = {
4310 eCSSKeyword_decimal
,
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
);
4322 aName
= mToken
.mIdent
;
4323 if (nsCSSProps::IsPredefinedCounterStyle(aName
)) {
4330 CSSParserImpl::ParseCounterDescriptor(nsCSSCounterStyleRule
* aRule
)
4332 if (eCSSToken_Ident
!= mToken
.mType
) {
4333 REPORT_UNEXPECTED_TOKEN(PECounterDescExpected
);
4337 nsString descName
= mToken
.mIdent
;
4338 if (!ExpectSymbol(':', true)) {
4339 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon
);
4344 nsCSSCounterDesc descID
= nsCSSProps::LookupCounterDesc(descName
);
4347 if (descID
== eCSSCounterDesc_UNKNOWN
) {
4348 REPORT_UNEXPECTED_P(PEUnknownCounterDesc
, descName
);
4352 if (!ParseCounterDescriptorValue(descID
, value
)) {
4353 REPORT_UNEXPECTED_P(PEValueParsingError
, descName
);
4357 if (!ExpectEndProperty()) {
4361 aRule
->SetDesc(descID
, value
);
4366 CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID
,
4369 // Should also include VARIANT_IMAGE, but it is not supported currently.
4371 static const int32_t VARIANT_COUNTER_SYMBOL
=
4372 VARIANT_STRING
| VARIANT_IDENTIFIER
;
4375 case eCSSCounterDesc_System
: {
4377 if (!ParseEnum(system
, nsCSSProps::kCounterSystemKTable
)) {
4380 switch (system
.GetIntValue()) {
4381 case NS_STYLE_COUNTER_SYSTEM_FIXED
: {
4383 if (!ParseVariant(start
, VARIANT_INTEGER
, nullptr)) {
4384 start
.SetIntValue(1, eCSSUnit_Integer
);
4386 aValue
.SetPairValue(system
, start
);
4389 case NS_STYLE_COUNTER_SYSTEM_EXTENDS
: {
4391 if (!ParseCounterStyleName(name
, false)) {
4392 REPORT_UNEXPECTED_TOKEN(PECounterExtendsNotIdent
);
4395 aValue
.SetPairValue(system
, nsCSSValue(name
, eCSSUnit_Ident
));
4404 case eCSSCounterDesc_Negative
: {
4405 nsCSSValue first
, second
;
4406 if (!ParseVariant(first
, VARIANT_COUNTER_SYMBOL
, nullptr)) {
4409 if (!ParseVariant(second
, VARIANT_COUNTER_SYMBOL
, nullptr)) {
4412 aValue
.SetPairValue(first
, second
);
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)) {
4425 nsCSSValuePairList
* item
= aValue
.SetPairListValue();
4427 nsCSSValuePair pair
;
4428 if (!ParseCounterRange(pair
)) {
4431 item
->mXValue
= pair
.mXValue
;
4432 item
->mYValue
= pair
.mYValue
;
4433 if (!ExpectSymbol(',', true)) {
4436 item
->mNext
= new nsCSSValuePairList
;
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
))) {
4449 aValue
.SetPairValue(width
, symbol
);
4453 case eCSSCounterDesc_Fallback
: {
4455 if (!ParseCounterStyleName(name
, false)) {
4458 aValue
.SetStringValue(name
, eCSSUnit_Ident
);
4462 case eCSSCounterDesc_Symbols
: {
4463 nsCSSValueList
* item
= nullptr;
4466 if (!ParseVariant(value
, VARIANT_COUNTER_SYMBOL
, nullptr)) {
4470 item
= aValue
.SetListValue();
4472 item
->mNext
= new nsCSSValueList
;
4475 item
->mValue
= value
;
4477 // should always return in the loop
4480 case eCSSCounterDesc_AdditiveSymbols
: {
4481 nsCSSValuePairList
* item
= nullptr;
4482 int32_t lastWeight
= -1;
4484 nsCSSValue weight
, symbol
;
4485 bool hasWeight
= ParseNonNegativeInteger(weight
);
4486 if (!ParseVariant(symbol
, VARIANT_COUNTER_SYMBOL
, nullptr) ||
4487 (!hasWeight
&& !ParseNonNegativeInteger(weight
))) {
4490 if (lastWeight
!= -1 && weight
.GetIntValue() >= lastWeight
) {
4491 REPORT_UNEXPECTED(PECounterASWeight
);
4494 lastWeight
= weight
.GetIntValue();
4496 item
= aValue
.SetPairListValue();
4498 item
->mNext
= new nsCSSValuePairList
;
4501 item
->mXValue
= weight
;
4502 item
->mYValue
= symbol
;
4503 if (!ExpectSymbol(',', 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.
4523 if (ParseCounterStyleName(name
, false)) {
4524 aValue
.SetStringValue(name
, eCSSUnit_Ident
);
4531 NS_NOTREACHED("unknown descriptor");
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
)) {
4545 if (lower
.GetUnit() != eCSSUnit_Enumerated
&&
4546 upper
.GetUnit() != eCSSUnit_Enumerated
&&
4547 lower
.GetIntValue() > upper
.GetIntValue()) {
4550 aPair
= nsCSSValuePair(lower
, upper
);
4555 CSSParserImpl::SkipUntil(char16_t aStopSymbol
)
4557 nsCSSToken
* tk
= &mToken
;
4558 nsAutoTArray
<char16_t
, 16> stack
;
4559 stack
.AppendElement(aStopSymbol
);
4561 if (!GetToken(true)) {
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) {
4573 // Just handle out-of-memory by parsing incorrectly. It's
4574 // highly unlikely we're dealing with a legitimate style sheet
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(')');
4591 CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol
)
4593 nsCSSToken
* tk
= &mToken
;
4594 nsAutoTArray
<char16_t
, 16> stack
;
4595 stack
.AppendElement(aStopSymbol
);
4597 if (!GetToken(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) {
4609 // Just handle out-of-memory by parsing incorrectly. It's
4610 // highly unlikely we're dealing with a legitimate style sheet
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
||
4624 } else if (eCSSToken_Function
== tk
->mType
||
4625 eCSSToken_Bad_URL
== tk
->mType
) {
4626 stack
.AppendElement(')');
4632 CSSParserImpl::SkipUntilOneOf(const char16_t
* aStopSymbolChars
)
4634 nsCSSToken
* tk
= &mToken
;
4635 nsDependentString
stopSymbolChars(aStopSymbolChars
);
4637 if (!GetToken(true)) {
4640 if (eCSSToken_Symbol
== tk
->mType
) {
4641 char16_t symbol
= tk
->mSymbol
;
4642 if (stopSymbolChars
.FindChar(symbol
) != -1) {
4644 } else if ('{' == symbol
) {
4646 } else if ('[' == symbol
) {
4648 } else if ('(' == symbol
) {
4651 } else if (eCSSToken_Function
== tk
->mType
||
4652 eCSSToken_Bad_URL
== tk
->mType
) {
4659 CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack
& aStopSymbolChars
)
4661 uint32_t i
= aStopSymbolChars
.Length();
4663 SkipUntil(aStopSymbolChars
[i
]);
4668 CSSParserImpl::SkipDeclaration(bool aCheckForBraces
)
4670 nsCSSToken
* tk
= &mToken
;
4672 if (!GetToken(true)) {
4673 if (aCheckForBraces
) {
4674 REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF
);
4678 if (eCSSToken_Symbol
== tk
->mType
) {
4679 char16_t symbol
= tk
->mSymbol
;
4680 if (';' == symbol
) {
4683 if (aCheckForBraces
) {
4684 if ('}' == symbol
) {
4689 if ('{' == symbol
) {
4691 } else if ('(' == symbol
) {
4693 } else if ('[' == symbol
) {
4696 } else if (eCSSToken_Function
== tk
->mType
||
4697 eCSSToken_Bad_URL
== tk
->mType
) {
4705 CSSParserImpl::SkipRuleSet(bool aInsideBraces
)
4707 nsCSSToken
* tk
= &mToken
;
4709 if (!GetToken(true)) {
4710 REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF
);
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
4719 } else if ('{' == symbol
) {
4722 } else if ('(' == symbol
) {
4724 } else if ('[' == symbol
) {
4727 } else if (eCSSToken_Function
== tk
->mType
||
4728 eCSSToken_Bad_URL
== tk
->mType
) {
4735 CSSParserImpl::PushGroup(css::GroupRule
* aRule
)
4737 mGroupStack
.AppendElement(aRule
);
4741 CSSParserImpl::PopGroup()
4743 uint32_t count
= mGroupStack
.Length();
4745 mGroupStack
.RemoveElementAt(count
- 1);
4750 CSSParserImpl::AppendRule(css::Rule
* aRule
)
4752 uint32_t count
= mGroupStack
.Length();
4754 mGroupStack
[count
- 1]->AppendStyleRule(aRule
);
4757 mSheet
->AppendStyleRule(aRule
);
4762 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc
, void* aData
,
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
);
4772 SkipRuleSet(aInsideBraces
);
4775 NS_ASSERTION(nullptr != slist
, "null selector list");
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
) {
4789 fputs("{\n", stdout
);
4790 declaration
->List();
4791 fputs("}\n", stdout
);
4794 // Translate the selector list and declaration block into style data
4796 nsRefPtr
<css::StyleRule
> rule
= new css::StyleRule(slist
, declaration
,
4798 (*aAppendFunc
)(rule
, aData
);
4804 CSSParserImpl::ParseSelectorList(nsCSSSelectorList
*& aListHead
,
4807 nsCSSSelectorList
* list
= nullptr;
4808 if (! ParseSelectorGroup(list
)) {
4809 // must have at least one selector group
4810 aListHead
= nullptr;
4813 NS_ASSERTION(nullptr != list
, "no selector list");
4816 // After that there must either be a "," or a "{" (the latter if
4817 // StopChar is nonzero)
4818 nsCSSToken
* tk
= &mToken
;
4820 if (! GetToken(true)) {
4821 if (aStopChar
== char16_t(0)) {
4825 REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF
);
4829 if (eCSSToken_Symbol
== tk
->mType
) {
4830 if (',' == tk
->mSymbol
) {
4831 nsCSSSelectorList
* newList
= nullptr;
4832 // Another selector group must follow
4833 if (! ParseSelectorGroup(newList
)) {
4836 // add new list to the end of the selector list
4837 list
->mNext
= newList
;
4840 } else if (aStopChar
== tk
->mSymbol
&& aStopChar
!= char16_t(0)) {
4845 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra
);
4851 aListHead
= nullptr;
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));
4867 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList
*& aList
)
4869 char16_t combinator
= 0;
4870 nsAutoPtr
<nsCSSSelectorList
> list(new nsCSSSelectorList());
4873 if (!ParseSelector(list
, combinator
)) {
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
4893 char16_t symbol
= mToken
.mSymbol
;
4894 if (symbol
== '+' || symbol
== '>' || symbol
== '~') {
4895 combinator
= mToken
.mSymbol
;
4897 UngetToken(); // not a combinator
4898 if (symbol
== ',' || symbol
== '{' || symbol
== ')') {
4899 break; // end of selector group
4905 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra
);
4910 aList
= list
.forget();
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
);
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
,
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
;
4988 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType
);
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
;
5026 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType
);
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
;
5059 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType
);
5061 return eSelectorParsingStatus_Error
;
5063 if (! GetToken(false)) { // premature eof is ok (here!)
5064 return eSelectorParsingStatus_Done
;
5068 SetDefaultNamespaceOnSelector(aSelector
);
5072 // restore last token read in case of a negated type selector
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
;
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
;
5104 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected
);
5106 return eSelectorParsingStatus_Error
;
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
;
5123 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected
);
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
;
5143 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected
);
5145 return eSelectorParsingStatus_Error
;
5150 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected
);
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
)) {
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
;
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/
5261 // additional attributes not in HTML4
5262 "direction", // marquee
5266 const char* htmlAttr
;
5267 while ((htmlAttr
= caseInsensitiveHTMLAttribute
[i
++])) {
5268 if (attr
.LowerCaseEqualsASCII(htmlAttr
)) {
5269 isCaseSensitive
= false;
5274 aDataMask
|= SEL_MASK_ATTRIB
;
5275 aSelector
.AddAttribute(nameSpaceID
, attr
, func
, value
, isCaseSensitive
);
5278 REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose
);
5280 return eSelectorParsingStatus_Error
;
5284 REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue
);
5286 return eSelectorParsingStatus_Error
;
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
,
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 "
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
);
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
);
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
);
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
;
5373 pseudoElementType
= nsCSSPseudoElements::ePseudo_NotPseudoElement
;
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
5386 bool isTree
= (eCSSToken_Function
== mToken
.mType
) && isTreePseudo
;
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
);
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
) !=
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
);
5425 return eSelectorParsingStatus_Error
;
5428 // If it starts with "::", it better be a pseudo-element
5429 if (parsingPseudoElement
&&
5432 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE
);
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
);
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
);
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
);
5465 return eSelectorParsingStatus_Error
;
5468 aDataMask
|= SEL_MASK_PCLASS
;
5469 if (eCSSToken_Function
== mToken
.mType
) {
5470 nsSelectorParsingStatus parsingStatus
;
5471 if (nsCSSPseudoClasses::HasStringArg(pseudoClassType
)) {
5473 ParsePseudoClassWithIdentArg(aSelector
, pseudoClassType
);
5475 else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType
)) {
5477 ParsePseudoClassWithNthPairArg(aSelector
, pseudoClassType
);
5480 NS_ABORT_IF_FALSE(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType
),
5481 "unexpected pseudo with function token");
5482 parsingStatus
= ParsePseudoClassWithSelectorListArg(aSelector
,
5485 if (eSelectorParsingStatus_Continue
!= parsingStatus
) {
5486 if (eSelectorParsingStatus_Error
== parsingStatus
) {
5489 return parsingStatus
;
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
);
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|
5508 if (!parsingPseudoElement
&&
5509 !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo
)
5514 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly
);
5516 return eSelectorParsingStatus_Error
;
5519 if (0 == (aDataMask
& SEL_MASK_PELEM
)) {
5520 aDataMask
|= SEL_MASK_PELEM
;
5521 NS_ADDREF(*aPseudoElement
= pseudo
);
5522 *aPseudoElementType
= pseudoElementType
;
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
;
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(':')) {
5544 return eSelectorParsingStatus_Continue
;
5546 if ((mToken
.IsSymbol('{') || mToken
.IsSymbol(','))) {
5548 return eSelectorParsingStatus_Done
;
5550 REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC
);
5552 return eSelectorParsingStatus_Error
;
5554 else { // multiple pseudo elements, not legal
5555 REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE
);
5557 return eSelectorParsingStatus_Error
;
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
5569 NS_NOTREACHED("How did this happen?");
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 ']'
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
);
5631 return parsingStatus
;
5633 // close the parenthesis
5634 if (!ExpectSymbol(')', true)) {
5635 REPORT_UNEXPECTED_TOKEN(PENegationNoClose
);
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
);
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('-')) {
5708 if (mToken
.IsSymbol('-')) {
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-"))) {
5726 } else if (StringBeginsWith(mToken
.mIdent
, NS_LITERAL_STRING("-n-")) && !hasSign
[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]) {
5741 else if (mToken
.mIdent
.LowerCaseEqualsLiteral("even") && !hasSign
[0]) {
5746 else if (mToken
.mIdent
.LowerCaseEqualsLiteral("n")) {
5747 numbers
[0] = sign
[0];
5749 else if (mToken
.mIdent
.LowerCaseEqualsLiteral("-n") && !hasSign
[0]) {
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(')')
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];
5769 if (! GetToken(false)) {
5770 numbers
[1] = intValue
;
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);
5783 numbers
[1] = intValue
;
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(')')
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)
5802 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth
);
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('-')) {
5817 if (mToken
.IsSymbol('-')) {
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
);
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
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 ]*
5886 CSSParserImpl::ParseSelector(nsCSSSelectorList
* aList
,
5887 char16_t aPrevCombinator
)
5889 if (! GetToken(true)) {
5890 REPORT_UNEXPECTED_EOF(PESelectorEOF
);
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
) {
5936 else { // not a selector token, we're done
5937 parsingStatus
= eSelectorParsingStatus_Done
;
5942 if (parsingStatus
!= eSelectorParsingStatus_Continue
) {
5946 if (! GetToken(false)) { // premature eof is ok (here!)
5947 parsingStatus
= eSelectorParsingStatus_Done
;
5952 if (parsingStatus
== eSelectorParsingStatus_Error
) {
5957 if (selector
->mNext
) {
5958 REPORT_UNEXPECTED(PESelectorGroupExtraCombinator
);
5960 REPORT_UNEXPECTED(PESelectorGroupNoSelector
);
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
);
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
);
5981 aList
->mWeight
+= selector
->CalcWeight();
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
);
5998 css::Declaration
* declaration
= new css::Declaration();
5999 mData
.AssertInitialState();
6003 if (!ParseDeclaration(declaration
, aFlags
, true, &changed
, aContext
)) {
6004 if (!SkipDeclaration(checkForBraces
)) {
6007 if (checkForBraces
) {
6008 if (ExpectSymbol('}', true)) {
6012 // Since the skipped declaration didn't end the block we parse
6013 // the next declaration.
6016 declaration
->CompressFrom(&mData
);
6022 CSSParserImpl::ParseColor(nsCSSValue
& aValue
)
6024 if (!GetToken(true)) {
6025 REPORT_UNEXPECTED_EOF(PEColorEOF
);
6029 nsCSSToken
* tk
= &mToken
;
6031 switch (tk
->mType
) {
6033 case eCSSToken_Hash
:
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
:
6041 aValue
.SetIntegerColorValue(rgba
, unit
);
6046 case eCSSToken_Ident
:
6047 if (NS_ColorNameToRGB(tk
->mIdent
, &rgba
)) {
6048 aValue
.SetStringValue(tk
->mIdent
, eCSSUnit_Ident
);
6052 nsCSSKeyword keyword
= nsCSSKeywords::LookupKeyword(tk
->mIdent
);
6053 if (eCSSKeyword_UNKNOWN
< keyword
) { // known keyword
6055 if (nsCSSProps::FindKeyword(keyword
, nsCSSProps::kColorKTable
, value
)) {
6056 aValue
.SetIntValue(value
, eCSSUnit_EnumColor
);
6062 case eCSSToken_Function
:
6063 if (mToken
.mIdent
.LowerCaseEqualsLiteral("rgb")) {
6064 // rgb ( component , component , component )
6065 if (GetToken(true)) {
6068 if (mToken
.mType
== eCSSToken_Number
) {
6070 if (ParseNumberColorComponent(r
, ',') &&
6071 ParseNumberColorComponent(g
, ',') &&
6072 ParseNumberColorComponent(b
, ')')) {
6073 aValue
.SetIntegerColorValue(NS_RGB(r
, g
, b
), eCSSUnit_RGBColor
);
6078 if (ParsePercentageColorComponent(r
, ',') &&
6079 ParsePercentageColorComponent(g
, ',') &&
6080 ParsePercentageColorComponent(b
, ')')) {
6081 aValue
.SetFloatColorValue(r
, g
, b
, 1.0f
,
6082 eCSSUnit_PercentageRGBColor
);
6089 else if (mToken
.mIdent
.LowerCaseEqualsLiteral("rgba")) {
6090 // rgba ( component , component , component , opacity )
6091 if (GetToken(true)) {
6094 if (mToken
.mType
== eCSSToken_Number
) {
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
);
6106 if (ParsePercentageColorComponent(r
, ',') &&
6107 ParsePercentageColorComponent(g
, ',') &&
6108 ParsePercentageColorComponent(b
, ',') &&
6109 ParseColorOpacity(a
)) {
6110 aValue
.SetFloatColorValue(r
, g
, b
, a
, eCSSUnit_PercentageRGBAColor
);
6117 else if (mToken
.mIdent
.LowerCaseEqualsLiteral("hsl")) {
6118 // hsl ( hue , saturation , lightness )
6119 // "hue" is a number, "saturation" and "lightness" are percentages.
6121 if (ParseHSLColor(h
, s
, l
, ')')) {
6122 aValue
.SetFloatColorValue(h
, s
, l
, 1.0f
, eCSSUnit_HSLColor
);
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.
6133 if (ParseHSLColor(h
, s
, l
, ',') &&
6134 ParseColorOpacity(a
)) {
6135 aValue
.SetFloatColorValue(h
, s
, l
, a
, eCSSUnit_HSLAColor
);
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
6163 switch (tk
->mType
) {
6164 case eCSSToken_Ident
:
6165 str
.Assign(tk
->mIdent
);
6168 case eCSSToken_Number
:
6169 if (tk
->mIntegerValid
) {
6170 PR_snprintf(buffer
, sizeof(buffer
), "%06d", tk
->mInteger
);
6171 str
.AssignWithConversion(buffer
);
6175 case eCSSToken_Dimension
:
6176 if (tk
->mIdent
.Length() <= 6) {
6177 PR_snprintf(buffer
, sizeof(buffer
), "%06.0f", tk
->mNumber
);
6179 temp
.AssignWithConversion(buffer
);
6180 temp
.Right(str
, 6 - tk
->mIdent
.Length());
6181 str
.Append(tk
->mIdent
);
6185 // There is a whole bunch of cases that are
6186 // not handled by this switch. Ignore them.
6189 if (NS_HexToRGB(str
, &rgba
)) {
6190 aValue
.SetIntegerColorValue(rgba
, eCSSUnit_HexColor
);
6196 REPORT_UNEXPECTED_TOKEN(PEColorNotColor
);
6202 CSSParserImpl::ParseNumberColorComponent(uint8_t& aComponent
, char aStop
)
6204 if (!GetToken(true)) {
6205 REPORT_UNEXPECTED_EOF(PEColorComponentEOF
);
6209 if (mToken
.mType
!= eCSSToken_Number
|| !mToken
.mIntegerValid
) {
6210 REPORT_UNEXPECTED_TOKEN(PEExpectedInt
);
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
);
6223 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm
, aStop
);
6228 CSSParserImpl::ParsePercentageColorComponent(float& aComponent
, char aStop
)
6230 if (!GetToken(true)) {
6231 REPORT_UNEXPECTED_EOF(PEColorComponentEOF
);
6235 if (mToken
.mType
!= eCSSToken_Percentage
) {
6236 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent
);
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)) {
6249 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm
, aStop
);
6255 CSSParserImpl::ParseHSLColor(float& aHue
, float& aSaturation
, float& aLightness
,
6261 if (!GetToken(true)) {
6262 REPORT_UNEXPECTED_EOF(PEColorHueEOF
);
6265 if (mToken
.mType
!= eCSSToken_Number
) {
6266 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber
);
6272 // hue values are wraparound
6275 if (!ExpectSymbol(',', true)) {
6276 REPORT_UNEXPECTED_TOKEN(PEExpectedComma
);
6280 // Get the saturation
6281 if (!GetToken(true)) {
6282 REPORT_UNEXPECTED_EOF(PEColorSaturationEOF
);
6285 if (mToken
.mType
!= eCSSToken_Percentage
) {
6286 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent
);
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
);
6299 // Get the lightness
6300 if (!GetToken(true)) {
6301 REPORT_UNEXPECTED_EOF(PEColorLightnessEOF
);
6304 if (mToken
.mType
!= eCSSToken_Percentage
) {
6305 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent
);
6310 if (l
< 0.0f
) l
= 0.0f
;
6311 if (l
> 1.0f
) l
= 1.0f
;
6313 if (ExpectSymbol(aStop
, true)) {
6320 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm
, aStop
);
6326 CSSParserImpl::ParseColorOpacity(uint8_t& aOpacity
)
6329 if (!ParseColorOpacity(floatOpacity
)) {
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");
6344 CSSParserImpl::ParseColorOpacity(float& aOpacity
)
6346 if (!GetToken(true)) {
6347 REPORT_UNEXPECTED_EOF(PEColorOpacityEOF
);
6351 if (mToken
.mType
!= eCSSToken_Number
) {
6352 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber
);
6357 if (!ExpectSymbol(')', true)) {
6358 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen
);
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
;
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)) {
6385 if (eCSSToken_Ident
== mToken
.mType
) {
6386 fakeSelector
.AddClass(mToken
.mIdent
);
6388 else if (!mToken
.IsSymbol(',')) {
6394 *aPseudoElementArgs
= fakeSelector
.mClassList
;
6395 fakeSelector
.mClassList
= nullptr;
6400 //----------------------------------------------------------------------
6403 CSSParserImpl::ParseDeclaration(css::Declaration
* aDeclaration
,
6405 bool aMustCallValueAppended
,
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
;
6421 if (!GetToken(true)) {
6422 if (checkForBraces
) {
6423 REPORT_UNEXPECTED_EOF(PEDeclEndEOF
);
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
);
6438 if (tk
->IsSymbol(';')) {
6439 // dangling semicolons are skipped
6443 if (!tk
->IsSymbol('}')) {
6444 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected
);
6445 REPORT_UNEXPECTED(PEDeclSkipped
);
6448 if (eCSSToken_AtKeyword
== tk
->mType
) {
6449 SkipAtRule(checkForBraces
);
6450 return true; // Not a declaration, but don't skip until ';'
6453 // Not a declaration...
6458 // Don't report property parse errors if we're inside a failing @supports
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
);
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
);
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
);
6501 mTempData
.ClearProperty(propID
);
6502 mTempData
.AssertInitialState();
6509 // Look for "!important".
6510 PriorityParsingStatus status
;
6511 if ((aFlags
& eParseDeclaration_AllowImportant
) != 0) {
6512 status
= ParsePriority();
6514 status
= ePriority_None
;
6517 // Look for a semicolon or close brace.
6518 if (status
!= ePriority_Error
) {
6519 if (!GetToken(true)) {
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
6526 if (!checkForBraces
) {
6527 status
= ePriority_Error
;
6531 status
= ePriority_Error
;
6535 if (status
== ePriority_Error
) {
6536 if (checkForBraces
) {
6537 REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2
);
6539 REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd
);
6541 REPORT_UNEXPECTED(PEDeclDropped
);
6543 if (!customProperty
) {
6544 mTempData
.ClearProperty(propID
);
6546 mTempData
.AssertInitialState();
6550 if (customProperty
) {
6551 MOZ_ASSERT(Substring(propertyName
, 0,
6552 CSS_CUSTOM_NAME_PREFIX_LENGTH
).EqualsLiteral("--"));
6554 nsDependentString
varName(propertyName
, CSS_CUSTOM_NAME_PREFIX_LENGTH
);
6555 aDeclaration
->AddVariableDeclaration(varName
, variableType
, variableValue
,
6556 status
== ePriority_Important
, false);
6558 *aChanged
|= mData
.TransferFromBlock(mTempData
, propID
,
6559 status
== ePriority_Important
,
6560 false, aMustCallValueAppended
,
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
6616 CSSParserImpl::ParseEnum(nsCSSValue
& aValue
,
6617 const KTableValue aKeywordTable
[])
6619 nsSubstring
* ident
= NextIdent();
6620 if (nullptr == ident
) {
6623 nsCSSKeyword keyword
= nsCSSKeywords::LookupKeyword(*ident
);
6624 if (eCSSKeyword_UNKNOWN
< keyword
) {
6626 if (nsCSSProps::FindKeyword(keyword
, aKeywordTable
, value
)) {
6627 aValue
.SetIntValue(value
, eCSSUnit_Enumerated
);
6632 // Put the unknown identifier back and return
6639 char name
[6]; // needs to be long enough for the longest unit, with
6640 // terminating null.
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
}
6678 CSSParserImpl::TranslateDimension(nsCSSValue
& aValue
,
6679 int32_t aVariantMask
,
6681 const nsString
& aUnit
)
6685 if (!aUnit
.IsEmpty()) {
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
;
6696 if (i
== ArrayLength(UnitData
)) {
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.
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
;
6724 NS_ERROR("Variant mask does not include dimension; why were we called?");
6728 if ((type
& aVariantMask
) != 0) {
6729 aValue
.SetFloatValue(aNumber
, units
);
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
6739 #define VARIANT_ALL_NONNUMERIC \
6746 VARIANT_IDENTIFIER | \
6747 VARIANT_IDENTIFIER_NO_INHERIT | \
6753 VARIANT_GRADIENT | \
6754 VARIANT_TIMING_FUNCTION | \
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.
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
|
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) {
6785 else if (aValue
.GetUnit() == eCSSUnit_Percent
) {
6786 if (aValue
.GetPercentValue() < 0) {
6790 } else if (aValue
.GetUnit() == eCSSUnit_Integer
) {
6791 if (aValue
.GetIntValue() < 0) {
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.
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
|
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) {
6823 } else if (eCSSUnit_Number
== aValue
.GetUnit()) {
6824 if (aValue
.GetFloatValue() < 1.0f
) {
6834 // Assigns to aValue iff it returns true.
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)) {
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();
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();
6879 else if (eCSSKeyword_initial
== keyword
) {
6880 aValue
.SetInitialValue();
6883 else if (eCSSKeyword_unset
== keyword
&&
6884 nsLayoutUtils::UnsetValueEnabled()) {
6885 aValue
.SetUnsetValue();
6889 if ((aVariantMask
& VARIANT_NONE
) != 0) {
6890 if (eCSSKeyword_none
== keyword
) {
6891 aValue
.SetNoneValue();
6895 if ((aVariantMask
& VARIANT_ALL
) != 0) {
6896 if (eCSSKeyword_all
== keyword
) {
6897 aValue
.SetAllValue();
6901 if ((aVariantMask
& VARIANT_NORMAL
) != 0) {
6902 if (eCSSKeyword_normal
== keyword
) {
6903 aValue
.SetNormalValue();
6907 if ((aVariantMask
& VARIANT_SYSFONT
) != 0) {
6908 if (eCSSKeyword__moz_use_system_font
== keyword
&&
6909 !IsParsingCompoundProperty()) {
6910 aValue
.SetSystemFontValue();
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) {
6928 if (nsCSSProps::FindKeyword(keyword
, aKeywordTable
, value
)) {
6929 aValue
.SetIntValue(value
, eCSSUnit_Enumerated
);
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
);
6942 if (((aVariantMask
& VARIANT_INTEGER
) != 0) &&
6943 (eCSSToken_Number
== tk
->mType
) && tk
->mIntegerValid
) {
6944 aValue
.SetIntValue(tk
->mInteger
, eCSSUnit_Integer
);
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)) {
6960 if (TranslateDimension(aValue
, aVariantMask
, tk
->mNumber
, tk
->mIdent
)) {
6963 // Put the token back; we didn't parse it, so we shouldn't consume it
6967 if (((aVariantMask
& VARIANT_PERCENT
) != 0) &&
6968 (eCSSToken_Percentage
== tk
->mType
)) {
6969 aValue
.SetPercentValue(tk
->mNumber
);
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
);
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
);
6990 if (((aVariantMask
& VARIANT_URL
) != 0) &&
6991 eCSSToken_URL
== tk
->mType
) {
6992 SetValueToURL(aValue
, tk
->mIdent
);
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-"))) {
7004 bool isRepeating
= false;
7005 if (StringBeginsWith(tmp
, NS_LITERAL_STRING("repeating-"))) {
7006 tmp
.Rebind(tmp
, 10);
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
7040 if (ParseColor(aValue
)) {
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
);
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
);
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
)) {
7079 if (((aVariantMask
& VARIANT_TIMING_FUNCTION
) != 0) &&
7080 (eCSSToken_Function
== tk
->mType
)) {
7081 if (tk
->mIdent
.LowerCaseEqualsLiteral("cubic-bezier")) {
7082 if (!ParseTransitionTimingFunctionValues(aValue
)) {
7088 if (tk
->mIdent
.LowerCaseEqualsLiteral("steps")) {
7089 if (!ParseTransitionStepTimingFunctionValues(aValue
)) {
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
);
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
);
7120 if (keyword
== eCSSKeyword_inherit
||
7121 keyword
== eCSSKeyword_initial
||
7122 keyword
== eCSSKeyword_unset
||
7123 keyword
== eCSSKeyword_default
||
7125 nsCSSProps::FindIndexOfKeyword(keyword
, aPropertyKTable
) >= 0)) {
7128 if (aExcludedKeywords
) {
7129 for (uint32_t i
= 0;; i
++) {
7130 nsCSSKeyword excludedKeyword
= aExcludedKeywords
[i
];
7131 if (excludedKeyword
== eCSSKeyword_UNKNOWN
) {
7134 if (excludedKeyword
== keyword
) {
7139 aValue
.SetStringValue(mToken
.mIdent
, eCSSUnit_Ident
);
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.
7151 if (!GetToken(true)) {
7154 if (eCSSToken_Ident
!= mToken
.mType
) {
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)) {
7169 if (eCSSToken_String
!= mToken
.mType
) {
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)) {
7184 int32_t typeItem
= eCSSUnit_Counters
== unit
? 2 : 1;
7185 val
->Item(typeItem
).SetStringValue(type
, eCSSUnit_Ident
);
7187 if (!ExpectSymbol(')', true)) {
7191 aValue
.SetArrayValue(val
, unit
);
7200 CSSParserImpl::ParseAttr(nsCSSValue
& aValue
)
7202 if (!GetToken(true)) {
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
) {
7214 attr
.AppendInt(nameSpaceID
, 10);
7215 attr
.Append(char16_t('|'));
7216 if (! GetToken(false)) {
7217 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF
);
7220 if (eCSSToken_Ident
== mToken
.mType
) {
7221 attr
.Append(mToken
.mIdent
);
7224 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected
);
7229 else { // no namespace
7233 else if (mToken
.IsSymbol('*')) { // namespace wildcard
7234 // Wildcard namespace makes no sense here and is not allowed
7235 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected
);
7239 else if (mToken
.IsSymbol('|')) { // explicit NO namespace
7240 if (! GetToken(false)) {
7241 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF
);
7244 if (eCSSToken_Ident
== mToken
.mType
) {
7245 attr
.Append(mToken
.mIdent
);
7248 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected
);
7254 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected
);
7258 if (!ExpectSymbol(')', true)) {
7261 aValue
.SetStringValue(attr
, eCSSUnit_Attr
);
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");
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
);
7284 * Parse the image-orientation property, which has the grammar:
7285 * <angle> flip? | flip | from-image
7288 CSSParserImpl::ParseImageOrientation(nsCSSValue
& aValue
)
7290 if (ParseVariant(aValue
, VARIANT_INHERIT
, nullptr)) {
7291 // 'inherit', 'initial' and 'unset' must be alone
7295 // Check for an angle with optional 'flip'.
7297 if (ParseVariant(angle
, VARIANT_ANGLE
, nullptr)) {
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
);
7312 // The remaining possibilities (bare 'flip' and 'from-image') are both
7313 // keywords, so we can handle them at the same time.
7315 if (ParseVariant(keyword
, VARIANT_KEYWORD
, nsCSSProps::kImageOrientationKTable
)) {
7320 // All possibilities failed.
7325 * Parse the arguments of -moz-image-rect() function.
7326 * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
7329 CSSParserImpl::ParseImageRect(nsCSSValue
& aImage
)
7331 // A non-iterative for loop to break out when an error occurs.
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)) {
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))
7363 aImage
= newFunction
;
7371 // <element>: -moz-element(# <element_id> )
7373 CSSParserImpl::ParseElement(nsCSSValue
& aValue
)
7375 // A non-iterative for loop to break out when an error occurs.
7377 if (!GetToken(true))
7380 if (mToken
.mType
== eCSSToken_ID
) {
7381 aValue
.SetStringValue(mToken
.mIdent
, eCSSUnit_Element
);
7387 if (!ExpectSymbol(')', 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.
7399 // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
7401 CSSParserImpl::ParseFlex()
7403 // First check for inherit / initial / unset
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
);
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
));
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
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.
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)) {
7471 // Failed to parse anything after our flex-basis -- that's fine. We can
7472 // skip the remaining parsing.
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
)) {
7509 AppendValue(eCSSProperty_flex_grow
, flexGrow
);
7510 AppendValue(eCSSProperty_flex_shrink
, flexShrink
);
7511 AppendValue(eCSSProperty_flex_basis
, flexBasis
);
7516 // flex-flow: <flex-direction> || <flex-wrap>
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
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
]);
7550 CSSParserImpl::ParseGridAutoFlow()
7553 if (ParseVariant(value
, VARIANT_INHERIT
, nullptr)) {
7554 AppendValue(eCSSProperty_grid_auto_flow
, value
);
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
,
7563 if (!ParseBitmaskValues(value
, nsCSSProps::kGridAutoFlowKTable
, mask
)) {
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
)) {
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
);
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
) {
7608 item
->mNext
= new nsCSSValueList
;
7611 MOZ_ASSERT(aValue
.GetUnit() == eCSSUnit_Null
, "Unexpected unit");
7612 item
= aValue
.SetListValue();
7615 if (!(eCSSToken_Ident
== mToken
.mType
&&
7616 ParseCustomIdent(item
->mValue
, mToken
.mIdent
))) {
7619 return CSSParseResult::Error
;
7621 if (!GetToken(true) || mToken
.IsSymbol(')')) {
7622 return CSSParseResult::Ok
;
7624 item
->mNext
= new nsCSSValueList
;
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.
7634 CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList
** aTailPtr
)
7636 if (!(GetToken(true) &&
7637 mToken
.mType
== eCSSToken_Number
&&
7638 mToken
.mIntegerValid
&&
7639 mToken
.mInteger
> 0)) {
7643 int32_t repetitions
= std::min(mToken
.mInteger
,
7644 GRID_TEMPLATE_MAX_REPETITIONS
);
7645 if (!ExpectSymbol(',', true)) {
7650 // Parse at least one <line-names>
7651 nsCSSValueList
* tail
= *aTailPtr
;
7653 tail
->mNext
= new nsCSSValueList
;
7655 if (ParseGridLineNames(tail
->mValue
) != CSSParseResult::Ok
) {
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
;
7669 tail
->mNext
= new nsCSSValueList
;
7671 tail
->mValue
= repeatedItem
->mValue
;
7672 if (repeatedItem
== lastRepeatedItem
) {
7675 repeatedItem
= repeatedItem
->mNext
;
7682 // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>?
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
);
7691 // First try to parse repeat(<positive-integer>, <line-names>+)
7692 if (!GetToken(true)) {
7695 if (mToken
.mType
== eCSSToken_Function
&&
7696 mToken
.mIdent
.LowerCaseEqualsLiteral("repeat")) {
7697 if (!ParseGridLineNameListRepeat(&item
)) {
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
) {
7709 if (result
== CSSParseResult::Error
) {
7712 item
->mNext
= new nsCSSValueList
;
7714 item
->mValue
= lineNames
;
7719 // Parse a <track-breadth>
7721 CSSParserImpl::ParseGridTrackBreadth(nsCSSValue
& aValue
)
7723 if (ParseNonNegativeVariant(aValue
,
7724 VARIANT_LPCALC
| VARIANT_KEYWORD
,
7725 nsCSSProps::kGridTrackBreadthKTable
)) {
7729 // Attempt to parse <flex> (a dimension with the "fr" unit)
7730 if (!GetToken(true)) {
7733 if (!(eCSSToken_Dimension
== mToken
.mType
&&
7734 mToken
.mIdent
.LowerCaseEqualsLiteral("fr") &&
7735 mToken
.mNumber
>= 0)) {
7739 aValue
.SetFloatValue(mToken
.mNumber
, eCSSUnit_FlexFraction
);
7743 // Parse a <track-size>
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"))) {
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
;
7770 return CSSParseResult::Error
;
7774 CSSParserImpl::ParseGridAutoColumnsRows(nsCSSProperty aPropID
)
7777 if (ParseVariant(value
, VARIANT_INHERIT
, nullptr) ||
7778 ParseGridTrackSize(value
) == CSSParseResult::Ok
) {
7779 AppendValue(aPropID
, value
);
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
;
7805 // First try to parse repeat()
7806 if (!GetToken(true)) {
7809 if (mToken
.mType
== eCSSToken_Function
&&
7810 mToken
.mIdent
.LowerCaseEqualsLiteral("repeat")) {
7811 if (!ParseGridTrackListRepeat(&item
)) {
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
) {
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.)
7829 item
->mNext
= new nsCSSValueList
;
7831 item
->mValue
= trackSize
;
7833 item
->mNext
= new nsCSSValueList
;
7836 if (ParseGridLineNames(item
->mValue
) == CSSParseResult::Error
) {
7841 // Require at least one <track-size>.
7842 if (item
== firstLineNamesItem
) {
7846 MOZ_ASSERT(aValue
.GetListValue() &&
7847 aValue
.GetListValue()->mNext
&&
7848 aValue
.GetListValue()->mNext
->mNext
,
7849 "<track-list> should have a minimum length of 3");
7853 // Takes ownership of |aSecond|
7855 ConcatLineNames(nsCSSValue
& aFirst
, nsCSSValue
& aSecond
)
7857 if (aSecond
.GetUnit() == eCSSUnit_Null
) {
7861 if (aFirst
.GetUnit() == eCSSUnit_Null
) {
7862 // Empty or omitted <line-names>. Replace it.
7867 // Join the two <line-names> lists.
7868 nsCSSValueList
* source
= aSecond
.GetListValue();
7869 nsCSSValueList
* target
= aFirst
.GetListValue();
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.
7891 CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList
** aTailPtr
)
7893 if (!(GetToken(true) &&
7894 mToken
.mType
== eCSSToken_Number
&&
7895 mToken
.mIntegerValid
&&
7896 mToken
.mInteger
> 0)) {
7900 int32_t repetitions
= std::min(mToken
.mInteger
,
7901 GRID_TEMPLATE_MAX_REPETITIONS
);
7902 if (!ExpectSymbol(',', true)) {
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
;
7915 if (ParseGridLineNames(firstLineNames
) == CSSParseResult::Error
) {
7920 if (ParseGridTrackSize(trackSize
) != CSSParseResult::Ok
) {
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
;
7931 if (ParseGridLineNames(lastLineNames
) == CSSParseResult::Error
) {
7936 if (ExpectSymbol(')', true)) {
7941 if (ParseGridTrackSize(trackSize
) != CSSParseResult::Ok
) {
7946 item
->mNext
= new nsCSSValueList
;
7948 item
->mValue
= lastLineNames
;
7949 // Do not append to this list at the next iteration.
7950 lastLineNames
.Reset();
7952 item
->mNext
= new nsCSSValueList
;
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)
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();
7978 target
->mValue
= source
->mValue
;
7979 source
= source
->mNext
;
7983 target
->mNext
= new nsCSSValueList
;
7984 target
= target
->mNext
;
7988 if (firstLineNames
.GetUnit() != eCSSUnit_Null
) {
7990 target
->mNext
= new nsCSSValueList
;
7991 target
= target
->mNext
;
7993 target
= joinerLineNames
.SetListValue();
7995 nsCSSValueList
* source
= firstLineNames
.GetListValue();
7997 target
->mValue
= source
->mValue
;
7998 source
= source
->mNext
;
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
;
8030 tail
->mValue
= joinerLineNames
;
8032 nsCSSValueList
* repeatedItem
= firstTrackSizeItem
;
8034 tail
->mNext
= new nsCSSValueList
;
8036 tail
->mValue
= repeatedItem
->mValue
;
8037 if (repeatedItem
== lastTrackSizeItem
) {
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
;
8048 tail
->mValue
= lastLineNames
;
8055 CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSProperty aPropID
)
8058 if (ParseVariant(value
, VARIANT_INHERIT
| VARIANT_NONE
, nullptr)) {
8059 AppendValue(aPropID
, value
);
8063 nsSubstring
* ident
= NextIdent();
8065 if (ident
->LowerCaseEqualsLiteral("subgrid")) {
8066 if (!ParseOptionalLineNameListAfterSubgrid(value
)) {
8069 AppendValue(aPropID
, value
);
8075 nsCSSValue firstLineNames
;
8076 if (ParseGridLineNames(firstLineNames
) == CSSParseResult::Error
||
8077 !ParseGridTrackListWithFirstLineNames(value
, firstLineNames
)) {
8080 AppendValue(aPropID
, value
);
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
)) {
8100 if (token
.isTrash
) {
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
++;
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.");
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:
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
8135 // Next row of an existing named area
8136 currentArea
->mRowEnd
++;
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.");
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
8163 aAreas
->mNColumns
= column
;
8164 } else if (aAreas
->mNColumns
!= column
) {
8171 CSSParserImpl::ParseGridTemplateAreas()
8174 if (ParseVariant(value
, VARIANT_INHERIT
| VARIANT_NONE
, nullptr)) {
8175 AppendValue(eCSSProperty_grid_template_areas
, value
);
8179 nsRefPtr
<css::GridTemplateAreasValue
> areas
=
8180 new css::GridTemplateAreasValue();
8181 nsDataHashtable
<nsStringHashKey
, uint32_t> areaIndices
;
8183 if (!GetToken(true)) {
8186 if (eCSSToken_String
!= mToken
.mType
) {
8190 if (!ParseGridTemplateAreasLine(mToken
.mIdent
, areas
, areaIndices
)) {
8195 if (areas
->NRows() == 0) {
8199 AppendValue(eCSSProperty_grid_template_areas
, nsCSSValue(areas
));
8204 CSSParserImpl::ParseGridTemplate()
8208 // <'grid-template-columns'> / <'grid-template-rows'> |
8209 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
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
);
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
);
8232 // 'subgrid' can appear either by itself,
8233 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
8234 nsSubstring
* ident
= NextIdent();
8236 if (ident
->LowerCaseEqualsLiteral("subgrid")) {
8237 if (!ParseOptionalLineNameListAfterSubgrid(value
)) {
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.
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
);
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
||
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
);
8277 if (!(ParseGridTrackListWithFirstLineNames(value
, firstLineNames
) &&
8278 ExpectSymbol('/', true))) {
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.
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
);
8307 nsSubstring
* ident
= NextIdent();
8309 if (ident
->LowerCaseEqualsLiteral("subgrid")) {
8310 if (!ParseOptionalLineNameListAfterSubgrid(rowsValue
)) {
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
);
8322 nsCSSValue firstLineNames
;
8323 if (ParseGridLineNames(firstLineNames
) == CSSParseResult::Error
||
8327 if (aColumnsIsTrackList
&& mToken
.mType
== eCSSToken_String
) {
8328 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
8329 return ParseGridTemplateAfterString(firstLineNames
);
8333 if (!ParseGridTrackListWithFirstLineNames(rowsValue
, firstLineNames
)) {
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
);
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>
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
;
8362 if (!ParseGridTemplateAreasLine(mToken
.mIdent
, areas
, areaIndices
)) {
8366 rowsItem
->mNext
= new nsCSSValueList
;
8367 rowsItem
= rowsItem
->mNext
;
8368 CSSParseResult result
= ParseGridTrackSize(rowsItem
->mValue
);
8369 if (result
== CSSParseResult::Error
) {
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
) {
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
) {
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)) {
8394 if (eCSSToken_String
!= mToken
.mType
) {
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)) {
8407 if (eCSSToken_String
!= mToken
.mType
) {
8413 AppendValue(eCSSProperty_grid_template_areas
, nsCSSValue(areas
));
8414 AppendValue(eCSSProperty_grid_template_rows
, rowsValue
);
8418 // <'grid-template'> |
8419 // [ <'grid-auto-flow'> [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]? ]
8421 CSSParserImpl::ParseGrid()
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
);
8433 // An empty value is always invalid.
8434 if (!GetToken(true)) {
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
) {
8447 return ParseGridAutoFlow() && ParseGridShorthandAutoProps();
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.
8466 CSSParserImpl::ParseGridShorthandAutoProps()
8468 nsCSSValue autoColumnsValue
;
8469 nsCSSValue autoRowsValue
;
8470 CSSParseResult result
= ParseGridTrackSize(autoColumnsValue
);
8471 if (result
== CSSParseResult::Error
) {
8474 if (result
== CSSParseResult::NotFound
) {
8475 autoColumnsValue
.SetAutoValue();
8476 autoRowsValue
.SetAutoValue();
8478 if (!ExpectSymbol('/', true)) {
8479 autoRowsValue
.SetAutoValue();
8480 } else if (ParseGridTrackSize(autoRowsValue
) != CSSParseResult::Ok
) {
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
);
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.
8503 CSSParserImpl::ParseGridLine(nsCSSValue
& aValue
)
8508 // [ <integer> && <custom-ident>? ] |
8509 // [ span && [ <integer> || <custom-ident> ] ]
8511 // Syntactically, this simplifies to:
8515 // [ span? && [ <integer> || <custom-ident> ] ]
8517 if (ParseVariant(aValue
, VARIANT_AUTO
, nullptr)) {
8521 static const nsCSSKeyword kGridLineKeywords
[] = {
8523 eCSSKeyword_UNKNOWN
// End-of-array marker
8525 bool hasSpan
= false;
8526 bool hasIdent
= false;
8527 Maybe
<int32_t> integer
;
8530 if (!GetToken(true)) {
8533 if (mToken
.mType
== eCSSToken_Ident
&&
8534 mToken
.mIdent
.LowerCaseEqualsLiteral("span")) {
8536 if (!GetToken(true)) {
8543 mToken
.mType
== eCSSToken_Ident
&&
8544 ParseCustomIdent(ident
, mToken
.mIdent
, kGridLineKeywords
)) {
8546 } else if (integer
.isNothing() &&
8547 mToken
.mType
== eCSSToken_Number
&&
8548 mToken
.mIntegerValid
&&
8549 mToken
.mInteger
!= 0) {
8550 integer
.emplace(mToken
.mInteger
);
8555 } while (!(integer
.isSome() && hasIdent
) && GetToken(true));
8557 // Require at least one of <integer> or <custom-ident>
8558 if (!(integer
.isSome() || hasIdent
)) {
8562 if (!hasSpan
&& GetToken(true)) {
8563 if (mToken
.mType
== eCSSToken_Ident
&&
8564 mToken
.mIdent
.LowerCaseEqualsLiteral("span")) {
8571 nsCSSValueList
* item
= aValue
.SetListValue();
8573 // Given "span", a negative <integer> is invalid.
8574 if (integer
.isSome() && integer
.ref() < 0) {
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
;
8583 if (integer
.isSome()) {
8584 item
->mValue
.SetIntValue(integer
.ref(), eCSSUnit_Integer
);
8586 item
->mNext
= new nsCSSValueList
;
8591 item
->mValue
= ident
;
8597 CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSProperty aPropID
)
8600 if (ParseVariant(value
, VARIANT_INHERIT
, nullptr) ||
8601 ParseGridLine(value
)) {
8602 AppendValue(aPropID
, value
);
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()
8612 HandleGridLineFallback(const nsCSSValue
& aFallback
, nsCSSValue
& aValue
)
8614 if (aFallback
.GetUnit() == eCSSUnit_List
&&
8615 aFallback
.GetListValue()->mValue
.GetUnit() == eCSSUnit_Ident
&&
8616 !aFallback
.GetListValue()->mNext
) {
8619 aValue
.SetAutoValue();
8624 CSSParserImpl::ParseGridColumnRow(nsCSSProperty aStartPropID
,
8625 nsCSSProperty aEndPropID
)
8628 nsCSSValue secondValue
;
8629 if (ParseVariant(value
, VARIANT_INHERIT
, nullptr)) {
8630 AppendValue(aStartPropID
, value
);
8631 AppendValue(aEndPropID
, value
);
8635 if (!ParseGridLine(value
)) {
8638 if (GetToken(true)) {
8639 if (mToken
.IsSymbol('/')) {
8640 if (ParseGridLine(secondValue
)) {
8641 AppendValue(aStartPropID
, value
);
8642 AppendValue(aEndPropID
, secondValue
);
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
);
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]);
8674 if (!ParseGridLine(values
[i
])) {
8677 if (++i
== 4 || !GetToken(true)) {
8680 if (!mToken
.IsSymbol('/')) {
8686 MOZ_ASSERT(i
>= 1, "should have parsed at least one grid-line (or returned)");
8688 HandleGridLineFallback(values
[0], values
[1]);
8691 HandleGridLineFallback(values
[0], values
[2]);
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]);
8704 // <color-stop> : <color> [ <percentage> | <length> ]?
8706 CSSParserImpl::ParseColorStop(nsCSSValueGradient
* aGradient
)
8708 nsCSSValueGradientStop
* stop
= aGradient
->mStops
.AppendElement();
8709 if (!ParseVariant(stop
->mColor
, VARIANT_COLOR
, nullptr)) {
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();
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>]*
8742 CSSParserImpl::ParseLinearGradient(nsCSSValue
& aValue
, bool aIsRepeating
,
8745 nsRefPtr
<nsCSSValueGradient
> cssGradient
8746 = new nsCSSValueGradient(false, aIsRepeating
);
8748 if (!GetToken(true)) {
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)) {
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
))) {
8776 if (!ExpectSymbol(',', true)) {
8781 return ParseGradientColorStops(cssGradient
, aValue
);
8788 if (ParseVariant(cssGradient
->mAngle
, VARIANT_ANGLE
, nullptr) &&
8789 !ExpectSymbol(',', true)) {
8794 return ParseGradientColorStops(cssGradient
, aValue
);
8797 nsCSSTokenType ty
= mToken
.mType
;
8798 nsString id
= mToken
.mIdent
;
8801 // <legacy-gradient-line>
8802 bool haveGradientLine
= IsLegacyGradientLine(ty
, id
);
8803 if (haveGradientLine
) {
8804 cssGradient
->mIsLegacySyntax
= true;
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)) {
8815 if (!ExpectSymbol(',', true) &&
8816 // if we didn't already get an angle, we might have one now,
8817 // otherwise it's an error
8819 !ParseVariant(cssGradient
->mAngle
, VARIANT_ANGLE
, nullptr) ||
8820 // now we better have a comma
8821 !ExpectSymbol(',', true))) {
8828 return ParseGradientColorStops(cssGradient
, aValue
);
8832 CSSParserImpl::ParseRadialGradient(nsCSSValue
& aValue
, bool aIsRepeating
,
8835 nsRefPtr
<nsCSSValueGradient
> cssGradient
8836 = new nsCSSValueGradient(true, aIsRepeating
);
8838 // [ <shape> || <size> ]
8840 ParseVariant(cssGradient
->GetRadialShape(), VARIANT_KEYWORD
,
8841 nsCSSProps::kRadialGradientShapeKTable
);
8843 bool haveSize
= ParseVariant(cssGradient
->GetRadialSize(), VARIANT_KEYWORD
,
8845 nsCSSProps::kRadialGradientLegacySizeKTable
:
8846 nsCSSProps::kRadialGradientSizeKTable
);
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.
8857 cssGradient
->GetRadialShape().GetUnit() == eCSSUnit_Enumerated
?
8858 cssGradient
->GetRadialShape().GetIntValue() : -1;
8859 // <length> | [<length> | <percentage>]{2}
8860 cssGradient
->mIsExplicitSize
= true;
8862 ParseNonNegativeVariant(cssGradient
->GetRadiusX(), VARIANT_LP
, nullptr);
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;
8874 // vertical extent is optional
8876 ParseNonNegativeVariant(cssGradient
->GetRadiusY(), VARIANT_LP
, nullptr);
8878 nsCSSValue shapeValue
;
8879 haveShape
= ParseVariant(shapeValue
, VARIANT_KEYWORD
,
8880 nsCSSProps::kRadialGradientShapeKTable
);
8882 shape
= shapeValue
.GetIntValue();
8886 ? shape
== NS_STYLE_GRADIENT_SHAPE_CIRCULAR
8887 : cssGradient
->GetRadiusX().GetUnit() == eCSSUnit_Percent
||
8888 shape
== NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL
) {
8895 if ((haveShape
|| haveSize
) && ExpectSymbol(',', true)) {
8896 // [ <shape> || <size> ] ,
8897 return ParseGradientColorStops(cssGradient
, aValue
);
8900 if (!GetToken(true)) {
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)) {
8914 return ParseGradientColorStops(cssGradient
, aValue
);
8917 // <color-stops> only
8919 return ParseGradientColorStops(cssGradient
, aValue
);
8921 MOZ_ASSERT(!cssGradient
->mIsExplicitSize
);
8923 nsCSSTokenType ty
= mToken
.mType
;
8924 nsString id
= mToken
.mIdent
;
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
) {
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)) {
8945 if (!ExpectSymbol(',', true) &&
8946 // if we didn't already get an angle, we might have one now,
8947 // otherwise it's an error
8949 !ParseVariant(cssGradient
->mAngle
, VARIANT_ANGLE
, nullptr) ||
8950 // now we better have a comma
8951 !ExpectSymbol(',', true))) {
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
) {
8965 ParseVariant(cssGradient
->GetRadialShape(), VARIANT_KEYWORD
,
8966 nsCSSProps::kRadialGradientShapeKTable
);
8968 ParseVariant(cssGradient
->GetRadialSize(), VARIANT_KEYWORD
,
8969 nsCSSProps::kRadialGradientLegacySizeKTable
);
8971 // could be in either order
8974 ParseVariant(cssGradient
->GetRadialShape(), VARIANT_KEYWORD
,
8975 nsCSSProps::kRadialGradientShapeKTable
);
8979 if ((haveShape
|| haveSize
) && !ExpectSymbol(',', true)) {
8984 return ParseGradientColorStops(cssGradient
, aValue
);
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;
9000 case eCSSToken_Percentage
:
9001 case eCSSToken_Number
:
9002 case eCSSToken_Dimension
:
9003 haveGradientLine
= true;
9006 case eCSSToken_Function
:
9007 if (aId
.LowerCaseEqualsLiteral("calc") ||
9008 aId
.LowerCaseEqualsLiteral("-moz-calc")) {
9009 haveGradientLine
= true;
9014 case eCSSToken_Hash
:
9018 case eCSSToken_Ident
: {
9019 // This is only a gradient line if it's a box position keyword.
9020 nsCSSKeyword kw
= nsCSSKeywords::LookupKeyword(aId
);
9022 if (kw
!= eCSSKeyword_UNKNOWN
&&
9023 nsCSSProps::FindKeyword(kw
, nsCSSProps::kBackgroundPositionKTable
,
9025 haveGradientLine
= true;
9035 return haveGradientLine
;
9039 CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient
* aGradient
,
9042 // At least two color stops are required
9043 if (!ParseColorStop(aGradient
) ||
9044 !ExpectSymbol(',', true) ||
9045 !ParseColorStop(aGradient
)) {
9050 // Additional color stops
9051 while (ExpectSymbol(',', true)) {
9052 if (!ParseColorStop(aGradient
)) {
9058 if (!ExpectSymbol(')', true)) {
9063 aValue
.SetGradientValue(aGradient
);
9068 CSSParserImpl::ParseChoice(nsCSSValue aValues
[],
9069 const nsCSSProperty aPropIDs
[], int32_t aNumIDs
)
9072 nsAutoParseCompoundProperty
compound(this);
9075 for (loop
= 0; loop
< aNumIDs
; loop
++) {
9076 // Try each property parser in order
9077 int32_t hadFound
= found
;
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
])) {
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..
9091 if (found
== hadFound
) { // found nothing new
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()) {
9122 else if (eCSSUnit_Initial
== aValues
[loop
].GetUnit()) {
9126 else if (eCSSUnit_Unset
== aValues
[loop
].GetUnit()) {
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
9148 CSSParserImpl::ParseBoxProperties(const nsCSSProperty aPropIDs
[])
9150 // Get up to four values for the property
9153 NS_FOR_CSS_SIDES (index
) {
9154 if (! ParseSingleValueProperty(result
.*(nsCSSRect::sides
[index
]),
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
) {
9175 // Provide missing values by replicating some of the values found
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
]));
9191 // Similar to ParseBoxProperties, except there is only one property
9192 // with the result as its value, not four. Requires values be nonnegative.
9194 CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask
,
9195 /** outparam */ nsCSSValue
& aValue
)
9197 nsCSSRect
& result
= aValue
.SetRectValue();
9200 NS_FOR_CSS_SIDES (index
) {
9201 if (!ParseNonNegativeVariant(result
.*(nsCSSRect::sides
[index
]),
9202 aVariantMask
, nullptr)) {
9212 // Provide missing values by replicating some of the values found
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
;
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");
9233 if (!ParseSingleValueProperty(value
, subprops
[0])) {
9237 AppendValue(subprops
[0], value
);
9238 nsCSSValue
typeVal(aSourceType
, eCSSUnit_Enumerated
);
9239 AppendValue(subprops
[1], typeVal
);
9240 AppendValue(subprops
[2], typeVal
);
9245 CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID
)
9247 nsCSSValue dimenX
, dimenY
;
9248 // required first value
9249 if (! ParseNonNegativeVariant(dimenX
, VARIANT_HLP
| VARIANT_CALC
, nullptr))
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
);
9263 value
.SetPairValue(dimenX
, dimenY
);
9264 AppendValue(aPropID
, value
);
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
,
9289 if (ExpectSymbol('/', true)) {
9290 NS_FOR_CSS_SIDES (side
) {
9291 if (! ParseNonNegativeVariant(dimenY
.*nsCSSRect::sides
[side
],
9292 VARIANT_LP
| VARIANT_CALC
, nullptr))
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
)
9309 // if we have no Y-values, use the X-values
9315 // Provide missing values by replicating some of the values found
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
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
];
9333 AppendValue(aPropIDs
[side
], x
);
9336 pair
.SetPairValue(x
, y
);
9337 AppendValue(aPropIDs
[side
], pair
);
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
9376 CSSParserImpl::SaveInputState(CSSParserInputState
& aState
)
9378 aState
.mToken
= mToken
;
9379 aState
.mHavePushBack
= mHavePushBack
;
9380 mScanner
->SavePosition(aState
.mPosition
);
9384 CSSParserImpl::RestoreSavedInputState(const CSSParserInputState
& aState
)
9386 mToken
= aState
.mToken
;
9387 mHavePushBack
= aState
.mHavePushBack
;
9388 mScanner
->RestoreSavedPosition(aState
.mPosition
);
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
9410 CSSParserInputState stateBeforeProperty
;
9411 SaveInputState(stateBeforeProperty
);
9412 mScanner
->ClearSeenVariableReference();
9414 NS_ASSERTION(aPropID
< eCSSProperty_COUNT
, "index out of range");
9415 bool allowVariables
= true;
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;
9425 case CSS_PROPERTY_PARSE_FUNCTION
: {
9426 result
= ParsePropertyByFunction(aPropID
);
9429 case CSS_PROPERTY_PARSE_VALUE
: {
9432 if (ParseSingleValueProperty(value
, aPropID
)) {
9433 AppendValue(aPropID
, value
);
9436 // XXX Report errors?
9439 case CSS_PROPERTY_PARSE_VALUE_LIST
: {
9440 result
= ParseValueList(aPropID
);
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");
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()) {
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
9483 CSSParserInputState stateAtError
;
9484 SaveInputState(stateAtError
);
9486 const char16_t stopChars
[] = { ';', '!', '}', ')', 0 };
9487 SkipUntilOneOf(stopChars
);
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
);
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
9506 RestoreSavedInputState(stateBeforeProperty
);
9508 if (!mInSupportsCondition
) {
9509 mScanner
->StartRecording();
9512 CSSVariableDeclarations::Type type
;
9514 nsString impliedCharacters
;
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] ==
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
);
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
);
9588 if (!mInSupportsCondition
) {
9589 mScanner
->StopRecording();
9594 if (mNavQuirkMode
) {
9595 mHashlessColorQuirk
= false;
9596 mUnitlessLengthQuirk
= false;
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
:
9727 case eCSSProperty_flex_flow
:
9728 return ParseFlexFlow();
9729 case eCSSProperty_font
:
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
:
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
:
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
:
9830 NS_ABORT_IF_FALSE(false, "should not be called");
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)
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");
9863 if (nsCSSProps::PropHasFlags(aPropID
, CSS_PROPERTY_VALUE_PARSER_FUNCTION
)) {
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
);
9900 NS_ABORT_IF_FALSE(false, "should not reach here");
9905 uint32_t variant
= nsCSSProps::ParserVariant(aPropID
);
9907 NS_ABORT_IF_FALSE(false, "not a single value property");
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
))
9920 const KTableValue
*kwtable
= nsCSSProps::kKeywordTableTable
[aPropID
];
9921 switch (nsCSSProps::ValueRestrictions(aPropID
)) {
9923 NS_ABORT_IF_FALSE(false, "should not be reached");
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
9936 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc 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
: {
9944 if (!ParseFamily(value
) ||
9945 value
.GetUnit() != eCSSUnit_FontFamilyList
)
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()) {
9956 aValue
.SetStringValue(fontlist
[0].mName
, eCSSUnit_String
);
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
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
);
10012 BoxPositionMaskToCSSValue(int32_t aMask
, bool isX
)
10014 int32_t val
= NS_STYLE_BG_POSITION_CENTER
;
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
;
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
);
10036 CSSParserImpl::ParseBackground()
10038 nsAutoParseCompoundProperty
compound(this);
10040 // background-color can only be set once, so it's not a list.
10043 // Check first for inherit/initial/unset.
10044 if (ParseVariant(color
, VARIANT_INHERIT
, nullptr)) {
10046 for (const nsCSSProperty
* subprops
=
10047 nsCSSProps::SubpropertyEntryFor(eCSSProperty_background
);
10048 *subprops
!= eCSSProperty_UNKNOWN
; ++subprops
) {
10049 AppendValue(*subprops
, color
);
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());
10062 if (!ParseBackgroundItem(state
)) {
10065 // If we saw a color, this must be the last item.
10066 if (color
.GetUnit() != eCSSUnit_Null
) {
10069 // If there's a comma, expect another item.
10070 if (!ExpectSymbol(',', true)) {
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
);
10106 // Parse one item of the background shorthand property.
10108 CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState
& aState
)
10111 // Fill in the values that the shorthand will set if we don't find
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,
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
10147 if (tt
== eCSSToken_Ident
) {
10148 nsCSSKeyword keyword
= nsCSSKeywords::LookupKeyword(mToken
.mIdent
);
10150 if (keyword
== eCSSKeyword_inherit
||
10151 keyword
== eCSSKeyword_initial
||
10152 keyword
== eCSSKeyword_unset
) {
10154 } else if (keyword
== eCSSKeyword_none
) {
10158 if (!ParseSingleValueProperty(aState
.mImage
->mValue
,
10159 eCSSProperty_background_image
)) {
10160 NS_NOTREACHED("should be able to parse");
10163 } else if (nsCSSProps::FindKeyword(keyword
,
10164 nsCSSProps::kBackgroundAttachmentKTable
, dummy
)) {
10168 if (!ParseSingleValueProperty(aState
.mAttachment
->mValue
,
10169 eCSSProperty_background_attachment
)) {
10170 NS_NOTREACHED("should be able to parse");
10173 } else if (nsCSSProps::FindKeyword(keyword
,
10174 nsCSSProps::kBackgroundRepeatKTable
, dummy
)) {
10178 nsCSSValuePair scratch
;
10179 if (!ParseBackgroundRepeatValues(scratch
)) {
10180 NS_NOTREACHED("should be able to parse");
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
)
10189 havePositionAndSize
= true;
10190 if (!ParseBackgroundPositionValues(aState
.mPosition
->mValue
, false)) {
10193 if (ExpectSymbol('/', true)) {
10194 nsCSSValuePair scratch
;
10195 if (!ParseBackgroundSizeValues(scratch
)) {
10198 aState
.mSize
->mXValue
= scratch
.mXValue
;
10199 aState
.mSize
->mYValue
= scratch
.mYValue
;
10201 } else if (nsCSSProps::FindKeyword(keyword
,
10202 nsCSSProps::kBackgroundOriginKTable
, dummy
)) {
10206 if (!ParseSingleValueProperty(aState
.mOrigin
->mValue
,
10207 eCSSProperty_background_origin
)) {
10208 NS_NOTREACHED("should be able to parse");
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
;
10241 if (!ParseSingleValueProperty(aState
.mColor
,
10242 eCSSProperty_background_color
)) {
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")))) {
10261 if (!ParseSingleValueProperty(aState
.mImage
->mValue
,
10262 eCSSProperty_background_image
)) {
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
)
10273 havePositionAndSize
= true;
10274 if (!ParseBackgroundPositionValues(aState
.mPosition
->mValue
, false)) {
10277 if (ExpectSymbol('/', true)) {
10278 nsCSSValuePair scratch
;
10279 if (!ParseBackgroundSizeValues(scratch
)) {
10282 aState
.mSize
->mXValue
= scratch
.mXValue
;
10283 aState
.mSize
->mYValue
= scratch
.mYValue
;
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
)) {
10296 haveSomething
= true;
10299 return haveSomething
;
10302 // This function is very similar to ParseBackgroundPosition and
10303 // ParseBackgroundSize.
10305 CSSParserImpl::ParseValueList(nsCSSProperty aPropID
)
10307 // aPropID is a single value prop-id
10309 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10310 if (!ParseVariant(value
, VARIANT_INHERIT
, nullptr)) {
10311 nsCSSValueList
* item
= value
.SetListValue();
10313 if (!ParseSingleValueProperty(item
->mValue
, aPropID
)) {
10316 if (!ExpectSymbol(',', true)) {
10319 item
->mNext
= new nsCSSValueList
;
10320 item
= item
->mNext
;
10323 AppendValue(aPropID
, value
);
10328 CSSParserImpl::ParseBackgroundRepeat()
10331 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10332 if (!ParseVariant(value
, VARIANT_INHERIT
, nullptr)) {
10333 nsCSSValuePair valuePair
;
10334 if (!ParseBackgroundRepeatValues(valuePair
)) {
10337 nsCSSValuePairList
* item
= value
.SetPairListValue();
10339 item
->mXValue
= valuePair
.mXValue
;
10340 item
->mYValue
= valuePair
.mYValue
;
10341 if (!ExpectSymbol(',', true)) {
10344 if (!ParseBackgroundRepeatValues(valuePair
)) {
10347 item
->mNext
= new nsCSSValuePairList
;
10348 item
= item
->mNext
;
10352 AppendValue(eCSSProperty_background_repeat
, value
);
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.
10378 // This function is very similar to ParseBackgroundList and ParseBackgroundSize.
10380 CSSParserImpl::ParseBackgroundPosition()
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)) {
10389 nsCSSValueList
* item
= value
.SetListValue();
10391 item
->mValue
= itemValue
;
10392 if (!ExpectSymbol(',', true)) {
10395 if (!ParseBackgroundPositionValues(itemValue
, false)) {
10398 item
->mNext
= new nsCSSValueList
;
10399 item
= item
->mNext
;
10402 AppendValue(eCSSProperty_background_position
, value
);
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
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
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
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'
10452 yValue
= BoxPositionMaskToCSSValue(yVal
, false);
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
);
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.
10469 if (ParseEnum(xValue
, nsCSSProps::kBackgroundPositionKTable
)) {
10470 int32_t bit
= xValue
.GetIntValue();
10472 if (ParseEnum(xValue
, nsCSSProps::kBackgroundPositionKTable
)) {
10473 bit
= xValue
.GetIntValue();
10474 if (mask
& (bit
& ~BG_CENTER
)) {
10475 // Only the 'center' keyword can be duplicated.
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'
10488 xValue
= BoxPositionMaskToCSSValue(mask
, 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
))) {
10502 // Create style values
10503 xValue
= BoxPositionMaskToCSSValue(mask
, true);
10504 yValue
= BoxPositionMaskToCSSValue(mask
, false);
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)) {
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
)) {
10540 switch (valueCount
) {
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()) {
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
10563 if (eCSSUnit_Enumerated
!= value
->Item(0).GetUnit() ||
10564 BG_CENTER
== value
->Item(0).GetIntValue()) {
10568 // Remaining value must be a keyword.
10569 if (eCSSUnit_Enumerated
!= value
->Item(2).GetUnit()) {
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
10578 if (BG_CENTER
== value
->Item(1).GetIntValue()) {
10582 // Remaining value must be a keyword.
10583 if (eCSSUnit_Enumerated
!= value
->Item(0).GetUnit()) {
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
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()) {
10602 value
->Item(2) = value
->Item(1); // move yEdge to correct position
10607 // First value must represent horizontal position.
10608 if ((BG_TOP
| BG_BOTTOM
) & value
->Item(0).GetIntValue()) {
10611 value
->Item(3) = value
->Item(1); // move yOffset to correct position
10616 if (eCSSUnit_Enumerated
== value
->Item(1).GetUnit()) {
10618 // Second value must represent vertical position.
10619 if ((BG_LEFT
| BG_RIGHT
) & value
->Item(1).GetIntValue()) {
10622 value
->Item(2) = value
->Item(1); // move yEdge to correct position
10623 value
->Item(1) = value
->Item(0); // move xOffset to correct position
10628 value
->Item(3) = value
->Item(1); // move yOffset to correct position
10629 value
->Item(1) = value
->Item(0); // move xOffset to correct position
10636 // "If only one value is specified, the second value is assumed to be
10638 if (eCSSUnit_Enumerated
== value
->Item(0).GetUnit()) {
10641 value
->Item(1) = value
->Item(0); // move xOffset to correct position
10644 yEdge
.SetIntValue(NS_STYLE_BG_POSITION_CENTER
, eCSSUnit_Enumerated
);
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
)) {
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
;
10684 yOffset
= swapOffset
;
10690 // This function is very similar to ParseBackgroundList and
10691 // ParseBackgroundPosition.
10693 CSSParserImpl::ParseBackgroundSize()
10696 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10697 if (!ParseVariant(value
, VARIANT_INHERIT
, nullptr)) {
10698 nsCSSValuePair valuePair
;
10699 if (!ParseBackgroundSizeValues(valuePair
)) {
10702 nsCSSValuePairList
* item
= value
.SetPairListValue();
10704 item
->mXValue
= valuePair
.mXValue
;
10705 item
->mYValue
= valuePair
.mYValue
;
10706 if (!ExpectSymbol(',', true)) {
10709 if (!ParseBackgroundSizeValues(valuePair
)) {
10712 item
->mNext
= new nsCSSValuePairList
;
10713 item
= item
->mNext
;
10716 AppendValue(eCSSProperty_background_size
, value
);
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.
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();
10750 // Now address 'contain' and 'cover'.
10751 if (!ParseEnum(xValue
, nsCSSProps::kBackgroundSizeKTable
))
10756 #undef BG_SIZE_VARIANT
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
);
10775 CSSParserImpl::SetBorderImageInitialValues()
10777 // border-image-source: none
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
));
10787 nsCSSValueList
* sliceList
= slice
.SetListValue();
10788 sliceList
->mValue
= sliceBoxValue
;
10789 AppendValue(eCSSProperty_border_image_slice
, slice
);
10791 // border-image-width: 1
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
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
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
);
10813 CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit
,
10814 bool* aConsumedTokens
)
10816 // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
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
10826 AppendValue(eCSSProperty_border_image_slice
, value
);
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;
10845 // Try parsing "fill" keyword again if the first time failed because keyword
10846 // and slice dimensions can be in any order.
10848 hasFill
= ParseEnum(imageSliceFillValue
,
10849 nsCSSProps::kBorderImageSliceKTable
);
10852 nsCSSValueList
* borderImageSlice
= value
.SetListValue();
10853 // Put the box value into the list.
10854 borderImageSlice
->mValue
= imageSliceBoxValue
;
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
);
10867 CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit
)
10869 // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
10872 if (aAcceptsInherit
&& ParseVariant(value
, VARIANT_INHERIT
, nullptr)) {
10873 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10875 AppendValue(eCSSProperty_border_image_width
, value
);
10879 // Parse the box dimensions.
10880 if (!ParseGroupedBoxProperty(VARIANT_ALPN
, value
)) {
10884 AppendValue(eCSSProperty_border_image_width
, value
);
10889 CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit
)
10891 // border-image-outset: initial | [<length>|<number>]{1,4}
10894 if (aAcceptsInherit
&& ParseVariant(value
, VARIANT_INHERIT
, nullptr)) {
10895 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10897 AppendValue(eCSSProperty_border_image_outset
, value
);
10901 // Parse the box dimensions.
10902 if (!ParseGroupedBoxProperty(VARIANT_LN
, value
)) {
10906 AppendValue(eCSSProperty_border_image_outset
, value
);
10911 CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit
)
10914 if (aAcceptsInherit
&& ParseVariant(value
, VARIANT_INHERIT
, nullptr)) {
10915 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10917 AppendValue(eCSSProperty_border_image_repeat
, value
);
10921 nsCSSValuePair result
;
10922 if (!ParseEnum(result
.mXValue
, nsCSSProps::kBorderImageRepeatKTable
)) {
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
);
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>
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.
10959 // No empty property.
10960 if (CheckEndProperty()) {
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;
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)) {
11001 } else if (!foundBorderImageWidth
) {
11002 // If this part has an trailing slash, the whole declaration is
11010 // If we consumed some tokens for <border-image-slice> but did not
11011 // successfully parse it, we have an error.
11012 if (sliceConsumedTokens
) {
11018 // <border-image-repeat>
11019 if (!foundRepeat
&& ParseBorderImageRepeat(false)) {
11020 foundRepeat
= true;
11031 CSSParserImpl::ParseBorderSpacing()
11033 nsCSSValue xValue
, yValue
;
11034 if (!ParseNonNegativeVariant(xValue
, VARIANT_HL
| VARIANT_CALC
, nullptr)) {
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
);
11048 pair
.SetPairValue(xValue
, yValue
);
11049 AppendValue(eCSSProperty_border_spacing
, pair
);
11055 CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs
[],
11058 const int32_t numProps
= 3;
11059 nsCSSValue values
[numProps
];
11061 int32_t found
= ParseChoice(values
, aPropIDs
, numProps
);
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
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
);
11126 extraValue
.SetNoneValue();
11127 SetBorderImageInitialValues();
11130 NS_FOR_CSS_SIDES(side
) {
11131 AppendValue(kBorderColorsProps
[side
], extraValue
);
11135 // Just set our one side
11136 for (int32_t index
= 0; index
< numProps
; index
++) {
11137 AppendValue(aPropIDs
[index
], values
[index
]);
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
);
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
);
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
);
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
);
11210 CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty
)
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();
11217 if (!ParseVariant(cur
->mValue
, VARIANT_COLOR
| VARIANT_KEYWORD
,
11218 nsCSSProps::kBorderColorKTable
)) {
11221 if (CheckEndProperty()) {
11224 cur
->mNext
= new nsCSSValueList
;
11228 AppendValue(aProperty
, value
);
11232 // Parse the top level of a calc() expression.
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.
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
))
11254 if (!ExpectSymbol(')', true))
11257 aValue
.SetArrayValue(arr
, eCSSUnit_Calc
);
11258 mUnitlessLengthQuirk
= oldUnitlessLengthQuirk
;
11263 mUnitlessLengthQuirk
= oldUnitlessLengthQuirk
;
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
11281 CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue
& aValue
,
11282 int32_t& aVariantMask
)
11284 NS_ABORT_IF_FALSE(aVariantMask
!= 0, "unexpected variant mask");
11285 nsCSSValue
*storage
= &aValue
;
11288 if (!ParseCalcMultiplicativeExpression(*storage
, aVariantMask
, &haveWS
))
11291 if (!haveWS
|| !GetToken(false))
11294 if (mToken
.IsSymbol('+')) {
11295 unit
= eCSSUnit_Calc_Plus
;
11296 } else if (mToken
.IsSymbol('-')) {
11297 unit
= eCSSUnit_Calc_Minus
;
11302 if (!RequireWhitespace())
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
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.
11341 CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue
& aValue
,
11342 int32_t& aVariantMask
,
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
;
11351 int32_t variantMask
;
11352 if (afterDivision
|| gotValue
) {
11353 variantMask
= VARIANT_NUMBER
;
11355 variantMask
= aVariantMask
| VARIANT_NUMBER
;
11357 if (!ParseCalcTerm(*storage
, variantMask
))
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
11368 ReduceNumberCalcOps ops
;
11369 float number
= mozilla::css::ComputeCalc(*storage
, ops
);
11370 if (number
== 0.0 && afterDivision
)
11372 storage
->SetFloatValue(number
, eCSSUnit_Number
);
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
;
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;
11402 *aHadFinalWS
= hadWS
;
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
11414 if (aVariantMask
& VARIANT_NUMBER
) {
11416 aVariantMask
&= ~int32_t(VARIANT_NUMBER
);
11418 aVariantMask
= VARIANT_NUMBER
;
11422 // We had to find a value, but we didn't.
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.
11439 CSSParserImpl::ParseCalcTerm(nsCSSValue
& aValue
, int32_t& aVariantMask
)
11441 NS_ABORT_IF_FALSE(aVariantMask
!= 0, "unexpected variant mask");
11442 if (!GetToken(true))
11444 // Either an additive expression in parentheses...
11445 if (mToken
.IsSymbol('(')) {
11446 if (!ParseCalcAdditiveExpression(aValue
, aVariantMask
) ||
11447 !ExpectSymbol(')', true)) {
11453 // ... or just a value
11455 // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
11456 // always gets picked up
11457 if (!ParseVariant(aValue
, aVariantMask
| VARIANT_NUMBER
, nullptr)) {
11460 // ...and do the VARIANT_NUMBER check ourselves.
11461 if (!(aVariantMask
& VARIANT_NUMBER
) && aValue
.GetUnit() == eCSSUnit_Number
) {
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
;
11470 aVariantMask
&= ~int32_t(VARIANT_NUMBER
);
11476 // This function consumes all consecutive whitespace and returns whether
11479 CSSParserImpl::RequireWhitespace()
11481 if (!GetToken(false))
11483 if (mToken
.mType
!= eCSSToken_Whitespace
) {
11487 // Skip any additional whitespace tokens.
11488 if (GetToken(true)) {
11495 CSSParserImpl::ParseRect(nsCSSProperty aPropID
)
11498 if (ParseVariant(val
, VARIANT_INHERIT
| VARIANT_AUTO
, nullptr)) {
11499 AppendValue(aPropID
, val
);
11503 if (! GetToken(true)) {
11507 if (mToken
.mType
== eCSSToken_Function
&&
11508 mToken
.mIdent
.LowerCaseEqualsLiteral("rect")) {
11509 nsCSSRect
& rect
= val
.SetRectValue();
11511 NS_FOR_CSS_SIDES(side
) {
11512 if (! ParseVariant(rect
.*(nsCSSRect::sides
[side
]),
11513 VARIANT_AL
, nullptr)) {
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)) {
11526 if (!ExpectSymbol(')', true)) {
11534 AppendValue(aPropID
, val
);
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
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
);
11558 if ((found
& (1|2|4)) == (1|2|4) &&
11559 values
[0].GetUnit() == eCSSUnit_Auto
) {
11560 // We filled all 3 values, which is invalid
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
]);
11580 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
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");
11612 // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must
11614 if (!ParseVariant(value
, VARIANT_HMK
| VARIANT_NONE
,
11615 kContentSolitaryKWs
)) {
11616 nsCSSValueList
* cur
= value
.SetListValue();
11618 if (!ParseVariant(cur
->mValue
, VARIANT_CONTENT
, kContentListKWs
)) {
11621 if (CheckEndProperty()) {
11624 cur
->mNext
= new nsCSSValueList
;
11628 AppendValue(eCSSProperty_content
, value
);
11633 CSSParserImpl::ParseCounterData(nsCSSProperty aPropID
)
11635 static const nsCSSKeyword kCounterDataKTable
[] = {
11637 eCSSKeyword_UNKNOWN
11640 if (!ParseVariant(value
, VARIANT_INHERIT
| VARIANT_NONE
, nullptr)) {
11641 if (!GetToken(true)) {
11644 if (mToken
.mType
!= eCSSToken_Ident
) {
11649 nsCSSValuePairList
*cur
= value
.SetPairListValue();
11651 if (!ParseCustomIdent(cur
->mXValue
, mToken
.mIdent
, kCounterDataKTable
)) {
11654 if (!GetToken(true)) {
11657 if (mToken
.mType
== eCSSToken_Number
&& mToken
.mIntegerValid
) {
11658 cur
->mYValue
.SetIntValue(mToken
.mInteger
, eCSSUnit_Integer
);
11662 if (!GetToken(true)) {
11665 if (mToken
.mType
!= eCSSToken_Ident
) {
11669 cur
->mNext
= new nsCSSValuePairList
;
11673 AppendValue(aPropID
, value
);
11678 CSSParserImpl::ParseCursor()
11681 // 'inherit', 'initial' and 'unset' must be alone
11682 if (!ParseVariant(value
, VARIANT_INHERIT
, nullptr)) {
11683 nsCSSValueList
* cur
= value
.SetListValue();
11685 if (!ParseVariant(cur
->mValue
, VARIANT_UK
, nsCSSProps::kCursorKTable
)) {
11688 if (cur
->mValue
.GetUnit() != eCSSUnit_URL
) { // keyword must be last
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)) {
11703 cur
->mValue
.SetArrayValue(val
, eCSSUnit_Array
);
11705 if (!ExpectSymbol(',', true)) { // url must not be last
11708 cur
->mNext
= new nsCSSValueList
;
11712 AppendValue(eCSSProperty_cursor
, value
);
11718 CSSParserImpl::ParseFont()
11720 static const nsCSSProperty fontIDs
[] = {
11721 eCSSProperty_font_style
,
11722 eCSSProperty_font_variant_caps
,
11723 eCSSProperty_font_weight
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
);
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
);
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
);
11779 eCSSUnit_Inherit
== values
[0].GetUnit() ||
11780 eCSSUnit_Initial
== values
[0].GetUnit() ||
11781 eCSSUnit_Unset
== values
[0].GetUnit()) { // illegal data
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();
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
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
11807 if (! ParseNonNegativeVariant(size
, VARIANT_KEYWORD
| VARIANT_LP
,
11808 nsCSSProps::kFontSizeKTable
)) {
11812 // Get optional "/" line-height
11813 nsCSSValue lineHeight
;
11814 if (ExpectSymbol('/', true)) {
11815 if (! ParseNonNegativeVariant(lineHeight
,
11816 VARIANT_NUMBER
| VARIANT_LP
| VARIANT_NORMAL
,
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
));
11865 CSSParserImpl::ParseFontSynthesis(nsCSSValue
& aValue
)
11867 if (!ParseVariant(aValue
, VARIANT_HK
| VARIANT_NONE
,
11868 nsCSSProps::kFontSynthesisKTable
)) {
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())
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
) {
11890 aValue
.SetIntValue(nextIntValue
| intValue
, eCSSUnit_Enumerated
);
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.
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
11924 MaxElementsForAlternateType(nsCSSKeyword keyword
)
11926 uint16_t maxElems
= 1;
11927 if (keyword
== eCSSKeyword_styleset
||
11928 keyword
== eCSSKeyword_character_variant
) {
11929 maxElems
= MAX_ALLOWED_FEATURES
;
11935 CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature
,
11936 nsCSSValue
& aValue
)
11938 if (!GetToken(true)) {
11942 bool isIdent
= (mToken
.mType
== eCSSToken_Ident
);
11943 if (mToken
.mType
!= eCSSToken_Function
&& !isIdent
) {
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
,
11955 nsCSSProps::kFontVariantAlternatesKTable
:
11956 nsCSSProps::kFontVariantAlternatesFuncsKTable
),
11959 // failed, pop token
11965 aValue
.SetIntValue(aWhichFeature
, eCSSUnit_Enumerated
);
11969 return ParseFunction(keyword
, nullptr, VARIANT_IDENTIFIER
,
11970 1, MaxElementsForAlternateType(keyword
), aValue
);
11974 CSSParserImpl::ParseFontVariantAlternates(nsCSSValue
& aValue
)
11976 if (ParseVariant(aValue
, VARIANT_INHERIT
| VARIANT_NORMAL
, nullptr)) {
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;
11989 while (ParseSingleAlternate(feature
, value
)) {
11991 // check to make sure value not already set
11992 if (feature
== 0 ||
11993 feature
& featureFlags
) {
11997 featureFlags
|= feature
;
11999 // if function, need to add to the list of functions
12000 if (value
.GetUnit() == eCSSUnit_Function
) {
12002 list
= listValue
.GetListValue();
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.
12016 nsCSSValue featureValue
;
12017 featureValue
.SetIntValue(featureFlags
, eCSSUnit_Enumerated
);
12018 aValue
.SetPairValue(featureValue
, listValue
);
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
) {
12033 const int32_t *m
= aMasks
;
12036 while (*m
!= MASK_END_VALUE
) {
12037 if (*m
& aNewValue
) {
12038 c
= aMergedValue
& *m
;
12048 aMergedValue
|= aNewValue
;
12052 // aMasks - array of masks for mutually-exclusive property values,
12053 // e.g. proportial-nums, tabular-nums
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
)) {
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
)) {
12076 aValue
.SetIntValue(mergedValue
, eCSSUnit_Enumerated
);
12081 static const int32_t maskEastAsian
[] = {
12082 NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK
,
12083 NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK
,
12088 CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue
& aValue
)
12090 if (ParseVariant(aValue
, VARIANT_INHERIT
| VARIANT_NORMAL
, nullptr)) {
12094 NS_ASSERTION(maskEastAsian
[ArrayLength(maskEastAsian
) - 1] ==
12096 "incorrectly terminated array");
12098 return ParseBitmaskValues(aValue
, nsCSSProps::kFontVariantEastAsianKTable
,
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
,
12111 CSSParserImpl::ParseFontVariantLigatures(nsCSSValue
& aValue
)
12113 if (ParseVariant(aValue
,
12114 VARIANT_INHERIT
| VARIANT_NORMAL
| VARIANT_NONE
,
12119 NS_ASSERTION(maskLigatures
[ArrayLength(maskLigatures
) - 1] ==
12121 "incorrectly terminated array");
12123 return ParseBitmaskValues(aValue
, nsCSSProps::kFontVariantLigaturesKTable
,
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
,
12135 CSSParserImpl::ParseFontVariantNumeric(nsCSSValue
& aValue
)
12137 if (ParseVariant(aValue
, VARIANT_INHERIT
| VARIANT_NORMAL
, nullptr)) {
12141 NS_ASSERTION(maskNumeric
[ArrayLength(maskNumeric
) - 1] ==
12143 "incorrectly terminated array");
12145 return ParseBitmaskValues(aValue
, nsCSSProps::kFontVariantNumericKTable
,
12150 CSSParserImpl::ParseFontVariant()
12152 // parse single values - normal/inherit/none
12154 nsCSSValue
normal(eCSSUnit_Normal
);
12156 if (ParseVariant(value
,
12157 VARIANT_INHERIT
| VARIANT_NORMAL
| VARIANT_NONE
,
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
);
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
) {
12191 nsCSSKeyword keyword
= nsCSSKeywords::LookupKeyword(mToken
.mIdent
);
12192 if (keyword
== eCSSKeyword_UNKNOWN
) {
12199 // function? ==> font-variant-alternates
12201 if (!nsCSSProps::FindKeyword(keyword
,
12202 nsCSSProps::kFontVariantAlternatesFuncsKTable
,
12204 (feature
& altFeatures
)) {
12209 altFeatures
|= feature
;
12210 nsCSSValue funcValue
;
12211 if (!ParseFunction(keyword
, nullptr, VARIANT_IDENTIFIER
, 1,
12212 MaxElementsForAlternateType(keyword
), funcValue
) ||
12213 funcValue
.GetUnit() != eCSSUnit_Function
) {
12219 altList
= altListValue
.GetListValue();
12221 altList
->mNext
= new nsCSSValueList
;
12222 altList
= altList
->mNext
;
12224 altList
->mValue
= funcValue
;
12225 } else if (nsCSSProps::FindKeyword(keyword
,
12226 nsCSSProps::kFontVariantCapsKTable
,
12228 if (capsFeatures
!= 0) {
12229 // multiple values for font-variant-caps
12233 capsFeatures
= feature
;
12234 } else if (nsCSSProps::FindKeyword(keyword
,
12235 nsCSSProps::kFontVariantAlternatesKTable
,
12237 if (feature
& altFeatures
) {
12238 // same value repeated
12242 altFeatures
|= feature
;
12243 } else if (nsCSSProps::FindKeyword(keyword
,
12244 nsCSSProps::kFontVariantEastAsianKTable
,
12246 if (!MergeBitmaskValue(feature
, maskEastAsian
, eastAsianFeatures
)) {
12247 // multiple mutually exclusive values
12251 } else if (nsCSSProps::FindKeyword(keyword
,
12252 nsCSSProps::kFontVariantLigaturesKTable
,
12254 if (keyword
== eCSSKeyword_none
||
12255 !MergeBitmaskValue(feature
, maskLigatures
, ligFeatures
)) {
12256 // none or multiple mutually exclusive values
12260 } else if (nsCSSProps::FindKeyword(keyword
,
12261 nsCSSProps::kFontVariantNumericKTable
,
12263 if (!MergeBitmaskValue(feature
, maskNumeric
, numericFeatures
)) {
12264 // multiple mutually exclusive values
12268 } else if (nsCSSProps::FindKeyword(keyword
,
12269 nsCSSProps::kFontVariantPositionKTable
,
12271 if (posFeatures
!= 0) {
12272 // multiple values for font-variant-caps
12276 posFeatures
= feature
;
12278 // bogus keyword, bail...
12291 nsCSSValue featureValue
;
12292 featureValue
.SetIntValue(altFeatures
, eCSSUnit_Enumerated
);
12293 value
.SetPairValue(featureValue
, altListValue
);
12294 AppendValue(eCSSProperty_font_variant_alternates
, value
);
12296 AppendValue(eCSSProperty_font_variant_alternates
, normal
);
12299 if (capsFeatures
) {
12300 value
.SetIntValue(capsFeatures
, eCSSUnit_Enumerated
);
12301 AppendValue(eCSSProperty_font_variant_caps
, value
);
12303 AppendValue(eCSSProperty_font_variant_caps
, normal
);
12306 if (eastAsianFeatures
) {
12307 value
.SetIntValue(eastAsianFeatures
, eCSSUnit_Enumerated
);
12308 AppendValue(eCSSProperty_font_variant_east_asian
, value
);
12310 AppendValue(eCSSProperty_font_variant_east_asian
, normal
);
12314 value
.SetIntValue(ligFeatures
, eCSSUnit_Enumerated
);
12315 AppendValue(eCSSProperty_font_variant_ligatures
, value
);
12317 AppendValue(eCSSProperty_font_variant_ligatures
, normal
);
12320 if (numericFeatures
) {
12321 value
.SetIntValue(numericFeatures
, eCSSUnit_Enumerated
);
12322 AppendValue(eCSSProperty_font_variant_numeric
, value
);
12324 AppendValue(eCSSProperty_font_variant_numeric
, normal
);
12328 value
.SetIntValue(posFeatures
, eCSSUnit_Enumerated
);
12329 AppendValue(eCSSProperty_font_variant_position
, value
);
12331 AppendValue(eCSSProperty_font_variant_position
, normal
);
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))) {
12359 CSSParserImpl::ParseOneFamily(nsAString
& aFamily
,
12363 if (!GetToken(true))
12366 nsCSSToken
* tk
= &mToken
;
12368 aOneKeyword
= false;
12370 if (eCSSToken_Ident
== tk
->mType
) {
12371 aOneKeyword
= true;
12372 aFamily
.Append(tk
->mIdent
);
12374 if (!GetToken(false))
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
) {
12395 } else if (eCSSToken_String
== tk
->mType
) {
12397 aFamily
.Append(tk
->mIdent
); // XXX What if it had escaped quotes?
12408 AppendGeneric(nsCSSKeyword aKeyword
, FontFamilyList
*aFamilyList
)
12410 switch (aKeyword
) {
12411 case eCSSKeyword_serif
:
12412 aFamilyList
->Append(FontFamilyName(eFamily_serif
));
12414 case eCSSKeyword_sans_serif
:
12415 aFamilyList
->Append(FontFamilyName(eFamily_sans_serif
));
12417 case eCSSKeyword_monospace
:
12418 aFamilyList
->Append(FontFamilyName(eFamily_monospace
));
12420 case eCSSKeyword_cursive
:
12421 aFamilyList
->Append(FontFamilyName(eFamily_cursive
));
12423 case eCSSKeyword_fantasy
:
12424 aFamilyList
->Append(FontFamilyName(eFamily_fantasy
));
12426 case eCSSKeyword__moz_fixed
:
12427 aFamilyList
->Append(FontFamilyName(eFamily_moz_fixed
));
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
))
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;
12452 nsCSSKeyword keyword
= nsCSSKeywords::LookupKeyword(family
);
12454 case eCSSKeyword_inherit
:
12455 aValue
.SetInheritValue();
12457 case eCSSKeyword_default
:
12458 // 605231 - don't parse unquoted 'default' reserved keyword
12460 case eCSSKeyword_initial
:
12461 aValue
.SetInitialValue();
12463 case eCSSKeyword_unset
:
12464 if (nsLayoutUtils::UnsetValueEnabled()) {
12465 aValue
.SetUnsetValue();
12469 case eCSSKeyword__moz_use_system_font
:
12470 if (!IsParsingCompoundProperty()) {
12471 aValue
.SetSystemFontValue();
12476 foundGeneric
= AppendGeneric(keyword
, familyList
);
12480 if (!foundGeneric
) {
12481 familyList
->Append(
12482 FontFamilyName(family
, (quoted
? eQuotedName
: eUnquotedName
)));
12486 if (!ExpectSymbol(',', true))
12489 nsAutoString nextFamily
;
12490 if (!ParseOneFamily(nextFamily
, single
, quoted
))
12493 // at this point unquoted keywords are not allowed
12494 // as font family names but can appear within names
12495 foundGeneric
= false;
12497 nsCSSKeyword keyword
= nsCSSKeywords::LookupKeyword(nextFamily
);
12499 case eCSSKeyword_inherit
:
12500 case eCSSKeyword_initial
:
12501 case eCSSKeyword_default
:
12502 case eCSSKeyword__moz_use_system_font
:
12504 case eCSSKeyword_unset
:
12505 if (nsLayoutUtils::UnsetValueEnabled()) {
12510 foundGeneric
= AppendGeneric(keyword
, familyList
);
12515 if (!foundGeneric
) {
12516 familyList
->Append(
12517 FontFamilyName(nextFamily
, (quoted
? eQuotedName
: eUnquotedName
)));
12521 if (familyList
->IsEmpty()) {
12525 aValue
.SetFontFamilyListValue(familyList
);
12529 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
12530 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
12531 // local-src: 'local(' ( string | ident ) ')'
12534 CSSParserImpl::ParseFontSrc(nsCSSValue
& aValue
)
12536 // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>?
12537 InfallibleTArray
<nsCSSValue
> values
;
12540 if (!GetToken(true))
12543 if (mToken
.mType
== eCSSToken_URL
) {
12544 SetValueToURL(cur
, mToken
.mIdent
);
12545 values
.AppendElement(cur
);
12546 if (!ParseFontSrcFormat(values
))
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
)) {
12563 if (!ExpectSymbol(')', true)) {
12570 nsCSSKeyword keyword
= nsCSSKeywords::LookupKeyword(family
);
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
:
12584 cur
.SetStringValue(family
, eCSSUnit_Local_Font
);
12585 values
.AppendElement(cur
);
12587 // We don't know what to do with this token; unget it and error out
12592 if (!ExpectSymbol(',', true))
12596 if (values
.Length() == 0)
12599 nsRefPtr
<nsCSSValue::Array
> srcVals
12600 = nsCSSValue::Array::Create(values
.Length());
12603 for (i
= 0; i
< values
.Length(); i
++)
12604 srcVals
->Item(i
) = values
[i
];
12605 aValue
.SetArrayValue(srcVals
, eCSSUnit_Array
);
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")) {
12621 if (!GetToken(true))
12622 return false; // EOF - no need for SkipUntil
12624 if (mToken
.mType
!= eCSSToken_String
) {
12630 nsCSSValue
cur(mToken
.mIdent
, eCSSUnit_Font_Format
);
12631 values
.AppendElement(cur
);
12632 } while (ExpectSymbol(',', true));
12634 if (!ExpectSymbol(')', true)) {
12642 // font-ranges: urange ( ',' urange )*
12644 CSSParserImpl::ParseFontRanges(nsCSSValue
& aValue
)
12646 InfallibleTArray
<uint32_t> ranges
;
12648 if (!GetToken(true))
12651 if (mToken
.mType
!= eCSSToken_URange
) {
12656 // An invalid range token is a parsing error, causing the entire
12657 // descriptor to be ignored.
12658 if (!mToken
.mIntegerValid
)
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
12668 if (low
<= 0x10FFFF && low
<= high
) {
12669 if (high
> 0x10FFFF)
12672 ranges
.AppendElement(low
);
12673 ranges
.AppendElement(high
);
12675 if (!ExpectSymbol(',', true))
12679 if (ranges
.Length() == 0)
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
);
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
12703 ValidFontFeatureTag(const nsString
& aTag
)
12705 if (aTag
.Length() != FEATURE_TAG_LENGTH
) {
12709 for (i
= 0; i
< FEATURE_TAG_LENGTH
; i
++) {
12710 uint32_t ch
= aTag
[i
];
12711 if (ch
< 0x20 || ch
> 0x7e) {
12719 CSSParserImpl::ParseFontFeatureSettings(nsCSSValue
& aValue
)
12721 if (ParseVariant(aValue
, VARIANT_INHERIT
| VARIANT_NORMAL
, nullptr)) {
12725 nsCSSValuePairList
*cur
= aValue
.SetPairListValue();
12728 if (!GetToken(true)) {
12732 if (mToken
.mType
!= eCSSToken_String
||
12733 !ValidFontFeatureTag(mToken
.mIdent
)) {
12737 cur
->mXValue
.SetStringValue(mToken
.mIdent
, eCSSUnit_String
);
12739 if (!GetToken(true)) {
12740 cur
->mYValue
.SetIntValue(1, eCSSUnit_Integer
);
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
);
12755 // something other than value/on/off, set default value
12756 cur
->mYValue
.SetIntValue(1, eCSSUnit_Integer
);
12760 if (!ExpectSymbol(',', true)) {
12764 cur
->mNext
= new nsCSSValuePairList
;
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
)];
12790 ParseChoice(values
, listStyleIDs
, ArrayLength(listStyleIDs
));
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.
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
]);
12830 CSSParserImpl::ParseListStyleType(nsCSSValue
& aValue
)
12832 if (ParseVariant(aValue
, VARIANT_INHERIT
, nullptr)) {
12837 if (ParseCounterStyleName(name
, false)) {
12838 aValue
.SetStringValue(name
, eCSSUnit_Ident
);
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
);
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()) {
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
);
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
);
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
);
12918 for (index
= 0; index
< numProps
; index
++) {
12919 AppendValue(kOutlineIDs
[index
], values
[index
]);
12925 CSSParserImpl::ParseOverflow()
12927 nsCSSValue overflow
;
12928 if (!ParseVariant(overflow
, VARIANT_HK
, nsCSSProps::kOverflowKTable
)) {
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
);
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
);
12945 AppendValue(eCSSProperty_overflow_x
, overflowX
);
12946 AppendValue(eCSSProperty_overflow_y
, overflowY
);
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
);
12973 CSSParserImpl::ParseQuotes()
12976 if (!ParseVariant(value
, VARIANT_HOS
, nullptr)) {
12979 if (value
.GetUnit() == eCSSUnit_String
) {
12980 nsCSSValue open
= value
;
12981 nsCSSValuePairList
* quotes
= value
.SetPairListValue();
12983 quotes
->mXValue
= open
;
12984 // get mandatory close
12985 if (!ParseVariant(quotes
->mYValue
, VARIANT_STRING
, nullptr)) {
12988 // look for another open
12989 if (!ParseVariant(open
, VARIANT_STRING
, nullptr)) {
12992 quotes
->mNext
= new nsCSSValuePairList
;
12993 quotes
= quotes
->mNext
;
12996 AppendValue(eCSSProperty_quotes
, value
);
13001 CSSParserImpl::ParseSize()
13003 nsCSSValue width
, height
;
13004 if (!ParseVariant(width
, VARIANT_AHKL
, nsCSSProps::kPageSizeKTable
)) {
13007 if (width
.IsLengthUnit()) {
13008 ParseVariant(height
, VARIANT_LENGTH
, nullptr);
13011 if (width
== height
|| height
.GetUnit() == eCSSUnit_Null
) {
13012 AppendValue(eCSSProperty_size
, width
);
13015 pair
.SetPairValue(width
, height
);
13016 AppendValue(eCSSProperty_size
, pair
);
13022 CSSParserImpl::ParseTextDecoration()
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
13051 if (!ParseVariant(value
, VARIANT_HK
, kTextDecorationKTable
)) {
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
);
13072 // look for more keywords
13073 nsCSSValue keyword
;
13075 for (index
= 0; index
< 3; index
++) {
13076 if (!ParseEnum(keyword
, kTextDecorationKTable
)) {
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.
13085 intValue
|= newValue
;
13088 line
.SetIntValue(intValue
, eCSSUnit_Enumerated
);
13092 line
= color
= style
= value
;
13096 AppendValue(eCSSProperty_text_decoration_line
, line
);
13097 AppendValue(eCSSProperty_text_decoration_color
, color
);
13098 AppendValue(eCSSProperty_text_decoration_style
, style
);
13104 CSSParserImpl::ParseTextAlign(nsCSSValue
& aValue
, const KTableValue aTable
[])
13106 if (ParseVariant(aValue
, VARIANT_INHERIT
, nullptr)) {
13107 // 'inherit', 'initial' and 'unset' must be alone
13112 if (!ParseVariant(left
, VARIANT_KEYWORD
, aTable
)) {
13116 if (!nsLayoutUtils::IsTextAlignTrueValueEnabled()) {
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
) {
13128 aValue
.SetPairValue(left
, right
);
13130 // Single value 'true' is not allowed.
13131 if (left
.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE
) {
13140 CSSParserImpl::ParseTextAlign(nsCSSValue
& aValue
)
13142 return ParseTextAlign(aValue
, nsCSSProps::kTextAlignKTable
);
13146 CSSParserImpl::ParseTextAlignLast(nsCSSValue
& aValue
)
13148 return ParseTextAlign(aValue
, nsCSSProps::kTextAlignLastKTable
);
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
;
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.
13170 intValue
|= newValue
;
13176 aValue
.SetIntValue(intValue
, eCSSUnit_Enumerated
);
13185 CSSParserImpl::ParseTextOverflow(nsCSSValue
& aValue
)
13187 if (ParseVariant(aValue
, VARIANT_INHERIT
, nullptr)) {
13188 // 'inherit', 'initial' and 'unset' must be alone
13193 if (!ParseVariant(left
, VARIANT_KEYWORD
| VARIANT_STRING
,
13194 nsCSSProps::kTextOverflowKTable
))
13198 if (ParseVariant(right
, VARIANT_KEYWORD
| VARIANT_STRING
,
13199 nsCSSProps::kTextOverflowKTable
))
13200 aValue
.SetPairValue(left
, right
);
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
)) {
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()) {
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
) {
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
)) {
13240 aValue
.SetIntValue(nextIntValue
| intValue
, eCSSUnit_Enumerated
);
13247 CSSParserImpl::ParseTextCombineUpright(nsCSSValue
& aValue
)
13249 if (!ParseVariant(aValue
, VARIANT_HK
,
13250 nsCSSProps::kTextCombineUprightKTable
)) {
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)) {
13260 if (mToken
.mType
== eCSSToken_Number
&& mToken
.mIntegerValid
) {
13261 switch (mToken
.mInteger
) {
13262 case 2: // already set, nothing to do
13265 aValue
.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3
,
13266 eCSSUnit_Enumerated
);
13269 aValue
.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4
,
13270 eCSSUnit_Enumerated
);
13273 // invalid digits value
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
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)) {
13308 aOutput
.AppendElement(newValue
);
13310 if (ExpectSymbol(',', true)) {
13311 // Move on to the next argument if we see a comma.
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.
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.
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.
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
)) {
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
)];
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
,
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
,
13420 eTwoLengthPercentCalcs
,
13421 eTwoLengthPercentCalcsOneLengthCalc
,
13428 eThreeNumbersOneAngle
,
13433 eNumVariantMasks
};
13434 static const int32_t kMaxElemsPerFunction
= 16;
13435 static const int32_t kVariantMasks
[eNumVariantMasks
][kMaxElemsPerFunction
] = {
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
},
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
}};
13461 static const uint8_t kVariantMaskLengths
[eNumVariantMasks
] =
13462 {1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 6, 16, 16};
13465 int32_t variantIndex
= eNumVariantMasks
;
13468 case eCSSKeyword_translatex
:
13469 case eCSSKeyword_translatey
:
13470 /* Exactly one length or percent. */
13471 variantIndex
= eLengthPercentCalc
;
13475 case eCSSKeyword_translatez
:
13476 /* Exactly one length */
13477 variantIndex
= eLengthCalc
;
13481 case eCSSKeyword_translate3d
:
13482 /* Exactly two lengthds or percents and a number */
13483 variantIndex
= eTwoLengthPercentCalcsOneLengthCalc
;
13487 case eCSSKeyword_scalez
:
13488 case eCSSKeyword_scalex
:
13489 case eCSSKeyword_scaley
:
13490 /* Exactly one scale factor. */
13491 variantIndex
= eNumber
;
13495 case eCSSKeyword_scale3d
:
13496 /* Exactly three scale factors. */
13497 variantIndex
= eThreeNumbers
;
13501 case eCSSKeyword_rotatex
:
13502 case eCSSKeyword_rotatey
:
13503 case eCSSKeyword_rotate
:
13504 case eCSSKeyword_rotatez
:
13505 /* Exactly one angle. */
13506 variantIndex
= eAngle
;
13510 case eCSSKeyword_rotate3d
:
13511 variantIndex
= eThreeNumbersOneAngle
;
13515 case eCSSKeyword_translate
:
13516 /* One or two lengths or percents. */
13517 variantIndex
= eTwoLengthPercentCalcs
;
13521 case eCSSKeyword_skew
:
13522 /* Exactly one or two angles. */
13523 variantIndex
= eTwoAngles
;
13527 case eCSSKeyword_scale
:
13528 /* One or two scale factors. */
13529 variantIndex
= eTwoNumbers
;
13533 case eCSSKeyword_skewx
:
13534 /* Exactly one angle. */
13535 variantIndex
= eAngle
;
13539 case eCSSKeyword_skewy
:
13540 /* Exactly one angle. */
13541 variantIndex
= eAngle
;
13545 case eCSSKeyword_matrix
:
13546 /* Six values, all numbers. */
13547 variantIndex
= aIsPrefixed
? eMatrixPrefixed
: eMatrix
;
13551 case eCSSKeyword_matrix3d
:
13552 /* 16 matrix values, all numbers */
13553 variantIndex
= aIsPrefixed
? eMatrix3dPrefixed
: eMatrix3d
;
13557 case eCSSKeyword_perspective
:
13558 /* Exactly one scale number. */
13559 variantIndex
= ePositiveLength
;
13564 /* Oh dear, we didn't match. Report an error. */
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!");
13574 NS_ASSERTION(aMaxElems
<= kVariantMaskLengths
[variantIndex
],
13575 "Invalid aMaxElems for this variant mask.");
13578 // Convert the index into a mask.
13579 aVariantMask
= kVariantMasks
[variantIndex
];
13584 bool CSSParserImpl::ParseWillChange()
13586 nsCSSValue listValue
;
13587 nsCSSValueList
* currentListValue
= listValue
.SetListValue();
13590 const uint32_t variantMask
= VARIANT_IDENTIFIER
|
13596 if (!ParseVariant(value
, variantMask
, nullptr)) {
13600 if (value
.GetUnit() == eCSSUnit_None
||
13601 value
.GetUnit() == eCSSUnit_All
)
13606 if (value
.GetUnit() != eCSSUnit_Ident
) {
13608 AppendValue(eCSSProperty_will_change
, value
);
13616 value
.GetStringValue(str
);
13617 if (str
.LowerCaseEqualsLiteral("default")) {
13621 currentListValue
->mValue
= value
;
13623 if (!ExpectSymbol(',', true)) {
13626 currentListValue
->mNext
= new nsCSSValueList
;
13627 currentListValue
= currentListValue
->mNext
;
13631 AppendValue(eCSSProperty_will_change
, listValue
);
13635 /* Reads a single transform function from the tokenizer stream, reporting an
13636 * error if something goes wrong.
13639 CSSParserImpl::ParseSingleTransform(bool aIsPrefixed
, nsCSSValue
& aValue
)
13641 if (!GetToken(true))
13644 if (mToken
.mType
!= eCSSToken_Function
) {
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
))
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
)
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
;
13673 if (!ParseSingleTransform(aIsPrefixed
, cur
->mValue
)) {
13676 if (CheckEndProperty()) {
13679 cur
->mNext
= new nsCSSValueList
;
13683 AppendValue(eCSSProperty_transform
, value
);
13687 bool CSSParserImpl::ParseTransformOrigin(bool aPerspective
)
13689 nsCSSValuePair position
;
13690 if (!ParseBoxPositionValues(position
, true))
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
);
13709 if (aPerspective
) {
13710 value
.SetPairValue(position
.mXValue
, position
.mYValue
);
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
);
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.
13730 CSSParserImpl::ParseDropShadow(nsCSSValue
* aValue
)
13732 // Use nsCSSValueList to reuse the shadow resolving code in
13733 // nsRuleNode and nsComputedDOMStyle.
13735 nsCSSValueList
* cur
= shadow
.SetListValue();
13736 if (!ParseShadowItem(cur
->mValue
, false))
13739 if (!ExpectSymbol(')', true))
13742 nsCSSValue::Array
* dropShadow
= aValue
->InitFunction(eCSSKeyword_drop_shadow
, 1);
13744 // Copy things over.
13745 dropShadow
->Item(1) = shadow
;
13751 * Reads a single url or filter function from the tokenizer stream, reporting an
13752 * error if something goes wrong.
13755 CSSParserImpl::ParseSingleFilter(nsCSSValue
* aValue
)
13757 if (ParseVariant(*aValue
, VARIANT_URL
, nullptr)) {
13761 if (!nsLayoutUtils::CSSFiltersEnabled()) {
13762 // With CSS Filters disabled, we should only accept an SVG reference filter.
13763 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL
);
13767 if (!GetToken(true)) {
13768 REPORT_UNEXPECTED_EOF(PEFilterEOF
);
13772 if (mToken
.mType
!= eCSSToken_Function
) {
13773 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction
);
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
)) {
13785 // Unrecognized filter function.
13786 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction
);
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;
13802 case eCSSKeyword_brightness
:
13803 case eCSSKeyword_contrast
:
13804 case eCSSKeyword_saturate
:
13806 case eCSSKeyword_grayscale
:
13807 case eCSSKeyword_invert
:
13808 case eCSSKeyword_sepia
:
13809 case eCSSKeyword_opacity
:
13810 clampArgumentToOne
= true;
13812 case eCSSKeyword_hue_rotate
:
13813 variantMask
= VARIANT_ANGLE
;
13814 rejectNegativeArgument
= false;
13817 // Unrecognized filter function.
13818 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction
);
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
);
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
);
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
);
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);
13875 CSSParserImpl::ParseFilter()
13878 // 'inherit', 'initial', 'unset' and 'none' must be alone
13879 if (!ParseVariant(value
, VARIANT_INHERIT
| VARIANT_NONE
, nullptr)) {
13880 nsCSSValueList
* cur
= value
.SetListValue();
13882 if (!ParseSingleFilter(&cur
->mValue
)) {
13885 if (CheckEndProperty()) {
13888 if (!nsLayoutUtils::CSSFiltersEnabled()) {
13889 // With CSS Filters disabled, we should only accept one SVG reference
13891 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue
);
13894 cur
->mNext
= new nsCSSValueList
;
13898 AppendValue(eCSSProperty_filter
, value
);
13903 CSSParserImpl::ParseTransitionProperty()
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();
13914 if (!ParseVariant(cur
->mValue
, VARIANT_IDENTIFIER
| VARIANT_ALL
, nullptr)) {
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())) {
13929 if (!ExpectSymbol(',', true)) {
13932 cur
->mNext
= new nsCSSValueList
;
13936 AppendValue(eCSSProperty_transition_property
, value
);
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)) {
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
);
13969 CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent
,
13973 if (!GetToken(true)) {
13976 nsCSSToken
* tk
= &mToken
;
13977 if (tk
->mType
== eCSSToken_Number
) {
13978 float num
= tk
->mNumber
;
13979 if (aCheckRange
&& (num
< 0.0 || num
> 1.0)) {
13983 if (ExpectSymbol(aStop
, true)) {
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)) {
14004 int32_t type
= NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END
;
14005 if (ExpectSymbol(',', true)) {
14006 if (!GetToken(true)) {
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
;
14022 val
->Item(1).SetIntValue(type
, eCSSUnit_Enumerated
);
14024 if (!ExpectSymbol(')', true)) {
14028 aValue
.SetArrayValue(val
, eCSSUnit_Steps
);
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();
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
;
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
) {
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))
14093 if (CheckEndProperty()) {
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
]);
14135 // else we just hit a ',' so continue parsing the next compound transition
14138 return eParseAnimationOrTransitionShorthand_Values
;
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
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
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
;
14189 const nsCSSValue
& val
= l
->mValue
;
14190 if (val
.GetUnit() == eCSSUnit_None
) {
14191 if (multipleItems
) {
14192 // This is a syntax error.
14196 // Unbox a solitary 'none'.
14197 values
[3].SetNoneValue();
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())) {
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
]);
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
]);
14273 CSSParserImpl::ParseShadowItem(nsCSSValue
& aValue
, bool aIsBoxShadow
)
14275 // A shadow list item is an array, with entries in this sequence:
14280 IndexSpread
, // only for box-shadow
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
,
14299 if (xOrColor
.IsLengthUnit() || xOrColor
.IsCalcUnit()) {
14300 val
->Item(IndexX
) = xOrColor
;
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
;
14310 // X coordinate mandatory after color
14311 if (!ParseVariant(val
->Item(IndexX
), VARIANT_LENGTH
| VARIANT_CALC
,
14317 // Y coordinate; mandatory
14318 if (!ParseVariant(val
->Item(IndexY
), VARIANT_LENGTH
| VARIANT_CALC
,
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
,
14329 val
->Item(IndexRadius
).IsLengthUnit() &&
14330 val
->Item(IndexRadius
).GetFloatValue() < 0) {
14334 if (aIsBoxShadow
) {
14336 ParseVariant(val
->Item(IndexSpread
), VARIANT_LENGTH
| VARIANT_CALC
, nullptr);
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
);
14355 CSSParserImpl::ParseShadowList(nsCSSProperty aProperty
)
14357 nsAutoParseCompoundProperty
compound(this);
14358 bool isBoxShadow
= aProperty
== eCSSProperty_box_shadow
;
14361 // 'inherit', 'initial', 'unset' and 'none' must be alone
14362 if (!ParseVariant(value
, VARIANT_INHERIT
| VARIANT_NONE
, nullptr)) {
14363 nsCSSValueList
* cur
= value
.SetListValue();
14365 if (!ParseShadowItem(cur
->mValue
, isBoxShadow
)) {
14368 if (!ExpectSymbol(',', true)) {
14371 cur
->mNext
= new nsCSSValueList
;
14375 AppendValue(aProperty
, value
);
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
);
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
;
14403 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector
& aSelector
)
14405 if (mNameSpaceMap
) {
14406 aSelector
.SetNameSpace(mNameSpaceMap
->FindNameSpaceID(nullptr));
14408 aSelector
.SetNameSpace(kNameSpaceID_Unknown
); // wildcard
14413 CSSParserImpl::ParsePaint(nsCSSProperty aPropID
)
14417 if (!ParseVariant(x
, VARIANT_HC
| VARIANT_NONE
| VARIANT_URL
|
14418 VARIANT_OPENTYPE_SVG_KEYWORD
,
14419 nsCSSProps::kContextPatternKTable
)) {
14423 bool canHaveFallback
= x
.GetUnit() == eCSSUnit_URL
||
14424 x
.GetUnit() == eCSSUnit_Enumerated
;
14425 if (canHaveFallback
) {
14426 if (!ParseVariant(y
, VARIANT_COLOR
| VARIANT_NONE
, nullptr))
14430 if (!canHaveFallback
) {
14431 AppendValue(aPropID
, x
);
14434 val
.SetPairValue(x
, y
);
14435 AppendValue(aPropID
, val
);
14441 CSSParserImpl::ParseDasharray()
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();
14451 if (!ParseNonNegativeVariant(cur
->mValue
, VARIANT_LPN
, nullptr)) {
14454 if (CheckEndProperty()) {
14457 // skip optional commas between elements
14458 (void)ExpectSymbol(',', true);
14460 cur
->mNext
= new nsCSSValueList
;
14464 AppendValue(eCSSProperty_stroke_dasharray
, value
);
14469 CSSParserImpl::ParseMarker()
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
);
14482 CSSParserImpl::ParsePaintOrder()
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");
14501 if (!ParseVariant(value
, VARIANT_HK
, kPaintOrderKTable
)) {
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.
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;
14520 if (seen
& (1 << component
)) {
14521 // Already seen this component.
14526 seen
|= (1 << component
);
14527 order
|= (component
<< position
);
14528 position
+= NS_STYLE_PAINT_ORDER_BITWIDTH
;
14529 if (!ParseEnum(value
, kPaintOrderKTable
)) {
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.
14541 // Fill in the remaining paint-order components in the order of their
14542 // constant values.
14544 for (component
= 1;
14545 component
<= NS_STYLE_PAINT_ORDER_LAST_VALUE
;
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
);
14565 CSSParserImpl::BackslashDropped()
14567 return mScanner
->GetEOFCharacters() &
14568 nsCSSScanner::eEOFCharacters_DropBackslash
;
14572 CSSParserImpl::AppendImpliedEOFCharacters(nsAString
& aResult
)
14574 nsCSSScanner::AppendImpliedEOFCharacters(mScanner
->GetEOFCharacters(),
14579 CSSParserImpl::ParseAll()
14582 if (!ParseVariant(value
, VARIANT_INHERIT
, nullptr)) {
14586 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p
, eCSSProperty_all
) {
14587 AppendValue(*p
, value
);
14593 CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type
* aType
,
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();
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
);
14624 // This was either 'inherit' or 'initial'; we don't need the recorded
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);
14647 aValue
= variableValue
;
14652 CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type
* aType
,
14653 bool* aDropBackslash
,
14654 nsString
& aImpliedCharacters
,
14655 void (*aFunc
)(const nsAString
&, void*),
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
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:
14670 // Note that is valid for a custom property to have a value that consists
14671 // solely of white space, such as:
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
);
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.
14697 REPORT_UNEXPECTED_TOKEN(PEVariableEmpty
);
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
);
14712 // Look for 'initial', 'inherit' or 'unset' as the first non-white space
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());
14730 *aDropBackslash
= false;
14731 AppendImpliedEOFCharacters(aImpliedCharacters
);
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());
14745 *aDropBackslash
= false;
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()) {
14763 MOZ_ASSERT(!BackslashDropped());
14764 *aType
= CSSVariableDeclarations::eTokenStream
;
14765 *aDropBackslash
= false;
14767 } else if (!references
.IsEmpty() &&
14768 references
.LastElement() == stack
.Length() - 1) {
14769 REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback
);
14770 SkipUntilAllOf(stack
);
14773 } else if (mToken
.mSymbol
== ')' ||
14774 mToken
.mSymbol
== ']' ||
14775 mToken
.mSymbol
== '}') {
14777 if (stack
.IsEmpty()) {
14779 MOZ_ASSERT(!BackslashDropped());
14780 *aType
= CSSVariableDeclarations::eTokenStream
;
14781 *aDropBackslash
= false;
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
) {
14797 case eCSSToken_Function
:
14798 if (mToken
.mIdent
.LowerCaseEqualsLiteral("var")) {
14799 if (!GetToken(true)) {
14800 // EOF directly after "var(".
14801 REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF
);
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.
14809 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName
);
14811 SkipUntilAllOf(stack
);
14815 MOZ_ASSERT(Substring(mToken
.mIdent
, 0,
14816 CSS_CUSTOM_NAME_PREFIX_LENGTH
).
14817 EqualsLiteral("--"));
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
);
14835 references
.AppendElement(stack
.Length());
14836 stack
.AppendElement(')');
14837 } else if (mToken
.IsSymbol(')')) {
14838 // Correctly closed variable reference.
14840 // Malformed variable reference.
14841 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen
);
14843 SkipUntilAllOf(stack
);
14847 stack
.AppendElement(')');
14851 case eCSSToken_Bad_String
:
14852 SkipUntilAllOf(stack
);
14855 case eCSSToken_Bad_URL
:
14857 SkipUntilAllOf(stack
);
14863 } while (GetToken(true));
14865 // Append any implied closing characters.
14866 *aDropBackslash
= BackslashDropped();
14867 AppendImpliedEOFCharacters(aImpliedCharacters
);
14868 uint32_t i
= stack
.Length();
14870 aImpliedCharacters
.Append(stack
[i
]);
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
) {
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();
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
;
14929 gFreeList
= impl
->mNextFree
;
14930 impl
->mNextFree
= nullptr;
14932 impl
= new CSSParserImpl();
14936 impl
->SetChildLoader(aLoader
);
14937 impl
->SetQuirkMode(aLoader
->GetCompatibilityMode() ==
14938 eCompatibility_NavQuirks
);
14941 impl
->SetStyleSheet(aSheet
);
14944 mImpl
= static_cast<void*>(impl
);
14947 nsCSSParser::~nsCSSParser()
14949 CSSParserImpl
*impl
= static_cast<CSSParserImpl
*>(mImpl
);
14951 impl
->mNextFree
= gFreeList
;
14956 nsCSSParser::Shutdown()
14958 CSSParserImpl
*tofree
= gFreeList
;
14959 CSSParserImpl
*next
;
14962 next
= tofree
->mNextFree
;
14971 nsCSSParser::SetStyleSheet(CSSStyleSheet
* aSheet
)
14973 return static_cast<CSSParserImpl
*>(mImpl
)->
14974 SetStyleSheet(aSheet
);
14978 nsCSSParser::SetQuirkMode(bool aQuirkMode
)
14980 return static_cast<CSSParserImpl
*>(mImpl
)->
14981 SetQuirkMode(aQuirkMode
);
14985 nsCSSParser::SetChildLoader(mozilla::css::Loader
* aChildLoader
)
14987 return static_cast<CSSParserImpl
*>(mImpl
)->
14988 SetChildLoader(aChildLoader
);
14992 nsCSSParser::ParseSheet(const nsAString
& aInput
,
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
);
15005 nsCSSParser::ParseStyleAttribute(const nsAString
& aAttributeValue
,
15008 nsIPrincipal
* aNodePrincipal
,
15009 css::StyleRule
** aResult
)
15011 return static_cast<CSSParserImpl
*>(mImpl
)->
15012 ParseStyleAttribute(aAttributeValue
, aDocURI
, aBaseURI
,
15013 aNodePrincipal
, aResult
);
15017 nsCSSParser::ParseDeclarations(const nsAString
& aBuffer
,
15020 nsIPrincipal
* aSheetPrincipal
,
15021 css::Declaration
* aDeclaration
,
15024 return static_cast<CSSParserImpl
*>(mImpl
)->
15025 ParseDeclarations(aBuffer
, aSheetURI
, aBaseURI
, aSheetPrincipal
,
15026 aDeclaration
, aChanged
);
15030 nsCSSParser::ParseRule(const nsAString
& aRule
,
15033 nsIPrincipal
* aSheetPrincipal
,
15034 css::Rule
** aResult
)
15036 return static_cast<CSSParserImpl
*>(mImpl
)->
15037 ParseRule(aRule
, aSheetURI
, aBaseURI
, aSheetPrincipal
, aResult
);
15041 nsCSSParser::ParseProperty(const nsCSSProperty aPropID
,
15042 const nsAString
& aPropValue
,
15045 nsIPrincipal
* aSheetPrincipal
,
15046 css::Declaration
* aDeclaration
,
15051 return static_cast<CSSParserImpl
*>(mImpl
)->
15052 ParseProperty(aPropID
, aPropValue
, aSheetURI
, aBaseURI
,
15053 aSheetPrincipal
, aDeclaration
, aChanged
,
15054 aIsImportant
, aIsSVGMode
);
15058 nsCSSParser::ParseVariable(const nsAString
& aVariableName
,
15059 const nsAString
& aPropValue
,
15062 nsIPrincipal
* aSheetPrincipal
,
15063 css::Declaration
* aDeclaration
,
15067 return static_cast<CSSParserImpl
*>(mImpl
)->
15068 ParseVariable(aVariableName
, aPropValue
, aSheetURI
, aBaseURI
,
15069 aSheetPrincipal
, aDeclaration
, aChanged
, aIsImportant
);
15073 nsCSSParser::ParseMediaList(const nsSubstring
& aBuffer
,
15075 uint32_t aLineNumber
,
15076 nsMediaList
* aMediaList
,
15079 static_cast<CSSParserImpl
*>(mImpl
)->
15080 ParseMediaList(aBuffer
, aURI
, aLineNumber
, aMediaList
, aHTMLMode
);
15084 nsCSSParser::ParseSourceSizeList(const nsAString
& aBuffer
,
15086 uint32_t aLineNumber
,
15087 InfallibleTArray
< nsAutoPtr
<nsMediaQuery
> >& aQueries
,
15088 InfallibleTArray
<nsCSSValue
>& aValues
,
15091 return static_cast<CSSParserImpl
*>(mImpl
)->
15092 ParseSourceSizeList(aBuffer
, aURI
, aLineNumber
, aQueries
, aValues
,
15097 nsCSSParser::ParseFontFamilyListString(const nsSubstring
& aBuffer
,
15099 uint32_t aLineNumber
,
15100 nsCSSValue
& aValue
)
15102 return static_cast<CSSParserImpl
*>(mImpl
)->
15103 ParseFontFamilyListString(aBuffer
, aURI
, aLineNumber
, aValue
);
15107 nsCSSParser::ParseColorString(const nsSubstring
& aBuffer
,
15109 uint32_t aLineNumber
,
15110 nsCSSValue
& aValue
,
15111 bool aSuppressErrors
/* false */)
15113 return static_cast<CSSParserImpl
*>(mImpl
)->
15114 ParseColorString(aBuffer
, aURI
, aLineNumber
, aValue
, aSuppressErrors
);
15118 nsCSSParser::ParseSelectorString(const nsSubstring
& aSelectorString
,
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
,
15130 uint32_t aLineNumber
)
15132 return static_cast<CSSParserImpl
*>(mImpl
)->
15133 ParseKeyframeRule(aBuffer
, aURI
, aLineNumber
);
15137 nsCSSParser::ParseKeyframeSelectorString(const nsSubstring
& aSelectorString
,
15139 uint32_t aLineNumber
,
15140 InfallibleTArray
<float>& aSelectorList
)
15142 return static_cast<CSSParserImpl
*>(mImpl
)->
15143 ParseKeyframeSelectorString(aSelectorString
, aURI
, aLineNumber
,
15148 nsCSSParser::EvaluateSupportsDeclaration(const nsAString
& aProperty
,
15149 const nsAString
& aValue
,
15152 nsIPrincipal
* aDocPrincipal
)
15154 return static_cast<CSSParserImpl
*>(mImpl
)->
15155 EvaluateSupportsDeclaration(aProperty
, aValue
, aDocURL
, aBaseURL
,
15160 nsCSSParser::EvaluateSupportsCondition(const nsAString
& aCondition
,
15163 nsIPrincipal
* aDocPrincipal
)
15165 return static_cast<CSSParserImpl
*>(mImpl
)->
15166 EvaluateSupportsCondition(aCondition
, aDocURL
, aBaseURL
, aDocPrincipal
);
15170 nsCSSParser::EnumerateVariableReferences(const nsAString
& aPropertyValue
,
15171 VariableEnumFunc aFunc
,
15174 return static_cast<CSSParserImpl
*>(mImpl
)->
15175 EnumerateVariableReferences(aPropertyValue
, aFunc
, aData
);
15179 nsCSSParser::ResolveVariableValue(const nsAString
& aPropertyValue
,
15180 const CSSVariableValues
* aVariables
,
15182 nsCSSTokenSerializationType
& aFirstToken
,
15183 nsCSSTokenSerializationType
& aLastToken
)
15185 return static_cast<CSSParserImpl
*>(mImpl
)->
15186 ResolveVariableValue(aPropertyValue
, aVariables
,
15187 aResult
, aFirstToken
, aLastToken
);
15191 nsCSSParser::ParsePropertyWithVariableReferences(
15192 nsCSSProperty aPropertyID
,
15193 nsCSSProperty aShorthandPropertyID
,
15194 const nsAString
& aValue
,
15195 const CSSVariableValues
* aVariables
,
15196 nsRuleData
* aRuleData
,
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
);
15212 nsCSSParser::ParseCounterStyleName(const nsAString
& aBuffer
,
15216 return static_cast<CSSParserImpl
*>(mImpl
)->
15217 ParseCounterStyleName(aBuffer
, aURL
, aName
);
15221 nsCSSParser::ParseCounterDescriptor(nsCSSCounterDesc aDescID
,
15222 const nsAString
& aBuffer
,
15225 nsIPrincipal
* aSheetPrincipal
,
15226 nsCSSValue
& aValue
)
15228 return static_cast<CSSParserImpl
*>(mImpl
)->
15229 ParseCounterDescriptor(aDescID
, aBuffer
,
15230 aSheetURL
, aBaseURL
, aSheetPrincipal
, aValue
);
15234 nsCSSParser::IsValueValidForProperty(const nsCSSProperty aPropID
,
15235 const nsAString
& aPropValue
)
15237 return static_cast<CSSParserImpl
*>(mImpl
)->
15238 IsValueValidForProperty(aPropID
, aPropValue
);