layout/style/nsCSSParser.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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"
    12 #include "nsCSSParser.h"
    13 #include "nsCSSProps.h"
    14 #include "nsCSSKeywords.h"
    15 #include "nsCSSScanner.h"
    16 #include "mozilla/css/ErrorReporter.h"
    17 #include "mozilla/css/Loader.h"
    18 #include "mozilla/css/StyleRule.h"
    19 #include "mozilla/css/ImportRule.h"
    20 #include "nsCSSRules.h"
    21 #include "mozilla/css/NameSpaceRule.h"
    22 #include "nsTArray.h"
    23 #include "nsCSSStyleSheet.h"
    24 #include "mozilla/css/Declaration.h"
    25 #include "nsStyleConsts.h"
    26 #include "nsNetUtil.h"
    27 #include "nsCOMPtr.h"
    28 #include "nsString.h"
    29 #include "nsIAtom.h"
    30 #include "nsColor.h"
    31 #include "nsCSSPseudoClasses.h"
    32 #include "nsCSSPseudoElements.h"
    33 #include "nsNameSpaceManager.h"
    34 #include "nsXMLNameSpaceMap.h"
    35 #include "nsError.h"
    36 #include "nsIMediaList.h"
    37 #include "nsStyleUtil.h"
    38 #include "nsIPrincipal.h"
    39 #include "prprf.h"
    40 #include "nsContentUtils.h"
    41 #include "nsAutoPtr.h"
    42 #include "CSSCalc.h"
    43 #include "nsMediaFeatures.h"
    44 #include "nsLayoutUtils.h"
    45 #include "mozilla/Preferences.h"
    46 #include "nsRuleData.h"
    47 #include "mozilla/CSSVariableValues.h"
    48 #include "mozilla/dom/URL.h"
    50 using namespace mozilla;
    52 typedef nsCSSProps::KTableValue KTableValue;
    54 const uint32_t
    55 nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
    56 #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \
    57                  stylestruct_, stylestructoffset_, animtype_)                 \
    58   parsevariant_,
    59 #include "nsCSSPropList.h"
    60 #undef CSS_PROP
    61 };
    63 // Maximum number of repetitions for the repeat() function
    64 // in the grid-template-columns and grid-template-rows properties,
    65 // to limit high memory usage from small stylesheets.
    66 // Must be a positive integer. Should be large-ish.
    67 #define GRID_TEMPLATE_MAX_REPETITIONS 10000
    69 // End-of-array marker for mask arguments to ParseBitmaskValues
    70 #define MASK_END_VALUE  (-1)
    72 MOZ_BEGIN_ENUM_CLASS(CSSParseResult, int32_t)
    73   // Parsed something successfully:
    74   Ok,
    75   // Did not find what we were looking for, but did not consume any token:
    76   NotFound,
    77   // Unexpected token or token value, too late for UngetToken() to be enough:
    78   Error
    79 MOZ_END_ENUM_CLASS(CSSParseResult)
    81 namespace {
    83 // Rule processing function
    84 typedef void (* RuleAppendFunc) (css::Rule* aRule, void* aData);
    85 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer);
    86 static void AppendRuleToSheet(css::Rule* aRule, void* aParser);
    88 struct CSSParserInputState {
    89   nsCSSScannerPosition mPosition;
    90   nsCSSToken mToken;
    91   bool mHavePushBack;
    92 };
    94 // Your basic top-down recursive descent style parser
    95 // The exposed methods and members of this class are precisely those
    96 // needed by nsCSSParser, far below.
    97 class CSSParserImpl {
    98 public:
    99   CSSParserImpl();
   100   ~CSSParserImpl();
   102   nsresult SetStyleSheet(nsCSSStyleSheet* aSheet);
   104   nsresult SetQuirkMode(bool aQuirkMode);
   106   nsresult SetChildLoader(mozilla::css::Loader* aChildLoader);
   108   // Clears everything set by the above Set*() functions.
   109   void Reset();
   111   nsresult ParseSheet(const nsAString& aInput,
   112                       nsIURI*          aSheetURI,
   113                       nsIURI*          aBaseURI,
   114                       nsIPrincipal*    aSheetPrincipal,
   115                       uint32_t         aLineNumber,
   116                       bool             aAllowUnsafeRules);
   118   nsresult ParseStyleAttribute(const nsAString&  aAttributeValue,
   119                                nsIURI*           aDocURL,
   120                                nsIURI*           aBaseURL,
   121                                nsIPrincipal*     aNodePrincipal,
   122                                css::StyleRule**  aResult);
   124   nsresult ParseDeclarations(const nsAString&  aBuffer,
   125                              nsIURI*           aSheetURL,
   126                              nsIURI*           aBaseURL,
   127                              nsIPrincipal*     aSheetPrincipal,
   128                              css::Declaration* aDeclaration,
   129                              bool*           aChanged);
   131   nsresult ParseRule(const nsAString&        aRule,
   132                      nsIURI*                 aSheetURL,
   133                      nsIURI*                 aBaseURL,
   134                      nsIPrincipal*           aSheetPrincipal,
   135                      css::Rule**             aResult);
   137   nsresult ParseProperty(const nsCSSProperty aPropID,
   138                          const nsAString& aPropValue,
   139                          nsIURI* aSheetURL,
   140                          nsIURI* aBaseURL,
   141                          nsIPrincipal* aSheetPrincipal,
   142                          css::Declaration* aDeclaration,
   143                          bool* aChanged,
   144                          bool aIsImportant,
   145                          bool aIsSVGMode);
   147   void ParseMediaList(const nsSubstring& aBuffer,
   148                       nsIURI* aURL, // for error reporting
   149                       uint32_t aLineNumber, // for error reporting
   150                       nsMediaList* aMediaList,
   151                       bool aHTMLMode);
   153   nsresult ParseVariable(const nsAString& aVariableName,
   154                          const nsAString& aPropValue,
   155                          nsIURI* aSheetURL,
   156                          nsIURI* aBaseURL,
   157                          nsIPrincipal* aSheetPrincipal,
   158                          css::Declaration* aDeclaration,
   159                          bool* aChanged,
   160                          bool aIsImportant);
   162   bool ParseColorString(const nsSubstring& aBuffer,
   163                         nsIURI* aURL, // for error reporting
   164                         uint32_t aLineNumber, // for error reporting
   165                         nsCSSValue& aValue);
   167   nsresult ParseSelectorString(const nsSubstring& aSelectorString,
   168                                nsIURI* aURL, // for error reporting
   169                                uint32_t aLineNumber, // for error reporting
   170                                nsCSSSelectorList **aSelectorList);
   172   already_AddRefed<nsCSSKeyframeRule>
   173   ParseKeyframeRule(const nsSubstring& aBuffer,
   174                     nsIURI*            aURL,
   175                     uint32_t           aLineNumber);
   177   bool ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
   178                                    nsIURI* aURL, // for error reporting
   179                                    uint32_t aLineNumber, // for error reporting
   180                                    InfallibleTArray<float>& aSelectorList);
   182   bool EvaluateSupportsDeclaration(const nsAString& aProperty,
   183                                    const nsAString& aValue,
   184                                    nsIURI* aDocURL,
   185                                    nsIURI* aBaseURL,
   186                                    nsIPrincipal* aDocPrincipal);
   188   bool EvaluateSupportsCondition(const nsAString& aCondition,
   189                                  nsIURI* aDocURL,
   190                                  nsIURI* aBaseURL,
   191                                  nsIPrincipal* aDocPrincipal);
   193   typedef nsCSSParser::VariableEnumFunc VariableEnumFunc;
   195   /**
   196    * Parses a CSS token stream value and invokes a callback function for each
   197    * variable reference that is encountered.
   198    *
   199    * @param aPropertyValue The CSS token stream value.
   200    * @param aFunc The callback function to invoke; its parameters are the
   201    *   variable name found and the aData argument passed in to this function.
   202    * @param aData User data to pass in to the callback.
   203    * @return Whether aPropertyValue could be parsed as a valid CSS token stream
   204    *   value (e.g., without syntactic errors in variable references).
   205    */
   206   bool EnumerateVariableReferences(const nsAString& aPropertyValue,
   207                                    VariableEnumFunc aFunc,
   208                                    void* aData);
   210   /**
   211    * Parses aPropertyValue as a CSS token stream value and resolves any
   212    * variable references using the variables in aVariables.
   213    *
   214    * @param aPropertyValue The CSS token stream value.
   215    * @param aVariables The set of variable values to use when resolving variable
   216    *   references.
   217    * @param aResult Out parameter that gets the resolved value.
   218    * @param aFirstToken Out parameter that gets the type of the first token in
   219    *   aResult.
   220    * @param aLastToken Out parameter that gets the type of the last token in
   221    *   aResult.
   222    * @return Whether aResult could be parsed successfully and variable reference
   223    *   substitution succeeded.
   224    */
   225   bool ResolveVariableValue(const nsAString& aPropertyValue,
   226                             const CSSVariableValues* aVariables,
   227                             nsString& aResult,
   228                             nsCSSTokenSerializationType& aFirstToken,
   229                             nsCSSTokenSerializationType& aLastToken);
   231   /**
   232    * Parses a string as a CSS token stream value for particular property,
   233    * resolving any variable references.  The parsed property value is stored
   234    * in the specified nsRuleData object.  If aShorthandPropertyID has a value
   235    * other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
   236    * otherwise, aPropertyID will be parsed.  Either way, only aPropertyID,
   237    * a longhand property, will be copied over to the rule data.
   238    *
   239    * If the property cannot be parsed, it will be treated as if 'initial' or
   240    * 'inherit' were specified, for non-inherited and inherited properties
   241    * respectively.
   242    *
   243    * @param aPropertyID The ID of the longhand property whose value is to be
   244    *   copied to the rule data.
   245    * @param aShorthandPropertyID The ID of the shorthand property to be parsed.
   246    *   If a longhand property is to be parsed, aPropertyID is that property,
   247    *   and aShorthandPropertyID must be eCSSProperty_UNKNOWN.
   248    * @param aValue The CSS token stream value.
   249    * @param aVariables The set of variable values to use when resolving variable
   250    *   references.
   251    * @param aRuleData The rule data object into which parsed property value for
   252    *   aPropertyID will be stored.
   253    */
   254   void ParsePropertyWithVariableReferences(nsCSSProperty aPropertyID,
   255                                            nsCSSProperty aShorthandPropertyID,
   256                                            const nsAString& aValue,
   257                                            const CSSVariableValues* aVariables,
   258                                            nsRuleData* aRuleData,
   259                                            nsIURI* aDocURL,
   260                                            nsIURI* aBaseURL,
   261                                            nsIPrincipal* aDocPrincipal,
   262                                            nsCSSStyleSheet* aSheet,
   263                                            uint32_t aLineNumber,
   264                                            uint32_t aLineOffset);
   266   nsCSSProperty LookupEnabledProperty(const nsAString& aProperty) {
   267     static_assert(nsCSSProps::eEnabledForAllContent == 0,
   268                   "nsCSSProps::eEnabledForAllContent should be zero for "
   269                   "this bitfield to work");
   270     nsCSSProps::EnabledState enabledState = nsCSSProps::eEnabledForAllContent;
   271     if (mUnsafeRulesEnabled) {
   272       enabledState |= nsCSSProps::eEnabledInUASheets;
   273     }
   274     if (mIsChromeOrCertifiedApp) {
   275       enabledState |= nsCSSProps::eEnabledInChromeOrCertifiedApp;
   276     }
   277     return nsCSSProps::LookupProperty(aProperty, enabledState);
   278   }
   280 protected:
   281   class nsAutoParseCompoundProperty;
   282   friend class nsAutoParseCompoundProperty;
   284   class nsAutoFailingSupportsRule;
   285   friend class nsAutoFailingSupportsRule;
   287   class nsAutoSuppressErrors;
   288   friend class nsAutoSuppressErrors;
   290   void AppendRule(css::Rule* aRule);
   291   friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule
   293   /**
   294    * This helper class automatically calls SetParsingCompoundProperty in its
   295    * constructor and takes care of resetting it to false in its destructor.
   296    */
   297   class nsAutoParseCompoundProperty {
   298     public:
   299       nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
   300       {
   301         NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
   302                      "already parsing compound property");
   303         NS_ASSERTION(aParser, "Null parser?");
   304         aParser->SetParsingCompoundProperty(true);
   305       }
   307       ~nsAutoParseCompoundProperty()
   308       {
   309         mParser->SetParsingCompoundProperty(false);
   310       }
   311     private:
   312       CSSParserImpl* mParser;
   313   };
   315   /**
   316    * This helper class conditionally sets mInFailingSupportsRule to
   317    * true if aCondition = false, and resets it to its original value in its
   318    * destructor.  If we are already somewhere within a failing @supports
   319    * rule, passing in aCondition = true does not change mInFailingSupportsRule.
   320    */
   321   class nsAutoFailingSupportsRule {
   322     public:
   323       nsAutoFailingSupportsRule(CSSParserImpl* aParser,
   324                                 bool aCondition)
   325         : mParser(aParser),
   326           mOriginalValue(aParser->mInFailingSupportsRule)
   327       {
   328         if (!aCondition) {
   329           mParser->mInFailingSupportsRule = true;
   330         }
   331       }
   333       ~nsAutoFailingSupportsRule()
   334       {
   335         mParser->mInFailingSupportsRule = mOriginalValue;
   336       }
   338     private:
   339       CSSParserImpl* mParser;
   340       bool mOriginalValue;
   341   };
   343   /**
   344    * Auto class to set aParser->mSuppressErrors to the specified value
   345    * and restore it to its original value later.
   346    */
   347   class nsAutoSuppressErrors {
   348     public:
   349       nsAutoSuppressErrors(CSSParserImpl* aParser,
   350                            bool aSuppressErrors = true)
   351         : mParser(aParser),
   352           mOriginalValue(aParser->mSuppressErrors)
   353       {
   354         mParser->mSuppressErrors = aSuppressErrors;
   355       }
   357       ~nsAutoSuppressErrors()
   358       {
   359         mParser->mSuppressErrors = mOriginalValue;
   360       }
   362     private:
   363       CSSParserImpl* mParser;
   364       bool mOriginalValue;
   365   };
   367   // the caller must hold on to aString until parsing is done
   368   void InitScanner(nsCSSScanner& aScanner,
   369                    css::ErrorReporter& aReporter,
   370                    nsIURI* aSheetURI, nsIURI* aBaseURI,
   371                    nsIPrincipal* aSheetPrincipal);
   372   void ReleaseScanner(void);
   373   bool IsSVGMode() const {
   374     return mScanner->IsSVGMode();
   375   }
   377   /**
   378    * Saves the current input state, which includes any currently pushed
   379    * back token, and the current position of the scanner.
   380    */
   381   void SaveInputState(CSSParserInputState& aState);
   383   /**
   384    * Restores the saved input state by pushing back any saved pushback
   385    * token and calling RestoreSavedPosition on the scanner.
   386    */
   387   void RestoreSavedInputState(const CSSParserInputState& aState);
   389   bool GetToken(bool aSkipWS);
   390   void UngetToken();
   391   bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum);
   393   bool ExpectSymbol(char16_t aSymbol, bool aSkipWS);
   394   bool ExpectEndProperty();
   395   bool CheckEndProperty();
   396   nsSubstring* NextIdent();
   398   // returns true when the stop symbol is found, and false for EOF
   399   bool SkipUntil(char16_t aStopSymbol);
   400   void SkipUntilOneOf(const char16_t* aStopSymbolChars);
   401   // For each character in aStopSymbolChars from the end of the array
   402   // to the start, calls SkipUntil with that character.
   403   typedef nsAutoTArray<char16_t, 16> StopSymbolCharStack;
   404   void SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars);
   405   // returns true if the stop symbol or EOF is found, and false for an
   406   // unexpected ')', ']' or '}'; this not safe to call outside variable
   407   // resolution, as it doesn't handle mismatched content
   408   bool SkipBalancedContentUntil(char16_t aStopSymbol);
   410   void SkipRuleSet(bool aInsideBraces);
   411   bool SkipAtRule(bool aInsideBlock);
   412   bool SkipDeclaration(bool aCheckForBraces);
   414   void PushGroup(css::GroupRule* aRule);
   415   void PopGroup();
   417   bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData,
   418                     bool aInsideBraces = false);
   419   bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData,
   420                    bool aInAtRule);
   421   bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   422   bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   423   bool ParseURLOrString(nsString& aURL);
   424   bool GatherMedia(nsMediaList* aMedia,
   425                    bool aInAtRule);
   426   bool ParseMediaQuery(bool aInAtRule, nsMediaQuery **aQuery,
   427                        bool *aHitStop);
   428   bool ParseMediaQueryExpression(nsMediaQuery* aQuery);
   429   void ProcessImport(const nsString& aURLSpec,
   430                      nsMediaList* aMedia,
   431                      RuleAppendFunc aAppendFunc,
   432                      void* aProcessData);
   433   bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc,
   434                       void* aProcessData);
   435   bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   436   bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   437   bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   438   void ProcessNameSpace(const nsString& aPrefix,
   439                         const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
   440                         void* aProcessData);
   442   bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   443   bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
   444                                   void* aProcessData);
   445   bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule);
   446   bool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
   447   bool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
   448                                 nsCSSValue& aValue);
   450   bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   451   bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   452   already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule();
   453   bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList);
   455   bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData);
   456   bool ParseSupportsCondition(bool& aConditionMet);
   457   bool ParseSupportsConditionNegation(bool& aConditionMet);
   458   bool ParseSupportsConditionInParens(bool& aConditionMet);
   459   bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet);
   460   bool ParseSupportsConditionTerms(bool& aConditionMet);
   461   enum SupportsConditionTermOperator { eAnd, eOr };
   462   bool ParseSupportsConditionTermsAfterOperator(
   463                                        bool& aConditionMet,
   464                                        SupportsConditionTermOperator aOperator);
   466   /**
   467    * Parses the current input stream for a CSS token stream value and resolves
   468    * any variable references using the variables in aVariables.
   469    *
   470    * @param aVariables The set of variable values to use when resolving variable
   471    *   references.
   472    * @param aResult Out parameter that, if the function returns true, will be
   473    *   replaced with the resolved value.
   474    * @return Whether aResult could be parsed successfully and variable reference
   475    *   substitution succeeded.
   476    */
   477   bool ResolveValueWithVariableReferences(
   478                               const CSSVariableValues* aVariables,
   479                               nsString& aResult,
   480                               nsCSSTokenSerializationType& aResultFirstToken,
   481                               nsCSSTokenSerializationType& aResultLastToken);
   482   // Helper function for ResolveValueWithVariableReferences.
   483   bool ResolveValueWithVariableReferencesRec(
   484                              nsString& aResult,
   485                              nsCSSTokenSerializationType& aResultFirstToken,
   486                              nsCSSTokenSerializationType& aResultLastToken,
   487                              const CSSVariableValues* aVariables);
   489   enum nsSelectorParsingStatus {
   490     // we have parsed a selector and we saw a token that cannot be
   491     // part of a selector:
   492     eSelectorParsingStatus_Done,
   493     // we should continue parsing the selector:
   494     eSelectorParsingStatus_Continue,
   495     // we saw an unexpected token or token value,
   496     // or we saw end-of-file with an unfinished selector:
   497     eSelectorParsingStatus_Error
   498   };
   499   nsSelectorParsingStatus ParseIDSelector(int32_t&       aDataMask,
   500                                           nsCSSSelector& aSelector);
   502   nsSelectorParsingStatus ParseClassSelector(int32_t&       aDataMask,
   503                                              nsCSSSelector& aSelector);
   505   // aPseudoElement and aPseudoElementArgs are the location where
   506   // pseudo-elements (as opposed to pseudo-classes) are stored;
   507   // pseudo-classes are stored on aSelector.  aPseudoElement and
   508   // aPseudoElementArgs must be non-null iff !aIsNegated.
   509   nsSelectorParsingStatus ParsePseudoSelector(int32_t&       aDataMask,
   510                                               nsCSSSelector& aSelector,
   511                                               bool           aIsNegated,
   512                                               nsIAtom**      aPseudoElement,
   513                                               nsAtomList**   aPseudoElementArgs,
   514                                               nsCSSPseudoElements::Type* aPseudoElementType);
   516   nsSelectorParsingStatus ParseAttributeSelector(int32_t&       aDataMask,
   517                                                  nsCSSSelector& aSelector);
   519   nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t&       aDataMask,
   520                                                        nsCSSSelector& aSelector,
   521                                                        bool           aIsNegated);
   523   nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
   524                                                        nsCSSPseudoClasses::Type aType);
   526   nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
   527                                                          nsCSSPseudoClasses::Type aType);
   529   nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
   530                                                               nsCSSPseudoClasses::Type aType);
   532   nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t&       aDataMask,
   533                                                      nsCSSSelector& aSelector);
   535   // If aStopChar is non-zero, the selector list is done when we hit
   536   // aStopChar.  Otherwise, it's done when we hit EOF.
   537   bool ParseSelectorList(nsCSSSelectorList*& aListHead,
   538                            char16_t aStopChar);
   539   bool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
   540   bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator);
   542   enum {
   543     eParseDeclaration_InBraces       = 1 << 0,
   544     eParseDeclaration_AllowImportant = 1 << 1
   545   };
   546   enum nsCSSContextType {
   547     eCSSContext_General,
   548     eCSSContext_Page
   549   };
   551   css::Declaration* ParseDeclarationBlock(uint32_t aFlags,
   552                                           nsCSSContextType aContext = eCSSContext_General);
   553   bool ParseDeclaration(css::Declaration* aDeclaration,
   554                         uint32_t aFlags,
   555                         bool aMustCallValueAppended,
   556                         bool* aChanged,
   557                         nsCSSContextType aContext = eCSSContext_General);
   559   bool ParseProperty(nsCSSProperty aPropID);
   560   bool ParsePropertyByFunction(nsCSSProperty aPropID);
   561   bool ParseSingleValueProperty(nsCSSValue& aValue,
   562                                   nsCSSProperty aPropID);
   564   enum PriorityParsingStatus {
   565     ePriority_None,
   566     ePriority_Important,
   567     ePriority_Error
   568   };
   569   PriorityParsingStatus ParsePriority();
   571 #ifdef MOZ_XUL
   572   bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs);
   573 #endif
   575   void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties);
   577   // Property specific parsing routines
   578   bool ParseBackground();
   580   struct BackgroundParseState {
   581     nsCSSValue&  mColor;
   582     nsCSSValueList* mImage;
   583     nsCSSValuePairList* mRepeat;
   584     nsCSSValueList* mAttachment;
   585     nsCSSValueList* mClip;
   586     nsCSSValueList* mOrigin;
   587     nsCSSValueList* mPosition;
   588     nsCSSValuePairList* mSize;
   589     BackgroundParseState(
   590         nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
   591         nsCSSValueList* aAttachment, nsCSSValueList* aClip,
   592         nsCSSValueList* aOrigin, nsCSSValueList* aPosition,
   593         nsCSSValuePairList* aSize) :
   594         mColor(aColor), mImage(aImage), mRepeat(aRepeat),
   595         mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
   596         mPosition(aPosition), mSize(aSize) {};
   597   };
   599   bool ParseBackgroundItem(BackgroundParseState& aState);
   601   bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id
   602   bool ParseBackgroundRepeat();
   603   bool ParseBackgroundRepeatValues(nsCSSValuePair& aValue);
   604   bool ParseBackgroundPosition();
   606   // ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
   607   // which is still used by some properties. See ParseBackgroundPositionValues
   608   // for the css3-background syntax.
   609   bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit,
   610                               bool aAllowExplicitCenter = true); // deprecated
   611   bool ParseBackgroundPositionValues(nsCSSValue& aOut, bool aAcceptsInherit);
   613   bool ParseBackgroundSize();
   614   bool ParseBackgroundSizeValues(nsCSSValuePair& aOut);
   615   bool ParseBorderColor();
   616   bool ParseBorderColors(nsCSSProperty aProperty);
   617   void SetBorderImageInitialValues();
   618   bool ParseBorderImageRepeat(bool aAcceptsInherit);
   619   // If ParseBorderImageSlice returns false, aConsumedTokens indicates
   620   // whether or not any tokens were consumed (in other words, was the property
   621   // in error or just not present).  If ParseBorderImageSlice returns true
   622   // aConsumedTokens is always true.
   623   bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens);
   624   bool ParseBorderImageWidth(bool aAcceptsInherit);
   625   bool ParseBorderImageOutset(bool aAcceptsInherit);
   626   bool ParseBorderImage();
   627   bool ParseBorderSpacing();
   628   bool ParseBorderSide(const nsCSSProperty aPropIDs[],
   629                          bool aSetAllSides);
   630   bool ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
   631                                     int32_t aSourceType);
   632   bool ParseBorderStyle();
   633   bool ParseBorderWidth();
   635   bool ParseCalc(nsCSSValue &aValue, int32_t aVariantMask);
   636   bool ParseCalcAdditiveExpression(nsCSSValue& aValue,
   637                                      int32_t& aVariantMask);
   638   bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
   639                                            int32_t& aVariantMask,
   640                                            bool *aHadFinalWS);
   641   bool ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask);
   642   bool RequireWhitespace();
   644   // For "flex" shorthand property, defined in CSS Flexbox spec
   645   bool ParseFlex();
   646   // For "flex-flow" shorthand property, defined in CSS Flexbox spec
   647   bool ParseFlexFlow();
   649   // CSS Grid
   650   bool ParseGridAutoFlow();
   652   // Parse a <line-names> expression.
   653   // If successful, either leave aValue untouched,
   654   // to indicate that we parsed the empty list,
   655   // or set it to a eCSSUnit_List of eCSSUnit_Ident.
   656   //
   657   // To parse an optional <line-names> (ie. if not finding an open paren
   658   // is considered the same as an empty list),
   659   // treat CSSParseResult::NotFound the same as CSSParseResult::Ok.
   660   //
   661   // If aValue is already a eCSSUnit_List, append to that list.
   662   CSSParseResult ParseGridLineNames(nsCSSValue& aValue);
   663   bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr);
   664   bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue);
   665   bool ParseGridTrackBreadth(nsCSSValue& aValue);
   666   CSSParseResult ParseGridTrackSize(nsCSSValue& aValue);
   667   bool ParseGridAutoColumnsRows(nsCSSProperty aPropID);
   668   bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr);
   670   // Assuming a [ <line-names>? ] has already been parsed,
   671   // parse the rest of a <track-list>.
   672   //
   673   // This exists because [ <line-names>? ] is ambiguous in the
   674   // 'grid-template' shorthand: it can be either the start of a <track-list>,
   675   // or of the intertwined syntax that sets both
   676   // grid-template-rows and grid-template-areas.
   677   //
   678   // On success, |aValue| will be a list of odd length >= 3,
   679   // starting with a <line-names> (which is itself a list)
   680   // and alternating between that and <track-size>.
   681   bool ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
   682                                             const nsCSSValue& aFirstLineNames);
   683   bool ParseGridTemplateColumnsRows(nsCSSProperty aPropID);
   685   // |aAreaIndices| is a lookup table to help us parse faster,
   686   // mapping area names to indices in |aResult.mNamedAreas|.
   687   bool ParseGridTemplateAreasLine(const nsAutoString& aInput,
   688                                   css::GridTemplateAreasValue* aResult,
   689                                   nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices);
   690   bool ParseGridTemplateAreas();
   691   bool ParseGridTemplate();
   692   bool ParseGridTemplateAfterSlash(bool aColumnsIsTrackList);
   693   bool ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames);
   694   bool ParseGrid();
   695   bool ParseGridShorthandAutoProps();
   696   bool ParseGridLine(nsCSSValue& aValue);
   697   bool ParseGridAutoPosition();
   698   bool ParseGridColumnRowStartEnd(nsCSSProperty aPropID);
   699   bool ParseGridColumnRow(nsCSSProperty aStartPropID,
   700                           nsCSSProperty aEndPropID);
   701   bool ParseGridArea();
   703   // for 'clip' and '-moz-image-region'
   704   bool ParseRect(nsCSSProperty aPropID);
   705   bool ParseColumns();
   706   bool ParseContent();
   707   bool ParseCounterData(nsCSSProperty aPropID);
   708   bool ParseCursor();
   709   bool ParseFont();
   710   bool ParseFontSynthesis(nsCSSValue& aValue);
   711   bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue);
   712   bool ParseFontVariantAlternates(nsCSSValue& aValue);
   713   bool ParseBitmaskValues(nsCSSValue& aValue,
   714                           const KTableValue aKeywordTable[],
   715                           const int32_t aMasks[]);
   716   bool ParseFontVariantEastAsian(nsCSSValue& aValue);
   717   bool ParseFontVariantLigatures(nsCSSValue& aValue);
   718   bool ParseFontVariantNumeric(nsCSSValue& aValue);
   719   bool ParseFontWeight(nsCSSValue& aValue);
   720   bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword);
   721   bool ParseFamily(nsCSSValue& aValue);
   722   bool ParseFontFeatureSettings(nsCSSValue& aValue);
   723   bool ParseFontSrc(nsCSSValue& aValue);
   724   bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
   725   bool ParseFontRanges(nsCSSValue& aValue);
   726   bool ParseListStyle();
   727   bool ParseMargin();
   728   bool ParseMarks(nsCSSValue& aValue);
   729   bool ParseTransform(bool aIsPrefixed);
   730   bool ParseOutline();
   731   bool ParseOverflow();
   732   bool ParsePadding();
   733   bool ParseQuotes();
   734   bool ParseSize();
   735   bool ParseTextAlign(nsCSSValue& aValue,
   736                       const KTableValue aTable[]);
   737   bool ParseTextAlign(nsCSSValue& aValue);
   738   bool ParseTextAlignLast(nsCSSValue& aValue);
   739   bool ParseTextDecoration();
   740   bool ParseTextDecorationLine(nsCSSValue& aValue);
   741   bool ParseTextCombineUpright(nsCSSValue& aValue);
   742   bool ParseTextOverflow(nsCSSValue& aValue);
   743   bool ParseTouchAction(nsCSSValue& aValue);
   745   bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow);
   746   bool ParseShadowList(nsCSSProperty aProperty);
   747   bool ParseTransitionProperty();
   748   bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
   749   bool ParseTransitionTimingFunctionValueComponent(float& aComponent,
   750                                                      char aStop,
   751                                                      bool aCheckRange);
   752   bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
   753   enum ParseAnimationOrTransitionShorthandResult {
   754     eParseAnimationOrTransitionShorthand_Values,
   755     eParseAnimationOrTransitionShorthand_Inherit,
   756     eParseAnimationOrTransitionShorthand_Error
   757   };
   758   ParseAnimationOrTransitionShorthandResult
   759     ParseAnimationOrTransitionShorthand(const nsCSSProperty* aProperties,
   760                                         const nsCSSValue* aInitialValues,
   761                                         nsCSSValue* aValues,
   762                                         size_t aNumProperties);
   763   bool ParseTransition();
   764   bool ParseAnimation();
   765   bool ParseWillChange();
   767   bool ParsePaint(nsCSSProperty aPropID);
   768   bool ParseDasharray();
   769   bool ParseMarker();
   770   bool ParsePaintOrder();
   771   bool ParseAll();
   773   /**
   774    * Parses a variable value from a custom property declaration.
   775    *
   776    * @param aType Out parameter into which will be stored the type of variable
   777    *   value, indicating whether the parsed value was a token stream or one of
   778    *   the CSS-wide keywords.
   779    * @param aValue Out parameter into which will be stored the token stream
   780    *   as a string, if the parsed custom property did take a token stream.
   781    * @return Whether parsing succeeded.
   782    */
   783   bool ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
   784                                 nsString& aValue);
   786   /**
   787    * Parses a CSS variable value.  This could be 'initial', 'inherit', 'unset'
   788    * or a token stream, which may or may not include variable references.
   789    *
   790    * @param aType Out parameter into which the type of the variable value
   791    *   will be stored.
   792    * @param aDropBackslash Out parameter indicating whether during variable
   793    *   value parsing there was a trailing backslash before EOF that must
   794    *   be dropped when serializing the variable value.
   795    * @param aImpliedCharacters Out parameter appended to which will be any
   796    *   characters that were implied when encountering EOF and which
   797    *   must be included at the end of the serialized variable value.
   798    * @param aFunc A callback function to invoke when a variable reference
   799    *   is encountered.  May be null.  Arguments are the variable name
   800    *   and the aData argument passed in to this function.
   801    * @param User data to pass in to the callback.
   802    * @return Whether parsing succeeded.
   803    */
   804   bool ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
   805                                bool* aDropBackslash,
   806                                nsString& aImpliedCharacters,
   807                                void (*aFunc)(const nsAString&, void*),
   808                                void* aData);
   810   /**
   811    * Returns whether the scanner dropped a backslash just before EOF.
   812    */
   813   bool BackslashDropped();
   815   /**
   816    * Calls AppendImpliedEOFCharacters on mScanner.
   817    */
   818   void AppendImpliedEOFCharacters(nsAString& aResult);
   820   // Reused utility parsing routines
   821   void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
   822   bool ParseBoxProperties(const nsCSSProperty aPropIDs[]);
   823   bool ParseGroupedBoxProperty(int32_t aVariantMask,
   824                                nsCSSValue& aValue);
   825   bool ParseDirectionalBoxProperty(nsCSSProperty aProperty,
   826                                      int32_t aSourceType);
   827   bool ParseBoxCornerRadius(const nsCSSProperty aPropID);
   828   bool ParseBoxCornerRadii(const nsCSSProperty aPropIDs[]);
   829   int32_t ParseChoice(nsCSSValue aValues[],
   830                       const nsCSSProperty aPropIDs[], int32_t aNumIDs);
   831   bool ParseColor(nsCSSValue& aValue);
   832   bool ParseNumberColorComponent(uint8_t& aComponent, char aStop);
   833   bool ParsePercentageColorComponent(float& aComponent, char aStop);
   834   // ParseHSLColor parses everything starting with the opening '('
   835   // up through and including the aStop char.
   836   bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
   837                      char aStop);
   838   // ParseColorOpacity will enforce that the color ends with a ')'
   839   // after the opacity
   840   bool ParseColorOpacity(uint8_t& aOpacity);
   841   bool ParseColorOpacity(float& aOpacity);
   842   bool ParseEnum(nsCSSValue& aValue,
   843                  const KTableValue aKeywordTable[]);
   844   bool ParseVariant(nsCSSValue& aValue,
   845                     int32_t aVariantMask,
   846                     const KTableValue aKeywordTable[]);
   847   bool ParseNonNegativeVariant(nsCSSValue& aValue,
   848                                int32_t aVariantMask,
   849                                const KTableValue aKeywordTable[]);
   850   bool ParseOneOrLargerVariant(nsCSSValue& aValue,
   851                                int32_t aVariantMask,
   852                                const KTableValue aKeywordTable[]);
   854   // http://dev.w3.org/csswg/css-values/#custom-idents
   855   // Parse an identifier that is none of:
   856   // * a CSS-wide keyword
   857   // * "default"
   858   // * a keyword in |aExcludedKeywords|
   859   // * a keyword in |aPropertyKTable|
   860   //
   861   // |aExcludedKeywords| is an array of nsCSSKeyword
   862   // that ends with a eCSSKeyword_UNKNOWN marker.
   863   //
   864   // |aPropertyKTable| can be used if some of the keywords to exclude
   865   // also appear in an existing nsCSSProps::KTableValue,
   866   // to avoid duplicating them.
   867   bool ParseCustomIdent(nsCSSValue& aValue,
   868                         const nsAutoString& aIdentValue,
   869                         const nsCSSKeyword aExcludedKeywords[] = nullptr,
   870                         const nsCSSProps::KTableValue aPropertyKTable[] = nullptr);
   871   bool ParseCounter(nsCSSValue& aValue);
   872   bool ParseAttr(nsCSSValue& aValue);
   873   bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL);
   874   bool TranslateDimension(nsCSSValue& aValue, int32_t aVariantMask,
   875                             float aNumber, const nsString& aUnit);
   876   bool ParseImageOrientation(nsCSSValue& aAngle);
   877   bool ParseImageRect(nsCSSValue& aImage);
   878   bool ParseElement(nsCSSValue& aValue);
   879   bool ParseColorStop(nsCSSValueGradient* aGradient);
   880   bool ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating,
   881                            bool aIsLegacy);
   882   bool ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating,
   883                            bool aIsLegacy);
   884   bool IsLegacyGradientLine(const nsCSSTokenType& aType,
   885                             const nsString& aId);
   886   bool ParseGradientColorStops(nsCSSValueGradient* aGradient,
   887                                nsCSSValue& aValue);
   889   void SetParsingCompoundProperty(bool aBool) {
   890     mParsingCompoundProperty = aBool;
   891   }
   892   bool IsParsingCompoundProperty(void) const {
   893     return mParsingCompoundProperty;
   894   }
   896   /* Functions for transform Parsing */
   897   bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue);
   898   bool ParseFunction(nsCSSKeyword aFunction, const int32_t aAllowedTypes[],
   899                      int32_t aVariantMaskAll, uint16_t aMinElems,
   900                      uint16_t aMaxElems, nsCSSValue &aValue);
   901   bool ParseFunctionInternals(const int32_t aVariantMask[],
   902                               int32_t aVariantMaskAll,
   903                               uint16_t aMinElems,
   904                               uint16_t aMaxElems,
   905                               InfallibleTArray<nsCSSValue>& aOutput);
   907   /* Functions for transform-origin/perspective-origin Parsing */
   908   bool ParseTransformOrigin(bool aPerspective);
   910   /* Functions for filter parsing */
   911   bool ParseFilter();
   912   bool ParseSingleFilter(nsCSSValue* aValue);
   913   bool ParseDropShadow(nsCSSValue* aValue);
   915   /* Find and return the namespace ID associated with aPrefix.
   916      If aPrefix has not been declared in an @namespace rule, returns
   917      kNameSpaceID_Unknown. */
   918   int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
   920   /* Find the correct default namespace, and set it on aSelector. */
   921   void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
   923   // Current token. The value is valid after calling GetToken and invalidated
   924   // by UngetToken.
   925   nsCSSToken mToken;
   927   // Our scanner.
   928   nsCSSScanner* mScanner;
   930   // Our error reporter.
   931   css::ErrorReporter* mReporter;
   933   // The URI to be used as a base for relative URIs.
   934   nsCOMPtr<nsIURI> mBaseURI;
   936   // The URI to be used as an HTTP "Referer" and for error reporting.
   937   nsCOMPtr<nsIURI> mSheetURI;
   939   // The principal of the sheet involved
   940   nsCOMPtr<nsIPrincipal> mSheetPrincipal;
   942   // The sheet we're parsing into
   943   nsRefPtr<nsCSSStyleSheet> mSheet;
   945   // Used for @import rules
   946   mozilla::css::Loader* mChildLoader; // not ref counted, it owns us
   948   // Sheet section we're in.  This is used to enforce correct ordering of the
   949   // various rule types (eg the fact that a @charset rule must come before
   950   // anything else).  Note that there are checks of similar things in various
   951   // places in nsCSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
   952   enum nsCSSSection {
   953     eCSSSection_Charset,
   954     eCSSSection_Import,
   955     eCSSSection_NameSpace,
   956     eCSSSection_General
   957   };
   958   nsCSSSection  mSection;
   960   nsXMLNameSpaceMap *mNameSpaceMap;  // weak, mSheet owns it
   962   // After an UngetToken is done this flag is true. The next call to
   963   // GetToken clears the flag.
   964   bool mHavePushBack : 1;
   966   // True if we are in quirks mode; false in standards or almost standards mode
   967   bool          mNavQuirkMode : 1;
   969   // True when the hashless color quirk applies.
   970   bool mHashlessColorQuirk : 1;
   972   // True when the unitless length quirk applies.
   973   bool mUnitlessLengthQuirk : 1;
   975   // True if unsafe rules should be allowed
   976   bool mUnsafeRulesEnabled : 1;
   978   // True if we are in parsing rules for Chrome or Certified App content,
   979   // in which case CSS properties with the
   980   // CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP
   981   // flag should be allowed.
   982   bool mIsChromeOrCertifiedApp : 1;
   984   // True if viewport units should be allowed.
   985   bool mViewportUnitsEnabled : 1;
   987   // True for parsing media lists for HTML attributes, where we have to
   988   // ignore CSS comments.
   989   bool mHTMLMediaMode : 1;
   991   // This flag is set when parsing a non-box shorthand; it's used to not apply
   992   // some quirks during shorthand parsing
   993   bool          mParsingCompoundProperty : 1;
   995   // True if we are in the middle of parsing an @supports condition.
   996   // This is used to avoid recording the input stream when variable references
   997   // are encountered in a property declaration in the @supports condition.
   998   bool mInSupportsCondition : 1;
  1000   // True if we are somewhere within a @supports rule whose condition is
  1001   // false.
  1002   bool mInFailingSupportsRule : 1;
  1004   // True if we will suppress all parse errors (except unexpected EOFs).
  1005   // This is used to prevent errors for declarations inside a failing
  1006   // @supports rule.
  1007   bool mSuppressErrors : 1;
  1009   // Stack of rule groups; used for @media and such.
  1010   InfallibleTArray<nsRefPtr<css::GroupRule> > mGroupStack;
  1012   // During the parsing of a property (which may be a shorthand), the data
  1013   // are stored in |mTempData|.  (It is needed to ensure that parser
  1014   // errors cause the data to be ignored, and to ensure that a
  1015   // non-'!important' declaration does not override an '!important'
  1016   // one.)
  1017   nsCSSExpandedDataBlock mTempData;
  1019   // All data from successfully parsed properties are placed into |mData|.
  1020   nsCSSExpandedDataBlock mData;
  1022 public:
  1023   // Used from nsCSSParser constructors and destructors
  1024   CSSParserImpl* mNextFree;
  1025 };
  1027 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer)
  1029   css::Rule **pointer = static_cast<css::Rule**>(aPointer);
  1030   NS_ADDREF(*pointer = aRule);
  1033 static void AppendRuleToSheet(css::Rule* aRule, void* aParser)
  1035   CSSParserImpl* parser = (CSSParserImpl*) aParser;
  1036   parser->AppendRule(aRule);
  1039 #define REPORT_UNEXPECTED(msg_) \
  1040   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_); }
  1042 #define REPORT_UNEXPECTED_P(msg_, param_) \
  1043   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_); }
  1045 #define REPORT_UNEXPECTED_TOKEN(msg_) \
  1046   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken); }
  1048 #define REPORT_UNEXPECTED_TOKEN_CHAR(msg_, ch_) \
  1049   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken, ch_); }
  1051 #define REPORT_UNEXPECTED_EOF(lf_) \
  1052   mReporter->ReportUnexpectedEOF(#lf_)
  1054 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
  1055   mReporter->ReportUnexpectedEOF(ch_)
  1057 #define OUTPUT_ERROR() \
  1058   mReporter->OutputError()
  1060 #define OUTPUT_ERROR_WITH_POSITION(linenum_, lineoff_) \
  1061   mReporter->OutputError(linenum_, lineoff_)
  1063 #define CLEAR_ERROR() \
  1064   mReporter->ClearError()
  1066 CSSParserImpl::CSSParserImpl()
  1067   : mToken(),
  1068     mScanner(nullptr),
  1069     mReporter(nullptr),
  1070     mChildLoader(nullptr),
  1071     mSection(eCSSSection_Charset),
  1072     mNameSpaceMap(nullptr),
  1073     mHavePushBack(false),
  1074     mNavQuirkMode(false),
  1075     mHashlessColorQuirk(false),
  1076     mUnitlessLengthQuirk(false),
  1077     mUnsafeRulesEnabled(false),
  1078     mIsChromeOrCertifiedApp(false),
  1079     mViewportUnitsEnabled(true),
  1080     mHTMLMediaMode(false),
  1081     mParsingCompoundProperty(false),
  1082     mInSupportsCondition(false),
  1083     mInFailingSupportsRule(false),
  1084     mSuppressErrors(false),
  1085     mNextFree(nullptr)
  1089 CSSParserImpl::~CSSParserImpl()
  1091   mData.AssertInitialState();
  1092   mTempData.AssertInitialState();
  1095 nsresult
  1096 CSSParserImpl::SetStyleSheet(nsCSSStyleSheet* aSheet)
  1098   if (aSheet != mSheet) {
  1099     // Switch to using the new sheet, if any
  1100     mGroupStack.Clear();
  1101     mSheet = aSheet;
  1102     if (mSheet) {
  1103       mNameSpaceMap = mSheet->GetNameSpaceMap();
  1104     } else {
  1105       mNameSpaceMap = nullptr;
  1107   } else if (mSheet) {
  1108     mNameSpaceMap = mSheet->GetNameSpaceMap();
  1111   return NS_OK;
  1114 nsresult
  1115 CSSParserImpl::SetQuirkMode(bool aQuirkMode)
  1117   mNavQuirkMode = aQuirkMode;
  1118   return NS_OK;
  1121 nsresult
  1122 CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader)
  1124   mChildLoader = aChildLoader;  // not ref counted, it owns us
  1125   return NS_OK;
  1128 void
  1129 CSSParserImpl::Reset()
  1131   NS_ASSERTION(!mScanner, "resetting with scanner active");
  1132   SetStyleSheet(nullptr);
  1133   SetQuirkMode(false);
  1134   SetChildLoader(nullptr);
  1137 void
  1138 CSSParserImpl::InitScanner(nsCSSScanner& aScanner,
  1139                            css::ErrorReporter& aReporter,
  1140                            nsIURI* aSheetURI, nsIURI* aBaseURI,
  1141                            nsIPrincipal* aSheetPrincipal)
  1143   NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
  1144   NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
  1145   NS_PRECONDITION(!mScanner, "already have scanner");
  1147   mScanner = &aScanner;
  1148   mReporter = &aReporter;
  1149   mScanner->SetErrorReporter(mReporter);
  1151   mBaseURI = aBaseURI;
  1152   mSheetURI = aSheetURI;
  1153   mSheetPrincipal = aSheetPrincipal;
  1154   mHavePushBack = false;
  1157 void
  1158 CSSParserImpl::ReleaseScanner()
  1160   mScanner = nullptr;
  1161   mReporter = nullptr;
  1162   mBaseURI = nullptr;
  1163   mSheetURI = nullptr;
  1164   mSheetPrincipal = nullptr;
  1167 nsresult
  1168 CSSParserImpl::ParseSheet(const nsAString& aInput,
  1169                           nsIURI*          aSheetURI,
  1170                           nsIURI*          aBaseURI,
  1171                           nsIPrincipal*    aSheetPrincipal,
  1172                           uint32_t         aLineNumber,
  1173                           bool             aAllowUnsafeRules)
  1175   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
  1176   NS_PRECONDITION(aBaseURI, "need base URI");
  1177   NS_PRECONDITION(aSheetURI, "need sheet URI");
  1178   NS_PRECONDITION(mSheet, "Must have sheet to parse into");
  1179   NS_ENSURE_STATE(mSheet);
  1181 #ifdef DEBUG
  1182   nsIURI* uri = mSheet->GetSheetURI();
  1183   bool equal;
  1184   NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
  1185                "Sheet URI does not match passed URI");
  1186   NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
  1187                                                         &equal)) &&
  1188                equal,
  1189                "Sheet principal does not match passed principal");
  1190 #endif
  1192   nsCSSScanner scanner(aInput, aLineNumber);
  1193   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
  1194   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
  1196   int32_t ruleCount = mSheet->StyleRuleCount();
  1197   if (0 < ruleCount) {
  1198     const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1);
  1199     if (lastRule) {
  1200       switch (lastRule->GetType()) {
  1201         case css::Rule::CHARSET_RULE:
  1202         case css::Rule::IMPORT_RULE:
  1203           mSection = eCSSSection_Import;
  1204           break;
  1205         case css::Rule::NAMESPACE_RULE:
  1206           mSection = eCSSSection_NameSpace;
  1207           break;
  1208         default:
  1209           mSection = eCSSSection_General;
  1210           break;
  1214   else {
  1215     mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
  1218   mUnsafeRulesEnabled = aAllowUnsafeRules;
  1219   mIsChromeOrCertifiedApp =
  1220     dom::IsChromeURI(aSheetURI) ||
  1221     aSheetPrincipal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
  1223   nsCSSToken* tk = &mToken;
  1224   for (;;) {
  1225     // Get next non-whitespace token
  1226     if (!GetToken(true)) {
  1227       OUTPUT_ERROR();
  1228       break;
  1230     if (eCSSToken_HTMLComment == tk->mType) {
  1231       continue; // legal here only
  1233     if (eCSSToken_AtKeyword == tk->mType) {
  1234       ParseAtRule(AppendRuleToSheet, this, false);
  1235       continue;
  1237     UngetToken();
  1238     if (ParseRuleSet(AppendRuleToSheet, this)) {
  1239       mSection = eCSSSection_General;
  1242   ReleaseScanner();
  1244   mUnsafeRulesEnabled = false;
  1245   mIsChromeOrCertifiedApp = false;
  1247   // XXX check for low level errors
  1248   return NS_OK;
  1251 /**
  1252  * Determines whether the identifier contained in the given string is a
  1253  * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
  1254  */
  1255 static bool
  1256 NonMozillaVendorIdentifier(const nsAString& ident)
  1258   return (ident.First() == char16_t('-') &&
  1259           !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
  1260          ident.First() == char16_t('_');
  1264 nsresult
  1265 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
  1266                                    nsIURI*          aDocURI,
  1267                                    nsIURI*          aBaseURI,
  1268                                    nsIPrincipal*    aNodePrincipal,
  1269                                    css::StyleRule** aResult)
  1271   NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
  1272   NS_PRECONDITION(aBaseURI, "need base URI");
  1274   // XXX line number?
  1275   nsCSSScanner scanner(aAttributeValue, 0);
  1276   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI);
  1277   InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal);
  1279   mSection = eCSSSection_General;
  1281   uint32_t parseFlags = eParseDeclaration_AllowImportant;
  1283   css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
  1284   if (declaration) {
  1285     // Create a style rule for the declaration
  1286     NS_ADDREF(*aResult = new css::StyleRule(nullptr, declaration));
  1287   } else {
  1288     *aResult = nullptr;
  1291   ReleaseScanner();
  1293   // XXX check for low level errors
  1294   return NS_OK;
  1297 nsresult
  1298 CSSParserImpl::ParseDeclarations(const nsAString&  aBuffer,
  1299                                  nsIURI*           aSheetURI,
  1300                                  nsIURI*           aBaseURI,
  1301                                  nsIPrincipal*     aSheetPrincipal,
  1302                                  css::Declaration* aDeclaration,
  1303                                  bool*           aChanged)
  1305   *aChanged = false;
  1307   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
  1309   nsCSSScanner scanner(aBuffer, 0);
  1310   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
  1311   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
  1313   mSection = eCSSSection_General;
  1315   mData.AssertInitialState();
  1316   aDeclaration->ClearData();
  1317   // We could check if it was already empty, but...
  1318   *aChanged = true;
  1320   for (;;) {
  1321     // If we cleared the old decl, then we want to be calling
  1322     // ValueAppended as we parse.
  1323     if (!ParseDeclaration(aDeclaration, eParseDeclaration_AllowImportant,
  1324                           true, aChanged)) {
  1325       if (!SkipDeclaration(false)) {
  1326         break;
  1331   aDeclaration->CompressFrom(&mData);
  1332   ReleaseScanner();
  1333   return NS_OK;
  1336 nsresult
  1337 CSSParserImpl::ParseRule(const nsAString&        aRule,
  1338                          nsIURI*                 aSheetURI,
  1339                          nsIURI*                 aBaseURI,
  1340                          nsIPrincipal*           aSheetPrincipal,
  1341                          css::Rule**             aResult)
  1343   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
  1344   NS_PRECONDITION(aBaseURI, "need base URI");
  1346   *aResult = nullptr;
  1348   nsCSSScanner scanner(aRule, 0);
  1349   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
  1350   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
  1352   mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
  1354   nsCSSToken* tk = &mToken;
  1355   // Get first non-whitespace token
  1356   nsresult rv = NS_OK;
  1357   if (!GetToken(true)) {
  1358     REPORT_UNEXPECTED(PEParseRuleWSOnly);
  1359     OUTPUT_ERROR();
  1360     rv = NS_ERROR_DOM_SYNTAX_ERR;
  1361   } else {
  1362     if (eCSSToken_AtKeyword == tk->mType) {
  1363       // FIXME: perhaps aInsideBlock should be true when we are?
  1364       ParseAtRule(AssignRuleToPointer, aResult, false);
  1365     } else {
  1366       UngetToken();
  1367       ParseRuleSet(AssignRuleToPointer, aResult);
  1370     if (*aResult && GetToken(true)) {
  1371       // garbage after rule
  1372       REPORT_UNEXPECTED_TOKEN(PERuleTrailing);
  1373       NS_RELEASE(*aResult);
  1376     if (!*aResult) {
  1377       rv = NS_ERROR_DOM_SYNTAX_ERR;
  1378       OUTPUT_ERROR();
  1382   ReleaseScanner();
  1383   return rv;
  1386 // See Bug 723197
  1387 #ifdef _MSC_VER
  1388 #pragma optimize( "", off )
  1389 #pragma warning( push )
  1390 #pragma warning( disable : 4748 )
  1391 #endif
  1393 nsresult
  1394 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
  1395                              const nsAString& aPropValue,
  1396                              nsIURI* aSheetURI,
  1397                              nsIURI* aBaseURI,
  1398                              nsIPrincipal* aSheetPrincipal,
  1399                              css::Declaration* aDeclaration,
  1400                              bool* aChanged,
  1401                              bool aIsImportant,
  1402                              bool aIsSVGMode)
  1404   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
  1405   NS_PRECONDITION(aBaseURI, "need base URI");
  1406   NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
  1408   mData.AssertInitialState();
  1409   mTempData.AssertInitialState();
  1410   aDeclaration->AssertMutable();
  1412   nsCSSScanner scanner(aPropValue, 0);
  1413   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
  1414   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
  1415   mSection = eCSSSection_General;
  1416   scanner.SetSVGMode(aIsSVGMode);
  1418   *aChanged = false;
  1420   // Check for unknown or preffed off properties
  1421   if (eCSSProperty_UNKNOWN == aPropID ||
  1422       !(nsCSSProps::IsEnabled(aPropID) ||
  1423         (mUnsafeRulesEnabled &&
  1424          nsCSSProps::PropHasFlags(aPropID,
  1425                                   CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS)))) {
  1426     NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
  1427     REPORT_UNEXPECTED_P(PEUnknownProperty, propName);
  1428     REPORT_UNEXPECTED(PEDeclDropped);
  1429     OUTPUT_ERROR();
  1430     ReleaseScanner();
  1431     return NS_OK;
  1434   bool parsedOK = ParseProperty(aPropID);
  1435   // We should now be at EOF
  1436   if (parsedOK && GetToken(true)) {
  1437     REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
  1438     parsedOK = false;
  1441   if (!parsedOK) {
  1442     NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
  1443     REPORT_UNEXPECTED_P(PEValueParsingError, propName);
  1444     REPORT_UNEXPECTED(PEDeclDropped);
  1445     OUTPUT_ERROR();
  1446     mTempData.ClearProperty(aPropID);
  1447   } else {
  1449     // We know we don't need to force a ValueAppended call for the new
  1450     // value.  So if we are not processing a shorthand, and there's
  1451     // already a value for this property in the declaration at the
  1452     // same importance level, then we can just copy our parsed value
  1453     // directly into the declaration without going through the whole
  1454     // expand/compress thing.
  1455     if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData,
  1456                                        aChanged)) {
  1457       // Do it the slow way
  1458       aDeclaration->ExpandTo(&mData);
  1459       *aChanged = mData.TransferFromBlock(mTempData, aPropID, aIsImportant,
  1460                                           true, false, aDeclaration);
  1461       aDeclaration->CompressFrom(&mData);
  1463     CLEAR_ERROR();
  1466   mTempData.AssertInitialState();
  1468   ReleaseScanner();
  1469   return NS_OK;
  1472 nsresult
  1473 CSSParserImpl::ParseVariable(const nsAString& aVariableName,
  1474                              const nsAString& aPropValue,
  1475                              nsIURI* aSheetURI,
  1476                              nsIURI* aBaseURI,
  1477                              nsIPrincipal* aSheetPrincipal,
  1478                              css::Declaration* aDeclaration,
  1479                              bool* aChanged,
  1480                              bool aIsImportant)
  1482   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
  1483   NS_PRECONDITION(aBaseURI, "need base URI");
  1484   NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
  1485   NS_PRECONDITION(nsLayoutUtils::CSSVariablesEnabled(),
  1486                   "expected Variables to be enabled");
  1488   mData.AssertInitialState();
  1489   mTempData.AssertInitialState();
  1490   aDeclaration->AssertMutable();
  1492   nsCSSScanner scanner(aPropValue, 0);
  1493   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
  1494   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
  1495   mSection = eCSSSection_General;
  1497   *aChanged = false;
  1499   CSSVariableDeclarations::Type variableType;
  1500   nsString variableValue;
  1502   bool parsedOK = ParseVariableDeclaration(&variableType, variableValue);
  1504   // We should now be at EOF
  1505   if (parsedOK && GetToken(true)) {
  1506     REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
  1507     parsedOK = false;
  1510   if (!parsedOK) {
  1511     REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("--") +
  1512                                              aVariableName);
  1513     REPORT_UNEXPECTED(PEDeclDropped);
  1514     OUTPUT_ERROR();
  1515   } else {
  1516     CLEAR_ERROR();
  1517     aDeclaration->AddVariableDeclaration(aVariableName, variableType,
  1518                                          variableValue, aIsImportant, true);
  1519     *aChanged = true;
  1522   mTempData.AssertInitialState();
  1524   ReleaseScanner();
  1525   return NS_OK;
  1528 #ifdef _MSC_VER
  1529 #pragma warning( pop )
  1530 #pragma optimize( "", on )
  1531 #endif
  1533 void
  1534 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
  1535                               nsIURI* aURI, // for error reporting
  1536                               uint32_t aLineNumber, // for error reporting
  1537                               nsMediaList* aMediaList,
  1538                               bool aHTMLMode)
  1540   // XXX Are there cases where the caller wants to keep what it already
  1541   // has in case of parser error?  If GatherMedia ever changes to return
  1542   // a value other than true, we probably should avoid modifying aMediaList.
  1543   aMediaList->Clear();
  1545   // fake base URI since media lists don't have URIs in them
  1546   nsCSSScanner scanner(aBuffer, aLineNumber);
  1547   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
  1548   InitScanner(scanner, reporter, aURI, aURI, nullptr);
  1550   mHTMLMediaMode = aHTMLMode;
  1552     // XXXldb We need to make the scanner not skip CSS comments!  (Or
  1553     // should we?)
  1555   // For aHTMLMode, we used to follow the parsing rules in
  1556   // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
  1557   // which wouldn't work for media queries since they remove all but the
  1558   // first word.  However, they're changed in
  1559   // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
  1560   // (as of 2008-05-29) which says that the media attribute just points
  1561   // to a media query.  (The main substative difference is the relative
  1562   // precedence of commas and paretheses.)
  1564   DebugOnly<bool> parsedOK = GatherMedia(aMediaList, false);
  1565   NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid "
  1566                          "trashing aMediaList");
  1568   CLEAR_ERROR();
  1569   ReleaseScanner();
  1570   mHTMLMediaMode = false;
  1573 bool
  1574 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
  1575                                 nsIURI* aURI, // for error reporting
  1576                                 uint32_t aLineNumber, // for error reporting
  1577                                 nsCSSValue& aValue)
  1579   nsCSSScanner scanner(aBuffer, aLineNumber);
  1580   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
  1581   InitScanner(scanner, reporter, aURI, aURI, nullptr);
  1583   // Parse a color, and check that there's nothing else after it.
  1584   bool colorParsed = ParseColor(aValue) && !GetToken(true);
  1585   OUTPUT_ERROR();
  1586   ReleaseScanner();
  1587   return colorParsed;
  1590 nsresult
  1591 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
  1592                                    nsIURI* aURI, // for error reporting
  1593                                    uint32_t aLineNumber, // for error reporting
  1594                                    nsCSSSelectorList **aSelectorList)
  1596   nsCSSScanner scanner(aSelectorString, aLineNumber);
  1597   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
  1598   InitScanner(scanner, reporter, aURI, aURI, nullptr);
  1600   bool success = ParseSelectorList(*aSelectorList, char16_t(0));
  1602   // We deliberately do not call OUTPUT_ERROR here, because all our
  1603   // callers map a failure return to a JS exception, and if that JS
  1604   // exception is caught, people don't want to see parser diagnostics;
  1605   // see e.g. http://bugs.jquery.com/ticket/7535
  1606   // It would be nice to be able to save the parser diagnostics into
  1607   // the exception, so that if it _isn't_ caught we can report them
  1608   // along with the usual uncaught-exception message, but we don't
  1609   // have any way to do that at present; see bug 631621.
  1610   CLEAR_ERROR();
  1611   ReleaseScanner();
  1613   if (success) {
  1614     NS_ASSERTION(*aSelectorList, "Should have list!");
  1615     return NS_OK;
  1618   NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
  1620   return NS_ERROR_DOM_SYNTAX_ERR;
  1624 already_AddRefed<nsCSSKeyframeRule>
  1625 CSSParserImpl::ParseKeyframeRule(const nsSubstring&  aBuffer,
  1626                                  nsIURI*             aURI,
  1627                                  uint32_t            aLineNumber)
  1629   nsCSSScanner scanner(aBuffer, aLineNumber);
  1630   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
  1631   InitScanner(scanner, reporter, aURI, aURI, nullptr);
  1633   nsRefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule();
  1634   if (GetToken(true)) {
  1635     // extra garbage at the end
  1636     result = nullptr;
  1639   OUTPUT_ERROR();
  1640   ReleaseScanner();
  1642   return result.forget();
  1645 bool
  1646 CSSParserImpl::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
  1647                                            nsIURI* aURI, // for error reporting
  1648                                            uint32_t aLineNumber, // for error reporting
  1649                                            InfallibleTArray<float>& aSelectorList)
  1651   NS_ABORT_IF_FALSE(aSelectorList.IsEmpty(), "given list should start empty");
  1653   nsCSSScanner scanner(aSelectorString, aLineNumber);
  1654   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
  1655   InitScanner(scanner, reporter, aURI, aURI, nullptr);
  1657   bool success = ParseKeyframeSelectorList(aSelectorList) &&
  1658                  // must consume entire input string
  1659                  !GetToken(true);
  1661   OUTPUT_ERROR();
  1662   ReleaseScanner();
  1664   if (success) {
  1665     NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty");
  1666   } else {
  1667     aSelectorList.Clear();
  1670   return success;
  1673 bool
  1674 CSSParserImpl::EvaluateSupportsDeclaration(const nsAString& aProperty,
  1675                                            const nsAString& aValue,
  1676                                            nsIURI* aDocURL,
  1677                                            nsIURI* aBaseURL,
  1678                                            nsIPrincipal* aDocPrincipal)
  1680   nsCSSProperty propID = LookupEnabledProperty(aProperty);
  1681   if (propID == eCSSProperty_UNKNOWN) {
  1682     return false;
  1685   nsCSSScanner scanner(aValue, 0);
  1686   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
  1687   InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
  1688   nsAutoSuppressErrors suppressErrors(this);
  1690   bool parsedOK;
  1692   if (propID == eCSSPropertyExtra_variable) {
  1693     MOZ_ASSERT(Substring(aProperty, 0,
  1694                          CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
  1695     const nsDependentSubstring varName =
  1696       Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH);  // remove '--'
  1697     CSSVariableDeclarations::Type variableType;
  1698     nsString variableValue;
  1699     parsedOK = ParseVariableDeclaration(&variableType, variableValue) &&
  1700                !GetToken(true);
  1701   } else {
  1702     parsedOK = ParseProperty(propID) && !GetToken(true);
  1704     mTempData.ClearProperty(propID);
  1705     mTempData.AssertInitialState();
  1708   CLEAR_ERROR();
  1709   ReleaseScanner();
  1711   return parsedOK;
  1714 bool
  1715 CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration,
  1716                                          nsIURI* aDocURL,
  1717                                          nsIURI* aBaseURL,
  1718                                          nsIPrincipal* aDocPrincipal)
  1720   nsCSSScanner scanner(aDeclaration, 0);
  1721   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
  1722   InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
  1723   nsAutoSuppressErrors suppressErrors(this);
  1725   bool conditionMet;
  1726   bool parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true);
  1728   CLEAR_ERROR();
  1729   ReleaseScanner();
  1731   return parsedOK && conditionMet;
  1734 bool
  1735 CSSParserImpl::EnumerateVariableReferences(const nsAString& aPropertyValue,
  1736                                            VariableEnumFunc aFunc,
  1737                                            void* aData)
  1739   nsCSSScanner scanner(aPropertyValue, 0);
  1740   css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
  1741   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
  1742   nsAutoSuppressErrors suppressErrors(this);
  1744   CSSVariableDeclarations::Type type;
  1745   bool dropBackslash;
  1746   nsString impliedCharacters;
  1747   bool result = ParseValueWithVariables(&type, &dropBackslash,
  1748                                         impliedCharacters, aFunc, aData) &&
  1749                 !GetToken(true);
  1751   ReleaseScanner();
  1753   return result;
  1756 static bool
  1757 SeparatorRequiredBetweenTokens(nsCSSTokenSerializationType aToken1,
  1758                                nsCSSTokenSerializationType aToken2)
  1760   // The two lines marked with (*) do not correspond to entries in
  1761   // the table in the css-syntax spec but which we need to handle,
  1762   // as we treat them as whole tokens.
  1763   switch (aToken1) {
  1764     case eCSSTokenSerialization_Ident:
  1765       return aToken2 == eCSSTokenSerialization_Ident ||
  1766              aToken2 == eCSSTokenSerialization_Function ||
  1767              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
  1768              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
  1769              aToken2 == eCSSTokenSerialization_Number ||
  1770              aToken2 == eCSSTokenSerialization_Percentage ||
  1771              aToken2 == eCSSTokenSerialization_Dimension ||
  1772              aToken2 == eCSSTokenSerialization_URange ||
  1773              aToken2 == eCSSTokenSerialization_CDC ||
  1774              aToken2 == eCSSTokenSerialization_Symbol_OpenParen;
  1775     case eCSSTokenSerialization_AtKeyword_or_Hash:
  1776     case eCSSTokenSerialization_Dimension:
  1777       return aToken2 == eCSSTokenSerialization_Ident ||
  1778              aToken2 == eCSSTokenSerialization_Function ||
  1779              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
  1780              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
  1781              aToken2 == eCSSTokenSerialization_Number ||
  1782              aToken2 == eCSSTokenSerialization_Percentage ||
  1783              aToken2 == eCSSTokenSerialization_Dimension ||
  1784              aToken2 == eCSSTokenSerialization_URange ||
  1785              aToken2 == eCSSTokenSerialization_CDC;
  1786     case eCSSTokenSerialization_Symbol_Hash:
  1787       return aToken2 == eCSSTokenSerialization_Ident ||
  1788              aToken2 == eCSSTokenSerialization_Function ||
  1789              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
  1790              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
  1791              aToken2 == eCSSTokenSerialization_Number ||
  1792              aToken2 == eCSSTokenSerialization_Percentage ||
  1793              aToken2 == eCSSTokenSerialization_Dimension ||
  1794              aToken2 == eCSSTokenSerialization_URange;
  1795     case eCSSTokenSerialization_Symbol_Minus:
  1796     case eCSSTokenSerialization_Number:
  1797       return aToken2 == eCSSTokenSerialization_Ident ||
  1798              aToken2 == eCSSTokenSerialization_Function ||
  1799              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
  1800              aToken2 == eCSSTokenSerialization_Number ||
  1801              aToken2 == eCSSTokenSerialization_Percentage ||
  1802              aToken2 == eCSSTokenSerialization_Dimension ||
  1803              aToken2 == eCSSTokenSerialization_URange;
  1804     case eCSSTokenSerialization_Symbol_At:
  1805       return aToken2 == eCSSTokenSerialization_Ident ||
  1806              aToken2 == eCSSTokenSerialization_Function ||
  1807              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
  1808              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
  1809              aToken2 == eCSSTokenSerialization_URange;
  1810     case eCSSTokenSerialization_URange:
  1811       return aToken2 == eCSSTokenSerialization_Ident ||
  1812              aToken2 == eCSSTokenSerialization_Function ||
  1813              aToken2 == eCSSTokenSerialization_Number ||
  1814              aToken2 == eCSSTokenSerialization_Percentage ||
  1815              aToken2 == eCSSTokenSerialization_Dimension ||
  1816              aToken2 == eCSSTokenSerialization_Symbol_Question;
  1817     case eCSSTokenSerialization_Symbol_Dot_or_Plus:
  1818       return aToken2 == eCSSTokenSerialization_Number ||
  1819              aToken2 == eCSSTokenSerialization_Percentage ||
  1820              aToken2 == eCSSTokenSerialization_Dimension;
  1821     case eCSSTokenSerialization_Symbol_Assorted:
  1822     case eCSSTokenSerialization_Symbol_Asterisk:
  1823       return aToken2 == eCSSTokenSerialization_Symbol_Equals;
  1824     case eCSSTokenSerialization_Symbol_Bar:
  1825       return aToken2 == eCSSTokenSerialization_Symbol_Equals ||
  1826              aToken2 == eCSSTokenSerialization_Symbol_Bar ||
  1827              aToken2 == eCSSTokenSerialization_DashMatch;              // (*)
  1828     case eCSSTokenSerialization_Symbol_Slash:
  1829       return aToken2 == eCSSTokenSerialization_Symbol_Asterisk ||
  1830              aToken2 == eCSSTokenSerialization_ContainsMatch;          // (*)
  1831     default:
  1832       MOZ_ASSERT(aToken1 == eCSSTokenSerialization_Nothing ||
  1833                  aToken1 == eCSSTokenSerialization_Whitespace ||
  1834                  aToken1 == eCSSTokenSerialization_Percentage ||
  1835                  aToken1 == eCSSTokenSerialization_URL_or_BadURL ||
  1836                  aToken1 == eCSSTokenSerialization_Function ||
  1837                  aToken1 == eCSSTokenSerialization_CDC ||
  1838                  aToken1 == eCSSTokenSerialization_Symbol_OpenParen ||
  1839                  aToken1 == eCSSTokenSerialization_Symbol_Question ||
  1840                  aToken1 == eCSSTokenSerialization_Symbol_Assorted ||
  1841                  aToken1 == eCSSTokenSerialization_Symbol_Asterisk ||
  1842                  aToken1 == eCSSTokenSerialization_Symbol_Equals ||
  1843                  aToken1 == eCSSTokenSerialization_Symbol_Bar ||
  1844                  aToken1 == eCSSTokenSerialization_Symbol_Slash ||
  1845                  aToken1 == eCSSTokenSerialization_Other ||
  1846                  "unexpected nsCSSTokenSerializationType value");
  1847       return false;
  1851 /**
  1852  * Appends aValue to aResult, possibly inserting an empty CSS
  1853  * comment between the two to ensure that tokens from both strings
  1854  * remain separated.
  1855  */
  1856 static void
  1857 AppendTokens(nsAString& aResult,
  1858              nsCSSTokenSerializationType& aResultFirstToken,
  1859              nsCSSTokenSerializationType& aResultLastToken,
  1860              nsCSSTokenSerializationType aValueFirstToken,
  1861              nsCSSTokenSerializationType aValueLastToken,
  1862              const nsAString& aValue)
  1864   if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
  1865     aResult.AppendLiteral("/**/");
  1867   aResult.Append(aValue);
  1868   if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
  1869     aResultFirstToken = aValueFirstToken;
  1871   if (aValueLastToken != eCSSTokenSerialization_Nothing) {
  1872     aResultLastToken = aValueLastToken;
  1876 /**
  1877  * Stops the given scanner recording, and appends the recorded result
  1878  * to aResult, possibly inserting an empty CSS comment between the two to
  1879  * ensure that tokens from both strings remain separated.
  1880  */
  1881 static void
  1882 StopRecordingAndAppendTokens(nsString& aResult,
  1883                              nsCSSTokenSerializationType& aResultFirstToken,
  1884                              nsCSSTokenSerializationType& aResultLastToken,
  1885                              nsCSSTokenSerializationType aValueFirstToken,
  1886                              nsCSSTokenSerializationType aValueLastToken,
  1887                              nsCSSScanner* aScanner)
  1889   if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
  1890     aResult.AppendLiteral("/**/");
  1892   aScanner->StopRecording(aResult);
  1893   if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
  1894     aResultFirstToken = aValueFirstToken;
  1896   if (aValueLastToken != eCSSTokenSerialization_Nothing) {
  1897     aResultLastToken = aValueLastToken;
  1901 bool
  1902 CSSParserImpl::ResolveValueWithVariableReferencesRec(
  1903                                      nsString& aResult,
  1904                                      nsCSSTokenSerializationType& aResultFirstToken,
  1905                                      nsCSSTokenSerializationType& aResultLastToken,
  1906                                      const CSSVariableValues* aVariables)
  1908   // This function assumes we are already recording, and will leave the scanner
  1909   // recording when it returns.
  1910   MOZ_ASSERT(mScanner->IsRecording());
  1911   MOZ_ASSERT(aResult.IsEmpty());
  1913   // Stack of closing characters for currently open constructs.
  1914   nsAutoTArray<char16_t, 16> stack;
  1916   // The resolved value for this ResolveValueWithVariableReferencesRec call.
  1917   nsString value;
  1919   // The length of the scanner's recording before the currently parsed token.
  1920   // This is used so that when we encounter a "var(" token, we can strip
  1921   // it off the end of the recording, regardless of how long the token was.
  1922   // (With escapes, it could be longer than four characters.)
  1923   uint32_t lengthBeforeVar = 0;
  1925   // Tracking the type of token that appears at the start and end of |value|
  1926   // and that appears at the start and end of the scanner recording.  These are
  1927   // used to determine whether we need to insert "/**/" when pasting token
  1928   // streams together.
  1929   nsCSSTokenSerializationType valueFirstToken = eCSSTokenSerialization_Nothing,
  1930                               valueLastToken  = eCSSTokenSerialization_Nothing,
  1931                               recFirstToken   = eCSSTokenSerialization_Nothing,
  1932                               recLastToken    = eCSSTokenSerialization_Nothing;
  1934 #define UPDATE_RECORDING_TOKENS(type)                    \
  1935   if (recFirstToken == eCSSTokenSerialization_Nothing) { \
  1936     recFirstToken = type;                                \
  1937   }                                                      \
  1938   recLastToken = type;
  1940   while (GetToken(false)) {
  1941     switch (mToken.mType) {
  1942       case eCSSToken_Symbol: {
  1943         nsCSSTokenSerializationType type = eCSSTokenSerialization_Other;
  1944         if (mToken.mSymbol == '(') {
  1945           stack.AppendElement(')');
  1946           type = eCSSTokenSerialization_Symbol_OpenParen;
  1947         } else if (mToken.mSymbol == '[') {
  1948           stack.AppendElement(']');
  1949         } else if (mToken.mSymbol == '{') {
  1950           stack.AppendElement('}');
  1951         } else if (mToken.mSymbol == ';') {
  1952           if (stack.IsEmpty()) {
  1953             // A ';' that is at the top level of the value or at the top level
  1954             // of a variable reference's fallback is invalid.
  1955             return false;
  1957         } else if (mToken.mSymbol == '!') {
  1958           if (stack.IsEmpty()) {
  1959             // An '!' that is at the top level of the value or at the top level
  1960             // of a variable reference's fallback is invalid.
  1961             return false;
  1963         } else if (mToken.mSymbol == ')' &&
  1964                    stack.IsEmpty()) {
  1965           // We're closing a "var(".
  1966           nsString finalTokens;
  1967           mScanner->StopRecording(finalTokens);
  1968           MOZ_ASSERT(finalTokens[finalTokens.Length() - 1] == ')');
  1969           finalTokens.Truncate(finalTokens.Length() - 1);
  1970           aResult.Append(value);
  1972           AppendTokens(aResult, valueFirstToken, valueLastToken,
  1973                        recFirstToken, recLastToken, finalTokens);
  1975           mScanner->StartRecording();
  1976           UngetToken();
  1977           aResultFirstToken = valueFirstToken;
  1978           aResultLastToken = valueLastToken;
  1979           return true;
  1980         } else if (mToken.mSymbol == ')' ||
  1981                    mToken.mSymbol == ']' ||
  1982                    mToken.mSymbol == '}') {
  1983           if (stack.IsEmpty() ||
  1984               stack.LastElement() != mToken.mSymbol) {
  1985             // A mismatched closing bracket is invalid.
  1986             return false;
  1988           stack.TruncateLength(stack.Length() - 1);
  1989         } else if (mToken.mSymbol == '#') {
  1990           type = eCSSTokenSerialization_Symbol_Hash;
  1991         } else if (mToken.mSymbol == '@') {
  1992           type = eCSSTokenSerialization_Symbol_At;
  1993         } else if (mToken.mSymbol == '.' ||
  1994                    mToken.mSymbol == '+') {
  1995           type = eCSSTokenSerialization_Symbol_Dot_or_Plus;
  1996         } else if (mToken.mSymbol == '-') {
  1997           type = eCSSTokenSerialization_Symbol_Minus;
  1998         } else if (mToken.mSymbol == '?') {
  1999           type = eCSSTokenSerialization_Symbol_Question;
  2000         } else if (mToken.mSymbol == '$' ||
  2001                    mToken.mSymbol == '^' ||
  2002                    mToken.mSymbol == '~') {
  2003           type = eCSSTokenSerialization_Symbol_Assorted;
  2004         } else if (mToken.mSymbol == '=') {
  2005           type = eCSSTokenSerialization_Symbol_Equals;
  2006         } else if (mToken.mSymbol == '|') {
  2007           type = eCSSTokenSerialization_Symbol_Bar;
  2008         } else if (mToken.mSymbol == '/') {
  2009           type = eCSSTokenSerialization_Symbol_Slash;
  2010         } else if (mToken.mSymbol == '*') {
  2011           type = eCSSTokenSerialization_Symbol_Asterisk;
  2013         UPDATE_RECORDING_TOKENS(type);
  2014         break;
  2017       case eCSSToken_Function:
  2018         if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
  2019           // Save the tokens before the "var(" to our resolved value.
  2020           nsString recording;
  2021           mScanner->StopRecording(recording);
  2022           recording.Truncate(lengthBeforeVar);
  2023           AppendTokens(value, valueFirstToken, valueLastToken,
  2024                        recFirstToken, recLastToken, recording);
  2025           recFirstToken = eCSSTokenSerialization_Nothing;
  2026           recLastToken = eCSSTokenSerialization_Nothing;
  2028           if (!GetToken(true) ||
  2029               mToken.mType != eCSSToken_Ident ||
  2030               !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
  2031             // "var(" must be followed by an identifier, and it must be a
  2032             // custom property name.
  2033             return false;
  2036           // Turn the custom property name into a variable name by removing the
  2037           // '--' prefix.
  2038           MOZ_ASSERT(Substring(mToken.mIdent, 0,
  2039                                CSS_CUSTOM_NAME_PREFIX_LENGTH).
  2040                        EqualsLiteral("--"));
  2041           nsDependentString variableName(mToken.mIdent,
  2042                                          CSS_CUSTOM_NAME_PREFIX_LENGTH);
  2044           // Get the value of the identified variable.  Note that we
  2045           // check if the variable value is the empty string, as that means
  2046           // that the variable was invalid at computed value time due to
  2047           // unresolveable variable references or cycles.
  2048           nsString variableValue;
  2049           nsCSSTokenSerializationType varFirstToken, varLastToken;
  2050           bool valid = aVariables->Get(variableName, variableValue,
  2051                                        varFirstToken, varLastToken) &&
  2052                        !variableValue.IsEmpty();
  2054           if (!GetToken(true) ||
  2055               mToken.IsSymbol(')')) {
  2056             mScanner->StartRecording();
  2057             if (!valid) {
  2058               // Invalid variable with no fallback.
  2059               return false;
  2061             // Valid variable with no fallback.
  2062             AppendTokens(value, valueFirstToken, valueLastToken,
  2063                          varFirstToken, varLastToken, variableValue);
  2064           } else if (mToken.IsSymbol(',')) {
  2065             mScanner->StartRecording();
  2066             if (!GetToken(false) ||
  2067                 mToken.IsSymbol(')')) {
  2068               // Comma must be followed by at least one fallback token.
  2069               return false;
  2071             UngetToken();
  2072             if (valid) {
  2073               // Valid variable with ignored fallback.
  2074               mScanner->StopRecording();
  2075               AppendTokens(value, valueFirstToken, valueLastToken,
  2076                            varFirstToken, varLastToken, variableValue);
  2077               bool ok = SkipBalancedContentUntil(')');
  2078               mScanner->StartRecording();
  2079               if (!ok) {
  2080                 return false;
  2082             } else {
  2083               nsString fallback;
  2084               if (!ResolveValueWithVariableReferencesRec(fallback,
  2085                                                          varFirstToken,
  2086                                                          varLastToken,
  2087                                                          aVariables)) {
  2088                 // Fallback value had invalid tokens or an invalid variable reference
  2089                 // that itself had no fallback.
  2090                 return false;
  2092               AppendTokens(value, valueFirstToken, valueLastToken,
  2093                            varFirstToken, varLastToken, fallback);
  2094               // Now we're either at the pushed back ')' that finished the
  2095               // fallback or at EOF.
  2096               DebugOnly<bool> gotToken = GetToken(false);
  2097               MOZ_ASSERT(!gotToken || mToken.IsSymbol(')'));
  2099           } else {
  2100             // Expected ',' or ')' after the variable name.
  2101             mScanner->StartRecording();
  2102             return false;
  2104         } else {
  2105           stack.AppendElement(')');
  2106           UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function);
  2108         break;
  2110       case eCSSToken_Bad_String:
  2111       case eCSSToken_Bad_URL:
  2112         return false;
  2114       case eCSSToken_Whitespace:
  2115         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace);
  2116         break;
  2118       case eCSSToken_AtKeyword:
  2119       case eCSSToken_Hash:
  2120         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash);
  2121         break;
  2123       case eCSSToken_Number:
  2124         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number);
  2125         break;
  2127       case eCSSToken_Dimension:
  2128         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension);
  2129         break;
  2131       case eCSSToken_Ident:
  2132         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident);
  2133         break;
  2135       case eCSSToken_Percentage:
  2136         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage);
  2137         break;
  2139       case eCSSToken_URange:
  2140         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange);
  2141         break;
  2143       case eCSSToken_URL:
  2144         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL);
  2145         break;
  2147       case eCSSToken_HTMLComment:
  2148         if (mToken.mIdent[0] == '-') {
  2149           UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC);
  2150         } else {
  2151           UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
  2153         break;
  2155       case eCSSToken_Dashmatch:
  2156         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch);
  2157         break;
  2159       case eCSSToken_Containsmatch:
  2160         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch);
  2161         break;
  2163       default:
  2164         NS_NOTREACHED("unexpected token type");
  2165         // fall through
  2166       case eCSSToken_ID:
  2167       case eCSSToken_String:
  2168       case eCSSToken_Includes:
  2169       case eCSSToken_Beginsmatch:
  2170       case eCSSToken_Endsmatch:
  2171         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
  2172         break;
  2175     lengthBeforeVar = mScanner->RecordingLength();
  2178 #undef UPDATE_RECORDING_TOKENS
  2180   aResult.Append(value);
  2181   StopRecordingAndAppendTokens(aResult, valueFirstToken, valueLastToken,
  2182                                recFirstToken, recLastToken, mScanner);
  2184   // Append any implicitly closed brackets.
  2185   if (!stack.IsEmpty()) {
  2186     do {
  2187       aResult.Append(stack.LastElement());
  2188       stack.TruncateLength(stack.Length() - 1);
  2189     } while (!stack.IsEmpty());
  2190     valueLastToken = eCSSTokenSerialization_Other;
  2193   mScanner->StartRecording();
  2194   aResultFirstToken = valueFirstToken;
  2195   aResultLastToken = valueLastToken;
  2196   return true;
  2199 bool
  2200 CSSParserImpl::ResolveValueWithVariableReferences(
  2201                                         const CSSVariableValues* aVariables,
  2202                                         nsString& aResult,
  2203                                         nsCSSTokenSerializationType& aFirstToken,
  2204                                         nsCSSTokenSerializationType& aLastToken)
  2206   aResult.Truncate(0);
  2208   // Start recording before we read the first token.
  2209   mScanner->StartRecording();
  2211   if (!GetToken(false)) {
  2212     // Value was empty since we reached EOF.
  2213     mScanner->StopRecording();
  2214     return false;
  2217   UngetToken();
  2219   nsString value;
  2220   nsCSSTokenSerializationType firstToken, lastToken;
  2221   bool ok = ResolveValueWithVariableReferencesRec(value, firstToken, lastToken, aVariables) &&
  2222             !GetToken(true);
  2224   mScanner->StopRecording();
  2226   if (ok) {
  2227     aResult = value;
  2228     aFirstToken = firstToken;
  2229     aLastToken = lastToken;
  2231   return ok;
  2234 bool
  2235 CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue,
  2236                                     const CSSVariableValues* aVariables,
  2237                                     nsString& aResult,
  2238                                     nsCSSTokenSerializationType& aFirstToken,
  2239                                     nsCSSTokenSerializationType& aLastToken)
  2241   nsCSSScanner scanner(aPropertyValue, 0);
  2243   // At this point, we know that aPropertyValue is syntactically correct
  2244   // for a token stream that has variable references.  We also won't be
  2245   // interpreting any of the stream as we parse it, apart from expanding
  2246   // var() references, so we don't need a base URL etc. or any useful
  2247   // error reporting.
  2248   css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
  2249   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
  2251   bool valid = ResolveValueWithVariableReferences(aVariables, aResult,
  2252                                                   aFirstToken, aLastToken);
  2254   ReleaseScanner();
  2255   return valid;
  2258 void
  2259 CSSParserImpl::ParsePropertyWithVariableReferences(
  2260                                             nsCSSProperty aPropertyID,
  2261                                             nsCSSProperty aShorthandPropertyID,
  2262                                             const nsAString& aValue,
  2263                                             const CSSVariableValues* aVariables,
  2264                                             nsRuleData* aRuleData,
  2265                                             nsIURI* aDocURL,
  2266                                             nsIURI* aBaseURL,
  2267                                             nsIPrincipal* aDocPrincipal,
  2268                                             nsCSSStyleSheet* aSheet,
  2269                                             uint32_t aLineNumber,
  2270                                             uint32_t aLineOffset)
  2272   mTempData.AssertInitialState();
  2274   bool valid;
  2275   nsString expandedValue;
  2277   // Resolve any variable references in the property value.
  2279     nsCSSScanner scanner(aValue, 0);
  2280     css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
  2281     InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
  2283     nsCSSTokenSerializationType firstToken, lastToken;
  2284     valid = ResolveValueWithVariableReferences(aVariables, expandedValue,
  2285                                                firstToken, lastToken);
  2286     if (!valid) {
  2287       NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropertyID));
  2288       REPORT_UNEXPECTED(PEInvalidVariableReference);
  2289       REPORT_UNEXPECTED_P(PEValueParsingError, propName);
  2290       if (nsCSSProps::IsInherited(aPropertyID)) {
  2291         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
  2292       } else {
  2293         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
  2295       OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
  2297     ReleaseScanner();
  2300   nsCSSProperty propertyToParse =
  2301     aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID :
  2302                                                    aPropertyID;
  2304   // Parse the property with that resolved value.
  2305   if (valid) {
  2306     nsCSSScanner scanner(expandedValue, 0);
  2307     css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
  2308     InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
  2309     valid = ParseProperty(propertyToParse);
  2310     if (valid && GetToken(true)) {
  2311       REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
  2312       valid = false;
  2314     if (!valid) {
  2315       NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(
  2316                                                               propertyToParse));
  2317       REPORT_UNEXPECTED_P(PEValueWithVariablesParsingError, propName);
  2318       if (nsCSSProps::IsInherited(aPropertyID)) {
  2319         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
  2320       } else {
  2321         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
  2323       OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
  2325     ReleaseScanner();
  2328   // If the property could not be parsed with the resolved value, then we
  2329   // treat it as if the value were 'initial' or 'inherit', depending on whether
  2330   // the property is an inherited property.
  2331   if (!valid) {
  2332     nsCSSValue defaultValue;
  2333     if (nsCSSProps::IsInherited(aPropertyID)) {
  2334       defaultValue.SetInheritValue();
  2335     } else {
  2336       defaultValue.SetInitialValue();
  2338     mTempData.AddLonghandProperty(aPropertyID, defaultValue);
  2341   // Copy the property value into the rule data.
  2342   mTempData.MapRuleInfoInto(aPropertyID, aRuleData);
  2344   mTempData.ClearProperty(propertyToParse);
  2345   mTempData.AssertInitialState();
  2348 //----------------------------------------------------------------------
  2350 bool
  2351 CSSParserImpl::GetToken(bool aSkipWS)
  2353   if (mHavePushBack) {
  2354     mHavePushBack = false;
  2355     if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) {
  2356       return true;
  2359   return mScanner->Next(mToken, aSkipWS);
  2362 void
  2363 CSSParserImpl::UngetToken()
  2365   NS_PRECONDITION(!mHavePushBack, "double pushback");
  2366   mHavePushBack = true;
  2369 bool
  2370 CSSParserImpl::GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum)
  2372   // Peek at next token so that mScanner updates line and column vals
  2373   if (!GetToken(aSkipWS)) {
  2374     return false;
  2376   UngetToken();
  2377   // The scanner uses one-indexing for line numbers but zero-indexing
  2378   // for column numbers.
  2379   *linenum = mScanner->GetLineNumber();
  2380   *colnum = 1 + mScanner->GetColumnNumber();
  2381   return true;
  2384 bool
  2385 CSSParserImpl::ExpectSymbol(char16_t aSymbol,
  2386                             bool aSkipWS)
  2388   if (!GetToken(aSkipWS)) {
  2389     // CSS2.1 specifies that all "open constructs" are to be closed at
  2390     // EOF.  It simplifies higher layers if we claim to have found an
  2391     // ), ], }, or ; if we encounter EOF while looking for one of them.
  2392     // Do still issue a diagnostic, to aid debugging.
  2393     if (aSymbol == ')' || aSymbol == ']' ||
  2394         aSymbol == '}' || aSymbol == ';') {
  2395       REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
  2396       return true;
  2398     else
  2399       return false;
  2401   if (mToken.IsSymbol(aSymbol)) {
  2402     return true;
  2404   UngetToken();
  2405   return false;
  2408 // Checks to see if we're at the end of a property.  If an error occurs during
  2409 // the check, does not signal a parse error.
  2410 bool
  2411 CSSParserImpl::CheckEndProperty()
  2413   if (!GetToken(true)) {
  2414     return true; // properties may end with eof
  2416   if ((eCSSToken_Symbol == mToken.mType) &&
  2417       ((';' == mToken.mSymbol) ||
  2418        ('!' == mToken.mSymbol) ||
  2419        ('}' == mToken.mSymbol) ||
  2420        (')' == mToken.mSymbol))) {
  2421     // XXX need to verify that ! is only followed by "important [;|}]
  2422     // XXX this requires a multi-token pushback buffer
  2423     UngetToken();
  2424     return true;
  2426   UngetToken();
  2427   return false;
  2430 // Checks if we're at the end of a property, raising an error if we're not.
  2431 bool
  2432 CSSParserImpl::ExpectEndProperty()
  2434   if (CheckEndProperty())
  2435     return true;
  2437   // If we're here, we read something incorrect, so we should report it.
  2438   REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
  2439   return false;
  2442 // Parses the priority suffix on a property, which at present may be
  2443 // either '!important' or nothing.
  2444 CSSParserImpl::PriorityParsingStatus
  2445 CSSParserImpl::ParsePriority()
  2447   if (!GetToken(true)) {
  2448     return ePriority_None; // properties may end with EOF
  2450   if (!mToken.IsSymbol('!')) {
  2451     UngetToken();
  2452     return ePriority_None; // dunno what it is, but it's not a priority
  2455   if (!GetToken(true)) {
  2456     // EOF is not ok after !
  2457     REPORT_UNEXPECTED_EOF(PEImportantEOF);
  2458     return ePriority_Error;
  2461   if (mToken.mType != eCSSToken_Ident ||
  2462       !mToken.mIdent.LowerCaseEqualsLiteral("important")) {
  2463     REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
  2464     UngetToken();
  2465     return ePriority_Error;
  2468   return ePriority_Important;
  2471 nsSubstring*
  2472 CSSParserImpl::NextIdent()
  2474   // XXX Error reporting?
  2475   if (!GetToken(true)) {
  2476     return nullptr;
  2478   if (eCSSToken_Ident != mToken.mType) {
  2479     UngetToken();
  2480     return nullptr;
  2482   return &mToken.mIdent;
  2485 bool
  2486 CSSParserImpl::SkipAtRule(bool aInsideBlock)
  2488   for (;;) {
  2489     if (!GetToken(true)) {
  2490       REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2);
  2491       return false;
  2493     if (eCSSToken_Symbol == mToken.mType) {
  2494       char16_t symbol = mToken.mSymbol;
  2495       if (symbol == ';') {
  2496         break;
  2498       if (aInsideBlock && symbol == '}') {
  2499         // The closing } doesn't belong to us.
  2500         UngetToken();
  2501         break;
  2503       if (symbol == '{') {
  2504         SkipUntil('}');
  2505         break;
  2506       } else if (symbol == '(') {
  2507         SkipUntil(')');
  2508       } else if (symbol == '[') {
  2509         SkipUntil(']');
  2511     } else if (eCSSToken_Function == mToken.mType ||
  2512                eCSSToken_Bad_URL == mToken.mType) {
  2513       SkipUntil(')');
  2516   return true;
  2519 bool
  2520 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
  2521                            void* aData,
  2522                            bool aInAtRule)
  2525   nsCSSSection newSection;
  2526   bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*);
  2528   if ((mSection <= eCSSSection_Charset) &&
  2529       (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
  2530     parseFunc = &CSSParserImpl::ParseCharsetRule;
  2531     newSection = eCSSSection_Import;  // only one charset allowed
  2533   } else if ((mSection <= eCSSSection_Import) &&
  2534              mToken.mIdent.LowerCaseEqualsLiteral("import")) {
  2535     parseFunc = &CSSParserImpl::ParseImportRule;
  2536     newSection = eCSSSection_Import;
  2538   } else if ((mSection <= eCSSSection_NameSpace) &&
  2539              mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
  2540     parseFunc = &CSSParserImpl::ParseNameSpaceRule;
  2541     newSection = eCSSSection_NameSpace;
  2543   } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
  2544     parseFunc = &CSSParserImpl::ParseMediaRule;
  2545     newSection = eCSSSection_General;
  2547   } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
  2548     parseFunc = &CSSParserImpl::ParseMozDocumentRule;
  2549     newSection = eCSSSection_General;
  2551   } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
  2552     parseFunc = &CSSParserImpl::ParseFontFaceRule;
  2553     newSection = eCSSSection_General;
  2555   } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values") &&
  2556              nsCSSFontFeatureValuesRule::PrefEnabled()) {
  2557     parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule;
  2558     newSection = eCSSSection_General;
  2560   } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
  2561     parseFunc = &CSSParserImpl::ParsePageRule;
  2562     newSection = eCSSSection_General;
  2564   } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation) &&
  2565               mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) ||
  2566              mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) {
  2567     parseFunc = &CSSParserImpl::ParseKeyframesRule;
  2568     newSection = eCSSSection_General;
  2570   } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports") &&
  2571              CSSSupportsRule::PrefEnabled()) {
  2572     parseFunc = &CSSParserImpl::ParseSupportsRule;
  2573     newSection = eCSSSection_General;
  2575   } else {
  2576     if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
  2577       REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
  2578       OUTPUT_ERROR();
  2580     // Skip over unsupported at rule, don't advance section
  2581     return SkipAtRule(aInAtRule);
  2584   // Inside of @-rules, only the rules that can occur anywhere
  2585   // are allowed.
  2586   bool unnestable = aInAtRule && newSection != eCSSSection_General;
  2587   if (unnestable) {
  2588     REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule);
  2591   if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) {
  2592     // Skip over invalid at rule, don't advance section
  2593     OUTPUT_ERROR();
  2594     return SkipAtRule(aInAtRule);
  2597   // Nested @-rules don't affect the top-level rule chain requirement
  2598   if (!aInAtRule) {
  2599     mSection = newSection;
  2602   return true;
  2605 bool
  2606 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
  2607                                 void* aData)
  2609   if (!GetToken(true)) {
  2610     REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
  2611     return false;
  2614   if (eCSSToken_String != mToken.mType) {
  2615     UngetToken();
  2616     REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
  2617     return false;
  2620   nsAutoString charset = mToken.mIdent;
  2622   if (!ExpectSymbol(';', true)) {
  2623     return false;
  2626   nsRefPtr<css::CharsetRule> rule = new css::CharsetRule(charset);
  2627   (*aAppendFunc)(rule, aData);
  2629   return true;
  2632 bool
  2633 CSSParserImpl::ParseURLOrString(nsString& aURL)
  2635   if (!GetToken(true)) {
  2636     return false;
  2638   if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) {
  2639     aURL = mToken.mIdent;
  2640     return true;
  2642   UngetToken();
  2643   return false;
  2646 bool
  2647 CSSParserImpl::ParseMediaQuery(bool aInAtRule,
  2648                                nsMediaQuery **aQuery,
  2649                                bool *aHitStop)
  2651   *aQuery = nullptr;
  2652   *aHitStop = false;
  2654   // "If the comma-separated list is the empty list it is assumed to
  2655   // specify the media query 'all'."  (css3-mediaqueries, section
  2656   // "Media Queries")
  2657   if (!GetToken(true)) {
  2658     *aHitStop = true;
  2659     // expected termination by EOF
  2660     if (!aInAtRule)
  2661       return true;
  2663     // unexpected termination by EOF
  2664     REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
  2665     return true;
  2668   if (eCSSToken_Symbol == mToken.mType && aInAtRule &&
  2669       (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) {
  2670     *aHitStop = true;
  2671     UngetToken();
  2672     return true;
  2674   UngetToken();
  2676   nsMediaQuery* query = new nsMediaQuery;
  2677   *aQuery = query;
  2679   if (ExpectSymbol('(', true)) {
  2680     // we got an expression without a media type
  2681     UngetToken(); // so ParseMediaQueryExpression can handle it
  2682     query->SetType(nsGkAtoms::all);
  2683     query->SetTypeOmitted();
  2684     // Just parse the first expression here.
  2685     if (!ParseMediaQueryExpression(query)) {
  2686       OUTPUT_ERROR();
  2687       query->SetHadUnknownExpression();
  2689   } else {
  2690     nsCOMPtr<nsIAtom> mediaType;
  2691     bool gotNotOrOnly = false;
  2692     for (;;) {
  2693       if (!GetToken(true)) {
  2694         REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
  2695         return false;
  2697       if (eCSSToken_Ident != mToken.mType) {
  2698         REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
  2699         UngetToken();
  2700         return false;
  2702       // case insensitive from CSS - must be lower cased
  2703       nsContentUtils::ASCIIToLower(mToken.mIdent);
  2704       mediaType = do_GetAtom(mToken.mIdent);
  2705       if (!mediaType) {
  2706         NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
  2708       if (!gotNotOrOnly && mediaType == nsGkAtoms::_not) {
  2709         gotNotOrOnly = true;
  2710         query->SetNegated();
  2711       } else if (!gotNotOrOnly && mediaType == nsGkAtoms::only) {
  2712         gotNotOrOnly = true;
  2713         query->SetHasOnly();
  2714       } else if (mediaType == nsGkAtoms::_not ||
  2715                  mediaType == nsGkAtoms::only ||
  2716                  mediaType == nsGkAtoms::_and ||
  2717                  mediaType == nsGkAtoms::_or) {
  2718         REPORT_UNEXPECTED_TOKEN(PEGatherMediaReservedMediaType);
  2719         UngetToken();
  2720         return false;
  2721       } else {
  2722         // valid media type
  2723         break;
  2726     query->SetType(mediaType);
  2729   for (;;) {
  2730     if (!GetToken(true)) {
  2731       *aHitStop = true;
  2732       // expected termination by EOF
  2733       if (!aInAtRule)
  2734         break;
  2736       // unexpected termination by EOF
  2737       REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
  2738       break;
  2741     if (eCSSToken_Symbol == mToken.mType && aInAtRule &&
  2742         (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) {
  2743       *aHitStop = true;
  2744       UngetToken();
  2745       break;
  2747     if (eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
  2748       // Done with the expressions for this query
  2749       break;
  2751     if (eCSSToken_Ident != mToken.mType ||
  2752         !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
  2753       REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
  2754       UngetToken();
  2755       return false;
  2757     if (!ParseMediaQueryExpression(query)) {
  2758       OUTPUT_ERROR();
  2759       query->SetHadUnknownExpression();
  2762   return true;
  2765 // Returns false only when there is a low-level error in the scanner
  2766 // (out-of-memory).
  2767 bool
  2768 CSSParserImpl::GatherMedia(nsMediaList* aMedia,
  2769                            bool aInAtRule)
  2771   for (;;) {
  2772     nsAutoPtr<nsMediaQuery> query;
  2773     bool hitStop;
  2774     if (!ParseMediaQuery(aInAtRule, getter_Transfers(query),
  2775                          &hitStop)) {
  2776       NS_ASSERTION(!hitStop, "should return true when hit stop");
  2777       OUTPUT_ERROR();
  2778       if (query) {
  2779         query->SetHadUnknownExpression();
  2781       if (aInAtRule) {
  2782         const char16_t stopChars[] =
  2783           { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) };
  2784         SkipUntilOneOf(stopChars);
  2785       } else {
  2786         SkipUntil(',');
  2788       // Rely on SkipUntilOneOf leaving mToken around as the last token read.
  2789       if (mToken.mType == eCSSToken_Symbol && aInAtRule &&
  2790           (mToken.mSymbol == '{' || mToken.mSymbol == ';'  || mToken.mSymbol == '}')) {
  2791         UngetToken();
  2792         hitStop = true;
  2795     if (query) {
  2796       aMedia->AppendQuery(query);
  2798     if (hitStop) {
  2799       break;
  2802   return true;
  2805 bool
  2806 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
  2808   if (!ExpectSymbol('(', true)) {
  2809     REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
  2810     return false;
  2812   if (! GetToken(true)) {
  2813     REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
  2814     return false;
  2816   if (eCSSToken_Ident != mToken.mType) {
  2817     REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
  2818     UngetToken();
  2819     SkipUntil(')');
  2820     return false;
  2823   nsMediaExpression *expr = aQuery->NewExpression();
  2825   // case insensitive from CSS - must be lower cased
  2826   nsContentUtils::ASCIIToLower(mToken.mIdent);
  2827   const char16_t *featureString;
  2828   if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
  2829     expr->mRange = nsMediaExpression::eMin;
  2830     featureString = mToken.mIdent.get() + 4;
  2831   } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
  2832     expr->mRange = nsMediaExpression::eMax;
  2833     featureString = mToken.mIdent.get() + 4;
  2834   } else {
  2835     expr->mRange = nsMediaExpression::eEqual;
  2836     featureString = mToken.mIdent.get();
  2839   nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
  2840   if (!mediaFeatureAtom) {
  2841     NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
  2843   const nsMediaFeature *feature = nsMediaFeatures::features;
  2844   for (; feature->mName; ++feature) {
  2845     if (*(feature->mName) == mediaFeatureAtom) {
  2846       break;
  2849   if (!feature->mName ||
  2850       (expr->mRange != nsMediaExpression::eEqual &&
  2851        feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
  2852     REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
  2853     SkipUntil(')');
  2854     return false;
  2856   expr->mFeature = feature;
  2858   if (!GetToken(true) || mToken.IsSymbol(')')) {
  2859     // Query expressions for any feature can be given without a value.
  2860     // However, min/max prefixes are not allowed.
  2861     if (expr->mRange != nsMediaExpression::eEqual) {
  2862       REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
  2863       return false;
  2865     expr->mValue.Reset();
  2866     return true;
  2869   if (!mToken.IsSymbol(':')) {
  2870     REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
  2871     UngetToken();
  2872     SkipUntil(')');
  2873     return false;
  2876   bool rv = false;
  2877   switch (feature->mValueType) {
  2878     case nsMediaFeature::eLength:
  2879       rv = ParseNonNegativeVariant(expr->mValue, VARIANT_LENGTH, nullptr);
  2880       break;
  2881     case nsMediaFeature::eInteger:
  2882     case nsMediaFeature::eBoolInteger:
  2883       rv = ParseNonNegativeVariant(expr->mValue, VARIANT_INTEGER, nullptr);
  2884       // Enforce extra restrictions for eBoolInteger
  2885       if (rv &&
  2886           feature->mValueType == nsMediaFeature::eBoolInteger &&
  2887           expr->mValue.GetIntValue() > 1)
  2888         rv = false;
  2889       break;
  2890     case nsMediaFeature::eFloat:
  2891       rv = ParseNonNegativeVariant(expr->mValue, VARIANT_NUMBER, nullptr);
  2892       break;
  2893     case nsMediaFeature::eIntRatio:
  2895         // Two integers separated by '/', with optional whitespace on
  2896         // either side of the '/'.
  2897         nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
  2898         expr->mValue.SetArrayValue(a, eCSSUnit_Array);
  2899         // We don't bother with ParseNonNegativeVariant since we have to
  2900         // check for != 0 as well; no need to worry about the UngetToken
  2901         // since we're throwing out up to the next ')' anyway.
  2902         rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nullptr) &&
  2903              a->Item(0).GetIntValue() > 0 &&
  2904              ExpectSymbol('/', true) &&
  2905              ParseVariant(a->Item(1), VARIANT_INTEGER, nullptr) &&
  2906              a->Item(1).GetIntValue() > 0;
  2908       break;
  2909     case nsMediaFeature::eResolution:
  2910       rv = GetToken(true);
  2911       if (!rv)
  2912         break;
  2913       rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f;
  2914       if (!rv) {
  2915         UngetToken();
  2916         break;
  2918       // No worries about whether unitless zero is allowed, since the
  2919       // value must be positive (and we checked that above).
  2920       NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
  2921       if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
  2922         expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
  2923       } else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) {
  2924         expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
  2925       } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
  2926         expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
  2927       } else {
  2928         rv = false;
  2930       break;
  2931     case nsMediaFeature::eEnumerated:
  2932       rv = ParseVariant(expr->mValue, VARIANT_KEYWORD,
  2933                         feature->mData.mKeywordTable);
  2934       break;
  2935     case nsMediaFeature::eIdent:
  2936       rv = ParseVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr);
  2937       break;
  2939   if (!rv || !ExpectSymbol(')', true)) {
  2940     REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
  2941     SkipUntil(')');
  2942     return false;
  2945   return true;
  2948 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
  2949 bool
  2950 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
  2952   nsRefPtr<nsMediaList> media = new nsMediaList();
  2954   nsAutoString url;
  2955   if (!ParseURLOrString(url)) {
  2956     REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
  2957     return false;
  2960   if (!ExpectSymbol(';', true)) {
  2961     if (!GatherMedia(media, true) ||
  2962         !ExpectSymbol(';', true)) {
  2963       REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
  2964       // don't advance section, simply ignore invalid @import
  2965       return false;
  2968     // Safe to assert this, since we ensured that there is something
  2969     // other than the ';' coming after the @import's url() token.
  2970     NS_ASSERTION(media->Length() != 0, "media list must be nonempty");
  2973   ProcessImport(url, media, aAppendFunc, aData);
  2974   return true;
  2978 void
  2979 CSSParserImpl::ProcessImport(const nsString& aURLSpec,
  2980                              nsMediaList* aMedia,
  2981                              RuleAppendFunc aAppendFunc,
  2982                              void* aData)
  2984   nsRefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec);
  2985   (*aAppendFunc)(rule, aData);
  2987   // Diagnose bad URIs even if we don't have a child loader.
  2988   nsCOMPtr<nsIURI> url;
  2989   // Charset will be deduced from mBaseURI, which is more or less correct.
  2990   nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI);
  2992   if (NS_FAILED(rv)) {
  2993     if (rv == NS_ERROR_MALFORMED_URI) {
  2994       // import url is bad
  2995       REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
  2996       OUTPUT_ERROR();
  2998     return;
  3001   if (mChildLoader) {
  3002     mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
  3006 // Parse the {} part of an @media or @-moz-document rule.
  3007 bool
  3008 CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
  3009                               RuleAppendFunc aAppendFunc,
  3010                               void* aData)
  3012   // XXXbz this could use better error reporting throughout the method
  3013   if (!ExpectSymbol('{', true)) {
  3014     return false;
  3017   // push rule on stack, loop over children
  3018   PushGroup(aRule);
  3019   nsCSSSection holdSection = mSection;
  3020   mSection = eCSSSection_General;
  3022   for (;;) {
  3023     // Get next non-whitespace token
  3024     if (! GetToken(true)) {
  3025       REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2);
  3026       break;
  3028     if (mToken.IsSymbol('}')) { // done!
  3029       UngetToken();
  3030       break;
  3032     if (eCSSToken_AtKeyword == mToken.mType) {
  3033       // Parse for nested rules
  3034       ParseAtRule(aAppendFunc, aData, true);
  3035       continue;
  3037     UngetToken();
  3038     ParseRuleSet(AppendRuleToSheet, this, true);
  3040   PopGroup();
  3042   if (!ExpectSymbol('}', true)) {
  3043     mSection = holdSection;
  3044     return false;
  3046   (*aAppendFunc)(aRule, aData);
  3047   return true;
  3050 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
  3051 bool
  3052 CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
  3054   nsRefPtr<nsMediaList> media = new nsMediaList();
  3056   if (GatherMedia(media, true)) {
  3057     // XXXbz this could use better error reporting throughout the method
  3058     nsRefPtr<css::MediaRule> rule = new css::MediaRule();
  3059     // Append first, so when we do SetMedia() the rule
  3060     // knows what its stylesheet is.
  3061     if (ParseGroupRule(rule, aAppendFunc, aData)) {
  3062       rule->SetMedia(media);
  3063       return true;
  3067   return false;
  3070 // Parse a @-moz-document rule.  This is like an @media rule, but instead
  3071 // of a medium it has a nonempty list of items where each item is either
  3072 // url(), url-prefix(), or domain().
  3073 bool
  3074 CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
  3076   css::DocumentRule::URL *urls = nullptr;
  3077   css::DocumentRule::URL **next = &urls;
  3078   do {
  3079     if (!GetToken(true)) {
  3080       REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF);
  3081       delete urls;
  3082       return false;
  3085     if (!(eCSSToken_URL == mToken.mType ||
  3086           (eCSSToken_Function == mToken.mType &&
  3087            (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
  3088             mToken.mIdent.LowerCaseEqualsLiteral("domain") ||
  3089             mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) {
  3090       REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc2);
  3091       UngetToken();
  3092       delete urls;
  3093       return false;
  3095     css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL;
  3096     next = &cur->next;
  3097     if (mToken.mType == eCSSToken_URL) {
  3098       cur->func = css::DocumentRule::eURL;
  3099       CopyUTF16toUTF8(mToken.mIdent, cur->url);
  3100     } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) {
  3101       // regexp() is different from url-prefix() and domain() (but
  3102       // probably the way they *should* have been* in that it requires a
  3103       // string argument, and doesn't try to behave like url().
  3104       cur->func = css::DocumentRule::eRegExp;
  3105       GetToken(true);
  3106       // copy before we know it's valid (but before ExpectSymbol changes
  3107       // mToken.mIdent)
  3108       CopyUTF16toUTF8(mToken.mIdent, cur->url);
  3109       if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) {
  3110         REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString);
  3111         SkipUntil(')');
  3112         delete urls;
  3113         return false;
  3115     } else {
  3116       if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
  3117         cur->func = css::DocumentRule::eURLPrefix;
  3118       } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
  3119         cur->func = css::DocumentRule::eDomain;
  3122       NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point");
  3123       if (!mScanner->NextURL(mToken) || mToken.mType != eCSSToken_URL) {
  3124         REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
  3125         SkipUntil(')');
  3126         delete urls;
  3127         return false;
  3130       // We could try to make the URL (as long as it's not domain())
  3131       // canonical and absolute with NS_NewURI and GetSpec, but I'm
  3132       // inclined to think we shouldn't.
  3133       CopyUTF16toUTF8(mToken.mIdent, cur->url);
  3135   } while (ExpectSymbol(',', true));
  3137   nsRefPtr<css::DocumentRule> rule = new css::DocumentRule();
  3138   rule->SetURLs(urls);
  3140   return ParseGroupRule(rule, aAppendFunc, aData);
  3143 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
  3144 bool
  3145 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
  3147   if (!GetToken(true)) {
  3148     REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
  3149     return false;
  3152   nsAutoString  prefix;
  3153   nsAutoString  url;
  3155   if (eCSSToken_Ident == mToken.mType) {
  3156     prefix = mToken.mIdent;
  3157     // user-specified identifiers are case-sensitive (bug 416106)
  3158   } else {
  3159     UngetToken();
  3162   if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) {
  3163     if (mHavePushBack) {
  3164       REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
  3165     } else {
  3166       REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
  3168     return false;
  3171   ProcessNameSpace(prefix, url, aAppendFunc, aData);
  3172   return true;
  3175 void
  3176 CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
  3177                                 const nsString& aURLSpec,
  3178                                 RuleAppendFunc aAppendFunc,
  3179                                 void* aData)
  3181   nsCOMPtr<nsIAtom> prefix;
  3183   if (!aPrefix.IsEmpty()) {
  3184     prefix = do_GetAtom(aPrefix);
  3185     if (!prefix) {
  3186       NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
  3190   nsRefPtr<css::NameSpaceRule> rule = new css::NameSpaceRule(prefix, aURLSpec);
  3191   (*aAppendFunc)(rule, aData);
  3193   // If this was the first namespace rule encountered, it will trigger
  3194   // creation of a namespace map.
  3195   if (!mNameSpaceMap) {
  3196     mNameSpaceMap = mSheet->GetNameSpaceMap();
  3200 // font-face-rule: '@font-face' '{' font-description '}'
  3201 // font-description: font-descriptor+
  3202 bool
  3203 CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
  3205   if (!ExpectSymbol('{', true)) {
  3206     REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart);
  3207     return false;
  3210   nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule());
  3212   for (;;) {
  3213     if (!GetToken(true)) {
  3214       REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
  3215       break;
  3217     if (mToken.IsSymbol('}')) { // done!
  3218       UngetToken();
  3219       break;
  3222     // ignore extra semicolons
  3223     if (mToken.IsSymbol(';'))
  3224       continue;
  3226     if (!ParseFontDescriptor(rule)) {
  3227       REPORT_UNEXPECTED(PEDeclSkipped);
  3228       OUTPUT_ERROR();
  3229       if (!SkipDeclaration(true))
  3230         break;
  3233   if (!ExpectSymbol('}', true)) {
  3234     REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd);
  3235     return false;
  3237   (*aAppendFunc)(rule, aData);
  3238   return true;
  3241 // font-descriptor: font-family-desc
  3242 //                | font-style-desc
  3243 //                | font-weight-desc
  3244 //                | font-stretch-desc
  3245 //                | font-src-desc
  3246 //                | unicode-range-desc
  3247 //
  3248 // All font-*-desc productions follow the pattern
  3249 //    IDENT ':' value ';'
  3250 //
  3251 // On entry to this function, mToken is the IDENT.
  3253 bool
  3254 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
  3256   if (eCSSToken_Ident != mToken.mType) {
  3257     REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
  3258     return false;
  3261   nsString descName = mToken.mIdent;
  3262   if (!ExpectSymbol(':', true)) {
  3263     REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
  3264     OUTPUT_ERROR();
  3265     return false;
  3268   nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
  3269   nsCSSValue value;
  3271   if (descID == eCSSFontDesc_UNKNOWN) {
  3272     if (NonMozillaVendorIdentifier(descName)) {
  3273       // silently skip other vendors' extensions
  3274       SkipDeclaration(true);
  3275       return true;
  3276     } else {
  3277       REPORT_UNEXPECTED_P(PEUnknownFontDesc, descName);
  3278       return false;
  3282   if (!ParseFontDescriptorValue(descID, value)) {
  3283     REPORT_UNEXPECTED_P(PEValueParsingError, descName);
  3284     return false;
  3287   if (!ExpectEndProperty())
  3288     return false;
  3290   aRule->SetDesc(descID, value);
  3291   return true;
  3294 // @font-feature-values <font-family># {
  3295 //   @<feature-type> {
  3296 //     <feature-ident> : <feature-index>+;
  3297 //     <feature-ident> : <feature-index>+;
  3298 //     ...
  3299 //   }
  3300 //   ...
  3301 // }
  3303 bool
  3304 CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
  3305                                           void* aData)
  3307   nsRefPtr<nsCSSFontFeatureValuesRule>
  3308                valuesRule(new nsCSSFontFeatureValuesRule());
  3310   // parse family list
  3311   nsCSSValue familyValue;
  3313   if (!ParseFamily(familyValue) ||
  3314       familyValue.GetUnit() != eCSSUnit_Families)
  3316     REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily);
  3317     return false;
  3320   // add family to rule
  3321   nsAutoString familyList;
  3322   bool hasGeneric;
  3323   familyValue.GetStringValue(familyList);
  3324   valuesRule->SetFamilyList(familyList, hasGeneric);
  3326   // family list has generic ==> parse error
  3327   if (hasGeneric) {
  3328     REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList);
  3329     return false;
  3332   // open brace
  3333   if (!ExpectSymbol('{', true)) {
  3334     REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart);
  3335     return false;
  3338   // list of sets of feature values, each set bound to a specific
  3339   // feature-type (e.g. swash, annotation)
  3340   for (;;) {
  3341     if (!GetToken(true)) {
  3342       REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
  3343       break;
  3345     if (mToken.IsSymbol('}')) { // done!
  3346       UngetToken();
  3347       break;
  3350     if (!ParseFontFeatureValueSet(valuesRule)) {
  3351       if (!SkipAtRule(false)) {
  3352         break;
  3356   if (!ExpectSymbol('}', true)) {
  3357     REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd);
  3358     SkipUntil('}');
  3359     return false;
  3362   (*aAppendFunc)(valuesRule, aData);
  3363   return true;
  3366 #define NUMVALUES_NO_LIMIT  0xFFFF
  3368 // parse a single value set containing name-value pairs for a single feature type
  3369 //   @<feature-type> { [ <feature-ident> : <feature-index>+ ; ]* }
  3370 //   Ex: @swash { flowing: 1; delicate: 2; }
  3371 bool
  3372 CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule
  3373                                                             *aFeatureValuesRule)
  3375   // -- @keyword (e.g. swash, styleset)
  3376   if (eCSSToken_AtKeyword != mToken.mType) {
  3377     REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt);
  3378     OUTPUT_ERROR();
  3379     UngetToken();
  3380     return false;
  3383   // which font-specific variant of font-variant-alternates
  3384   int32_t whichVariant;
  3385   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
  3386   if (keyword == eCSSKeyword_UNKNOWN ||
  3387       !nsCSSProps::FindKeyword(keyword,
  3388                                nsCSSProps::kFontVariantAlternatesFuncsKTable,
  3389                                whichVariant))
  3391     if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
  3392       REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue);
  3393       OUTPUT_ERROR();
  3395     UngetToken();
  3396     return false;
  3399   nsAutoString featureType(mToken.mIdent);
  3401   // open brace
  3402   if (!ExpectSymbol('{', true)) {
  3403     REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart);
  3404     return false;
  3407   // styleset and character-variant can be multi-valued, otherwise single value
  3408   int32_t limitNumValues;
  3410   switch (keyword) {
  3411     case eCSSKeyword_styleset:
  3412       limitNumValues = NUMVALUES_NO_LIMIT;
  3413       break;
  3414     case eCSSKeyword_character_variant:
  3415       limitNumValues = 2;
  3416       break;
  3417     default:
  3418       limitNumValues = 1;
  3419       break;
  3422   // -- ident integer+ [, ident integer+]
  3423   nsAutoTArray<gfxFontFeatureValueSet::ValueList, 5> values;
  3425   // list of font-feature-values-declaration's
  3426   for (;;) {
  3427     nsAutoString valueId;
  3429     if (!GetToken(true)) {
  3430       REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
  3431       break;
  3434     // ignore extra semicolons
  3435     if (mToken.IsSymbol(';')) {
  3436       continue;
  3439     // close brace ==> done
  3440     if (mToken.IsSymbol('}')) {
  3441       break;
  3444     // ident
  3445     if (eCSSToken_Ident != mToken.mType) {
  3446       REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent);
  3447       if (!SkipDeclaration(true)) {
  3448         break;
  3450       continue;
  3453     valueId.Assign(mToken.mIdent);
  3455     // colon
  3456     if (!ExpectSymbol(':', true)) {
  3457       REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
  3458       OUTPUT_ERROR();
  3459       if (!SkipDeclaration(true)) {
  3460         break;
  3462       continue;
  3465     // value list
  3466     nsAutoTArray<uint32_t,4>   featureSelectors;
  3468     nsCSSValue intValue;
  3469     while (ParseNonNegativeVariant(intValue, VARIANT_INTEGER, nullptr)) {
  3470       featureSelectors.AppendElement(uint32_t(intValue.GetIntValue()));
  3473     int32_t numValues = featureSelectors.Length();
  3475     if (numValues == 0) {
  3476       REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue);
  3477       OUTPUT_ERROR();
  3478       if (!SkipDeclaration(true)) {
  3479         break;
  3481       continue;
  3484     if (numValues > limitNumValues) {
  3485       REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType);
  3486       OUTPUT_ERROR();
  3487       if (!SkipDeclaration(true)) {
  3488         break;
  3490       continue;
  3493     if (!GetToken(true)) {
  3494       REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
  3495       gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
  3496       values.AppendElement(v);
  3497       break;
  3500     // ';' or '}' to end definition
  3501     if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) {
  3502       REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing);
  3503       OUTPUT_ERROR();
  3504       if (!SkipDeclaration(true)) {
  3505         break;
  3507       continue;
  3510     gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
  3511     values.AppendElement(v);
  3513     if (mToken.IsSymbol('}')) {
  3514       break;
  3518   aFeatureValuesRule->AddValueList(whichVariant, values);
  3519   return true;
  3522 bool
  3523 CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData)
  3525   if (!GetToken(true)) {
  3526     REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF);
  3527     return false;
  3530   if (mToken.mType != eCSSToken_Ident) {
  3531     REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
  3532     UngetToken();
  3533     return false;
  3535   nsString name(mToken.mIdent);
  3537   if (!ExpectSymbol('{', true)) {
  3538     REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace);
  3539     return false;
  3542   nsRefPtr<nsCSSKeyframesRule> rule = new nsCSSKeyframesRule(name);
  3544   while (!ExpectSymbol('}', true)) {
  3545     nsRefPtr<nsCSSKeyframeRule> kid = ParseKeyframeRule();
  3546     if (kid) {
  3547       rule->AppendStyleRule(kid);
  3548     } else {
  3549       OUTPUT_ERROR();
  3550       SkipRuleSet(true);
  3554   (*aAppendFunc)(rule, aData);
  3555   return true;
  3558 bool
  3559 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
  3561   // TODO: There can be page selectors after @page such as ":first", ":left".
  3562   uint32_t parseFlags = eParseDeclaration_InBraces |
  3563                         eParseDeclaration_AllowImportant;
  3565   // Forbid viewport units in @page rules. See bug 811391.
  3566   NS_ABORT_IF_FALSE(mViewportUnitsEnabled,
  3567                     "Viewport units should be enabled outside of @page rules.");
  3568   mViewportUnitsEnabled = false;
  3569   nsAutoPtr<css::Declaration> declaration(
  3570                                 ParseDeclarationBlock(parseFlags,
  3571                                                       eCSSContext_Page));
  3572   mViewportUnitsEnabled = true;
  3574   if (!declaration) {
  3575     return false;
  3578   // Takes ownership of declaration.
  3579   nsRefPtr<nsCSSPageRule> rule = new nsCSSPageRule(declaration);
  3581   (*aAppendFunc)(rule, aData);
  3582   return true;
  3585 already_AddRefed<nsCSSKeyframeRule>
  3586 CSSParserImpl::ParseKeyframeRule()
  3588   InfallibleTArray<float> selectorList;
  3589   if (!ParseKeyframeSelectorList(selectorList)) {
  3590     REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
  3591     return nullptr;
  3594   // Ignore !important in keyframe rules
  3595   uint32_t parseFlags = eParseDeclaration_InBraces;
  3596   nsAutoPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags));
  3597   if (!declaration) {
  3598     return nullptr;
  3601   // Takes ownership of declaration, and steals contents of selectorList.
  3602   nsRefPtr<nsCSSKeyframeRule> rule =
  3603     new nsCSSKeyframeRule(selectorList, declaration);
  3605   return rule.forget();
  3608 bool
  3609 CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList)
  3611   for (;;) {
  3612     if (!GetToken(true)) {
  3613       // The first time through the loop, this means we got an empty
  3614       // list.  Otherwise, it means we have a trailing comma.
  3615       return false;
  3617     float value;
  3618     switch (mToken.mType) {
  3619       case eCSSToken_Percentage:
  3620         value = mToken.mNumber;
  3621         break;
  3622       case eCSSToken_Ident:
  3623         if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
  3624           value = 0.0f;
  3625           break;
  3627         if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
  3628           value = 1.0f;
  3629           break;
  3631         // fall through
  3632       default:
  3633         UngetToken();
  3634         // The first time through the loop, this means we got an empty
  3635         // list.  Otherwise, it means we have a trailing comma.
  3636         return false;
  3638     aSelectorList.AppendElement(value);
  3639     if (!ExpectSymbol(',', true)) {
  3640       return true;
  3645 // supports_rule
  3646 //   : "@supports" supports_condition group_rule_body
  3647 //   ;
  3648 bool
  3649 CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData)
  3651   bool conditionMet = false;
  3652   nsString condition;
  3654   mScanner->StartRecording();
  3655   bool parsed = ParseSupportsCondition(conditionMet);
  3657   if (!parsed) {
  3658     mScanner->StopRecording();
  3659     return false;
  3662   if (!ExpectSymbol('{', true)) {
  3663     REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart);
  3664     mScanner->StopRecording();
  3665     return false;
  3668   UngetToken();
  3669   mScanner->StopRecording(condition);
  3671   // Remove the "{" that would follow the condition.
  3672   if (condition.Length() != 0) {
  3673     condition.Truncate(condition.Length() - 1);
  3676   // Remove spaces from the start and end of the recorded supports condition.
  3677   condition.Trim(" ", true, true, false);
  3679   // Record whether we are in a failing @supports, so that property parse
  3680   // errors don't get reported.
  3681   nsAutoFailingSupportsRule failing(this, conditionMet);
  3683   nsRefPtr<css::GroupRule> rule = new CSSSupportsRule(conditionMet, condition);
  3684   return ParseGroupRule(rule, aAppendFunc, aProcessData);
  3687 // supports_condition
  3688 //   : supports_condition_in_parens supports_condition_terms
  3689 //   | supports_condition_negation
  3690 //   ;
  3691 bool
  3692 CSSParserImpl::ParseSupportsCondition(bool& aConditionMet)
  3694   mInSupportsCondition = true;
  3696   if (!GetToken(true)) {
  3697     REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2);
  3698     return false;
  3701   UngetToken();
  3703   mScanner->ClearSeenBadToken();
  3705   if (mToken.IsSymbol('(') ||
  3706       mToken.mType == eCSSToken_Function ||
  3707       mToken.mType == eCSSToken_URL ||
  3708       mToken.mType == eCSSToken_Bad_URL) {
  3709     bool result = ParseSupportsConditionInParens(aConditionMet) &&
  3710                   ParseSupportsConditionTerms(aConditionMet) &&
  3711                   !mScanner->SeenBadToken();
  3712     mInSupportsCondition = false;
  3713     return result;
  3716   if (mToken.mType == eCSSToken_Ident &&
  3717       mToken.mIdent.LowerCaseEqualsLiteral("not")) {
  3718     bool result = ParseSupportsConditionNegation(aConditionMet) &&
  3719                   !mScanner->SeenBadToken();
  3720     mInSupportsCondition = false;
  3721     return result;
  3724   REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart);
  3725   mInSupportsCondition = false;
  3726   return false;
  3729 // supports_condition_negation
  3730 //   : 'not' S+ supports_condition_in_parens
  3731 //   ;
  3732 bool
  3733 CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet)
  3735   if (!GetToken(true)) {
  3736     REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF);
  3737     return false;
  3740   if (mToken.mType != eCSSToken_Ident ||
  3741       !mToken.mIdent.LowerCaseEqualsLiteral("not")) {
  3742     REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot);
  3743     return false;
  3746   if (!RequireWhitespace()) {
  3747     REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
  3748     return false;
  3751   if (ParseSupportsConditionInParens(aConditionMet)) {
  3752     aConditionMet = !aConditionMet;
  3753     return true;
  3756   return false;
  3759 // supports_condition_in_parens
  3760 //   : '(' S* supports_condition_in_parens_inside_parens ')' S*
  3761 //   | general_enclosed
  3762 //   ;
  3763 bool
  3764 CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet)
  3766   if (!GetToken(true)) {
  3767     REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF);
  3768     return false;
  3771   if (mToken.mType == eCSSToken_URL) {
  3772     aConditionMet = false;
  3773     return true;
  3776   if (mToken.mType == eCSSToken_Function ||
  3777       mToken.mType == eCSSToken_Bad_URL) {
  3778     if (!SkipUntil(')')) {
  3779       REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
  3780       return false;
  3782     aConditionMet = false;
  3783     return true;
  3786   if (!mToken.IsSymbol('(')) {
  3787     REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction);
  3788     UngetToken();
  3789     return false;
  3792   if (!ParseSupportsConditionInParensInsideParens(aConditionMet)) {
  3793     if (!SkipUntil(')')) {
  3794       REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
  3795       return false;
  3797     aConditionMet = false;
  3798     return true;
  3801   if (!(ExpectSymbol(')', true))) {
  3802     SkipUntil(')');
  3803     aConditionMet = false;
  3804     return true;
  3807   return true;
  3810 // supports_condition_in_parens_inside_parens
  3811 //   : core_declaration
  3812 //   | supports_condition_negation
  3813 //   | supports_condition_in_parens supports_condition_terms
  3814 //   ;
  3815 bool
  3816 CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet)
  3818   if (!GetToken(true)) {
  3819     return false;
  3822   if (mToken.mType == eCSSToken_Ident) {
  3823     if (!mToken.mIdent.LowerCaseEqualsLiteral("not")) {
  3824       nsAutoString propertyName = mToken.mIdent;
  3825       if (!ExpectSymbol(':', true)) {
  3826         return false;
  3829       nsCSSProperty propID = LookupEnabledProperty(propertyName);
  3830       if (propID == eCSSProperty_UNKNOWN) {
  3831         if (ExpectSymbol(')', true)) {
  3832           UngetToken();
  3833           return false;
  3835         aConditionMet = false;
  3836         SkipUntil(')');
  3837         UngetToken();
  3838       } else if (propID == eCSSPropertyExtra_variable) {
  3839         if (ExpectSymbol(')', false)) {
  3840           UngetToken();
  3841           return false;
  3843         CSSVariableDeclarations::Type variableType;
  3844         nsString variableValue;
  3845         aConditionMet =
  3846           ParseVariableDeclaration(&variableType, variableValue) &&
  3847           ParsePriority() != ePriority_Error;
  3848         if (!aConditionMet) {
  3849           SkipUntil(')');
  3850           UngetToken();
  3852       } else {
  3853         if (ExpectSymbol(')', true)) {
  3854           UngetToken();
  3855           return false;
  3857         aConditionMet = ParseProperty(propID) &&
  3858                         ParsePriority() != ePriority_Error;
  3859         if (!aConditionMet) {
  3860           SkipUntil(')');
  3861           UngetToken();
  3863         mTempData.ClearProperty(propID);
  3864         mTempData.AssertInitialState();
  3866       return true;
  3869     UngetToken();
  3870     return ParseSupportsConditionNegation(aConditionMet);
  3873   UngetToken();
  3874   return ParseSupportsConditionInParens(aConditionMet) &&
  3875          ParseSupportsConditionTerms(aConditionMet);
  3878 // supports_condition_terms
  3879 //   : S+ 'and' supports_condition_terms_after_operator('and')
  3880 //   | S+ 'or' supports_condition_terms_after_operator('or')
  3881 //   |
  3882 //   ;
  3883 bool
  3884 CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet)
  3886   if (!RequireWhitespace() || !GetToken(false)) {
  3887     return true;
  3890   if (mToken.mType != eCSSToken_Ident) {
  3891     UngetToken();
  3892     return true;
  3895   if (mToken.mIdent.LowerCaseEqualsLiteral("and")) {
  3896     return ParseSupportsConditionTermsAfterOperator(aConditionMet, eAnd);
  3899   if (mToken.mIdent.LowerCaseEqualsLiteral("or")) {
  3900     return ParseSupportsConditionTermsAfterOperator(aConditionMet, eOr);
  3903   UngetToken();
  3904   return true;
  3907 // supports_condition_terms_after_operator(operator)
  3908 //   : S+ supports_condition_in_parens ( <operator> supports_condition_in_parens )*
  3909 //   ;
  3910 bool
  3911 CSSParserImpl::ParseSupportsConditionTermsAfterOperator(
  3912                          bool& aConditionMet,
  3913                          CSSParserImpl::SupportsConditionTermOperator aOperator)
  3915   if (!RequireWhitespace()) {
  3916     REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
  3917     return false;
  3920   const char* token = aOperator == eAnd ? "and" : "or";
  3921   for (;;) {
  3922     bool termConditionMet = false;
  3923     if (!ParseSupportsConditionInParens(termConditionMet)) {
  3924       return false;
  3926     aConditionMet = aOperator == eAnd ? aConditionMet && termConditionMet :
  3927                                         aConditionMet || termConditionMet;
  3929     if (!GetToken(true)) {
  3930       return true;
  3933     if (mToken.mType != eCSSToken_Ident ||
  3934         !mToken.mIdent.LowerCaseEqualsASCII(token)) {
  3935       UngetToken();
  3936       return true;
  3941 bool
  3942 CSSParserImpl::SkipUntil(char16_t aStopSymbol)
  3944   nsCSSToken* tk = &mToken;
  3945   nsAutoTArray<char16_t, 16> stack;
  3946   stack.AppendElement(aStopSymbol);
  3947   for (;;) {
  3948     if (!GetToken(true)) {
  3949       return false;
  3951     if (eCSSToken_Symbol == tk->mType) {
  3952       char16_t symbol = tk->mSymbol;
  3953       uint32_t stackTopIndex = stack.Length() - 1;
  3954       if (symbol == stack.ElementAt(stackTopIndex)) {
  3955         stack.RemoveElementAt(stackTopIndex);
  3956         if (stackTopIndex == 0) {
  3957           return true;
  3960       // Just handle out-of-memory by parsing incorrectly.  It's
  3961       // highly unlikely we're dealing with a legitimate style sheet
  3962       // anyway.
  3963       } else if ('{' == symbol) {
  3964         stack.AppendElement('}');
  3965       } else if ('[' == symbol) {
  3966         stack.AppendElement(']');
  3967       } else if ('(' == symbol) {
  3968         stack.AppendElement(')');
  3970     } else if (eCSSToken_Function == tk->mType ||
  3971                eCSSToken_Bad_URL == tk->mType) {
  3972       stack.AppendElement(')');
  3977 bool
  3978 CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol)
  3980   nsCSSToken* tk = &mToken;
  3981   nsAutoTArray<char16_t, 16> stack;
  3982   stack.AppendElement(aStopSymbol);
  3983   for (;;) {
  3984     if (!GetToken(true)) {
  3985       return true;
  3987     if (eCSSToken_Symbol == tk->mType) {
  3988       char16_t symbol = tk->mSymbol;
  3989       uint32_t stackTopIndex = stack.Length() - 1;
  3990       if (symbol == stack.ElementAt(stackTopIndex)) {
  3991         stack.RemoveElementAt(stackTopIndex);
  3992         if (stackTopIndex == 0) {
  3993           return true;
  3996       // Just handle out-of-memory by parsing incorrectly.  It's
  3997       // highly unlikely we're dealing with a legitimate style sheet
  3998       // anyway.
  3999       } else if ('{' == symbol) {
  4000         stack.AppendElement('}');
  4001       } else if ('[' == symbol) {
  4002         stack.AppendElement(']');
  4003       } else if ('(' == symbol) {
  4004         stack.AppendElement(')');
  4005       } else if (')' == symbol ||
  4006                  ']' == symbol ||
  4007                  '}' == symbol) {
  4008         UngetToken();
  4009         return false;
  4011     } else if (eCSSToken_Function == tk->mType ||
  4012                eCSSToken_Bad_URL == tk->mType) {
  4013       stack.AppendElement(')');
  4018 void
  4019 CSSParserImpl::SkipUntilOneOf(const char16_t* aStopSymbolChars)
  4021   nsCSSToken* tk = &mToken;
  4022   nsDependentString stopSymbolChars(aStopSymbolChars);
  4023   for (;;) {
  4024     if (!GetToken(true)) {
  4025       break;
  4027     if (eCSSToken_Symbol == tk->mType) {
  4028       char16_t symbol = tk->mSymbol;
  4029       if (stopSymbolChars.FindChar(symbol) != -1) {
  4030         break;
  4031       } else if ('{' == symbol) {
  4032         SkipUntil('}');
  4033       } else if ('[' == symbol) {
  4034         SkipUntil(']');
  4035       } else if ('(' == symbol) {
  4036         SkipUntil(')');
  4038     } else if (eCSSToken_Function == tk->mType ||
  4039                eCSSToken_Bad_URL == tk->mType) {
  4040       SkipUntil(')');
  4045 void
  4046 CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars)
  4048   uint32_t i = aStopSymbolChars.Length();
  4049   while (i--) {
  4050     SkipUntil(aStopSymbolChars[i]);
  4054 bool
  4055 CSSParserImpl::SkipDeclaration(bool aCheckForBraces)
  4057   nsCSSToken* tk = &mToken;
  4058   for (;;) {
  4059     if (!GetToken(true)) {
  4060       if (aCheckForBraces) {
  4061         REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
  4063       return false;
  4065     if (eCSSToken_Symbol == tk->mType) {
  4066       char16_t symbol = tk->mSymbol;
  4067       if (';' == symbol) {
  4068         break;
  4070       if (aCheckForBraces) {
  4071         if ('}' == symbol) {
  4072           UngetToken();
  4073           break;
  4076       if ('{' == symbol) {
  4077         SkipUntil('}');
  4078       } else if ('(' == symbol) {
  4079         SkipUntil(')');
  4080       } else if ('[' == symbol) {
  4081         SkipUntil(']');
  4083     } else if (eCSSToken_Function == tk->mType ||
  4084                eCSSToken_Bad_URL == tk->mType) {
  4085       SkipUntil(')');
  4088   return true;
  4091 void
  4092 CSSParserImpl::SkipRuleSet(bool aInsideBraces)
  4094   nsCSSToken* tk = &mToken;
  4095   for (;;) {
  4096     if (!GetToken(true)) {
  4097       REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
  4098       break;
  4100     if (eCSSToken_Symbol == tk->mType) {
  4101       char16_t symbol = tk->mSymbol;
  4102       if ('}' == symbol && aInsideBraces) {
  4103         // leave block closer for higher-level grammar to consume
  4104         UngetToken();
  4105         break;
  4106       } else if ('{' == symbol) {
  4107         SkipUntil('}');
  4108         break;
  4109       } else if ('(' == symbol) {
  4110         SkipUntil(')');
  4111       } else if ('[' == symbol) {
  4112         SkipUntil(']');
  4114     } else if (eCSSToken_Function == tk->mType ||
  4115                eCSSToken_Bad_URL == tk->mType) {
  4116       SkipUntil(')');
  4121 void
  4122 CSSParserImpl::PushGroup(css::GroupRule* aRule)
  4124   mGroupStack.AppendElement(aRule);
  4127 void
  4128 CSSParserImpl::PopGroup()
  4130   uint32_t count = mGroupStack.Length();
  4131   if (0 < count) {
  4132     mGroupStack.RemoveElementAt(count - 1);
  4136 void
  4137 CSSParserImpl::AppendRule(css::Rule* aRule)
  4139   uint32_t count = mGroupStack.Length();
  4140   if (0 < count) {
  4141     mGroupStack[count - 1]->AppendStyleRule(aRule);
  4143   else {
  4144     mSheet->AppendStyleRule(aRule);
  4148 bool
  4149 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
  4150                             bool aInsideBraces)
  4152   // First get the list of selectors for the rule
  4153   nsCSSSelectorList* slist = nullptr;
  4154   uint32_t linenum, colnum;
  4155   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
  4156       !ParseSelectorList(slist, char16_t('{'))) {
  4157     REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
  4158     OUTPUT_ERROR();
  4159     SkipRuleSet(aInsideBraces);
  4160     return false;
  4162   NS_ASSERTION(nullptr != slist, "null selector list");
  4163   CLEAR_ERROR();
  4165   // Next parse the declaration block
  4166   uint32_t parseFlags = eParseDeclaration_InBraces |
  4167                         eParseDeclaration_AllowImportant;
  4168   css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
  4169   if (nullptr == declaration) {
  4170     delete slist;
  4171     return false;
  4174 #if 0
  4175   slist->Dump();
  4176   fputs("{\n", stdout);
  4177   declaration->List();
  4178   fputs("}\n", stdout);
  4179 #endif
  4181   // Translate the selector list and declaration block into style data
  4183   nsRefPtr<css::StyleRule> rule = new css::StyleRule(slist, declaration);
  4184   rule->SetLineNumberAndColumnNumber(linenum, colnum);
  4185   (*aAppendFunc)(rule, aData);
  4187   return true;
  4190 bool
  4191 CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
  4192                                  char16_t aStopChar)
  4194   nsCSSSelectorList* list = nullptr;
  4195   if (! ParseSelectorGroup(list)) {
  4196     // must have at least one selector group
  4197     aListHead = nullptr;
  4198     return false;
  4200   NS_ASSERTION(nullptr != list, "no selector list");
  4201   aListHead = list;
  4203   // After that there must either be a "," or a "{" (the latter if
  4204   // StopChar is nonzero)
  4205   nsCSSToken* tk = &mToken;
  4206   for (;;) {
  4207     if (! GetToken(true)) {
  4208       if (aStopChar == char16_t(0)) {
  4209         return true;
  4212       REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
  4213       break;
  4216     if (eCSSToken_Symbol == tk->mType) {
  4217       if (',' == tk->mSymbol) {
  4218         nsCSSSelectorList* newList = nullptr;
  4219         // Another selector group must follow
  4220         if (! ParseSelectorGroup(newList)) {
  4221           break;
  4223         // add new list to the end of the selector list
  4224         list->mNext = newList;
  4225         list = newList;
  4226         continue;
  4227       } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) {
  4228         UngetToken();
  4229         return true;
  4232     REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
  4233     UngetToken();
  4234     break;
  4237   delete aListHead;
  4238   aListHead = nullptr;
  4239   return false;
  4242 static bool IsUniversalSelector(const nsCSSSelector& aSelector)
  4244   return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
  4245                 (aSelector.mLowercaseTag == nullptr) &&
  4246                 (aSelector.mIDList == nullptr) &&
  4247                 (aSelector.mClassList == nullptr) &&
  4248                 (aSelector.mAttrList == nullptr) &&
  4249                 (aSelector.mNegations == nullptr) &&
  4250                 (aSelector.mPseudoClassList == nullptr));
  4253 bool
  4254 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
  4256   char16_t combinator = 0;
  4257   nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
  4259   for (;;) {
  4260     if (!ParseSelector(list, combinator)) {
  4261       return false;
  4264     // Look for a combinator.
  4265     if (!GetToken(false)) {
  4266       break; // EOF ok here
  4269     combinator = char16_t(0);
  4270     if (mToken.mType == eCSSToken_Whitespace) {
  4271       if (!GetToken(true)) {
  4272         break; // EOF ok here
  4274       combinator = char16_t(' ');
  4277     if (mToken.mType != eCSSToken_Symbol) {
  4278       UngetToken(); // not a combinator
  4279     } else {
  4280       char16_t symbol = mToken.mSymbol;
  4281       if (symbol == '+' || symbol == '>' || symbol == '~') {
  4282         combinator = mToken.mSymbol;
  4283       } else {
  4284         UngetToken(); // not a combinator
  4285         if (symbol == ',' || symbol == '{' || symbol == ')') {
  4286           break; // end of selector group
  4291     if (!combinator) {
  4292       REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
  4293       return false;
  4297   aList = list.forget();
  4298   return true;
  4301 #define SEL_MASK_NSPACE   0x01
  4302 #define SEL_MASK_ELEM     0x02
  4303 #define SEL_MASK_ID       0x04
  4304 #define SEL_MASK_CLASS    0x08
  4305 #define SEL_MASK_ATTRIB   0x10
  4306 #define SEL_MASK_PCLASS   0x20
  4307 #define SEL_MASK_PELEM    0x40
  4309 //
  4310 // Parses an ID selector #name
  4311 //
  4312 CSSParserImpl::nsSelectorParsingStatus
  4313 CSSParserImpl::ParseIDSelector(int32_t&       aDataMask,
  4314                                nsCSSSelector& aSelector)
  4316   NS_ASSERTION(!mToken.mIdent.IsEmpty(),
  4317                "Empty mIdent in eCSSToken_ID token?");
  4318   aDataMask |= SEL_MASK_ID;
  4319   aSelector.AddID(mToken.mIdent);
  4320   return eSelectorParsingStatus_Continue;
  4323 //
  4324 // Parses a class selector .name
  4325 //
  4326 CSSParserImpl::nsSelectorParsingStatus
  4327 CSSParserImpl::ParseClassSelector(int32_t&       aDataMask,
  4328                                   nsCSSSelector& aSelector)
  4330   if (! GetToken(false)) { // get ident
  4331     REPORT_UNEXPECTED_EOF(PEClassSelEOF);
  4332     return eSelectorParsingStatus_Error;
  4334   if (eCSSToken_Ident != mToken.mType) {  // malformed selector
  4335     REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
  4336     UngetToken();
  4337     return eSelectorParsingStatus_Error;
  4339   aDataMask |= SEL_MASK_CLASS;
  4341   aSelector.AddClass(mToken.mIdent);
  4343   return eSelectorParsingStatus_Continue;
  4346 //
  4347 // Parse a type element selector or a universal selector
  4348 // namespace|type or namespace|* or *|* or *
  4349 //
  4350 CSSParserImpl::nsSelectorParsingStatus
  4351 CSSParserImpl::ParseTypeOrUniversalSelector(int32_t&       aDataMask,
  4352                                             nsCSSSelector& aSelector,
  4353                                             bool           aIsNegated)
  4355   nsAutoString buffer;
  4356   if (mToken.IsSymbol('*')) {  // universal element selector, or universal namespace
  4357     if (ExpectSymbol('|', false)) {  // was namespace
  4358       aDataMask |= SEL_MASK_NSPACE;
  4359       aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
  4361       if (! GetToken(false)) {
  4362         REPORT_UNEXPECTED_EOF(PETypeSelEOF);
  4363         return eSelectorParsingStatus_Error;
  4365       if (eCSSToken_Ident == mToken.mType) {  // element name
  4366         aDataMask |= SEL_MASK_ELEM;
  4368         aSelector.SetTag(mToken.mIdent);
  4370       else if (mToken.IsSymbol('*')) {  // universal selector
  4371         aDataMask |= SEL_MASK_ELEM;
  4372         // don't set tag
  4374       else {
  4375         REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
  4376         UngetToken();
  4377         return eSelectorParsingStatus_Error;
  4380     else {  // was universal element selector
  4381       SetDefaultNamespaceOnSelector(aSelector);
  4382       aDataMask |= SEL_MASK_ELEM;
  4383       // don't set any tag in the selector
  4385     if (! GetToken(false)) {   // premature eof is ok (here!)
  4386       return eSelectorParsingStatus_Done;
  4389   else if (eCSSToken_Ident == mToken.mType) {    // element name or namespace name
  4390     buffer = mToken.mIdent; // hang on to ident
  4392     if (ExpectSymbol('|', false)) {  // was namespace
  4393       aDataMask |= SEL_MASK_NSPACE;
  4394       int32_t nameSpaceID = GetNamespaceIdForPrefix(buffer);
  4395       if (nameSpaceID == kNameSpaceID_Unknown) {
  4396         return eSelectorParsingStatus_Error;
  4398       aSelector.SetNameSpace(nameSpaceID);
  4400       if (! GetToken(false)) {
  4401         REPORT_UNEXPECTED_EOF(PETypeSelEOF);
  4402         return eSelectorParsingStatus_Error;
  4404       if (eCSSToken_Ident == mToken.mType) {  // element name
  4405         aDataMask |= SEL_MASK_ELEM;
  4406         aSelector.SetTag(mToken.mIdent);
  4408       else if (mToken.IsSymbol('*')) {  // universal selector
  4409         aDataMask |= SEL_MASK_ELEM;
  4410         // don't set tag
  4412       else {
  4413         REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
  4414         UngetToken();
  4415         return eSelectorParsingStatus_Error;
  4418     else {  // was element name
  4419       SetDefaultNamespaceOnSelector(aSelector);
  4420       aSelector.SetTag(buffer);
  4422       aDataMask |= SEL_MASK_ELEM;
  4424     if (! GetToken(false)) {   // premature eof is ok (here!)
  4425       return eSelectorParsingStatus_Done;
  4428   else if (mToken.IsSymbol('|')) {  // No namespace
  4429     aDataMask |= SEL_MASK_NSPACE;
  4430     aSelector.SetNameSpace(kNameSpaceID_None);  // explicit NO namespace
  4432     // get mandatory tag
  4433     if (! GetToken(false)) {
  4434       REPORT_UNEXPECTED_EOF(PETypeSelEOF);
  4435       return eSelectorParsingStatus_Error;
  4437     if (eCSSToken_Ident == mToken.mType) {  // element name
  4438       aDataMask |= SEL_MASK_ELEM;
  4439       aSelector.SetTag(mToken.mIdent);
  4441     else if (mToken.IsSymbol('*')) {  // universal selector
  4442       aDataMask |= SEL_MASK_ELEM;
  4443       // don't set tag
  4445     else {
  4446       REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
  4447       UngetToken();
  4448       return eSelectorParsingStatus_Error;
  4450     if (! GetToken(false)) {   // premature eof is ok (here!)
  4451       return eSelectorParsingStatus_Done;
  4454   else {
  4455     SetDefaultNamespaceOnSelector(aSelector);
  4458   if (aIsNegated) {
  4459     // restore last token read in case of a negated type selector
  4460     UngetToken();
  4462   return eSelectorParsingStatus_Continue;
  4465 //
  4466 // Parse attribute selectors [attr], [attr=value], [attr|=value],
  4467 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
  4468 //
  4469 CSSParserImpl::nsSelectorParsingStatus
  4470 CSSParserImpl::ParseAttributeSelector(int32_t&       aDataMask,
  4471                                       nsCSSSelector& aSelector)
  4473   if (! GetToken(true)) { // premature EOF
  4474     REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
  4475     return eSelectorParsingStatus_Error;
  4478   int32_t nameSpaceID = kNameSpaceID_None;
  4479   nsAutoString  attr;
  4480   if (mToken.IsSymbol('*')) { // wildcard namespace
  4481     nameSpaceID = kNameSpaceID_Unknown;
  4482     if (ExpectSymbol('|', false)) {
  4483       if (! GetToken(false)) { // premature EOF
  4484         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
  4485         return eSelectorParsingStatus_Error;
  4487       if (eCSSToken_Ident == mToken.mType) { // attr name
  4488         attr = mToken.mIdent;
  4490       else {
  4491         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
  4492         UngetToken();
  4493         return eSelectorParsingStatus_Error;
  4496     else {
  4497       REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
  4498       return eSelectorParsingStatus_Error;
  4501   else if (mToken.IsSymbol('|')) { // NO namespace
  4502     if (! GetToken(false)) { // premature EOF
  4503       REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
  4504       return eSelectorParsingStatus_Error;
  4506     if (eCSSToken_Ident == mToken.mType) { // attr name
  4507       attr = mToken.mIdent;
  4509     else {
  4510       REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
  4511       UngetToken();
  4512       return eSelectorParsingStatus_Error;
  4515   else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
  4516     attr = mToken.mIdent; // hang on to it
  4517     if (ExpectSymbol('|', false)) {  // was a namespace
  4518       nameSpaceID = GetNamespaceIdForPrefix(attr);
  4519       if (nameSpaceID == kNameSpaceID_Unknown) {
  4520         return eSelectorParsingStatus_Error;
  4522       if (! GetToken(false)) { // premature EOF
  4523         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
  4524         return eSelectorParsingStatus_Error;
  4526       if (eCSSToken_Ident == mToken.mType) { // attr name
  4527         attr = mToken.mIdent;
  4529       else {
  4530         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
  4531         UngetToken();
  4532         return eSelectorParsingStatus_Error;
  4536   else {  // malformed
  4537     REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
  4538     UngetToken();
  4539     return eSelectorParsingStatus_Error;
  4542   if (! GetToken(true)) { // premature EOF
  4543     REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
  4544     return eSelectorParsingStatus_Error;
  4546   if ((eCSSToken_Symbol == mToken.mType) ||
  4547       (eCSSToken_Includes == mToken.mType) ||
  4548       (eCSSToken_Dashmatch == mToken.mType) ||
  4549       (eCSSToken_Beginsmatch == mToken.mType) ||
  4550       (eCSSToken_Endsmatch == mToken.mType) ||
  4551       (eCSSToken_Containsmatch == mToken.mType)) {
  4552     uint8_t func;
  4553     if (eCSSToken_Includes == mToken.mType) {
  4554       func = NS_ATTR_FUNC_INCLUDES;
  4556     else if (eCSSToken_Dashmatch == mToken.mType) {
  4557       func = NS_ATTR_FUNC_DASHMATCH;
  4559     else if (eCSSToken_Beginsmatch == mToken.mType) {
  4560       func = NS_ATTR_FUNC_BEGINSMATCH;
  4562     else if (eCSSToken_Endsmatch == mToken.mType) {
  4563       func = NS_ATTR_FUNC_ENDSMATCH;
  4565     else if (eCSSToken_Containsmatch == mToken.mType) {
  4566       func = NS_ATTR_FUNC_CONTAINSMATCH;
  4568     else if (']' == mToken.mSymbol) {
  4569       aDataMask |= SEL_MASK_ATTRIB;
  4570       aSelector.AddAttribute(nameSpaceID, attr);
  4571       func = NS_ATTR_FUNC_SET;
  4573     else if ('=' == mToken.mSymbol) {
  4574       func = NS_ATTR_FUNC_EQUALS;
  4576     else {
  4577       REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
  4578       UngetToken(); // bad function
  4579       return eSelectorParsingStatus_Error;
  4581     if (NS_ATTR_FUNC_SET != func) { // get value
  4582       if (! GetToken(true)) { // premature EOF
  4583         REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
  4584         return eSelectorParsingStatus_Error;
  4586       if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
  4587         nsAutoString  value(mToken.mIdent);
  4588         if (! GetToken(true)) { // premature EOF
  4589           REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
  4590           return eSelectorParsingStatus_Error;
  4592         if (mToken.IsSymbol(']')) {
  4593           bool isCaseSensitive = true;
  4595           // For cases when this style sheet is applied to an HTML
  4596           // element in an HTML document, and the attribute selector is
  4597           // for a non-namespaced attribute, then check to see if it's
  4598           // one of the known attributes whose VALUE is
  4599           // case-insensitive.
  4600           if (nameSpaceID == kNameSpaceID_None) {
  4601             static const char* caseInsensitiveHTMLAttribute[] = {
  4602               // list based on http://www.w3.org/TR/html4/
  4603               "lang",
  4604               "dir",
  4605               "http-equiv",
  4606               "text",
  4607               "link",
  4608               "vlink",
  4609               "alink",
  4610               "compact",
  4611               "align",
  4612               "frame",
  4613               "rules",
  4614               "valign",
  4615               "scope",
  4616               "axis",
  4617               "nowrap",
  4618               "hreflang",
  4619               "rel",
  4620               "rev",
  4621               "charset",
  4622               "codetype",
  4623               "declare",
  4624               "valuetype",
  4625               "shape",
  4626               "nohref",
  4627               "media",
  4628               "bgcolor",
  4629               "clear",
  4630               "color",
  4631               "face",
  4632               "noshade",
  4633               "noresize",
  4634               "scrolling",
  4635               "target",
  4636               "method",
  4637               "enctype",
  4638               "accept-charset",
  4639               "accept",
  4640               "checked",
  4641               "multiple",
  4642               "selected",
  4643               "disabled",
  4644               "readonly",
  4645               "language",
  4646               "defer",
  4647               "type",
  4648               // additional attributes not in HTML4
  4649               "direction", // marquee
  4650               nullptr
  4651             };
  4652             short i = 0;
  4653             const char* htmlAttr;
  4654             while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
  4655               if (attr.LowerCaseEqualsASCII(htmlAttr)) {
  4656                 isCaseSensitive = false;
  4657                 break;
  4661           aDataMask |= SEL_MASK_ATTRIB;
  4662           aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
  4664         else {
  4665           REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
  4666           UngetToken();
  4667           return eSelectorParsingStatus_Error;
  4670       else {
  4671         REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
  4672         UngetToken();
  4673         return eSelectorParsingStatus_Error;
  4677   else {
  4678     REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
  4679     UngetToken(); // bad dog, no biscut!
  4680     return eSelectorParsingStatus_Error;
  4682    return eSelectorParsingStatus_Continue;
  4685 //
  4686 // Parse pseudo-classes and pseudo-elements
  4687 //
  4688 CSSParserImpl::nsSelectorParsingStatus
  4689 CSSParserImpl::ParsePseudoSelector(int32_t&       aDataMask,
  4690                                    nsCSSSelector& aSelector,
  4691                                    bool           aIsNegated,
  4692                                    nsIAtom**      aPseudoElement,
  4693                                    nsAtomList**   aPseudoElementArgs,
  4694                                    nsCSSPseudoElements::Type* aPseudoElementType)
  4696   NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs),
  4697                "expected location to store pseudo element");
  4698   NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs),
  4699                "negated selectors shouldn't have a place to store "
  4700                "pseudo elements");
  4701   if (! GetToken(false)) { // premature eof
  4702     REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
  4703     return eSelectorParsingStatus_Error;
  4706   // First, find out whether we are parsing a CSS3 pseudo-element
  4707   bool parsingPseudoElement = false;
  4708   if (mToken.IsSymbol(':')) {
  4709     parsingPseudoElement = true;
  4710     if (! GetToken(false)) { // premature eof
  4711       REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
  4712       return eSelectorParsingStatus_Error;
  4716   // Do some sanity-checking on the token
  4717   if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
  4718     // malformed selector
  4719     REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
  4720     UngetToken();
  4721     return eSelectorParsingStatus_Error;
  4724   // OK, now we know we have an mIdent.  Atomize it.  All the atoms, for
  4725   // pseudo-classes as well as pseudo-elements, start with a single ':'.
  4726   nsAutoString buffer;
  4727   buffer.Append(char16_t(':'));
  4728   buffer.Append(mToken.mIdent);
  4729   nsContentUtils::ASCIIToLower(buffer);
  4730   nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
  4731   if (!pseudo) {
  4732     NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
  4735   // stash away some info about this pseudo so we only have to get it once.
  4736   bool isTreePseudo = false;
  4737   nsCSSPseudoElements::Type pseudoElementType =
  4738     nsCSSPseudoElements::GetPseudoType(pseudo);
  4739   nsCSSPseudoClasses::Type pseudoClassType =
  4740     nsCSSPseudoClasses::GetPseudoType(pseudo);
  4741   bool pseudoClassIsUserAction =
  4742     nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
  4744   if (!mUnsafeRulesEnabled &&
  4745       pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount &&
  4746       nsCSSPseudoElements::PseudoElementIsChromeOnly(pseudoElementType)) {
  4747     // This pseudo-element is not exposed to content.
  4748     REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
  4749     UngetToken();
  4750     return eSelectorParsingStatus_Error;
  4753   // We currently allow :-moz-placeholder and ::-moz-placeholder. We have to
  4754   // be a bit stricter regarding the pseudo-element parsing rules.
  4755   if (pseudoElementType == nsCSSPseudoElements::ePseudo_mozPlaceholder &&
  4756       pseudoClassType == nsCSSPseudoClasses::ePseudoClass_mozPlaceholder) {
  4757     if (parsingPseudoElement) {
  4758       pseudoClassType = nsCSSPseudoClasses::ePseudoClass_NotPseudoClass;
  4759     } else {
  4760       pseudoElementType = nsCSSPseudoElements::ePseudo_NotPseudoElement;
  4764 #ifdef MOZ_XUL
  4765   isTreePseudo = (pseudoElementType == nsCSSPseudoElements::ePseudo_XULTree);
  4766   // If a tree pseudo-element is using the function syntax, it will
  4767   // get isTree set here and will pass the check below that only
  4768   // allows functions if they are in our list of things allowed to be
  4769   // functions.  If it is _not_ using the function syntax, isTree will
  4770   // be false, and it will still pass that check.  So the tree
  4771   // pseudo-elements are allowed to be either functions or not, as
  4772   // desired.
  4773   bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
  4774 #endif
  4775   bool isPseudoElement =
  4776     (pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount);
  4777   // anonymous boxes are only allowed if they're the tree boxes or we have
  4778   // enabled unsafe rules
  4779   bool isAnonBox = isTreePseudo ||
  4780     (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox &&
  4781      mUnsafeRulesEnabled);
  4782   bool isPseudoClass =
  4783     (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass);
  4785   NS_ASSERTION(!isPseudoClass ||
  4786                pseudoElementType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
  4787                "Why is this atom both a pseudo-class and a pseudo-element?");
  4788   NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1,
  4789                "Shouldn't be more than one of these");
  4791   if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
  4792     // Not a pseudo-class, not a pseudo-element.... forget it
  4793     REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
  4794     UngetToken();
  4795     return eSelectorParsingStatus_Error;
  4798   // If it's a function token, it better be on our "ok" list, and if the name
  4799   // is that of a function pseudo it better be a function token
  4800   if ((eCSSToken_Function == mToken.mType) !=
  4802 #ifdef MOZ_XUL
  4803        isTree ||
  4804 #endif
  4805        nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType ||
  4806        nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
  4807        nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
  4808        nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
  4809     // There are no other function pseudos
  4810     REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
  4811     UngetToken();
  4812     return eSelectorParsingStatus_Error;
  4815   // If it starts with "::", it better be a pseudo-element
  4816   if (parsingPseudoElement &&
  4817       !isPseudoElement &&
  4818       !isAnonBox) {
  4819     REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
  4820     UngetToken();
  4821     return eSelectorParsingStatus_Error;
  4824   if (!parsingPseudoElement &&
  4825       nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType) {
  4826     if (aIsNegated) { // :not() can't be itself negated
  4827       REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
  4828       UngetToken();
  4829       return eSelectorParsingStatus_Error;
  4831     // CSS 3 Negation pseudo-class takes one simple selector as argument
  4832     nsSelectorParsingStatus parsingStatus =
  4833       ParseNegatedSimpleSelector(aDataMask, aSelector);
  4834     if (eSelectorParsingStatus_Continue != parsingStatus) {
  4835       return parsingStatus;
  4838   else if (!parsingPseudoElement && isPseudoClass) {
  4839     if (aSelector.IsPseudoElement()) {
  4840       nsCSSPseudoElements::Type type = aSelector.PseudoType();
  4841       if (!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
  4842         // We only allow user action pseudo-classes on certain pseudo-elements.
  4843         REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
  4844         UngetToken();
  4845         return eSelectorParsingStatus_Error;
  4847       if (!pseudoClassIsUserAction) {
  4848         // CSS 4 Selectors says that pseudo-elements can only be followed by
  4849         // a user action pseudo-class.
  4850         REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
  4851         UngetToken();
  4852         return eSelectorParsingStatus_Error;
  4855     aDataMask |= SEL_MASK_PCLASS;
  4856     if (eCSSToken_Function == mToken.mType) {
  4857       nsSelectorParsingStatus parsingStatus;
  4858       if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
  4859         parsingStatus =
  4860           ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
  4862       else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) {
  4863         parsingStatus =
  4864           ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType);
  4866       else {
  4867         NS_ABORT_IF_FALSE(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
  4868                           "unexpected pseudo with function token");
  4869         parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
  4870                                                             pseudoClassType);
  4872       if (eSelectorParsingStatus_Continue != parsingStatus) {
  4873         if (eSelectorParsingStatus_Error == parsingStatus) {
  4874           SkipUntil(')');
  4876         return parsingStatus;
  4879     else {
  4880       aSelector.AddPseudoClass(pseudoClassType);
  4883   else if (isPseudoElement || isAnonBox) {
  4884     // Pseudo-element.  Make some more sanity checks.
  4886     if (aIsNegated) { // pseudo-elements can't be negated
  4887       REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
  4888       UngetToken();
  4889       return eSelectorParsingStatus_Error;
  4891     // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
  4892     // to have a single ':' on them.  Others (CSS3+ pseudo-elements and
  4893     // various -moz-* pseudo-elements) must have |parsingPseudoElement|
  4894     // set.
  4895     if (!parsingPseudoElement &&
  4896         !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
  4897 #ifdef MOZ_XUL
  4898         && !isTreePseudo
  4899 #endif
  4900         ) {
  4901       REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
  4902       UngetToken();
  4903       return eSelectorParsingStatus_Error;
  4906     if (0 == (aDataMask & SEL_MASK_PELEM)) {
  4907       aDataMask |= SEL_MASK_PELEM;
  4908       NS_ADDREF(*aPseudoElement = pseudo);
  4909       *aPseudoElementType = pseudoElementType;
  4911 #ifdef MOZ_XUL
  4912       if (isTree) {
  4913         // We have encountered a pseudoelement of the form
  4914         // -moz-tree-xxxx(a,b,c).  We parse (a,b,c) and add each
  4915         // item in the list to the pseudoclass list.  They will be pulled
  4916         // from the list later along with the pseudo-element.
  4917         if (!ParseTreePseudoElement(aPseudoElementArgs)) {
  4918           return eSelectorParsingStatus_Error;
  4921 #endif
  4923       // Pseudo-elements can only be followed by user action pseudo-classes
  4924       // or be the end of the selector.  So the next non-whitespace token must
  4925       // be ':', '{' or ',' or EOF.
  4926       if (!GetToken(true)) { // premature eof is ok (here!)
  4927         return eSelectorParsingStatus_Done;
  4929       if (parsingPseudoElement && mToken.IsSymbol(':')) {
  4930         UngetToken();
  4931         return eSelectorParsingStatus_Continue;
  4933       if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
  4934         UngetToken();
  4935         return eSelectorParsingStatus_Done;
  4937       REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC);
  4938       UngetToken();
  4939       return eSelectorParsingStatus_Error;
  4941     else {  // multiple pseudo elements, not legal
  4942       REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
  4943       UngetToken();
  4944       return eSelectorParsingStatus_Error;
  4947 #ifdef DEBUG
  4948   else {
  4949     // We should never end up here.  Indeed, if we ended up here, we know (from
  4950     // the current if/else cascade) that !isPseudoElement and !isAnonBox.  But
  4951     // then due to our earlier check we know that isPseudoClass.  Since we
  4952     // didn't fall into the isPseudoClass case in this cascade, we must have
  4953     // parsingPseudoElement.  But we've already checked the
  4954     // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
  4955     // it's happened.
  4956     NS_NOTREACHED("How did this happen?");
  4958 #endif
  4959   return eSelectorParsingStatus_Continue;
  4962 //
  4963 // Parse the argument of a negation pseudo-class :not()
  4964 //
  4965 CSSParserImpl::nsSelectorParsingStatus
  4966 CSSParserImpl::ParseNegatedSimpleSelector(int32_t&       aDataMask,
  4967                                           nsCSSSelector& aSelector)
  4969   if (! GetToken(true)) { // premature eof
  4970     REPORT_UNEXPECTED_EOF(PENegationEOF);
  4971     return eSelectorParsingStatus_Error;
  4974   if (mToken.IsSymbol(')')) {
  4975     REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
  4976     return eSelectorParsingStatus_Error;
  4979   // Create a new nsCSSSelector and add it to the end of
  4980   // aSelector.mNegations.
  4981   // Given the current parsing rules, every selector in mNegations
  4982   // contains only one simple selector (css3 definition) within it.
  4983   // This could easily change in future versions of CSS, and the only
  4984   // thing we need to change to support that is this parsing code and the
  4985   // serialization code for nsCSSSelector.
  4986   nsCSSSelector *newSel = new nsCSSSelector();
  4987   nsCSSSelector* negations = &aSelector;
  4988   while (negations->mNegations) {
  4989     negations = negations->mNegations;
  4991   negations->mNegations = newSel;
  4993   nsSelectorParsingStatus parsingStatus;
  4994   if (eCSSToken_ID == mToken.mType) { // #id
  4995     parsingStatus = ParseIDSelector(aDataMask, *newSel);
  4997   else if (mToken.IsSymbol('.')) {    // .class
  4998     parsingStatus = ParseClassSelector(aDataMask, *newSel);
  5000   else if (mToken.IsSymbol(':')) {    // :pseudo
  5001     parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true,
  5002                                         nullptr, nullptr, nullptr);
  5004   else if (mToken.IsSymbol('[')) {    // [attribute
  5005     parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
  5006     if (eSelectorParsingStatus_Error == parsingStatus) {
  5007       // Skip forward to the matching ']'
  5008       SkipUntil(']');
  5011   else {
  5012     // then it should be a type element or universal selector
  5013     parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true);
  5015   if (eSelectorParsingStatus_Error == parsingStatus) {
  5016     REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
  5017     SkipUntil(')');
  5018     return parsingStatus;
  5020   // close the parenthesis
  5021   if (!ExpectSymbol(')', true)) {
  5022     REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
  5023     SkipUntil(')');
  5024     return eSelectorParsingStatus_Error;
  5027   NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown ||
  5028                (!newSel->mIDList && !newSel->mClassList &&
  5029                 !newSel->mPseudoClassList && !newSel->mAttrList),
  5030                "Need to fix the serialization code to deal with this");
  5032   return eSelectorParsingStatus_Continue;
  5035 //
  5036 // Parse the argument of a pseudo-class that has an ident arg
  5037 //
  5038 CSSParserImpl::nsSelectorParsingStatus
  5039 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
  5040                                             nsCSSPseudoClasses::Type aType)
  5042   if (! GetToken(true)) { // premature eof
  5043     REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
  5044     return eSelectorParsingStatus_Error;
  5046   // We expect an identifier with a language abbreviation
  5047   if (eCSSToken_Ident != mToken.mType) {
  5048     REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
  5049     UngetToken();
  5050     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5053   // -moz-locale-dir and -moz-dir can only have values of 'ltr' or 'rtl'.
  5054   if (aType == nsCSSPseudoClasses::ePseudoClass_mozLocaleDir ||
  5055       aType == nsCSSPseudoClasses::ePseudoClass_dir) {
  5056     nsContentUtils::ASCIIToLower(mToken.mIdent); // case insensitive
  5057     if (!mToken.mIdent.EqualsLiteral("ltr") &&
  5058         !mToken.mIdent.EqualsLiteral("rtl")) {
  5059       REPORT_UNEXPECTED_TOKEN(PEBadDirValue);
  5060       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5064   // Add the pseudo with the language parameter
  5065   aSelector.AddPseudoClass(aType, mToken.mIdent.get());
  5067   // close the parenthesis
  5068   if (!ExpectSymbol(')', true)) {
  5069     REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
  5070     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5073   return eSelectorParsingStatus_Continue;
  5076 CSSParserImpl::nsSelectorParsingStatus
  5077 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
  5078                                               nsCSSPseudoClasses::Type aType)
  5080   int32_t numbers[2] = { 0, 0 };
  5081   int32_t sign[2] = { 1, 1 };
  5082   bool hasSign[2] = { false, false };
  5083   bool lookForB = true;
  5085   // Follow the whitespace rules as proposed in
  5086   // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
  5088   if (! GetToken(true)) {
  5089     REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
  5090     return eSelectorParsingStatus_Error;
  5093   if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
  5094     hasSign[0] = true;
  5095     if (mToken.IsSymbol('-')) {
  5096       sign[0] = -1;
  5098     if (! GetToken(false)) {
  5099       REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
  5100       return eSelectorParsingStatus_Error;
  5104   if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
  5105     // The CSS tokenization doesn't handle :nth-child() containing - well:
  5106     //   2n-1 is a dimension
  5107     //   n-1 is an identifier
  5108     // The easiest way to deal with that is to push everything from the
  5109     // minus on back onto the scanner's pushback buffer.
  5110     uint32_t truncAt = 0;
  5111     if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
  5112       truncAt = 1;
  5113     } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-")) && !hasSign[0]) {
  5114       truncAt = 2;
  5116     if (truncAt != 0) {
  5117       mScanner->Backup(mToken.mIdent.Length() - truncAt);
  5118       mToken.mIdent.Truncate(truncAt);
  5122   if (eCSSToken_Ident == mToken.mType) {
  5123     if (mToken.mIdent.LowerCaseEqualsLiteral("odd") && !hasSign[0]) {
  5124       numbers[0] = 2;
  5125       numbers[1] = 1;
  5126       lookForB = false;
  5128     else if (mToken.mIdent.LowerCaseEqualsLiteral("even") && !hasSign[0]) {
  5129       numbers[0] = 2;
  5130       numbers[1] = 0;
  5131       lookForB = false;
  5133     else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
  5134       numbers[0] = sign[0];
  5136     else if (mToken.mIdent.LowerCaseEqualsLiteral("-n") && !hasSign[0]) {
  5137       numbers[0] = -1;
  5139     else {
  5140       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
  5141       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5144   else if (eCSSToken_Number == mToken.mType) {
  5145     if (!mToken.mIntegerValid) {
  5146       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
  5147       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5149     // for +-an case
  5150     if (mToken.mHasSign && hasSign[0]) {
  5151       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
  5152       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5154     int32_t intValue = mToken.mInteger * sign[0];
  5155     // for -a/**/n case
  5156     if (! GetToken(false)) {
  5157       numbers[1] = intValue;
  5158       lookForB = false;
  5160     else {
  5161       if (eCSSToken_Ident == mToken.mType && mToken.mIdent.LowerCaseEqualsLiteral("n")) {
  5162         numbers[0] = intValue;
  5164       else if (eCSSToken_Ident == mToken.mType && StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
  5165         numbers[0] = intValue;
  5166         mScanner->Backup(mToken.mIdent.Length() - 1);
  5168       else {
  5169         UngetToken();
  5170         numbers[1] = intValue;
  5171         lookForB = false;
  5175   else if (eCSSToken_Dimension == mToken.mType) {
  5176     if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
  5177       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
  5178       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5180     // for +-an case
  5181     if ( mToken.mHasSign && hasSign[0] ) {
  5182       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
  5183       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5185     numbers[0] = mToken.mInteger * sign[0];
  5187   // XXX If it's a ')', is that valid?  (as 0n+0)
  5188   else {
  5189     REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
  5190     UngetToken();
  5191     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5194   if (! GetToken(true)) {
  5195     REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
  5196     return eSelectorParsingStatus_Error;
  5198   if (lookForB && !mToken.IsSymbol(')')) {
  5199     // The '+' or '-' sign can optionally be separated by whitespace.
  5200     // If it is separated by whitespace from what follows it, it appears
  5201     // as a separate token rather than part of the number token.
  5202     if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
  5203       hasSign[1] = true;
  5204       if (mToken.IsSymbol('-')) {
  5205         sign[1] = -1;
  5207       if (! GetToken(true)) {
  5208         REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
  5209         return eSelectorParsingStatus_Error;
  5212     if (eCSSToken_Number != mToken.mType ||
  5213         !mToken.mIntegerValid || mToken.mHasSign == hasSign[1]) {
  5214       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
  5215       UngetToken();
  5216       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5218     numbers[1] = mToken.mInteger * sign[1];
  5219     if (! GetToken(true)) {
  5220       REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
  5221       return eSelectorParsingStatus_Error;
  5224   if (!mToken.IsSymbol(')')) {
  5225     REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
  5226     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5228   aSelector.AddPseudoClass(aType, numbers);
  5229   return eSelectorParsingStatus_Continue;
  5232 //
  5233 // Parse the argument of a pseudo-class that has a selector list argument.
  5234 // Such selector lists cannot contain combinators, but can contain
  5235 // anything that goes between a pair of combinators.
  5236 //
  5237 CSSParserImpl::nsSelectorParsingStatus
  5238 CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
  5239                                                    nsCSSPseudoClasses::Type aType)
  5241   nsAutoPtr<nsCSSSelectorList> slist;
  5242   if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) {
  5243     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5246   // Check that none of the selectors in the list have combinators or
  5247   // pseudo-elements.
  5248   for (nsCSSSelectorList *l = slist; l; l = l->mNext) {
  5249     nsCSSSelector *s = l->mSelectors;
  5250     if (s->mNext || s->IsPseudoElement()) {
  5251       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5255   // Add the pseudo with the selector list parameter
  5256   aSelector.AddPseudoClass(aType, slist.forget());
  5258   // close the parenthesis
  5259   if (!ExpectSymbol(')', true)) {
  5260     REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
  5261     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
  5264   return eSelectorParsingStatus_Continue;
  5268 /**
  5269  * This is the format for selectors:
  5270  * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
  5271  */
  5272 bool
  5273 CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
  5274                              char16_t aPrevCombinator)
  5276   if (! GetToken(true)) {
  5277     REPORT_UNEXPECTED_EOF(PESelectorEOF);
  5278     return false;
  5281   nsCSSSelector* selector = aList->AddSelector(aPrevCombinator);
  5282   nsCOMPtr<nsIAtom> pseudoElement;
  5283   nsAutoPtr<nsAtomList> pseudoElementArgs;
  5284   nsCSSPseudoElements::Type pseudoElementType =
  5285     nsCSSPseudoElements::ePseudo_NotPseudoElement;
  5287   int32_t dataMask = 0;
  5288   nsSelectorParsingStatus parsingStatus =
  5289     ParseTypeOrUniversalSelector(dataMask, *selector, false);
  5291   while (parsingStatus == eSelectorParsingStatus_Continue) {
  5292     if (eCSSToken_ID == mToken.mType) { // #id
  5293       parsingStatus = ParseIDSelector(dataMask, *selector);
  5295     else if (mToken.IsSymbol('.')) {    // .class
  5296       parsingStatus = ParseClassSelector(dataMask, *selector);
  5298     else if (mToken.IsSymbol(':')) {    // :pseudo
  5299       parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
  5300                                           getter_AddRefs(pseudoElement),
  5301                                           getter_Transfers(pseudoElementArgs),
  5302                                           &pseudoElementType);
  5303       if (pseudoElement &&
  5304           pseudoElementType != nsCSSPseudoElements::ePseudo_AnonBox) {
  5305         // Pseudo-elements other than anonymous boxes are represented with
  5306         // a special ':' combinator.
  5308         aList->mWeight += selector->CalcWeight();
  5310         selector = aList->AddSelector(':');
  5312         selector->mLowercaseTag.swap(pseudoElement);
  5313         selector->mClassList = pseudoElementArgs.forget();
  5314         selector->SetPseudoType(pseudoElementType);
  5317     else if (mToken.IsSymbol('[')) {    // [attribute
  5318       parsingStatus = ParseAttributeSelector(dataMask, *selector);
  5319       if (eSelectorParsingStatus_Error == parsingStatus) {
  5320         SkipUntil(']');
  5323     else {  // not a selector token, we're done
  5324       parsingStatus = eSelectorParsingStatus_Done;
  5325       UngetToken();
  5326       break;
  5329     if (parsingStatus != eSelectorParsingStatus_Continue) {
  5330       break;
  5333     if (! GetToken(false)) { // premature eof is ok (here!)
  5334       parsingStatus = eSelectorParsingStatus_Done;
  5335       break;
  5339   if (parsingStatus == eSelectorParsingStatus_Error) {
  5340     return false;
  5343   if (!dataMask) {
  5344     if (selector->mNext) {
  5345       REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
  5346     } else {
  5347       REPORT_UNEXPECTED(PESelectorGroupNoSelector);
  5349     return false;
  5352   if (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox) {
  5353     // We got an anonymous box pseudo-element; it must be the only
  5354     // thing in this selector group.
  5355     if (selector->mNext || !IsUniversalSelector(*selector)) {
  5356       REPORT_UNEXPECTED(PEAnonBoxNotAlone);
  5357       return false;
  5360     // Rewrite the current selector as this pseudo-element.
  5361     // It does not contribute to selector weight.
  5362     selector->mLowercaseTag.swap(pseudoElement);
  5363     selector->mClassList = pseudoElementArgs.forget();
  5364     selector->SetPseudoType(pseudoElementType);
  5365     return true;
  5368   aList->mWeight += selector->CalcWeight();
  5370   return true;
  5373 css::Declaration*
  5374 CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext)
  5376   bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
  5378   if (checkForBraces) {
  5379     if (!ExpectSymbol('{', true)) {
  5380       REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
  5381       OUTPUT_ERROR();
  5382       return nullptr;
  5385   css::Declaration* declaration = new css::Declaration();
  5386   mData.AssertInitialState();
  5387   if (declaration) {
  5388     for (;;) {
  5389       bool changed;
  5390       if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) {
  5391         if (!SkipDeclaration(checkForBraces)) {
  5392           break;
  5394         if (checkForBraces) {
  5395           if (ExpectSymbol('}', true)) {
  5396             break;
  5399         // Since the skipped declaration didn't end the block we parse
  5400         // the next declaration.
  5403     declaration->CompressFrom(&mData);
  5405   return declaration;
  5408 bool
  5409 CSSParserImpl::ParseColor(nsCSSValue& aValue)
  5411   if (!GetToken(true)) {
  5412     REPORT_UNEXPECTED_EOF(PEColorEOF);
  5413     return false;
  5416   nsCSSToken* tk = &mToken;
  5417   nscolor rgba;
  5418   switch (tk->mType) {
  5419     case eCSSToken_ID:
  5420     case eCSSToken_Hash:
  5421       // #xxyyzz
  5422       if (NS_HexToRGB(tk->mIdent, &rgba)) {
  5423         MOZ_ASSERT(tk->mIdent.Length() == 3 || tk->mIdent.Length() == 6,
  5424                    "unexpected hex color length");
  5425         nsCSSUnit unit = tk->mIdent.Length() == 3 ?
  5426                            eCSSUnit_ShortHexColor :
  5427                            eCSSUnit_HexColor;
  5428         aValue.SetIntegerColorValue(rgba, unit);
  5429         return true;
  5431       break;
  5433     case eCSSToken_Ident:
  5434       if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
  5435         aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
  5436         return true;
  5438       else {
  5439         nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
  5440         if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
  5441           int32_t value;
  5442           if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
  5443             aValue.SetIntValue(value, eCSSUnit_EnumColor);
  5444             return true;
  5448       break;
  5449     case eCSSToken_Function:
  5450       if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
  5451         // rgb ( component , component , component )
  5452         if (GetToken(true)) {
  5453           UngetToken();
  5455         if (mToken.mType == eCSSToken_Number) {
  5456           uint8_t r, g, b;
  5457           if (ParseNumberColorComponent(r, ',') &&
  5458               ParseNumberColorComponent(g, ',') &&
  5459               ParseNumberColorComponent(b, ')')) {
  5460             aValue.SetIntegerColorValue(NS_RGB(r, g, b), eCSSUnit_RGBColor);
  5461             return true;
  5463         } else {
  5464           float r, g, b;
  5465           if (ParsePercentageColorComponent(r, ',') &&
  5466               ParsePercentageColorComponent(g, ',') &&
  5467               ParsePercentageColorComponent(b, ')')) {
  5468             aValue.SetFloatColorValue(r, g, b, 1.0f,
  5469                                       eCSSUnit_PercentageRGBColor);
  5470             return true;
  5473         SkipUntil(')');
  5474         return false;
  5476       else if (mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
  5477         // rgba ( component , component , component , opacity )
  5478         if (GetToken(true)) {
  5479           UngetToken();
  5481         if (mToken.mType == eCSSToken_Number) {
  5482           uint8_t r, g, b, a;
  5483           if (ParseNumberColorComponent(r, ',') &&
  5484               ParseNumberColorComponent(g, ',') &&
  5485               ParseNumberColorComponent(b, ',') &&
  5486               ParseColorOpacity(a)) {
  5487             aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a),
  5488                                         eCSSUnit_RGBAColor);
  5489             return true;
  5491         } else {
  5492           float r, g, b, a;
  5493           if (ParsePercentageColorComponent(r, ',') &&
  5494               ParsePercentageColorComponent(g, ',') &&
  5495               ParsePercentageColorComponent(b, ',') &&
  5496               ParseColorOpacity(a)) {
  5497             aValue.SetFloatColorValue(r, g, b, a, eCSSUnit_PercentageRGBAColor);
  5498             return true;
  5501         SkipUntil(')');
  5502         return false;
  5504       else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
  5505         // hsl ( hue , saturation , lightness )
  5506         // "hue" is a number, "saturation" and "lightness" are percentages.
  5507         float h, s, l;
  5508         if (ParseHSLColor(h, s, l, ')')) {
  5509           aValue.SetFloatColorValue(h, s, l, 1.0f, eCSSUnit_HSLColor);
  5510           return true;
  5512         SkipUntil(')');
  5513         return false;
  5515       else if (mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
  5516         // hsla ( hue , saturation , lightness , opacity )
  5517         // "hue" is a number, "saturation" and "lightness" are percentages,
  5518         // "opacity" is a number.
  5519         float h, s, l, a;
  5520         if (ParseHSLColor(h, s, l, ',') &&
  5521             ParseColorOpacity(a)) {
  5522           aValue.SetFloatColorValue(h, s, l, a, eCSSUnit_HSLAColor);
  5523           return true;
  5525         SkipUntil(')');
  5526         return false;
  5528       break;
  5529     default:
  5530       break;
  5533   // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
  5534   if (mHashlessColorQuirk) {
  5535     // - If the string starts with 'a-f', the nsCSSScanner builds the
  5536     //   token as a eCSSToken_Ident and we can parse the string as a
  5537     //   'xxyyzz' RGB color.
  5538     // - If it only contains '0-9' digits, the token is a
  5539     //   eCSSToken_Number and it must be converted back to a 6
  5540     //   characters string to be parsed as a RGB color.
  5541     // - If it starts with '0-9' and contains any 'a-f', the token is a
  5542     //   eCSSToken_Dimension, the mNumber part must be converted back to
  5543     //   a string and the mIdent part must be appended to that string so
  5544     //   that the resulting string has 6 characters.
  5545     // Note: This is a hack for Nav compatibility.  Do not attempt to
  5546     // simplify it by hacking into the ncCSSScanner.  This would be very
  5547     // bad.
  5548     nsAutoString str;
  5549     char buffer[20];
  5550     switch (tk->mType) {
  5551       case eCSSToken_Ident:
  5552         str.Assign(tk->mIdent);
  5553         break;
  5555       case eCSSToken_Number:
  5556         if (tk->mIntegerValid) {
  5557           PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
  5558           str.AssignWithConversion(buffer);
  5560         break;
  5562       case eCSSToken_Dimension:
  5563         if (tk->mIdent.Length() <= 6) {
  5564           PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
  5565           nsAutoString temp;
  5566           temp.AssignWithConversion(buffer);
  5567           temp.Right(str, 6 - tk->mIdent.Length());
  5568           str.Append(tk->mIdent);
  5570         break;
  5571       default:
  5572         // There is a whole bunch of cases that are
  5573         // not handled by this switch.  Ignore them.
  5574         break;
  5576     if (NS_HexToRGB(str, &rgba)) {
  5577       aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor);
  5578       return true;
  5582   // It's not a color
  5583   REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
  5584   UngetToken();
  5585   return false;
  5588 bool
  5589 CSSParserImpl::ParseNumberColorComponent(uint8_t& aComponent, char aStop)
  5591   if (!GetToken(true)) {
  5592     REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
  5593     return false;
  5596   if (mToken.mType != eCSSToken_Number || !mToken.mIntegerValid) {
  5597     REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
  5598     UngetToken();
  5599     return false;
  5602   float value = mToken.mNumber;
  5603   if (value < 0.0f) value = 0.0f;
  5604   if (value > 255.0f) value = 255.0f;
  5606   if (ExpectSymbol(aStop, true)) {
  5607     aComponent = NSToIntRound(value);
  5608     return true;
  5610   REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
  5611   return false;
  5614 bool
  5615 CSSParserImpl::ParsePercentageColorComponent(float& aComponent, char aStop)
  5617   if (!GetToken(true)) {
  5618     REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
  5619     return false;
  5622   if (mToken.mType != eCSSToken_Percentage) {
  5623     REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
  5624     UngetToken();
  5625     return false;
  5628   float value = mToken.mNumber;
  5629   if (value < 0.0f) value = 0.0f;
  5630   if (value > 1.0f) value = 1.0f;
  5632   if (ExpectSymbol(aStop, true)) {
  5633     aComponent = value;
  5634     return true;
  5636   REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
  5637   return false;
  5641 bool
  5642 CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
  5643                              char aStop)
  5645   float h, s, l;
  5647   // Get the hue
  5648   if (!GetToken(true)) {
  5649     REPORT_UNEXPECTED_EOF(PEColorHueEOF);
  5650     return false;
  5652   if (mToken.mType != eCSSToken_Number) {
  5653     REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
  5654     UngetToken();
  5655     return false;
  5657   h = mToken.mNumber;
  5658   h /= 360.0f;
  5659   // hue values are wraparound
  5660   h = h - floor(h);
  5662   if (!ExpectSymbol(',', true)) {
  5663     REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
  5664     return false;
  5667   // Get the saturation
  5668   if (!GetToken(true)) {
  5669     REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
  5670     return false;
  5672   if (mToken.mType != eCSSToken_Percentage) {
  5673     REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
  5674     UngetToken();
  5675     return false;
  5677   s = mToken.mNumber;
  5678   if (s < 0.0f) s = 0.0f;
  5679   if (s > 1.0f) s = 1.0f;
  5681   if (!ExpectSymbol(',', true)) {
  5682     REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
  5683     return false;
  5686   // Get the lightness
  5687   if (!GetToken(true)) {
  5688     REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
  5689     return false;
  5691   if (mToken.mType != eCSSToken_Percentage) {
  5692     REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
  5693     UngetToken();
  5694     return false;
  5696   l = mToken.mNumber;
  5697   if (l < 0.0f) l = 0.0f;
  5698   if (l > 1.0f) l = 1.0f;
  5700   if (ExpectSymbol(aStop, true)) {
  5701     aHue = h;
  5702     aSaturation = s;
  5703     aLightness = l;
  5704     return true;
  5707   REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
  5708   return false;
  5712 bool
  5713 CSSParserImpl::ParseColorOpacity(uint8_t& aOpacity)
  5715   float floatOpacity;
  5716   if (!ParseColorOpacity(floatOpacity)) {
  5717     return false;
  5720   uint8_t value = nsStyleUtil::FloatToColorComponent(floatOpacity);
  5721   // Need to compare to something slightly larger
  5722   // than 0.5 due to floating point inaccuracies.
  5723   NS_ASSERTION(fabs(255.0f*mToken.mNumber - value) <= 0.51f,
  5724                "FloatToColorComponent did something weird");
  5726   aOpacity = value;
  5727   return true;
  5730 bool
  5731 CSSParserImpl::ParseColorOpacity(float& aOpacity)
  5733   if (!GetToken(true)) {
  5734     REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
  5735     return false;
  5738   if (mToken.mType != eCSSToken_Number) {
  5739     REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
  5740     UngetToken();
  5741     return false;
  5744   if (!ExpectSymbol(')', true)) {
  5745     REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
  5746     return false;
  5749   if (mToken.mNumber < 0.0f) {
  5750     mToken.mNumber = 0.0f;
  5751   } else if (mToken.mNumber > 1.0f) {
  5752     mToken.mNumber = 1.0f;
  5755   aOpacity = mToken.mNumber;
  5756   return true;
  5759 #ifdef MOZ_XUL
  5760 bool
  5761 CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs)
  5763   // The argument to a tree pseudo-element is a sequence of identifiers
  5764   // that are either space- or comma-separated.  (Was the intent to
  5765   // allow only comma-separated?  That's not what was done.)
  5766   nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass
  5768   while (!ExpectSymbol(')', true)) {
  5769     if (!GetToken(true)) {
  5770       return false;
  5772     if (eCSSToken_Ident == mToken.mType) {
  5773       fakeSelector.AddClass(mToken.mIdent);
  5775     else if (!mToken.IsSymbol(',')) {
  5776       UngetToken();
  5777       SkipUntil(')');
  5778       return false;
  5781   *aPseudoElementArgs = fakeSelector.mClassList;
  5782   fakeSelector.mClassList = nullptr;
  5783   return true;
  5785 #endif
  5787 //----------------------------------------------------------------------
  5789 bool
  5790 CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
  5791                                 uint32_t aFlags,
  5792                                 bool aMustCallValueAppended,
  5793                                 bool* aChanged,
  5794                                 nsCSSContextType aContext)
  5796   NS_PRECONDITION(aContext == eCSSContext_General ||
  5797                   aContext == eCSSContext_Page,
  5798                   "Must be page or general context");
  5800   bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
  5802   mTempData.AssertInitialState();
  5804   // Get property name
  5805   nsCSSToken* tk = &mToken;
  5806   nsAutoString propertyName;
  5807   for (;;) {
  5808     if (!GetToken(true)) {
  5809       if (checkForBraces) {
  5810         REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
  5812       return false;
  5814     if (eCSSToken_Ident == tk->mType) {
  5815       propertyName = tk->mIdent;
  5816       // grab the ident before the ExpectSymbol trashes the token
  5817       if (!ExpectSymbol(':', true)) {
  5818         REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
  5819         REPORT_UNEXPECTED(PEDeclDropped);
  5820         OUTPUT_ERROR();
  5821         return false;
  5823       break;
  5825     if (tk->IsSymbol(';')) {
  5826       // dangling semicolons are skipped
  5827       continue;
  5830     if (!tk->IsSymbol('}')) {
  5831       REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
  5832       REPORT_UNEXPECTED(PEDeclSkipped);
  5833       OUTPUT_ERROR();
  5835       if (eCSSToken_AtKeyword == tk->mType) {
  5836         SkipAtRule(checkForBraces);
  5837         return true;  // Not a declaration, but don't skip until ';'
  5840     // Not a declaration...
  5841     UngetToken();
  5842     return false;
  5845   // Don't report property parse errors if we're inside a failing @supports
  5846   // rule.
  5847   nsAutoSuppressErrors suppressErrors(this, mInFailingSupportsRule);
  5849   // Information about a parsed non-custom property.
  5850   nsCSSProperty propID;
  5852   // Information about a parsed custom property.
  5853   CSSVariableDeclarations::Type variableType;
  5854   nsString variableValue;
  5856   // Check if the property name is a custom property.
  5857   bool customProperty = nsLayoutUtils::CSSVariablesEnabled() &&
  5858                         nsCSSProps::IsCustomPropertyName(propertyName) &&
  5859                         aContext == eCSSContext_General;
  5861   if (customProperty) {
  5862     if (!ParseVariableDeclaration(&variableType, variableValue)) {
  5863       REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
  5864       REPORT_UNEXPECTED(PEDeclDropped);
  5865       OUTPUT_ERROR();
  5866       return false;
  5868   } else {
  5869     // Map property name to its ID.
  5870     propID = LookupEnabledProperty(propertyName);
  5871     if (eCSSProperty_UNKNOWN == propID ||
  5872         (aContext == eCSSContext_Page &&
  5873          !nsCSSProps::PropHasFlags(propID,
  5874                                    CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property
  5875       if (!NonMozillaVendorIdentifier(propertyName)) {
  5876         REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName);
  5877         REPORT_UNEXPECTED(PEDeclDropped);
  5878         OUTPUT_ERROR();
  5880       return false;
  5882     // Then parse the property.
  5883     if (!ParseProperty(propID)) {
  5884       // XXX Much better to put stuff in the value parsers instead...
  5885       REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
  5886       REPORT_UNEXPECTED(PEDeclDropped);
  5887       OUTPUT_ERROR();
  5888       mTempData.ClearProperty(propID);
  5889       mTempData.AssertInitialState();
  5890       return false;
  5894   CLEAR_ERROR();
  5896   // Look for "!important".
  5897   PriorityParsingStatus status;
  5898   if ((aFlags & eParseDeclaration_AllowImportant) != 0) {
  5899     status = ParsePriority();
  5900   } else {
  5901     status = ePriority_None;
  5904   // Look for a semicolon or close brace.
  5905   if (status != ePriority_Error) {
  5906     if (!GetToken(true)) {
  5907       // EOF is always ok
  5908     } else if (mToken.IsSymbol(';')) {
  5909       // semicolon is always ok
  5910     } else if (mToken.IsSymbol('}')) {
  5911       // brace is ok if checkForBraces, but don't eat it
  5912       UngetToken();
  5913       if (!checkForBraces) {
  5914         status = ePriority_Error;
  5916     } else {
  5917       UngetToken();
  5918       status = ePriority_Error;
  5922   if (status == ePriority_Error) {
  5923     if (checkForBraces) {
  5924       REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
  5925     } else {
  5926       REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
  5928     REPORT_UNEXPECTED(PEDeclDropped);
  5929     OUTPUT_ERROR();
  5930     if (!customProperty) {
  5931       mTempData.ClearProperty(propID);
  5933     mTempData.AssertInitialState();
  5934     return false;
  5937   if (customProperty) {
  5938     MOZ_ASSERT(Substring(propertyName, 0,
  5939                          CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
  5940     // remove '--'
  5941     nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
  5942     aDeclaration->AddVariableDeclaration(varName, variableType, variableValue,
  5943                                          status == ePriority_Important, false);
  5944   } else {
  5945     *aChanged |= mData.TransferFromBlock(mTempData, propID,
  5946                                          status == ePriority_Important,
  5947                                          false, aMustCallValueAppended,
  5948                                          aDeclaration);
  5951   return true;
  5954 static const nsCSSProperty kBorderTopIDs[] = {
  5955   eCSSProperty_border_top_width,
  5956   eCSSProperty_border_top_style,
  5957   eCSSProperty_border_top_color
  5958 };
  5959 static const nsCSSProperty kBorderRightIDs[] = {
  5960   eCSSProperty_border_right_width_value,
  5961   eCSSProperty_border_right_style_value,
  5962   eCSSProperty_border_right_color_value,
  5963   eCSSProperty_border_right_width,
  5964   eCSSProperty_border_right_style,
  5965   eCSSProperty_border_right_color
  5966 };
  5967 static const nsCSSProperty kBorderBottomIDs[] = {
  5968   eCSSProperty_border_bottom_width,
  5969   eCSSProperty_border_bottom_style,
  5970   eCSSProperty_border_bottom_color
  5971 };
  5972 static const nsCSSProperty kBorderLeftIDs[] = {
  5973   eCSSProperty_border_left_width_value,
  5974   eCSSProperty_border_left_style_value,
  5975   eCSSProperty_border_left_color_value,
  5976   eCSSProperty_border_left_width,
  5977   eCSSProperty_border_left_style,
  5978   eCSSProperty_border_left_color
  5979 };
  5980 static const nsCSSProperty kBorderStartIDs[] = {
  5981   eCSSProperty_border_start_width_value,
  5982   eCSSProperty_border_start_style_value,
  5983   eCSSProperty_border_start_color_value,
  5984   eCSSProperty_border_start_width,
  5985   eCSSProperty_border_start_style,
  5986   eCSSProperty_border_start_color
  5987 };
  5988 static const nsCSSProperty kBorderEndIDs[] = {
  5989   eCSSProperty_border_end_width_value,
  5990   eCSSProperty_border_end_style_value,
  5991   eCSSProperty_border_end_color_value,
  5992   eCSSProperty_border_end_width,
  5993   eCSSProperty_border_end_style,
  5994   eCSSProperty_border_end_color
  5995 };
  5996 static const nsCSSProperty kColumnRuleIDs[] = {
  5997   eCSSProperty__moz_column_rule_width,
  5998   eCSSProperty__moz_column_rule_style,
  5999   eCSSProperty__moz_column_rule_color
  6000 };
  6002 bool
  6003 CSSParserImpl::ParseEnum(nsCSSValue& aValue,
  6004                          const KTableValue aKeywordTable[])
  6006   nsSubstring* ident = NextIdent();
  6007   if (nullptr == ident) {
  6008     return false;
  6010   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
  6011   if (eCSSKeyword_UNKNOWN < keyword) {
  6012     int32_t value;
  6013     if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
  6014       aValue.SetIntValue(value, eCSSUnit_Enumerated);
  6015       return true;
  6019   // Put the unknown identifier back and return
  6020   UngetToken();
  6021   return false;
  6025 struct UnitInfo {
  6026   char name[6];  // needs to be long enough for the longest unit, with
  6027                  // terminating null.
  6028   uint32_t length;
  6029   nsCSSUnit unit;
  6030   int32_t type;
  6031 };
  6033 #define STR_WITH_LEN(_str) \
  6034   _str, sizeof(_str) - 1
  6036 const UnitInfo UnitData[] = {
  6037   { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
  6038   { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
  6039   { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
  6040   { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
  6041   { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
  6042   { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
  6043   { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
  6044   { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH },
  6045   { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
  6046   { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH },
  6047   { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH },
  6048   { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH },
  6049   { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH },
  6050   { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH },
  6051   { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
  6052   { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
  6053   { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
  6054   { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
  6055   { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE },
  6056   { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
  6057   { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
  6058   { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
  6059   { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
  6060 };
  6062 #undef STR_WITH_LEN
  6064 bool
  6065 CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
  6066                                   int32_t aVariantMask,
  6067                                   float aNumber,
  6068                                   const nsString& aUnit)
  6070   nsCSSUnit units;
  6071   int32_t   type = 0;
  6072   if (!aUnit.IsEmpty()) {
  6073     uint32_t i;
  6074     for (i = 0; i < ArrayLength(UnitData); ++i) {
  6075       if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
  6076                                      UnitData[i].length)) {
  6077         units = UnitData[i].unit;
  6078         type = UnitData[i].type;
  6079         break;
  6083     if (i == ArrayLength(UnitData)) {
  6084       // Unknown unit
  6085       return false;
  6088     if (!mViewportUnitsEnabled &&
  6089         (eCSSUnit_ViewportWidth == units  ||
  6090          eCSSUnit_ViewportHeight == units ||
  6091          eCSSUnit_ViewportMin == units    ||
  6092          eCSSUnit_ViewportMax == units)) {
  6093       // Viewport units aren't allowed right now, probably because we're
  6094       // inside an @page declaration. Fail.
  6095       return false;
  6097   } else {
  6098     // Must be a zero number...
  6099     NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
  6100     if ((VARIANT_LENGTH & aVariantMask) != 0) {
  6101       units = eCSSUnit_Pixel;
  6102       type = VARIANT_LENGTH;
  6104     else if ((VARIANT_ANGLE & aVariantMask) != 0) {
  6105       NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE,
  6106                    "must have allowed zero angle");
  6107       units = eCSSUnit_Degree;
  6108       type = VARIANT_ANGLE;
  6110     else {
  6111       NS_ERROR("Variant mask does not include dimension; why were we called?");
  6112       return false;
  6115   if ((type & aVariantMask) != 0) {
  6116     aValue.SetFloatValue(aNumber, units);
  6117     return true;
  6119   return false;
  6122 // Note that this does include VARIANT_CALC, which is numeric.  This is
  6123 // because calc() parsing, as proposed, drops range restrictions inside
  6124 // the calc() expression and clamps the result of the calculation to the
  6125 // range.
  6126 #define VARIANT_ALL_NONNUMERIC \
  6127   VARIANT_KEYWORD | \
  6128   VARIANT_COLOR | \
  6129   VARIANT_URL | \
  6130   VARIANT_STRING | \
  6131   VARIANT_COUNTER | \
  6132   VARIANT_ATTR | \
  6133   VARIANT_IDENTIFIER | \
  6134   VARIANT_IDENTIFIER_NO_INHERIT | \
  6135   VARIANT_AUTO | \
  6136   VARIANT_INHERIT | \
  6137   VARIANT_NONE | \
  6138   VARIANT_NORMAL | \
  6139   VARIANT_SYSFONT | \
  6140   VARIANT_GRADIENT | \
  6141   VARIANT_TIMING_FUNCTION | \
  6142   VARIANT_ALL | \
  6143   VARIANT_CALC | \
  6144   VARIANT_OPENTYPE_SVG_KEYWORD
  6146 // Note that callers passing VARIANT_CALC in aVariantMask will get
  6147 // full-range parsing inside the calc() expression, and the code that
  6148 // computes the calc will be required to clamp the resulting value to an
  6149 // appropriate range.
  6150 bool
  6151 CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue,
  6152                                        int32_t aVariantMask,
  6153                                        const KTableValue aKeywordTable[])
  6155   // The variant mask must only contain non-numeric variants or the ones
  6156   // that we specifically handle.
  6157   NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
  6158                                       VARIANT_NUMBER |
  6159                                       VARIANT_LENGTH |
  6160                                       VARIANT_PERCENT |
  6161                                       VARIANT_INTEGER)) == 0,
  6162                     "need to update code below to handle additional variants");
  6164   if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
  6165     if (eCSSUnit_Number == aValue.GetUnit() ||
  6166         aValue.IsLengthUnit()){
  6167       if (aValue.GetFloatValue() < 0) {
  6168         UngetToken();
  6169         return false;
  6172     else if (aValue.GetUnit() == eCSSUnit_Percent) {
  6173       if (aValue.GetPercentValue() < 0) {
  6174         UngetToken();
  6175         return false;
  6177     } else if (aValue.GetUnit() == eCSSUnit_Integer) {
  6178       if (aValue.GetIntValue() < 0) {
  6179         UngetToken();
  6180         return false;
  6183     return true;
  6185   return false;
  6188 // Note that callers passing VARIANT_CALC in aVariantMask will get
  6189 // full-range parsing inside the calc() expression, and the code that
  6190 // computes the calc will be required to clamp the resulting value to an
  6191 // appropriate range.
  6192 bool
  6193 CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue,
  6194                                        int32_t aVariantMask,
  6195                                        const KTableValue aKeywordTable[])
  6197   // The variant mask must only contain non-numeric variants or the ones
  6198   // that we specifically handle.
  6199   NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
  6200                                       VARIANT_NUMBER |
  6201                                       VARIANT_INTEGER)) == 0,
  6202                     "need to update code below to handle additional variants");
  6204   if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
  6205     if (aValue.GetUnit() == eCSSUnit_Integer) {
  6206       if (aValue.GetIntValue() < 1) {
  6207         UngetToken();
  6208         return false;
  6210     } else if (eCSSUnit_Number == aValue.GetUnit()) {
  6211       if (aValue.GetFloatValue() < 1.0f) {
  6212         UngetToken();
  6213         return false;
  6216     return true;
  6218   return false;
  6221 // Assigns to aValue iff it returns true.
  6222 bool
  6223 CSSParserImpl::ParseVariant(nsCSSValue& aValue,
  6224                             int32_t aVariantMask,
  6225                             const KTableValue aKeywordTable[])
  6227   NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
  6228                !(aVariantMask & VARIANT_NUMBER),
  6229                "can't distinguish colors from numbers");
  6230   NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
  6231                !(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)),
  6232                "can't distinguish colors from lengths");
  6233   NS_ASSERTION(!(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)) ||
  6234                !(aVariantMask & VARIANT_NUMBER),
  6235                "can't distinguish lengths from numbers");
  6236   NS_ABORT_IF_FALSE(!(aVariantMask & VARIANT_IDENTIFIER) ||
  6237                     !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT),
  6238                     "must not set both VARIANT_IDENTIFIER and "
  6239                     "VARIANT_IDENTIFIER_NO_INHERIT");
  6241   if (!GetToken(true)) {
  6242     return false;
  6244   nsCSSToken* tk = &mToken;
  6245   if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
  6246       (eCSSToken_Ident == tk->mType)) {
  6247     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
  6248     if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
  6249       if ((aVariantMask & VARIANT_AUTO) != 0) {
  6250         if (eCSSKeyword_auto == keyword) {
  6251           aValue.SetAutoValue();
  6252           return true;
  6255       if ((aVariantMask & VARIANT_INHERIT) != 0) {
  6256         // XXX Should we check IsParsingCompoundProperty, or do all
  6257         // callers handle it?  (Not all callers set it, though, since
  6258         // they want the quirks that are disabled by setting it.)
  6260         // IMPORTANT: If new keywords are added here,
  6261         // they probably need to be added in ParseCustomIdent as well.
  6262         if (eCSSKeyword_inherit == keyword) {
  6263           aValue.SetInheritValue();
  6264           return true;
  6266         else if (eCSSKeyword_initial == keyword) {
  6267           aValue.SetInitialValue();
  6268           return true;
  6270         else if (eCSSKeyword_unset == keyword &&
  6271                  nsLayoutUtils::UnsetValueEnabled()) {
  6272           aValue.SetUnsetValue();
  6273           return true;
  6276       if ((aVariantMask & VARIANT_NONE) != 0) {
  6277         if (eCSSKeyword_none == keyword) {
  6278           aValue.SetNoneValue();
  6279           return true;
  6282       if ((aVariantMask & VARIANT_ALL) != 0) {
  6283         if (eCSSKeyword_all == keyword) {
  6284           aValue.SetAllValue();
  6285           return true;
  6288       if ((aVariantMask & VARIANT_NORMAL) != 0) {
  6289         if (eCSSKeyword_normal == keyword) {
  6290           aValue.SetNormalValue();
  6291           return true;
  6294       if ((aVariantMask & VARIANT_SYSFONT) != 0) {
  6295         if (eCSSKeyword__moz_use_system_font == keyword &&
  6296             !IsParsingCompoundProperty()) {
  6297           aValue.SetSystemFontValue();
  6298           return true;
  6301       if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) {
  6302         static bool sOpentypeSVGEnabled;
  6303         static bool sOpentypeSVGEnabledCached = false;
  6304         if (!sOpentypeSVGEnabledCached) {
  6305           sOpentypeSVGEnabledCached = true;
  6306           Preferences::AddBoolVarCache(&sOpentypeSVGEnabled,
  6307                                        "gfx.font_rendering.opentype_svg.enabled");
  6309         if (sOpentypeSVGEnabled) {
  6310           aVariantMask |= VARIANT_KEYWORD;
  6313       if ((aVariantMask & VARIANT_KEYWORD) != 0) {
  6314         int32_t value;
  6315         if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
  6316           aValue.SetIntValue(value, eCSSUnit_Enumerated);
  6317           return true;
  6322   // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or
  6323   // VARIANT_ZERO_ANGLE.
  6324   if (((aVariantMask & VARIANT_NUMBER) != 0) &&
  6325       (eCSSToken_Number == tk->mType)) {
  6326     aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
  6327     return true;
  6329   if (((aVariantMask & VARIANT_INTEGER) != 0) &&
  6330       (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
  6331     aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
  6332     return true;
  6334   if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
  6335                         VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
  6336        eCSSToken_Dimension == tk->mType) ||
  6337       ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
  6338        eCSSToken_Number == tk->mType &&
  6339        tk->mNumber == 0.0f)) {
  6340     if (((aVariantMask & VARIANT_POSITIVE_DIMENSION) != 0 && 
  6341          tk->mNumber <= 0.0) ||
  6342         ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 && 
  6343          tk->mNumber < 0.0)) {
  6344         UngetToken();
  6345         return false;
  6347     if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
  6348       return true;
  6350     // Put the token back; we didn't parse it, so we shouldn't consume it
  6351     UngetToken();
  6352     return false;
  6354   if (((aVariantMask & VARIANT_PERCENT) != 0) &&
  6355       (eCSSToken_Percentage == tk->mType)) {
  6356     aValue.SetPercentValue(tk->mNumber);
  6357     return true;
  6359   if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px
  6360     if (((aVariantMask & VARIANT_LENGTH) != 0) &&
  6361         (eCSSToken_Number == tk->mType)) {
  6362       aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
  6363       return true;
  6367   if (IsSVGMode() && !IsParsingCompoundProperty()) {
  6368     // STANDARD: SVG Spec states that lengths and coordinates can be unitless
  6369     // in which case they default to user-units (1 px = 1 user unit)
  6370     if (((aVariantMask & VARIANT_LENGTH) != 0) &&
  6371         (eCSSToken_Number == tk->mType)) {
  6372       aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
  6373       return true;
  6377   if (((aVariantMask & VARIANT_URL) != 0) &&
  6378       eCSSToken_URL == tk->mType) {
  6379     SetValueToURL(aValue, tk->mIdent);
  6380     return true;
  6382   if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
  6383       eCSSToken_Function == tk->mType) {
  6384     // a generated gradient
  6385     nsDependentString tmp(tk->mIdent, 0);
  6386     bool isLegacy = false;
  6387     if (StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) {
  6388       tmp.Rebind(tmp, 5);
  6389       isLegacy = true;
  6391     bool isRepeating = false;
  6392     if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) {
  6393       tmp.Rebind(tmp, 10);
  6394       isRepeating = true;
  6397     if (tmp.LowerCaseEqualsLiteral("linear-gradient")) {
  6398       return ParseLinearGradient(aValue, isRepeating, isLegacy);
  6400     if (tmp.LowerCaseEqualsLiteral("radial-gradient")) {
  6401       return ParseRadialGradient(aValue, isRepeating, isLegacy);
  6404   if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
  6405       eCSSToken_Function == tk->mType &&
  6406       tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
  6407     return ParseImageRect(aValue);
  6409   if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
  6410       eCSSToken_Function == tk->mType &&
  6411       tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) {
  6412     return ParseElement(aValue);
  6414   if ((aVariantMask & VARIANT_COLOR) != 0) {
  6415     if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
  6416         (eCSSToken_ID == tk->mType) ||
  6417         (eCSSToken_Hash == tk->mType) ||
  6418         (eCSSToken_Ident == tk->mType) ||
  6419         ((eCSSToken_Function == tk->mType) &&
  6420          (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
  6421           tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
  6422           tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
  6423           tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
  6425       // Put token back so that parse color can get it
  6426       UngetToken();
  6427       if (ParseColor(aValue)) {
  6428         return true;
  6430       return false;
  6433   if (((aVariantMask & VARIANT_STRING) != 0) &&
  6434       (eCSSToken_String == tk->mType)) {
  6435     nsAutoString  buffer;
  6436     buffer.Append(tk->mIdent);
  6437     aValue.SetStringValue(buffer, eCSSUnit_String);
  6438     return true;
  6440   if (((aVariantMask &
  6441         (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
  6442       (eCSSToken_Ident == tk->mType) &&
  6443       ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
  6444        !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
  6445          tk->mIdent.LowerCaseEqualsLiteral("initial") ||
  6446          (tk->mIdent.LowerCaseEqualsLiteral("unset") &&
  6447           nsLayoutUtils::UnsetValueEnabled())))) {
  6448     aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
  6449     return true;
  6451   if (((aVariantMask & VARIANT_COUNTER) != 0) &&
  6452       (eCSSToken_Function == tk->mType) &&
  6453       (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
  6454        tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
  6455     return ParseCounter(aValue);
  6457   if (((aVariantMask & VARIANT_ATTR) != 0) &&
  6458       (eCSSToken_Function == tk->mType) &&
  6459       tk->mIdent.LowerCaseEqualsLiteral("attr")) {
  6460     if (!ParseAttr(aValue)) {
  6461       SkipUntil(')');
  6462       return false;
  6464     return true;
  6466   if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
  6467       (eCSSToken_Function == tk->mType)) {
  6468     if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
  6469       if (!ParseTransitionTimingFunctionValues(aValue)) {
  6470         SkipUntil(')');
  6471         return false;
  6473       return true;
  6475     if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
  6476       if (!ParseTransitionStepTimingFunctionValues(aValue)) {
  6477         SkipUntil(')');
  6478         return false;
  6480       return true;
  6483   if ((aVariantMask & VARIANT_CALC) &&
  6484       (eCSSToken_Function == tk->mType) &&
  6485       (tk->mIdent.LowerCaseEqualsLiteral("calc") ||
  6486        tk->mIdent.LowerCaseEqualsLiteral("-moz-calc"))) {
  6487     // calc() currently allows only lengths and percents inside it.
  6488     return ParseCalc(aValue, aVariantMask & VARIANT_LP);
  6491   UngetToken();
  6492   return false;
  6495 bool
  6496 CSSParserImpl::ParseCustomIdent(nsCSSValue& aValue,
  6497                                 const nsAutoString& aIdentValue,
  6498                                 const nsCSSKeyword aExcludedKeywords[],
  6499                                 const nsCSSProps::KTableValue aPropertyKTable[])
  6501   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aIdentValue);
  6502   if (keyword == eCSSKeyword_UNKNOWN) {
  6503     // Fast path for identifiers that are not known CSS keywords:
  6504     aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
  6505     return true;
  6507   if (keyword == eCSSKeyword_inherit ||
  6508       keyword == eCSSKeyword_initial ||
  6509       keyword == eCSSKeyword_unset ||
  6510       keyword == eCSSKeyword_default ||
  6511       (aPropertyKTable &&
  6512         nsCSSProps::FindIndexOfKeyword(keyword, aPropertyKTable) >= 0)) {
  6513     return false;
  6515   if (aExcludedKeywords) {
  6516     for (uint32_t i = 0;; i++) {
  6517       nsCSSKeyword excludedKeyword = aExcludedKeywords[i];
  6518       if (excludedKeyword == eCSSKeyword_UNKNOWN) {
  6519         break;
  6521       if (excludedKeyword == keyword) {
  6522         return false;
  6526   aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
  6527   return true;
  6530 bool
  6531 CSSParserImpl::ParseCounter(nsCSSValue& aValue)
  6533   nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
  6534                     eCSSUnit_Counter : eCSSUnit_Counters);
  6536   // A non-iterative for loop to break out when an error occurs.
  6537   for (;;) {
  6538     if (!GetToken(true)) {
  6539       break;
  6541     if (eCSSToken_Ident != mToken.mType) {
  6542       UngetToken();
  6543       break;
  6546     nsRefPtr<nsCSSValue::Array> val =
  6547       nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
  6549     val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
  6551     if (eCSSUnit_Counters == unit) {
  6552       // must have a comma and then a separator string
  6553       if (!ExpectSymbol(',', true) || !GetToken(true)) {
  6554         break;
  6556       if (eCSSToken_String != mToken.mType) {
  6557         UngetToken();
  6558         break;
  6560       val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
  6563     // get optional type
  6564     int32_t type = NS_STYLE_LIST_STYLE_DECIMAL;
  6565     if (ExpectSymbol(',', true)) {
  6566       if (!GetToken(true)) {
  6567         break;
  6569       nsCSSKeyword keyword;
  6570       if (eCSSToken_Ident != mToken.mType ||
  6571           (keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) ==
  6572             eCSSKeyword_UNKNOWN ||
  6573           !nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable,
  6574                                    type)) {
  6575         UngetToken();
  6576         break;
  6580     int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1;
  6581     val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
  6583     if (!ExpectSymbol(')', true)) {
  6584       break;
  6587     aValue.SetArrayValue(val, unit);
  6588     return true;
  6591   SkipUntil(')');
  6592   return false;
  6595 bool
  6596 CSSParserImpl::ParseAttr(nsCSSValue& aValue)
  6598   if (!GetToken(true)) {
  6599     return false;
  6602   nsAutoString attr;
  6603   if (eCSSToken_Ident == mToken.mType) {  // attr name or namespace
  6604     nsAutoString  holdIdent(mToken.mIdent);
  6605     if (ExpectSymbol('|', false)) {  // namespace
  6606       int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent);
  6607       if (nameSpaceID == kNameSpaceID_Unknown) {
  6608         return false;
  6610       attr.AppendInt(nameSpaceID, 10);
  6611       attr.Append(char16_t('|'));
  6612       if (! GetToken(false)) {
  6613         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
  6614         return false;
  6616       if (eCSSToken_Ident == mToken.mType) {
  6617         attr.Append(mToken.mIdent);
  6619       else {
  6620         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
  6621         UngetToken();
  6622         return false;
  6625     else {  // no namespace
  6626       attr = holdIdent;
  6629   else if (mToken.IsSymbol('*')) {  // namespace wildcard
  6630     // Wildcard namespace makes no sense here and is not allowed
  6631     REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
  6632     UngetToken();
  6633     return false;
  6635   else if (mToken.IsSymbol('|')) {  // explicit NO namespace
  6636     if (! GetToken(false)) {
  6637       REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
  6638       return false;
  6640     if (eCSSToken_Ident == mToken.mType) {
  6641       attr.Append(mToken.mIdent);
  6643     else {
  6644       REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
  6645       UngetToken();
  6646       return false;
  6649   else {
  6650     REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
  6651     UngetToken();
  6652     return false;
  6654   if (!ExpectSymbol(')', true)) {
  6655     return false;
  6657   aValue.SetStringValue(attr, eCSSUnit_Attr);
  6658   return true;
  6661 bool
  6662 CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL)
  6664   if (!mSheetPrincipal) {
  6665     NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
  6666                   "origin principal");
  6667     return false;
  6670   nsRefPtr<nsStringBuffer> buffer(nsCSSValue::BufferFromString(aURL));
  6672   // Note: urlVal retains its own reference to |buffer|.
  6673   mozilla::css::URLValue *urlVal =
  6674     new mozilla::css::URLValue(buffer, mBaseURI, mSheetURI, mSheetPrincipal);
  6675   aValue.SetURLValue(urlVal);
  6676   return true;
  6679 /**
  6680  * Parse the image-orientation property, which has the grammar:
  6681  * <angle> flip? | flip | from-image
  6682  */
  6683 bool
  6684 CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue)
  6686   if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
  6687     // 'inherit', 'initial' and 'unset' must be alone
  6688     return true;
  6691   // Check for an angle with optional 'flip'.
  6692   nsCSSValue angle;
  6693   if (ParseVariant(angle, VARIANT_ANGLE, nullptr)) {
  6694     nsCSSValue flip;
  6696     if (ParseVariant(flip, VARIANT_KEYWORD, nsCSSProps::kImageOrientationFlipKTable)) {
  6697       nsRefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(2);
  6698       array->Item(0) = angle;
  6699       array->Item(1) = flip;
  6700       aValue.SetArrayValue(array, eCSSUnit_Array);
  6701     } else {
  6702       aValue = angle;
  6705     return true;
  6708   // The remaining possibilities (bare 'flip' and 'from-image') are both
  6709   // keywords, so we can handle them at the same time.
  6710   nsCSSValue keyword;
  6711   if (ParseVariant(keyword, VARIANT_KEYWORD, nsCSSProps::kImageOrientationKTable)) {
  6712     aValue = keyword;
  6713     return true;
  6716   // All possibilities failed.
  6717   return false;
  6720 /**
  6721  * Parse the arguments of -moz-image-rect() function.
  6722  * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
  6723  */
  6724 bool
  6725 CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
  6727   // A non-iterative for loop to break out when an error occurs.
  6728   for (;;) {
  6729     nsCSSValue newFunction;
  6730     static const uint32_t kNumArgs = 5;
  6731     nsCSSValue::Array* func =
  6732       newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs);
  6734     // func->Item(0) is reserved for the function name.
  6735     nsCSSValue& url    = func->Item(1);
  6736     nsCSSValue& top    = func->Item(2);
  6737     nsCSSValue& right  = func->Item(3);
  6738     nsCSSValue& bottom = func->Item(4);
  6739     nsCSSValue& left   = func->Item(5);
  6741     nsAutoString urlString;
  6742     if (!ParseURLOrString(urlString) ||
  6743         !SetValueToURL(url, urlString) ||
  6744         !ExpectSymbol(',', true)) {
  6745       break;
  6748     static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT;
  6749     if (!ParseNonNegativeVariant(top, VARIANT_SIDE, nullptr) ||
  6750         !ExpectSymbol(',', true) ||
  6751         !ParseNonNegativeVariant(right, VARIANT_SIDE, nullptr) ||
  6752         !ExpectSymbol(',', true) ||
  6753         !ParseNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) ||
  6754         !ExpectSymbol(',', true) ||
  6755         !ParseNonNegativeVariant(left, VARIANT_SIDE, nullptr) ||
  6756         !ExpectSymbol(')', true))
  6757       break;
  6759     aImage = newFunction;
  6760     return true;
  6763   SkipUntil(')');
  6764   return false;
  6767 // <element>: -moz-element(# <element_id> )
  6768 bool
  6769 CSSParserImpl::ParseElement(nsCSSValue& aValue)
  6771   // A non-iterative for loop to break out when an error occurs.
  6772   for (;;) {
  6773     if (!GetToken(true))
  6774       break;
  6776     if (mToken.mType == eCSSToken_ID) {
  6777       aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element);
  6778     } else {
  6779       UngetToken();
  6780       break;
  6783     if (!ExpectSymbol(')', true))
  6784       break;
  6786     return true;
  6789   // If we detect a syntax error, we must match the opening parenthesis of the
  6790   // function with the closing parenthesis and skip all the tokens in between.
  6791   SkipUntil(')');
  6792   return false;
  6795 // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
  6796 bool
  6797 CSSParserImpl::ParseFlex()
  6799   // First check for inherit / initial / unset
  6800   nsCSSValue tmpVal;
  6801   if (ParseVariant(tmpVal, VARIANT_INHERIT, nullptr)) {
  6802     AppendValue(eCSSProperty_flex_grow, tmpVal);
  6803     AppendValue(eCSSProperty_flex_shrink, tmpVal);
  6804     AppendValue(eCSSProperty_flex_basis, tmpVal);
  6805     return true;
  6808   // Next, check for 'none' == '0 0 auto'
  6809   if (ParseVariant(tmpVal, VARIANT_NONE, nullptr)) {
  6810     AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number));
  6811     AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number));
  6812     AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto));
  6813     return true;
  6816   // OK, try parsing our value as individual per-subproperty components:
  6817   //   [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
  6819   // Each subproperty has a default value that it takes when it's omitted in a
  6820   // "flex" shorthand value. These default values are *only* for the shorthand
  6821   // syntax -- they're distinct from the subproperties' own initial values.  We
  6822   // start with each subproperty at its default, as if we had "flex: 1 1 0%".
  6823   nsCSSValue flexGrow(1.0f, eCSSUnit_Number);
  6824   nsCSSValue flexShrink(1.0f, eCSSUnit_Number);
  6825   nsCSSValue flexBasis(0.0f, eCSSUnit_Percent);
  6827   // OVERVIEW OF PARSING STRATEGY:
  6828   // =============================
  6829   // a) Parse the first component as either flex-basis or flex-grow.
  6830   // b) If it wasn't flex-grow, parse the _next_ component as flex-grow.
  6831   // c) Now we've just parsed flex-grow -- so try parsing the next thing as
  6832   //    flex-shrink.
  6833   // d) Finally: If we didn't get flex-basis at the beginning, try to parse
  6834   //    it now, at the end.
  6835   //
  6836   // More details in each section below.
  6838   uint32_t flexBasisVariantMask =
  6839     (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT));
  6841   // (a) Parse first component. It can be either be a 'flex-basis' value or a
  6842   // 'flex-grow' value, so we use the flex-basis-specific variant mask, along
  6843   //  with VARIANT_NUMBER to accept 'flex-grow' values.
  6844   //
  6845   // NOTE: if we encounter unitless 0 here, we *must* interpret it as a
  6846   // 'flex-grow' value (a number), *not* as a 'flex-basis' value (a length).
  6847   // Conveniently, that's the behavior this combined variant-mask gives us --
  6848   // it'll treat unitless 0 as a number. The flexbox spec requires this:
  6849   // "a unitless zero that is not already preceded by two flex factors must be
  6850   //  interpreted as a flex factor.
  6851   if (!ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER,
  6852                                nsCSSProps::kWidthKTable)) {
  6853     // First component was not a valid flex-basis or flex-grow value. Fail.
  6854     return false;
  6857   // Record what we just parsed as either flex-basis or flex-grow:
  6858   bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number);
  6859   (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal;
  6861   // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow.
  6862   bool doneParsing = false;
  6863   if (wasFirstComponentFlexBasis) {
  6864     if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) {
  6865       flexGrow = tmpVal;
  6866     } else {
  6867       // Failed to parse anything after our flex-basis -- that's fine. We can
  6868       // skip the remaining parsing.
  6869       doneParsing = true;
  6873   if (!doneParsing) {
  6874     // (c) OK -- the last thing we parsed was flex-grow, so look for a
  6875     //     flex-shrink in the next position.
  6876     if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) {
  6877       flexShrink = tmpVal;
  6880     // d) Finally: If we didn't get flex-basis at the beginning, try to parse
  6881     //    it now, at the end.
  6882     //
  6883     // NOTE: If we encounter unitless 0 in this final position, we'll parse it
  6884     // as a 'flex-basis' value.  That's OK, because we know it must have
  6885     // been "preceded by 2 flex factors" (justification below), which gets us
  6886     // out of the spec's requirement of otherwise having to treat unitless 0
  6887     // as a flex factor.
  6888     //
  6889     // JUSTIFICATION: How do we know that a unitless 0 here must have been
  6890     // preceded by 2 flex factors? Well, suppose we had a unitless 0 that
  6891     // was preceded by only 1 flex factor.  Then, we would have already
  6892     // accepted this unitless 0 as the 'flex-shrink' value, up above (since
  6893     // it's a valid flex-shrink value), and we'd have moved on to the next
  6894     // token (if any). And of course, if we instead had a unitless 0 preceded
  6895     // by *no* flex factors (if it were the first token), we would've already
  6896     // parsed it in our very first call to ParseNonNegativeVariant().  So, any
  6897     // unitless 0 encountered here *must* have been preceded by 2 flex factors.
  6898     if (!wasFirstComponentFlexBasis &&
  6899         ParseNonNegativeVariant(tmpVal, flexBasisVariantMask,
  6900                                 nsCSSProps::kWidthKTable)) {
  6901       flexBasis = tmpVal;
  6905   AppendValue(eCSSProperty_flex_grow,   flexGrow);
  6906   AppendValue(eCSSProperty_flex_shrink, flexShrink);
  6907   AppendValue(eCSSProperty_flex_basis,  flexBasis);
  6909   return true;
  6912 // flex-flow: <flex-direction> || <flex-wrap>
  6913 bool
  6914 CSSParserImpl::ParseFlexFlow()
  6916   static const nsCSSProperty kFlexFlowSubprops[] = {
  6917     eCSSProperty_flex_direction,
  6918     eCSSProperty_flex_wrap
  6919   };
  6920   const size_t numProps = MOZ_ARRAY_LENGTH(kFlexFlowSubprops);
  6921   nsCSSValue values[numProps];
  6923   int32_t found = ParseChoice(values, kFlexFlowSubprops, numProps);
  6925   // Bail if we didn't successfully parse anything
  6926   if (found < 1) {
  6927     return false;
  6930   // If either property didn't get an explicit value, use its initial value.
  6931   if ((found & 1) == 0) {
  6932     values[0].SetIntValue(NS_STYLE_FLEX_DIRECTION_ROW, eCSSUnit_Enumerated);
  6934   if ((found & 2) == 0) {
  6935     values[1].SetIntValue(NS_STYLE_FLEX_WRAP_NOWRAP, eCSSUnit_Enumerated);
  6938   // Store these values and declare success!
  6939   for (size_t i = 0; i < numProps; i++) {
  6940     AppendValue(kFlexFlowSubprops[i], values[i]);
  6942   return true;
  6945 bool
  6946 CSSParserImpl::ParseGridAutoFlow()
  6948   nsCSSValue value;
  6949   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
  6950     AppendValue(eCSSProperty_grid_auto_flow, value);
  6951     return true;
  6954   static const int32_t mask[] = {
  6955     NS_STYLE_GRID_AUTO_FLOW_COLUMN | NS_STYLE_GRID_AUTO_FLOW_ROW,
  6956     MASK_END_VALUE
  6957   };
  6958   if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) {
  6959     return false;
  6961   int32_t bitField = value.GetIntValue();
  6963   // Requires one of these
  6964   if (!(bitField & NS_STYLE_GRID_AUTO_FLOW_NONE ||
  6965         bitField & NS_STYLE_GRID_AUTO_FLOW_COLUMN ||
  6966         bitField & NS_STYLE_GRID_AUTO_FLOW_ROW)) {
  6967     return false;
  6970   // 'none' is only valid if it occurs alone:
  6971   if (bitField & NS_STYLE_GRID_AUTO_FLOW_NONE &&
  6972       bitField != NS_STYLE_GRID_AUTO_FLOW_NONE) {
  6973     return false;
  6976   AppendValue(eCSSProperty_grid_auto_flow, value);
  6977   return true;
  6980 CSSParseResult
  6981 CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue)
  6983   if (!ExpectSymbol('(', true)) {
  6984     return CSSParseResult::NotFound;
  6986   if (!GetToken(true) || mToken.IsSymbol(')')) {
  6987     return CSSParseResult::Ok;
  6989   // 'return' so far leaves aValue untouched, to represent an empty list.
  6991   nsCSSValueList* item;
  6992   if (aValue.GetUnit() == eCSSUnit_List) {
  6993     // Find the end of an existing list.
  6994     // The grid-template shorthand uses this, at most once for a given list.
  6996     // NOTE: we could avoid this traversal by somehow keeping around
  6997     // a pointer to the last item from the previous call.
  6998     // It's not yet clear if this is worth the additional code complexity.
  6999     item = aValue.GetListValue();
  7000     while (item->mNext) {
  7001       item = item->mNext;
  7003     item->mNext = new nsCSSValueList;
  7004     item = item->mNext;
  7005   } else {
  7006     MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "Unexpected unit");
  7007     item = aValue.SetListValue();
  7009   for (;;) {
  7010     if (!(eCSSToken_Ident == mToken.mType &&
  7011           ParseCustomIdent(item->mValue, mToken.mIdent))) {
  7012       UngetToken();
  7013       SkipUntil(')');
  7014       return CSSParseResult::Error;
  7016     if (!GetToken(true) || mToken.IsSymbol(')')) {
  7017       return CSSParseResult::Ok;
  7019     item->mNext = new nsCSSValueList;
  7020     item = item->mNext;
  7024 // Assuming the 'repeat(' function token has already been consumed,
  7025 // parse the rest of repeat(<positive-integer>, <line-names>+)
  7026 // Append to the linked list whose end is given by |aTailPtr|,
  7027 // and updated |aTailPtr| to point to the new end of the list.
  7028 bool
  7029 CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr)
  7031   if (!(GetToken(true) &&
  7032         mToken.mType == eCSSToken_Number &&
  7033         mToken.mIntegerValid &&
  7034         mToken.mInteger > 0)) {
  7035     SkipUntil(')');
  7036     return false;
  7038   int32_t repetitions = std::min(mToken.mInteger,
  7039                                  GRID_TEMPLATE_MAX_REPETITIONS);
  7040   if (!ExpectSymbol(',', true)) {
  7041     SkipUntil(')');
  7042     return false;
  7045   // Parse at least one <line-names>
  7046   nsCSSValueList* tail = *aTailPtr;
  7047   do {
  7048     tail->mNext = new nsCSSValueList;
  7049     tail = tail->mNext;
  7050     if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) {
  7051       SkipUntil(')');
  7052       return false;
  7054   } while (!ExpectSymbol(')', true));
  7055   nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext;
  7056   nsCSSValueList* lastRepeatedItem = tail;
  7058   // Our repeated items are already in the target list once,
  7059   // so they need to be repeated |repetitions - 1| more times.
  7060   MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
  7061   while (--repetitions) {
  7062     nsCSSValueList* repeatedItem = firstRepeatedItem;
  7063     for (;;) {
  7064       tail->mNext = new nsCSSValueList;
  7065       tail = tail->mNext;
  7066       tail->mValue = repeatedItem->mValue;
  7067       if (repeatedItem == lastRepeatedItem) {
  7068         break;
  7070       repeatedItem = repeatedItem->mNext;
  7073   *aTailPtr = tail;
  7074   return true;
  7077 // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>?
  7078 bool
  7079 CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
  7081   nsCSSValueList* item = aValue.SetListValue();
  7082   // This marker distinguishes the value from a <track-list>.
  7083   item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID,
  7084                            eCSSUnit_Enumerated);
  7085   for (;;) {
  7086     // First try to parse repeat(<positive-integer>, <line-names>+)
  7087     if (!GetToken(true)) {
  7088       return true;
  7090     if (mToken.mType == eCSSToken_Function &&
  7091         mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
  7092       if (!ParseGridLineNameListRepeat(&item)) {
  7093         return false;
  7095     } else {
  7096       UngetToken();
  7098       // This was not a repeat() function. Try to parse <line-names>.
  7099       nsCSSValue lineNames;
  7100       CSSParseResult result = ParseGridLineNames(lineNames);
  7101       if (result == CSSParseResult::NotFound) {
  7102         return true;
  7104       if (result == CSSParseResult::Error) {
  7105         return false;
  7107       item->mNext = new nsCSSValueList;
  7108       item = item->mNext;
  7109       item->mValue = lineNames;
  7114 // Parse a <track-breadth>
  7115 bool
  7116 CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
  7118   if (ParseNonNegativeVariant(aValue,
  7119                               VARIANT_LPCALC | VARIANT_KEYWORD,
  7120                               nsCSSProps::kGridTrackBreadthKTable)) {
  7121     return true;
  7124   // Attempt to parse <flex> (a dimension with the "fr" unit)
  7125   if (!GetToken(true)) {
  7126     return false;
  7128   if (!(eCSSToken_Dimension == mToken.mType &&
  7129         mToken.mIdent.LowerCaseEqualsLiteral("fr") &&
  7130         mToken.mNumber >= 0)) {
  7131     UngetToken();
  7132     return false;
  7134   aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction);
  7135   return true;
  7138 // Parse a <track-size>
  7139 CSSParseResult
  7140 CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue)
  7142   // Attempt to parse 'auto' or a single <track-breadth>
  7143   if (ParseGridTrackBreadth(aValue) ||
  7144       ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
  7145     return CSSParseResult::Ok;
  7148   // Attempt to parse a minmax() function
  7149   if (!GetToken(true)) {
  7150     return CSSParseResult::NotFound;
  7152   if (!(eCSSToken_Function == mToken.mType &&
  7153         mToken.mIdent.LowerCaseEqualsLiteral("minmax"))) {
  7154     UngetToken();
  7155     return CSSParseResult::NotFound;
  7157   nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2);
  7158   if (ParseGridTrackBreadth(func->Item(1)) &&
  7159       ExpectSymbol(',', true) &&
  7160       ParseGridTrackBreadth(func->Item(2)) &&
  7161       ExpectSymbol(')', true)) {
  7162     return CSSParseResult::Ok;
  7164   SkipUntil(')');
  7165   return CSSParseResult::Error;
  7168 bool
  7169 CSSParserImpl::ParseGridAutoColumnsRows(nsCSSProperty aPropID)
  7171   nsCSSValue value;
  7172   if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
  7173       ParseGridTrackSize(value) == CSSParseResult::Ok) {
  7174     AppendValue(aPropID, value);
  7175     return true;
  7177   return false;
  7180 bool
  7181 CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
  7182                                                     const nsCSSValue& aFirstLineNames)
  7184   nsCSSValueList* firstLineNamesItem = aValue.SetListValue();
  7185   firstLineNamesItem->mValue = aFirstLineNames;
  7187   // This function is trying to parse <track-list>, which is
  7188   //   [ <line-names>? [ <track-size> | <repeat()> ] ]+ <line-names>?
  7189   // and we're already past the first "<line-names>?".
  7190   //
  7191   // Each iteration of the following loop attempts to parse either a
  7192   // repeat() or a <track-size> expression, and then an (optional)
  7193   // <line-names> expression.
  7194   //
  7195   // The only successful exit point from this loop is the ::NotFound
  7196   // case after ParseGridTrackSize(); i.e. we'll greedily parse
  7197   // repeat()/<track-size> until we can't find one.
  7198   nsCSSValueList* item = firstLineNamesItem;
  7199   for (;;) {
  7200     // First try to parse repeat()
  7201     if (!GetToken(true)) {
  7202       break;
  7204     if (mToken.mType == eCSSToken_Function &&
  7205         mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
  7206       if (!ParseGridTrackListRepeat(&item)) {
  7207         return false;
  7209     } else {
  7210       UngetToken();
  7212       // This was not a repeat() function. Try to parse <track-size>.
  7213       nsCSSValue trackSize;
  7214       CSSParseResult result = ParseGridTrackSize(trackSize);
  7215       if (result == CSSParseResult::Error) {
  7216         return false;
  7218       if (result == CSSParseResult::NotFound) {
  7219         // What we've parsed so far is a valid <track-list>
  7220         // (modulo the "at least one <track-size>" check below.)
  7221         // Stop here.
  7222         break;
  7224       item->mNext = new nsCSSValueList;
  7225       item = item->mNext;
  7226       item->mValue = trackSize;
  7228       item->mNext = new nsCSSValueList;
  7229       item = item->mNext;
  7231     if (ParseGridLineNames(item->mValue) == CSSParseResult::Error) {
  7232       return false;
  7236   // Require at least one <track-size>.
  7237   if (item == firstLineNamesItem) {
  7238     return false;
  7241   MOZ_ASSERT(aValue.GetListValue() &&
  7242              aValue.GetListValue()->mNext &&
  7243              aValue.GetListValue()->mNext->mNext,
  7244              "<track-list> should have a minimum length of 3");
  7245   return true;
  7248 // Takes ownership of |aSecond|
  7249 static void
  7250 ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond)
  7252   if (aSecond.GetUnit() == eCSSUnit_Null) {
  7253     // Nothing to do.
  7254     return;
  7256   if (aFirst.GetUnit() == eCSSUnit_Null) {
  7257     // Empty or omitted <line-names>. Replace it.
  7258     aFirst = aSecond;
  7259     return;
  7262   // Join the two <line-names> lists.
  7263   nsCSSValueList* source = aSecond.GetListValue();
  7264   nsCSSValueList* target = aFirst.GetListValue();
  7265   // Find the end:
  7266   while (target->mNext) {
  7267     target = target->mNext;
  7269   // Copy the first name. We can't take ownership of it
  7270   // as it'll be destroyed when |aSecond| goes out of scope.
  7271   target->mNext = new nsCSSValueList;
  7272   target = target->mNext;
  7273   target->mValue = source->mValue;
  7274   // Move the rest of the linked list.
  7275   target->mNext = source->mNext;
  7276   source->mNext = nullptr;
  7279 // Assuming the 'repeat(' function token has already been consumed,
  7280 // parse the rest of
  7281 // repeat( <positive-integer> ,
  7282 //         [ <line-names>? <track-size> ]+ <line-names>? )
  7283 // Append to the linked list whose end is given by |aTailPtr|,
  7284 // and updated |aTailPtr| to point to the new end of the list.
  7285 bool
  7286 CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
  7288   if (!(GetToken(true) &&
  7289         mToken.mType == eCSSToken_Number &&
  7290         mToken.mIntegerValid &&
  7291         mToken.mInteger > 0)) {
  7292     SkipUntil(')');
  7293     return false;
  7295   int32_t repetitions = std::min(mToken.mInteger,
  7296                                  GRID_TEMPLATE_MAX_REPETITIONS);
  7297   if (!ExpectSymbol(',', true)) {
  7298     SkipUntil(')');
  7299     return false;
  7302   // Parse [ <line-names>? <track-size> ]+ <line-names>?
  7303   // but keep the first and last <line-names> separate
  7304   // because they'll need to be joined.
  7305   // http://dev.w3.org/csswg/css-grid/#repeat-notation
  7306   nsCSSValue firstLineNames;
  7307   nsCSSValue trackSize;
  7308   nsCSSValue lastLineNames;
  7309   // Optional
  7310   if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) {
  7311     SkipUntil(')');
  7312     return false;
  7314   // Required
  7315   if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
  7316     SkipUntil(')');
  7317     return false;
  7319   // Use nsAutoPtr to free the list in case of early return.
  7320   nsAutoPtr<nsCSSValueList> firstTrackSizeItemAuto(new nsCSSValueList);
  7321   firstTrackSizeItemAuto->mValue = trackSize;
  7323   nsCSSValueList* item = firstTrackSizeItemAuto;
  7324   for (;;) {
  7325     // Optional
  7326     if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) {
  7327       SkipUntil(')');
  7328       return false;
  7331     if (ExpectSymbol(')', true)) {
  7332       break;
  7335     // Required
  7336     if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
  7337       SkipUntil(')');
  7338       return false;
  7341     item->mNext = new nsCSSValueList;
  7342     item = item->mNext;
  7343     item->mValue = lastLineNames;
  7344     // Do not append to this list at the next iteration.
  7345     lastLineNames.Reset();
  7347     item->mNext = new nsCSSValueList;
  7348     item = item->mNext;
  7349     item->mValue = trackSize;
  7351   nsCSSValueList* lastTrackSizeItem = item;
  7353   // [ <line-names>? <track-size> ]+ <line-names>?  is now parsed into:
  7354   // * firstLineNames: the first <line-names>
  7355   // * a linked list of odd length >= 1, from firstTrackSizeItem
  7356   //   (the first <track-size>) to lastTrackSizeItem (the last),
  7357   //   with the <line-names> sublists in between
  7358   // * lastLineNames: the last <line-names>
  7361   // Join the last and first <line-names> (in that order.)
  7362   // For example, repeat(3, (a) 100px (b) 200px (c)) results in
  7363   // (a) 100px (b) 200px (c a) 100px (b) 200px (c a) 100px (b) 200px (c)
  7364   // This is (c a).
  7365   // Make deep copies: the originals will be moved.
  7366   nsCSSValue joinerLineNames;
  7368     nsCSSValueList* target = nullptr;
  7369     if (lastLineNames.GetUnit() != eCSSUnit_Null) {
  7370       target = joinerLineNames.SetListValue();
  7371       nsCSSValueList* source = lastLineNames.GetListValue();
  7372       for (;;) {
  7373         target->mValue = source->mValue;
  7374         source = source->mNext;
  7375         if (!source) {
  7376           break;
  7378         target->mNext = new nsCSSValueList;
  7379         target = target->mNext;
  7383     if (firstLineNames.GetUnit() != eCSSUnit_Null) {
  7384       if (target) {
  7385         target->mNext = new nsCSSValueList;
  7386         target = target->mNext;
  7387       } else {
  7388         target = joinerLineNames.SetListValue();
  7390       nsCSSValueList* source = firstLineNames.GetListValue();
  7391       for (;;) {
  7392         target->mValue = source->mValue;
  7393         source = source->mNext;
  7394         if (!source) {
  7395           break;
  7397         target->mNext = new nsCSSValueList;
  7398         target = target->mNext;
  7403   // Join our first <line-names> with the one before repeat().
  7404   // (a) repeat(1, (b) 20px) expands to (a b) 20px
  7405   nsCSSValueList* previousItemBeforeRepeat = *aTailPtr;
  7406   ConcatLineNames(previousItemBeforeRepeat->mValue, firstLineNames);
  7408   // Move our linked list
  7409   // (first to last <track-size>, with the <line-names> sublists in between).
  7410   // This is the first repetition.
  7411   NS_ASSERTION(previousItemBeforeRepeat->mNext == nullptr,
  7412                "Expected the end of a linked list");
  7413   previousItemBeforeRepeat->mNext = firstTrackSizeItemAuto.forget();
  7414   nsCSSValueList* firstTrackSizeItem = previousItemBeforeRepeat->mNext;
  7415   nsCSSValueList* tail = lastTrackSizeItem;
  7417   // Repeat |repetitions - 1| more times:
  7418   // * the joiner <line-names>
  7419   // * the linked list
  7420   //   (first to last <track-size>, with the <line-names> sublists in between)
  7421   MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
  7422   while (--repetitions) {
  7423     tail->mNext = new nsCSSValueList;
  7424     tail = tail->mNext;
  7425     tail->mValue = joinerLineNames;
  7427     nsCSSValueList* repeatedItem = firstTrackSizeItem;
  7428     for (;;) {
  7429       tail->mNext = new nsCSSValueList;
  7430       tail = tail->mNext;
  7431       tail->mValue = repeatedItem->mValue;
  7432       if (repeatedItem == lastTrackSizeItem) {
  7433         break;
  7435       repeatedItem = repeatedItem->mNext;
  7439   // Finally, move our last <line-names>.
  7440   // Any <line-names> immediately after repeat() will append to it.
  7441   tail->mNext = new nsCSSValueList;
  7442   tail = tail->mNext;
  7443   tail->mValue = lastLineNames;
  7445   *aTailPtr = tail;
  7446   return true;
  7449 bool
  7450 CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSProperty aPropID)
  7452   nsCSSValue value;
  7453   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
  7454     AppendValue(aPropID, value);
  7455     return true;
  7458   nsSubstring* ident = NextIdent();
  7459   if (ident) {
  7460     if (ident->LowerCaseEqualsLiteral("subgrid")) {
  7461       if (!ParseOptionalLineNameListAfterSubgrid(value)) {
  7462         return false;
  7464       AppendValue(aPropID, value);
  7465       return true;
  7467     UngetToken();
  7470   nsCSSValue firstLineNames;
  7471   if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
  7472       !ParseGridTrackListWithFirstLineNames(value, firstLineNames)) {
  7473     return false;
  7475   AppendValue(aPropID, value);
  7476   return true;
  7479 bool
  7480 CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput,
  7481                                           css::GridTemplateAreasValue* aAreas,
  7482                                           nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices)
  7484   aAreas->mTemplates.AppendElement(mToken.mIdent);
  7486   nsCSSGridTemplateAreaScanner scanner(aInput);
  7487   nsCSSGridTemplateAreaToken token;
  7488   css::GridNamedArea* currentArea = nullptr;
  7489   uint32_t row = aAreas->NRows();
  7490   uint32_t column;
  7491   for (column = 1; scanner.Next(token); column++) {
  7492     if (token.isTrash) {
  7493       return false;
  7495     if (currentArea) {
  7496       if (token.mName == currentArea->mName) {
  7497         if (currentArea->mRowStart == row) {
  7498           // Next column in the first row of this named area.
  7499           currentArea->mColumnEnd++;
  7501         continue;
  7503       // We're exiting |currentArea|, so currentArea is ending at |column|.
  7504       // Make sure that this is consistent with currentArea on previous rows:
  7505       if (currentArea->mColumnEnd != column) {
  7506         NS_ASSERTION(currentArea->mRowStart != row,
  7507                      "Inconsistent column end for the first row of a named area.");
  7508         // Not a rectangle
  7509         return false;
  7511       currentArea = nullptr;
  7513     if (!token.mName.IsEmpty()) {
  7514       // Named cell that doesn't have a cell with the same name on its left.
  7516       // Check if this is the continuation of an existing named area:
  7517       uint32_t index;
  7518       if (aAreaIndices.Get(token.mName, &index)) {
  7519         MOZ_ASSERT(index < aAreas->mNamedAreas.Length(),
  7520                    "Invalid aAreaIndices hash table");
  7521         currentArea = &aAreas->mNamedAreas[index];
  7522         if (currentArea->mColumnStart != column ||
  7523             currentArea->mRowEnd != row) {
  7524           // Existing named area, but not forming a rectangle
  7525           return false;
  7527         // Next row of an existing named area
  7528         currentArea->mRowEnd++;
  7529       } else {
  7530         // New named area
  7531         aAreaIndices.Put(token.mName, aAreas->mNamedAreas.Length());
  7532         currentArea = aAreas->mNamedAreas.AppendElement();
  7533         currentArea->mName = token.mName;
  7534         // For column or row N (starting at 1),
  7535         // the start line is N, the end line is N + 1
  7536         currentArea->mColumnStart = column;
  7537         currentArea->mColumnEnd = column + 1;
  7538         currentArea->mRowStart = row;
  7539         currentArea->mRowEnd = row + 1;
  7543   if (currentArea && currentArea->mColumnEnd != column) {
  7544     NS_ASSERTION(currentArea->mRowStart != row,
  7545                  "Inconsistent column end for the first row of a named area.");
  7546     // Not a rectangle
  7547     return false;
  7550   // On the first row, set the number of columns
  7551   // that grid-template-areas contributes to the explicit grid.
  7552   // On other rows, check that the number of columns is consistent
  7553   // between rows.
  7554   if (row == 1) {
  7555     aAreas->mNColumns = column;
  7556   } else if (aAreas->mNColumns != column) {
  7557     return false;
  7559   return true;
  7562 bool
  7563 CSSParserImpl::ParseGridTemplateAreas()
  7565   nsCSSValue value;
  7566   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
  7567     AppendValue(eCSSProperty_grid_template_areas, value);
  7568     return true;
  7571   nsRefPtr<css::GridTemplateAreasValue> areas =
  7572     new css::GridTemplateAreasValue();
  7573   nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
  7574   for (;;) {
  7575     if (!GetToken(true)) {
  7576       break;
  7578     if (eCSSToken_String != mToken.mType) {
  7579       UngetToken();
  7580       break;
  7582     if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
  7583       return false;
  7587   if (areas->NRows() == 0) {
  7588     return false;
  7591   AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
  7592   return true;
  7595 bool
  7596 CSSParserImpl::ParseGridTemplate()
  7598   // none |
  7599   // subgrid |
  7600   // <'grid-template-columns'> / <'grid-template-rows'> |
  7601   // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
  7602   nsCSSValue value;
  7603   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
  7604     AppendValue(eCSSProperty_grid_template_areas, value);
  7605     AppendValue(eCSSProperty_grid_template_columns, value);
  7606     AppendValue(eCSSProperty_grid_template_rows, value);
  7607     return true;
  7610   // TODO (bug 983175): add parsing for 'subgrid' by itself
  7612   // 'none' can appear either by itself,
  7613   // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
  7614   if (ParseVariant(value, VARIANT_NONE, nullptr)) {
  7615     AppendValue(eCSSProperty_grid_template_columns, value);
  7616     if (ExpectSymbol('/', true)) {
  7617       return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
  7619     AppendValue(eCSSProperty_grid_template_areas, value);
  7620     AppendValue(eCSSProperty_grid_template_rows, value);
  7621     return true;
  7624   // 'subgrid' can appear either by itself,
  7625   // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
  7626   nsSubstring* ident = NextIdent();
  7627   if (ident) {
  7628     if (ident->LowerCaseEqualsLiteral("subgrid")) {
  7629       if (!ParseOptionalLineNameListAfterSubgrid(value)) {
  7630         return false;
  7632       AppendValue(eCSSProperty_grid_template_columns, value);
  7633       if (ExpectSymbol('/', true)) {
  7634         return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
  7636       if (value.GetListValue()->mNext) {
  7637         // Non-empty <line-name-list> after 'subgrid'.
  7638         // This is only valid as part of <'grid-template-columns'>,
  7639         // which must be followed by a slash.
  7640         return false;
  7642       // 'subgrid' by itself sets both grid-template-columns
  7643       // and grid-template-rows.
  7644       AppendValue(eCSSProperty_grid_template_rows, value);
  7645       value.SetNoneValue();
  7646       AppendValue(eCSSProperty_grid_template_areas, value);
  7647       return true;
  7649     UngetToken();
  7652   // [ <line-names>? ] here is ambiguous:
  7653   // it can be either the start of a <track-list>,
  7654   // or the start of [ <line-names>? <string> <track-size>? <line-names>? ]+
  7655   nsCSSValue firstLineNames;
  7656   if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
  7657       !GetToken(true)) {
  7658     return false;
  7660   if (mToken.mType == eCSSToken_String) {
  7661     // [ <track-list> / ]? was omitted
  7662     // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
  7663     value.SetNoneValue();
  7664     AppendValue(eCSSProperty_grid_template_columns, value);
  7665     return ParseGridTemplateAfterString(firstLineNames);
  7667   UngetToken();
  7669   if (!(ParseGridTrackListWithFirstLineNames(value, firstLineNames) &&
  7670         ExpectSymbol('/', true))) {
  7671     return false;
  7673   AppendValue(eCSSProperty_grid_template_columns, value);
  7674   return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ true);
  7677 // Helper for parsing the 'grid-template' shorthand
  7678 //
  7679 // NOTE: This parses the portion after the slash, for *one* of the
  7680 // following types of expressions:
  7681 // - <'grid-template-columns'> / <'grid-template-rows'>
  7682 // - <track-list> / [ <line-names>? <string> <track-size>? <line-names>? ]+
  7683 //
  7684 // We don't know which type of expression we've got until we've parsed the
  7685 // second half, since the pre-slash part is ambiguous. The various return
  7686 // clauses below are labeled with the type of expression they're completing.
  7687 bool
  7688 CSSParserImpl::ParseGridTemplateAfterSlash(bool aColumnsIsTrackList)
  7690   nsCSSValue rowsValue;
  7691   if (ParseVariant(rowsValue, VARIANT_NONE, nullptr)) {
  7692     // <'grid-template-columns'> / <'grid-template-rows'>
  7693     AppendValue(eCSSProperty_grid_template_rows, rowsValue);
  7694     nsCSSValue areasValue(eCSSUnit_None);  // implied
  7695     AppendValue(eCSSProperty_grid_template_areas, areasValue);
  7696     return true;
  7699   nsSubstring* ident = NextIdent();
  7700   if (ident) {
  7701     if (ident->LowerCaseEqualsLiteral("subgrid")) {
  7702       if (!ParseOptionalLineNameListAfterSubgrid(rowsValue)) {
  7703         return false;
  7705       // <'grid-template-columns'> / <'grid-template-rows'>
  7706       AppendValue(eCSSProperty_grid_template_rows, rowsValue);
  7707       nsCSSValue areasValue(eCSSUnit_None);  // implied
  7708       AppendValue(eCSSProperty_grid_template_areas, areasValue);
  7709       return true;
  7711     UngetToken();
  7714   nsCSSValue firstLineNames;
  7715   if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
  7716       !GetToken(true)) {
  7717     return false;
  7719   if (aColumnsIsTrackList && mToken.mType == eCSSToken_String) {
  7720     // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
  7721     return ParseGridTemplateAfterString(firstLineNames);
  7723   UngetToken();
  7725   if (!ParseGridTrackListWithFirstLineNames(rowsValue, firstLineNames)) {
  7726     return false;
  7729   // <'grid-template-columns'> / <'grid-template-rows'>
  7730   AppendValue(eCSSProperty_grid_template_rows, rowsValue);
  7731   nsCSSValue areasValue(eCSSUnit_None);  // implied
  7732   AppendValue(eCSSProperty_grid_template_areas, areasValue);
  7733   return true;
  7736 // Helper for parsing the 'grid-template' shorthand:
  7737 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
  7738 // with a <line-names>? already consumed, stored in |aFirstLineNames|,
  7739 // and the current token a <string>
  7740 bool
  7741 CSSParserImpl::ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames)
  7743   MOZ_ASSERT(mToken.mType == eCSSToken_String,
  7744              "ParseGridTemplateAfterString called with a non-string token");
  7746   nsCSSValue rowsValue;
  7747   nsRefPtr<css::GridTemplateAreasValue> areas =
  7748     new css::GridTemplateAreasValue();
  7749   nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
  7750   nsCSSValueList* rowsItem = rowsValue.SetListValue();
  7751   rowsItem->mValue = aFirstLineNames;
  7753   for (;;) {
  7754     if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
  7755       return false;
  7758     rowsItem->mNext = new nsCSSValueList;
  7759     rowsItem = rowsItem->mNext;
  7760     CSSParseResult result = ParseGridTrackSize(rowsItem->mValue);
  7761     if (result == CSSParseResult::Error) {
  7762       return false;
  7764     if (result == CSSParseResult::NotFound) {
  7765       rowsItem->mValue.SetAutoValue();
  7768     rowsItem->mNext = new nsCSSValueList;
  7769     rowsItem = rowsItem->mNext;
  7770     result = ParseGridLineNames(rowsItem->mValue);
  7771     if (result == CSSParseResult::Error) {
  7772       return false;
  7774     if (result == CSSParseResult::Ok) {
  7775       // Append to the same list as the previous call to ParseGridLineNames.
  7776       result = ParseGridLineNames(rowsItem->mValue);
  7777       if (result == CSSParseResult::Error) {
  7778         return false;
  7780       if (result == CSSParseResult::Ok) {
  7781         // Parsed <line-name> twice.
  7782         // The property value can not end here, we expect a string next.
  7783         if (!GetToken(true)) {
  7784           return false;
  7786         if (eCSSToken_String != mToken.mType) {
  7787           UngetToken();
  7788           return false;
  7790         continue;
  7794     // Did not find a <line-names>.
  7795     // Next, we expect either a string or the end of the property value.
  7796     if (!GetToken(true)) {
  7797       break;
  7799     if (eCSSToken_String != mToken.mType) {
  7800       UngetToken();
  7801       break;
  7805   AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
  7806   AppendValue(eCSSProperty_grid_template_rows, rowsValue);
  7807   return true;
  7810 // <'grid-template'> |
  7811 // [ <'grid-auto-flow'> [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]? ]
  7812 bool
  7813 CSSParserImpl::ParseGrid()
  7815   nsCSSValue value;
  7816   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
  7817     for (const nsCSSProperty* subprops =
  7818            nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid);
  7819          *subprops != eCSSProperty_UNKNOWN; ++subprops) {
  7820       AppendValue(*subprops, value);
  7822     return true;
  7825   // 'none' at the beginning could be a <'grid-auto-flow'>
  7826   // (which also covers 'none' by itself)
  7827   // or a <'grid-template-columns'> (as part of <'grid-template'>)
  7828   if (ParseVariant(value, VARIANT_NONE, nullptr)) {
  7829     if (ExpectSymbol('/', true)) {
  7830       AppendValue(eCSSProperty_grid_template_columns, value);
  7832       // Set grid-auto-* subproperties to their initial values.
  7833       value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated);
  7834       AppendValue(eCSSProperty_grid_auto_flow, value);
  7835       value.SetAutoValue();
  7836       AppendValue(eCSSProperty_grid_auto_columns, value);
  7837       AppendValue(eCSSProperty_grid_auto_rows, value);
  7839       return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
  7841     value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated);
  7842     AppendValue(eCSSProperty_grid_auto_flow, value);
  7843     return ParseGridShorthandAutoProps();
  7846   // An empty value is always invalid.
  7847   if (!GetToken(true)) {
  7848     return false;
  7851   // If the value starts with a 'dense', 'column' or 'row' keyword,
  7852   // it can only start with a <'grid-auto-flow'>
  7853   if (mToken.mType == eCSSToken_Ident) {
  7854     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
  7855     if (keyword == eCSSKeyword_dense ||
  7856         keyword == eCSSKeyword_column ||
  7857         keyword == eCSSKeyword_row) {
  7858       UngetToken();
  7859       return ParseGridAutoFlow() && ParseGridShorthandAutoProps();
  7862   UngetToken();
  7864   // Set other subproperties to their initial values
  7865   // and parse <'grid-template'>.
  7866   value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated);
  7867   AppendValue(eCSSProperty_grid_auto_flow, value);
  7868   value.SetAutoValue();
  7869   AppendValue(eCSSProperty_grid_auto_columns, value);
  7870   AppendValue(eCSSProperty_grid_auto_rows, value);
  7871   return ParseGridTemplate();
  7874 // Parse [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]?
  7875 // for the 'grid' shorthand.
  7876 // Assumes that <'grid-auto-flow'> was already parsed by the caller.
  7877 bool
  7878 CSSParserImpl::ParseGridShorthandAutoProps()
  7880   nsCSSValue autoColumnsValue;
  7881   nsCSSValue autoRowsValue;
  7882   CSSParseResult result = ParseGridTrackSize(autoColumnsValue);
  7883   if (result == CSSParseResult::Error) {
  7884     return false;
  7886   if (result == CSSParseResult::NotFound) {
  7887     autoColumnsValue.SetAutoValue();
  7888     autoRowsValue.SetAutoValue();
  7889   } else {
  7890     if (!ExpectSymbol('/', true)) {
  7891       autoRowsValue.SetAutoValue();
  7892     } else if (ParseGridTrackSize(autoRowsValue) != CSSParseResult::Ok) {
  7893       return false;
  7896   AppendValue(eCSSProperty_grid_auto_columns, autoColumnsValue);
  7897   AppendValue(eCSSProperty_grid_auto_rows, autoRowsValue);
  7898   nsCSSValue templateValue(eCSSUnit_None);  // Initial values
  7899   AppendValue(eCSSProperty_grid_template_areas, templateValue);
  7900   AppendValue(eCSSProperty_grid_template_columns, templateValue);
  7901   AppendValue(eCSSProperty_grid_template_rows, templateValue);
  7902   return true;
  7905 // Parse a <grid-line>.
  7906 // If successful, set aValue to eCSSUnit_Auto,
  7907 // or a eCSSUnit_List containing, in that order:
  7908 //
  7909 // * An optional eCSSUnit_Enumerated marking a "span" keyword.
  7910 // * An optional eCSSUnit_Integer
  7911 // * An optional eCSSUnit_Ident
  7912 //
  7913 // At least one of eCSSUnit_Integer or eCSSUnit_Ident is present.
  7914 bool
  7915 CSSParserImpl::ParseGridLine(nsCSSValue& aValue)
  7917   //  <grid-line> =
  7918   //    auto |
  7919   //    <custom-ident> |
  7920   //    [ <integer> && <custom-ident>? ] |
  7921   //    [ span && [ <integer> || <custom-ident> ] ]
  7922   //
  7923   // Syntactically, this simplifies to:
  7924   //
  7925   //  <grid-line> =
  7926   //    auto |
  7927   //    [ span? && [ <integer> || <custom-ident> ] ]
  7929   if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
  7930     return true;
  7933   static const nsCSSKeyword kGridLineKeywords[] = {
  7934     eCSSKeyword_span,
  7935     eCSSKeyword_UNKNOWN  // End-of-array marker
  7936   };
  7937   bool hasSpan = false;
  7938   bool hasInteger = false;
  7939   bool hasIdent = false;
  7940   int32_t integer;
  7941   nsCSSValue ident;
  7943   if (!GetToken(true)) {
  7944     return false;
  7946   if (mToken.mType == eCSSToken_Ident &&
  7947       mToken.mIdent.LowerCaseEqualsLiteral("span")) {
  7948     hasSpan = true;
  7949     if (!GetToken(true)) {
  7950       return false;
  7954   do {
  7955     if (!hasIdent &&
  7956         mToken.mType == eCSSToken_Ident &&
  7957         ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) {
  7958       hasIdent = true;
  7959     } else if (!hasInteger &&
  7960                mToken.mType == eCSSToken_Number &&
  7961                mToken.mIntegerValid &&
  7962                mToken.mInteger != 0) {
  7963       hasInteger = true;
  7964       integer = mToken.mInteger;
  7965     } else {
  7966       UngetToken();
  7967       break;
  7969   } while (!(hasInteger && hasIdent) && GetToken(true));
  7971   // Require at least one of <integer> or <custom-ident>
  7972   if (!(hasInteger || hasIdent)) {
  7973     return false;
  7976   if (!hasSpan && GetToken(true)) {
  7977     if (mToken.mType == eCSSToken_Ident &&
  7978         mToken.mIdent.LowerCaseEqualsLiteral("span")) {
  7979       hasSpan = true;
  7980     } else {
  7981       UngetToken();
  7985   nsCSSValueList* item = aValue.SetListValue();
  7986   if (hasSpan) {
  7987     // Given "span", a negative <integer> is invalid.
  7988     if (hasInteger && integer < 0) {
  7989       return false;
  7991     // '1' here is a dummy value.
  7992     // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword.
  7993     item->mValue.SetIntValue(1, eCSSUnit_Enumerated);
  7994     item->mNext = new nsCSSValueList;
  7995     item = item->mNext;
  7997   if (hasInteger) {
  7998     item->mValue.SetIntValue(integer, eCSSUnit_Integer);
  7999     if (hasIdent) {
  8000       item->mNext = new nsCSSValueList;
  8001       item = item->mNext;
  8004   if (hasIdent) {
  8005     item->mValue = ident;
  8007   return true;
  8010 bool
  8011 CSSParserImpl::ParseGridAutoPosition()
  8013   nsCSSValue value;
  8014   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
  8015     AppendValue(eCSSProperty_grid_auto_position, value);
  8016     return true;
  8018   nsCSSValue columnStartValue;
  8019   nsCSSValue rowStartValue;
  8020   if (ParseGridLine(columnStartValue) &&
  8021       ExpectSymbol('/', true) &&
  8022       ParseGridLine(rowStartValue)) {
  8023     value.SetPairValue(columnStartValue, rowStartValue);
  8024     AppendValue(eCSSProperty_grid_auto_position, value);
  8025     return true;
  8027   return false;
  8030 bool
  8031 CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSProperty aPropID)
  8033   nsCSSValue value;
  8034   if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
  8035       ParseGridLine(value)) {
  8036     AppendValue(aPropID, value);
  8037     return true;
  8039   return false;
  8042 // If |aFallback| is a List containing a single Ident, set |aValue| to that.
  8043 // Otherwise, set |aValue| to Auto.
  8044 // Used with |aFallback| from ParseGridLine()
  8045 static void
  8046 HandleGridLineFallback(const nsCSSValue& aFallback, nsCSSValue& aValue)
  8048   if (aFallback.GetUnit() == eCSSUnit_List &&
  8049       aFallback.GetListValue()->mValue.GetUnit() == eCSSUnit_Ident &&
  8050       !aFallback.GetListValue()->mNext) {
  8051     aValue = aFallback;
  8052   } else {
  8053     aValue.SetAutoValue();
  8057 bool
  8058 CSSParserImpl::ParseGridColumnRow(nsCSSProperty aStartPropID,
  8059                                   nsCSSProperty aEndPropID)
  8061   nsCSSValue value;
  8062   nsCSSValue secondValue;
  8063   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
  8064     AppendValue(aStartPropID, value);
  8065     AppendValue(aEndPropID, value);
  8066     return true;
  8069   if (!ParseGridLine(value)) {
  8070     return false;
  8072   if (GetToken(true)) {
  8073     if (mToken.IsSymbol('/')) {
  8074       if (ParseGridLine(secondValue)) {
  8075         AppendValue(aStartPropID, value);
  8076         AppendValue(aEndPropID, secondValue);
  8077         return true;
  8078       } else {
  8079         return false;
  8082     UngetToken();
  8085   // A single <custom-ident> is repeated to both properties,
  8086   // anything else sets the grid-{column,row}-end property to 'auto'.
  8087   HandleGridLineFallback(value, secondValue);
  8089   AppendValue(aStartPropID, value);
  8090   AppendValue(aEndPropID, secondValue);
  8091   return true;
  8094 bool
  8095 CSSParserImpl::ParseGridArea()
  8097   nsCSSValue values[4];
  8098   if (ParseVariant(values[0], VARIANT_INHERIT, nullptr)) {
  8099     AppendValue(eCSSProperty_grid_row_start, values[0]);
  8100     AppendValue(eCSSProperty_grid_column_start, values[0]);
  8101     AppendValue(eCSSProperty_grid_row_end, values[0]);
  8102     AppendValue(eCSSProperty_grid_column_end, values[0]);
  8103     return true;
  8106   int32_t i = 0;
  8107   for (;;) {
  8108     if (!ParseGridLine(values[i])) {
  8109       return false;
  8111     if (++i == 4 || !GetToken(true)) {
  8112       break;
  8114     if (!mToken.IsSymbol('/')) {
  8115       UngetToken();
  8116       break;
  8120   MOZ_ASSERT(i >= 1, "should have parsed at least one grid-line (or returned)");
  8121   if (i < 2) {
  8122     HandleGridLineFallback(values[0], values[1]);
  8124   if (i < 3) {
  8125     HandleGridLineFallback(values[0], values[2]);
  8127   if (i < 4) {
  8128     HandleGridLineFallback(values[1], values[3]);
  8131   AppendValue(eCSSProperty_grid_row_start, values[0]);
  8132   AppendValue(eCSSProperty_grid_column_start, values[1]);
  8133   AppendValue(eCSSProperty_grid_row_end, values[2]);
  8134   AppendValue(eCSSProperty_grid_column_end, values[3]);
  8135   return true;
  8138 // <color-stop> : <color> [ <percentage> | <length> ]?
  8139 bool
  8140 CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
  8142   nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
  8143   if (!ParseVariant(stop->mColor, VARIANT_COLOR, nullptr)) {
  8144     return false;
  8147   // Stop positions do not have to fall between the starting-point and
  8148   // ending-point, so we don't use ParseNonNegativeVariant.
  8149   if (!ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr)) {
  8150     stop->mLocation.SetNoneValue();
  8152   return true;
  8155 // <gradient>
  8156 //    : linear-gradient( <linear-gradient-line>? <color-stops> ')'
  8157 //    | radial-gradient( <radial-gradient-line>? <color-stops> ')'
  8158 //
  8159 // <linear-gradient-line> : [ to [left | right] || [top | bottom] ] ,
  8160 //                        | <legacy-gradient-line>
  8161 // <radial-gradient-line> : [ <shape> || <size> ] [ at <position> ]? ,
  8162 //                        | [ at <position> ] ,
  8163 //                        | <legacy-gradient-line>? <legacy-shape-size>?
  8164 // <shape> : circle | ellipse
  8165 // <size> : closest-side | closest-corner | farthest-side | farthest-corner
  8166 //        | <length> | [<length> | <percentage>]{2}
  8167 //
  8168 // <legacy-gradient-line> : [ <position> || <angle>] ,
  8169 //
  8170 // <legacy-shape-size> : [ <shape> || <legacy-size> ] ,
  8171 // <legacy-size> : closest-side | closest-corner | farthest-side
  8172 //               | farthest-corner | contain | cover
  8173 //
  8174 // <color-stops> : <color-stop> , <color-stop> [, <color-stop>]*
  8175 bool
  8176 CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating,
  8177                                    bool aIsLegacy)
  8179   nsRefPtr<nsCSSValueGradient> cssGradient
  8180     = new nsCSSValueGradient(false, aIsRepeating);
  8182   if (!GetToken(true)) {
  8183     return false;
  8186   if (mToken.mType == eCSSToken_Ident &&
  8187       mToken.mIdent.LowerCaseEqualsLiteral("to")) {
  8189     // "to" syntax doesn't allow explicit "center"
  8190     if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) {
  8191       SkipUntil(')');
  8192       return false;
  8195     // [ to [left | right] || [top | bottom] ] ,
  8196     const nsCSSValue& xValue = cssGradient->mBgPos.mXValue;
  8197     const nsCSSValue& yValue = cssGradient->mBgPos.mYValue;
  8198     if (xValue.GetUnit() != eCSSUnit_Enumerated ||
  8199         !(xValue.GetIntValue() & (NS_STYLE_BG_POSITION_LEFT |
  8200                                   NS_STYLE_BG_POSITION_CENTER |
  8201                                   NS_STYLE_BG_POSITION_RIGHT)) ||
  8202         yValue.GetUnit() != eCSSUnit_Enumerated ||
  8203         !(yValue.GetIntValue() & (NS_STYLE_BG_POSITION_TOP |
  8204                                   NS_STYLE_BG_POSITION_CENTER |
  8205                                   NS_STYLE_BG_POSITION_BOTTOM))) {
  8206       SkipUntil(')');
  8207       return false;
  8210     if (!ExpectSymbol(',', true)) {
  8211       SkipUntil(')');
  8212       return false;
  8215     return ParseGradientColorStops(cssGradient, aValue);
  8218   if (!aIsLegacy) {
  8219     UngetToken();
  8221     // <angle> ,
  8222     if (ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) &&
  8223         !ExpectSymbol(',', true)) {
  8224       SkipUntil(')');
  8225       return false;
  8228     return ParseGradientColorStops(cssGradient, aValue);
  8231   nsCSSTokenType ty = mToken.mType;
  8232   nsString id = mToken.mIdent;
  8233   UngetToken();
  8235   // <legacy-gradient-line>
  8236   bool haveGradientLine = IsLegacyGradientLine(ty, id);
  8237   if (haveGradientLine) {
  8238     cssGradient->mIsLegacySyntax = true;
  8239     bool haveAngle =
  8240       ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
  8242     // if we got an angle, we might now have a comma, ending the gradient-line
  8243     if (!haveAngle || !ExpectSymbol(',', true)) {
  8244       if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
  8245         SkipUntil(')');
  8246         return false;
  8249       if (!ExpectSymbol(',', true) &&
  8250           // if we didn't already get an angle, we might have one now,
  8251           // otherwise it's an error
  8252           (haveAngle ||
  8253            !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) ||
  8254            // now we better have a comma
  8255            !ExpectSymbol(',', true))) {
  8256         SkipUntil(')');
  8257         return false;
  8262   return ParseGradientColorStops(cssGradient, aValue);
  8265 bool
  8266 CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating,
  8267                                    bool aIsLegacy)
  8269   nsRefPtr<nsCSSValueGradient> cssGradient
  8270     = new nsCSSValueGradient(true, aIsRepeating);
  8272   // [ <shape> || <size> ]
  8273   bool haveShape =
  8274     ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
  8275                  nsCSSProps::kRadialGradientShapeKTable);
  8277   bool haveSize = ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
  8278                                aIsLegacy ?
  8279                                nsCSSProps::kRadialGradientLegacySizeKTable :
  8280                                nsCSSProps::kRadialGradientSizeKTable);
  8281   if (haveSize) {
  8282     if (!haveShape) {
  8283       // <size> <shape>
  8284       haveShape = ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
  8285                                nsCSSProps::kRadialGradientShapeKTable);
  8287   } else if (!aIsLegacy) {
  8288     // Save RadialShape before parsing RadiusX because RadialShape and
  8289     // RadiusX share the storage.
  8290     int32_t shape =
  8291       cssGradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated ?
  8292       cssGradient->GetRadialShape().GetIntValue() : -1;
  8293     // <length> | [<length> | <percentage>]{2}
  8294     cssGradient->mIsExplicitSize = true;
  8295     haveSize =
  8296       ParseNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP, nullptr);
  8297     if (!haveSize) {
  8298       // It was not an explicit size after all.
  8299       // Note that ParseNonNegativeVariant may have put something
  8300       // invalid into our storage, but only in the case where it was
  8301       // rejected only for being negative.  Since this means the token
  8302       // was a length or a percentage, we know it's not valid syntax
  8303       // (which must be a comma, the 'at' keyword, or a color), so we
  8304       // know this value will be dropped.  This means it doesn't matter
  8305       // that we have something invalid in our storage.
  8306       cssGradient->mIsExplicitSize = false;
  8307     } else {
  8308       // vertical extent is optional
  8309       bool haveYSize =
  8310         ParseNonNegativeVariant(cssGradient->GetRadiusY(), VARIANT_LP, nullptr);
  8311       if (!haveShape) {
  8312         nsCSSValue shapeValue;
  8313         haveShape = ParseVariant(shapeValue, VARIANT_KEYWORD,
  8314                                  nsCSSProps::kRadialGradientShapeKTable);
  8315         if (haveShape) {
  8316           shape = shapeValue.GetIntValue();
  8319       if (haveYSize
  8320             ? shape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR
  8321             : cssGradient->GetRadiusX().GetUnit() == eCSSUnit_Percent ||
  8322               shape == NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL) {
  8323         SkipUntil(')');
  8324         return false;
  8329   if ((haveShape || haveSize) && ExpectSymbol(',', true)) {
  8330     // [ <shape> || <size> ] ,
  8331     return ParseGradientColorStops(cssGradient, aValue);
  8334   if (!GetToken(true)) {
  8335     return false;
  8338   if (!aIsLegacy) {
  8339     if (mToken.mType == eCSSToken_Ident &&
  8340         mToken.mIdent.LowerCaseEqualsLiteral("at")) {
  8341       // [ <shape> || <size> ]? at <position> ,
  8342       if (!ParseBoxPositionValues(cssGradient->mBgPos, false) ||
  8343           !ExpectSymbol(',', true)) {
  8344         SkipUntil(')');
  8345         return false;
  8348       return ParseGradientColorStops(cssGradient, aValue);
  8351     // <color-stops> only
  8352     UngetToken();
  8353     return ParseGradientColorStops(cssGradient, aValue);
  8355   MOZ_ASSERT(!cssGradient->mIsExplicitSize);
  8357   nsCSSTokenType ty = mToken.mType;
  8358   nsString id = mToken.mIdent;
  8359   UngetToken();
  8361   // <legacy-gradient-line>
  8362   bool haveGradientLine = false;
  8363   // if we already encountered a shape or size,
  8364   // we can not have a gradient-line in legacy syntax
  8365   if (!haveShape && !haveSize) {
  8366       haveGradientLine = IsLegacyGradientLine(ty, id);
  8368   if (haveGradientLine) {
  8369     bool haveAngle =
  8370       ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
  8372     // if we got an angle, we might now have a comma, ending the gradient-line
  8373     if (!haveAngle || !ExpectSymbol(',', true)) {
  8374       if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
  8375         SkipUntil(')');
  8376         return false;
  8379       if (!ExpectSymbol(',', true) &&
  8380           // if we didn't already get an angle, we might have one now,
  8381           // otherwise it's an error
  8382           (haveAngle ||
  8383            !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) ||
  8384            // now we better have a comma
  8385            !ExpectSymbol(',', true))) {
  8386         SkipUntil(')');
  8387         return false;
  8391     if (cssGradient->mAngle.GetUnit() != eCSSUnit_None) {
  8392       cssGradient->mIsLegacySyntax = true;
  8396   // radial gradients might have a shape and size here for legacy syntax
  8397   if (!haveShape && !haveSize) {
  8398     haveShape =
  8399       ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
  8400                    nsCSSProps::kRadialGradientShapeKTable);
  8401     haveSize =
  8402       ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
  8403                    nsCSSProps::kRadialGradientLegacySizeKTable);
  8405     // could be in either order
  8406     if (!haveShape) {
  8407       haveShape =
  8408         ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
  8409                      nsCSSProps::kRadialGradientShapeKTable);
  8413   if ((haveShape || haveSize) && !ExpectSymbol(',', true)) {
  8414     SkipUntil(')');
  8415     return false;
  8418   return ParseGradientColorStops(cssGradient, aValue);
  8421 bool
  8422 CSSParserImpl::IsLegacyGradientLine(const nsCSSTokenType& aType,
  8423                                     const nsString& aId)
  8425   // N.B. ParseBoxPositionValues is not guaranteed to put back
  8426   // everything it scanned if it fails, so we must only call it
  8427   // if there is no alternative to consuming a <box-position>.
  8428   // ParseVariant, as used here, will either succeed and consume
  8429   // a single token, or fail and consume none, so we can be more
  8430   // cavalier about calling it.
  8432   bool haveGradientLine = false;
  8433   switch (aType) {
  8434   case eCSSToken_Percentage:
  8435   case eCSSToken_Number:
  8436   case eCSSToken_Dimension:
  8437     haveGradientLine = true;
  8438     break;
  8440   case eCSSToken_Function:
  8441     if (aId.LowerCaseEqualsLiteral("calc") ||
  8442         aId.LowerCaseEqualsLiteral("-moz-calc")) {
  8443       haveGradientLine = true;
  8444       break;
  8446     // fall through
  8447   case eCSSToken_ID:
  8448   case eCSSToken_Hash:
  8449     // this is a color
  8450     break;
  8452   case eCSSToken_Ident: {
  8453     // This is only a gradient line if it's a box position keyword.
  8454     nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(aId);
  8455     int32_t junk;
  8456     if (kw != eCSSKeyword_UNKNOWN &&
  8457         nsCSSProps::FindKeyword(kw, nsCSSProps::kBackgroundPositionKTable,
  8458                                 junk)) {
  8459       haveGradientLine = true;
  8461     break;
  8464   default:
  8465     // error
  8466     break;
  8469   return haveGradientLine;
  8472 bool
  8473 CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient,
  8474                                        nsCSSValue& aValue)
  8476   // At least two color stops are required
  8477   if (!ParseColorStop(aGradient) ||
  8478       !ExpectSymbol(',', true) ||
  8479       !ParseColorStop(aGradient)) {
  8480     SkipUntil(')');
  8481     return false;
  8484   // Additional color stops
  8485   while (ExpectSymbol(',', true)) {
  8486     if (!ParseColorStop(aGradient)) {
  8487       SkipUntil(')');
  8488       return false;
  8492   if (!ExpectSymbol(')', true)) {
  8493     SkipUntil(')');
  8494     return false;
  8497   aValue.SetGradientValue(aGradient);
  8498   return true;
  8501 int32_t
  8502 CSSParserImpl::ParseChoice(nsCSSValue aValues[],
  8503                            const nsCSSProperty aPropIDs[], int32_t aNumIDs)
  8505   int32_t found = 0;
  8506   nsAutoParseCompoundProperty compound(this);
  8508   int32_t loop;
  8509   for (loop = 0; loop < aNumIDs; loop++) {
  8510     // Try each property parser in order
  8511     int32_t hadFound = found;
  8512     int32_t index;
  8513     for (index = 0; index < aNumIDs; index++) {
  8514       int32_t bit = 1 << index;
  8515       if ((found & bit) == 0) {
  8516         if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) {
  8517           found |= bit;
  8518           // It's more efficient to break since it will reset |hadFound|
  8519           // to |found|.  Furthermore, ParseListStyle depends on our going
  8520           // through the properties in order for each value..
  8521           break;
  8525     if (found == hadFound) {  // found nothing new
  8526       break;
  8529   if (0 < found) {
  8530     if (1 == found) { // only first property
  8531       if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
  8532         for (loop = 1; loop < aNumIDs; loop++) {
  8533           aValues[loop].SetInheritValue();
  8535         found = ((1 << aNumIDs) - 1);
  8537       else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
  8538         for (loop = 1; loop < aNumIDs; loop++) {
  8539           aValues[loop].SetInitialValue();
  8541         found = ((1 << aNumIDs) - 1);
  8543       else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset
  8544         for (loop = 1; loop < aNumIDs; loop++) {
  8545           aValues[loop].SetUnsetValue();
  8547         found = ((1 << aNumIDs) - 1);
  8550     else {  // more than one value, verify no inherits, initials or unsets
  8551       for (loop = 0; loop < aNumIDs; loop++) {
  8552         if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
  8553           found = -1;
  8554           break;
  8556         else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
  8557           found = -1;
  8558           break;
  8560         else if (eCSSUnit_Unset == aValues[loop].GetUnit()) {
  8561           found = -1;
  8562           break;
  8567   return found;
  8570 void
  8571 CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue)
  8573   mTempData.AddLonghandProperty(aPropID, aValue);
  8576 /**
  8577  * Parse a "box" property. Box properties have 1 to 4 values. When less
  8578  * than 4 values are provided a standard mapping is used to replicate
  8579  * existing values.
  8580  */
  8581 bool
  8582 CSSParserImpl::ParseBoxProperties(const nsCSSProperty aPropIDs[])
  8584   // Get up to four values for the property
  8585   int32_t count = 0;
  8586   nsCSSRect result;
  8587   NS_FOR_CSS_SIDES (index) {
  8588     if (! ParseSingleValueProperty(result.*(nsCSSRect::sides[index]),
  8589                                    aPropIDs[index])) {
  8590       break;
  8592     count++;
  8594   if (count == 0) {
  8595     return false;
  8598   if (1 < count) { // verify no more than single inherit, initial or unset
  8599     NS_FOR_CSS_SIDES (index) {
  8600       nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
  8601       if (eCSSUnit_Inherit == unit ||
  8602           eCSSUnit_Initial == unit ||
  8603           eCSSUnit_Unset == unit) {
  8604         return false;
  8609   // Provide missing values by replicating some of the values found
  8610   switch (count) {
  8611     case 1: // Make right == top
  8612       result.mRight = result.mTop;
  8613     case 2: // Make bottom == top
  8614       result.mBottom = result.mTop;
  8615     case 3: // Make left == right
  8616       result.mLeft = result.mRight;
  8619   NS_FOR_CSS_SIDES (index) {
  8620     AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index]));
  8622   return true;
  8625 // Similar to ParseBoxProperties, except there is only one property
  8626 // with the result as its value, not four. Requires values be nonnegative.
  8627 bool
  8628 CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask,
  8629                                        /** outparam */ nsCSSValue& aValue)
  8631   nsCSSRect& result = aValue.SetRectValue();
  8633   int32_t count = 0;
  8634   NS_FOR_CSS_SIDES (index) {
  8635     if (!ParseNonNegativeVariant(result.*(nsCSSRect::sides[index]),
  8636                                  aVariantMask, nullptr)) {
  8637       break;
  8639     count++;
  8642   if (count == 0) {
  8643     return false;
  8646   // Provide missing values by replicating some of the values found
  8647   switch (count) {
  8648     case 1: // Make right == top
  8649       result.mRight = result.mTop;
  8650     case 2: // Make bottom == top
  8651       result.mBottom = result.mTop;
  8652     case 3: // Make left == right
  8653       result.mLeft = result.mRight;
  8656   return true;
  8659 bool
  8660 CSSParserImpl::ParseDirectionalBoxProperty(nsCSSProperty aProperty,
  8661                                            int32_t aSourceType)
  8663   const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty);
  8664   NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
  8665                "not box property with physical vs. logical cascading");
  8666   nsCSSValue value;
  8667   if (!ParseSingleValueProperty(value, subprops[0])) {
  8668     return false;
  8671   AppendValue(subprops[0], value);
  8672   nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
  8673   AppendValue(subprops[1], typeVal);
  8674   AppendValue(subprops[2], typeVal);
  8675   return true;
  8678 bool
  8679 CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID)
  8681   nsCSSValue dimenX, dimenY;
  8682   // required first value
  8683   if (! ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr))
  8684     return false;
  8686   // optional second value (forbidden if first value is inherit/initial/unset)
  8687   if (dimenX.GetUnit() != eCSSUnit_Inherit &&
  8688       dimenX.GetUnit() != eCSSUnit_Initial &&
  8689       dimenX.GetUnit() != eCSSUnit_Unset) {
  8690     ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr);
  8693   if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) {
  8694     AppendValue(aPropID, dimenX);
  8695   } else {
  8696     nsCSSValue value;
  8697     value.SetPairValue(dimenX, dimenY);
  8698     AppendValue(aPropID, value);
  8700   return true;
  8703 bool
  8704 CSSParserImpl::ParseBoxCornerRadii(const nsCSSProperty aPropIDs[])
  8706   // Rectangles are used as scratch storage.
  8707   // top => top-left, right => top-right,
  8708   // bottom => bottom-right, left => bottom-left.
  8709   nsCSSRect dimenX, dimenY;
  8710   int32_t countX = 0, countY = 0;
  8712   NS_FOR_CSS_SIDES (side) {
  8713     if (! ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side],
  8714                                   (side > 0 ? 0 : VARIANT_INHERIT) |
  8715                                     VARIANT_LP | VARIANT_CALC,
  8716                                   nullptr))
  8717       break;
  8718     countX++;
  8720   if (countX == 0)
  8721     return false;
  8723   if (ExpectSymbol('/', true)) {
  8724     NS_FOR_CSS_SIDES (side) {
  8725       if (! ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side],
  8726                                     VARIANT_LP | VARIANT_CALC, nullptr))
  8727         break;
  8728       countY++;
  8730     if (countY == 0)
  8731       return false;
  8734   // if 'initial', 'inherit' or 'unset' was used, it must be the only value
  8735   if (countX > 1 || countY > 0) {
  8736     nsCSSUnit unit = dimenX.mTop.GetUnit();
  8737     if (eCSSUnit_Inherit == unit ||
  8738         eCSSUnit_Initial == unit ||
  8739         eCSSUnit_Unset == unit)
  8740       return false;
  8743   // if we have no Y-values, use the X-values
  8744   if (countY == 0) {
  8745     dimenY = dimenX;
  8746     countY = countX;
  8749   // Provide missing values by replicating some of the values found
  8750   switch (countX) {
  8751     case 1: dimenX.mRight = dimenX.mTop;  // top-right same as top-left, and
  8752     case 2: dimenX.mBottom = dimenX.mTop; // bottom-right same as top-left, and 
  8753     case 3: dimenX.mLeft = dimenX.mRight; // bottom-left same as top-right
  8756   switch (countY) {
  8757     case 1: dimenY.mRight = dimenY.mTop;  // top-right same as top-left, and
  8758     case 2: dimenY.mBottom = dimenY.mTop; // bottom-right same as top-left, and 
  8759     case 3: dimenY.mLeft = dimenY.mRight; // bottom-left same as top-right
  8762   NS_FOR_CSS_SIDES(side) {
  8763     nsCSSValue& x = dimenX.*nsCSSRect::sides[side];
  8764     nsCSSValue& y = dimenY.*nsCSSRect::sides[side];
  8766     if (x == y) {
  8767       AppendValue(aPropIDs[side], x);
  8768     } else {
  8769       nsCSSValue pair;
  8770       pair.SetPairValue(x, y);
  8771       AppendValue(aPropIDs[side], pair);
  8774   return true;
  8777 // These must be in CSS order (top,right,bottom,left) for indexing to work
  8778 static const nsCSSProperty kBorderStyleIDs[] = {
  8779   eCSSProperty_border_top_style,
  8780   eCSSProperty_border_right_style_value,
  8781   eCSSProperty_border_bottom_style,
  8782   eCSSProperty_border_left_style_value
  8783 };
  8784 static const nsCSSProperty kBorderWidthIDs[] = {
  8785   eCSSProperty_border_top_width,
  8786   eCSSProperty_border_right_width_value,
  8787   eCSSProperty_border_bottom_width,
  8788   eCSSProperty_border_left_width_value
  8789 };
  8790 static const nsCSSProperty kBorderColorIDs[] = {
  8791   eCSSProperty_border_top_color,
  8792   eCSSProperty_border_right_color_value,
  8793   eCSSProperty_border_bottom_color,
  8794   eCSSProperty_border_left_color_value
  8795 };
  8796 static const nsCSSProperty kBorderRadiusIDs[] = {
  8797   eCSSProperty_border_top_left_radius,
  8798   eCSSProperty_border_top_right_radius,
  8799   eCSSProperty_border_bottom_right_radius,
  8800   eCSSProperty_border_bottom_left_radius
  8801 };
  8802 static const nsCSSProperty kOutlineRadiusIDs[] = {
  8803   eCSSProperty__moz_outline_radius_topLeft,
  8804   eCSSProperty__moz_outline_radius_topRight,
  8805   eCSSProperty__moz_outline_radius_bottomRight,
  8806   eCSSProperty__moz_outline_radius_bottomLeft
  8807 };
  8809 void
  8810 CSSParserImpl::SaveInputState(CSSParserInputState& aState)
  8812   aState.mToken = mToken;
  8813   aState.mHavePushBack = mHavePushBack;
  8814   mScanner->SavePosition(aState.mPosition);
  8817 void
  8818 CSSParserImpl::RestoreSavedInputState(const CSSParserInputState& aState)
  8820   mToken = aState.mToken;
  8821   mHavePushBack = aState.mHavePushBack;
  8822   mScanner->RestoreSavedPosition(aState.mPosition);
  8825 bool
  8826 CSSParserImpl::ParseProperty(nsCSSProperty aPropID)
  8828   // Can't use AutoRestore<bool> because it's a bitfield.
  8829   NS_ABORT_IF_FALSE(!mHashlessColorQuirk,
  8830                     "hashless color quirk should not be set");
  8831   NS_ABORT_IF_FALSE(!mUnitlessLengthQuirk,
  8832                     "unitless length quirk should not be set");
  8834   if (mNavQuirkMode) {
  8835     mHashlessColorQuirk =
  8836       nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_HASHLESS_COLOR_QUIRK);
  8837     mUnitlessLengthQuirk =
  8838       nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_UNITLESS_LENGTH_QUIRK);
  8841   // Save the current input state so that we can restore it later if we
  8842   // have to re-parse the property value as a variable-reference-containing
  8843   // token stream.
  8844   CSSParserInputState stateBeforeProperty;
  8845   SaveInputState(stateBeforeProperty);
  8846   mScanner->ClearSeenVariableReference();
  8848   NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
  8849   bool allowVariables = true;
  8850   bool result;
  8851   switch (nsCSSProps::PropertyParseType(aPropID)) {
  8852     case CSS_PROPERTY_PARSE_INACCESSIBLE: {
  8853       // The user can't use these
  8854       REPORT_UNEXPECTED(PEInaccessibleProperty2);
  8855       allowVariables = false;
  8856       result = false;
  8857       break;
  8859     case CSS_PROPERTY_PARSE_FUNCTION: {
  8860       result = ParsePropertyByFunction(aPropID);
  8861       break;
  8863     case CSS_PROPERTY_PARSE_VALUE: {
  8864       result = false;
  8865       nsCSSValue value;
  8866       if (ParseSingleValueProperty(value, aPropID)) {
  8867         AppendValue(aPropID, value);
  8868         result = true;
  8870       // XXX Report errors?
  8871       break;
  8873     case CSS_PROPERTY_PARSE_VALUE_LIST: {
  8874       result = ParseValueList(aPropID);
  8875       break;
  8877     default: {
  8878       result = false;
  8879       allowVariables = false;
  8880       NS_ABORT_IF_FALSE(false,
  8881                         "Property's flags field in nsCSSPropList.h is missing "
  8882                         "one of the CSS_PROPERTY_PARSE_* constants");
  8883       break;
  8887   if (result) {
  8888     // We need to call ExpectEndProperty() to decide whether to reparse
  8889     // with variables.  This is needed because the property parsing may
  8890     // have stopped upon finding a variable (e.g., 'margin: 1px var(a)')
  8891     // in a way that future variable substitutions will be valid, or
  8892     // because it parsed everything that's possible but we still want to
  8893     // act as though the property contains variables even though we know
  8894     // the substitution will never work (e.g., for 'margin: 1px 2px 3px
  8895     // 4px 5px var(a)').
  8896     //
  8897     // It would be nice to find a better solution here
  8898     // (and for the SkipUntilOneOf below), though, that doesn't depend
  8899     // on using what we don't accept for doing parsing correctly.
  8900     if (!ExpectEndProperty()) {
  8901       result = false;
  8905   bool seenVariable = mScanner->SeenVariableReference() ||
  8906     (stateBeforeProperty.mHavePushBack &&
  8907      stateBeforeProperty.mToken.mType == eCSSToken_Function &&
  8908      stateBeforeProperty.mToken.mIdent.LowerCaseEqualsLiteral("var"));
  8909   bool parseAsTokenStream;
  8911   if (!result && allowVariables) {
  8912     parseAsTokenStream = true;
  8913     if (!seenVariable) {
  8914       // We might have stopped parsing the property before its end and before
  8915       // finding a variable reference.  Keep checking until the end of the
  8916       // property.
  8917       CSSParserInputState stateAtError;
  8918       SaveInputState(stateAtError);
  8920       const char16_t stopChars[] = { ';', '!', '}', ')', 0 };
  8921       SkipUntilOneOf(stopChars);
  8922       UngetToken();
  8923       parseAsTokenStream = mScanner->SeenVariableReference();
  8925       if (!parseAsTokenStream) {
  8926         // If we parsed to the end of the propery and didn't find any variable
  8927         // references, then the real position we want to report the error at
  8928         // is |stateAtError|.
  8929         RestoreSavedInputState(stateAtError);
  8932   } else {
  8933     parseAsTokenStream = false;
  8936   if (parseAsTokenStream) {
  8937     // Go back to the start of the property value and parse it to make sure
  8938     // its variable references are syntactically valid and is otherwise
  8939     // balanced.
  8940     RestoreSavedInputState(stateBeforeProperty);
  8942     if (!mInSupportsCondition) {
  8943       mScanner->StartRecording();
  8946     CSSVariableDeclarations::Type type;
  8947     bool dropBackslash;
  8948     nsString impliedCharacters;
  8949     nsCSSValue value;
  8950     if (ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
  8951                                 nullptr, nullptr)) {
  8952       MOZ_ASSERT(type == CSSVariableDeclarations::eTokenStream,
  8953                  "a non-custom property reparsed since it contained variable "
  8954                  "references should not have been 'initial' or 'inherit'");
  8956       nsString propertyValue;
  8958       if (!mInSupportsCondition) {
  8959         // If we are in an @supports condition, we don't need to store the
  8960         // actual token stream on the nsCSSValue.
  8961         mScanner->StopRecording(propertyValue);
  8962         if (dropBackslash) {
  8963           MOZ_ASSERT(!propertyValue.IsEmpty() &&
  8964                      propertyValue[propertyValue.Length() - 1] == '\\');
  8965           propertyValue.Truncate(propertyValue.Length() - 1);
  8967         propertyValue.Append(impliedCharacters);
  8970       if (mHavePushBack) {
  8971         // If we came to the end of a property value that had a variable
  8972         // reference and a token was pushed back, then it would have been
  8973         // ended by '!', ')', ';', ']' or '}'.  We should remove it from the
  8974         // recorded property value.
  8975         MOZ_ASSERT(mToken.IsSymbol('!') ||
  8976                    mToken.IsSymbol(')') ||
  8977                    mToken.IsSymbol(';') ||
  8978                    mToken.IsSymbol(']') ||
  8979                    mToken.IsSymbol('}'));
  8980         if (!mInSupportsCondition) {
  8981           MOZ_ASSERT(!propertyValue.IsEmpty());
  8982           MOZ_ASSERT(propertyValue[propertyValue.Length() - 1] ==
  8983                      mToken.mSymbol);
  8984           propertyValue.Truncate(propertyValue.Length() - 1);
  8988       if (!mInSupportsCondition) {
  8989         if (nsCSSProps::IsShorthand(aPropID)) {
  8990           // If this is a shorthand property, we store the token stream on each
  8991           // of its corresponding longhand properties.
  8992           CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
  8993             nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
  8994             tokenStream->mPropertyID = *p;
  8995             tokenStream->mShorthandPropertyID = aPropID;
  8996             tokenStream->mTokenStream = propertyValue;
  8997             tokenStream->mBaseURI = mBaseURI;
  8998             tokenStream->mSheetURI = mSheetURI;
  8999             tokenStream->mSheetPrincipal = mSheetPrincipal;
  9000             tokenStream->mSheet = mSheet;
  9001             tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
  9002             tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
  9003             value.SetTokenStreamValue(tokenStream);
  9004             AppendValue(*p, value);
  9006         } else {
  9007           nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
  9008           tokenStream->mPropertyID = aPropID;
  9009           tokenStream->mTokenStream = propertyValue;
  9010           tokenStream->mBaseURI = mBaseURI;
  9011           tokenStream->mSheetURI = mSheetURI;
  9012           tokenStream->mSheetPrincipal = mSheetPrincipal;
  9013           tokenStream->mSheet = mSheet;
  9014           tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
  9015           tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
  9016           value.SetTokenStreamValue(tokenStream);
  9017           AppendValue(aPropID, value);
  9020       result = true;
  9021     } else {
  9022       if (!mInSupportsCondition) {
  9023         mScanner->StopRecording();
  9028   if (mNavQuirkMode) {
  9029     mHashlessColorQuirk = false;
  9030     mUnitlessLengthQuirk = false;
  9033   return result;
  9036 bool
  9037 CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID)
  9039   switch (aPropID) {  // handle shorthand or multiple properties
  9040   case eCSSProperty_background:
  9041     return ParseBackground();
  9042   case eCSSProperty_background_repeat:
  9043     return ParseBackgroundRepeat();
  9044   case eCSSProperty_background_position:
  9045     return ParseBackgroundPosition();
  9046   case eCSSProperty_background_size:
  9047     return ParseBackgroundSize();
  9048   case eCSSProperty_border:
  9049     return ParseBorderSide(kBorderTopIDs, true);
  9050   case eCSSProperty_border_color:
  9051     return ParseBorderColor();
  9052   case eCSSProperty_border_spacing:
  9053     return ParseBorderSpacing();
  9054   case eCSSProperty_border_style:
  9055     return ParseBorderStyle();
  9056   case eCSSProperty_border_bottom:
  9057     return ParseBorderSide(kBorderBottomIDs, false);
  9058   case eCSSProperty_border_end:
  9059     return ParseDirectionalBorderSide(kBorderEndIDs,
  9060                                       NS_BOXPROP_SOURCE_LOGICAL);
  9061   case eCSSProperty_border_left:
  9062     return ParseDirectionalBorderSide(kBorderLeftIDs,
  9063                                       NS_BOXPROP_SOURCE_PHYSICAL);
  9064   case eCSSProperty_border_right:
  9065     return ParseDirectionalBorderSide(kBorderRightIDs,
  9066                                       NS_BOXPROP_SOURCE_PHYSICAL);
  9067   case eCSSProperty_border_start:
  9068     return ParseDirectionalBorderSide(kBorderStartIDs,
  9069                                       NS_BOXPROP_SOURCE_LOGICAL);
  9070   case eCSSProperty_border_top:
  9071     return ParseBorderSide(kBorderTopIDs, false);
  9072   case eCSSProperty_border_bottom_colors:
  9073   case eCSSProperty_border_left_colors:
  9074   case eCSSProperty_border_right_colors:
  9075   case eCSSProperty_border_top_colors:
  9076     return ParseBorderColors(aPropID);
  9077   case eCSSProperty_border_image_slice:
  9078     return ParseBorderImageSlice(true, nullptr);
  9079   case eCSSProperty_border_image_width:
  9080     return ParseBorderImageWidth(true);
  9081   case eCSSProperty_border_image_outset:
  9082     return ParseBorderImageOutset(true);
  9083   case eCSSProperty_border_image_repeat:
  9084     return ParseBorderImageRepeat(true);
  9085   case eCSSProperty_border_image:
  9086     return ParseBorderImage();
  9087   case eCSSProperty_border_width:
  9088     return ParseBorderWidth();
  9089   case eCSSProperty_border_end_color:
  9090     return ParseDirectionalBoxProperty(eCSSProperty_border_end_color,
  9091                                        NS_BOXPROP_SOURCE_LOGICAL);
  9092   case eCSSProperty_border_left_color:
  9093     return ParseDirectionalBoxProperty(eCSSProperty_border_left_color,
  9094                                        NS_BOXPROP_SOURCE_PHYSICAL);
  9095   case eCSSProperty_border_right_color:
  9096     return ParseDirectionalBoxProperty(eCSSProperty_border_right_color,
  9097                                        NS_BOXPROP_SOURCE_PHYSICAL);
  9098   case eCSSProperty_border_start_color:
  9099     return ParseDirectionalBoxProperty(eCSSProperty_border_start_color,
  9100                                        NS_BOXPROP_SOURCE_LOGICAL);
  9101   case eCSSProperty_border_end_width:
  9102     return ParseDirectionalBoxProperty(eCSSProperty_border_end_width,
  9103                                        NS_BOXPROP_SOURCE_LOGICAL);
  9104   case eCSSProperty_border_left_width:
  9105     return ParseDirectionalBoxProperty(eCSSProperty_border_left_width,
  9106                                        NS_BOXPROP_SOURCE_PHYSICAL);
  9107   case eCSSProperty_border_right_width:
  9108     return ParseDirectionalBoxProperty(eCSSProperty_border_right_width,
  9109                                        NS_BOXPROP_SOURCE_PHYSICAL);
  9110   case eCSSProperty_border_start_width:
  9111     return ParseDirectionalBoxProperty(eCSSProperty_border_start_width,
  9112                                        NS_BOXPROP_SOURCE_LOGICAL);
  9113   case eCSSProperty_border_end_style:
  9114     return ParseDirectionalBoxProperty(eCSSProperty_border_end_style,
  9115                                        NS_BOXPROP_SOURCE_LOGICAL);
  9116   case eCSSProperty_border_left_style:
  9117     return ParseDirectionalBoxProperty(eCSSProperty_border_left_style,
  9118                                        NS_BOXPROP_SOURCE_PHYSICAL);
  9119   case eCSSProperty_border_right_style:
  9120     return ParseDirectionalBoxProperty(eCSSProperty_border_right_style,
  9121                                        NS_BOXPROP_SOURCE_PHYSICAL);
  9122   case eCSSProperty_border_start_style:
  9123     return ParseDirectionalBoxProperty(eCSSProperty_border_start_style,
  9124                                        NS_BOXPROP_SOURCE_LOGICAL);
  9125   case eCSSProperty_border_radius:
  9126     return ParseBoxCornerRadii(kBorderRadiusIDs);
  9127   case eCSSProperty__moz_outline_radius:
  9128     return ParseBoxCornerRadii(kOutlineRadiusIDs);
  9130   case eCSSProperty_border_top_left_radius:
  9131   case eCSSProperty_border_top_right_radius:
  9132   case eCSSProperty_border_bottom_right_radius:
  9133   case eCSSProperty_border_bottom_left_radius:
  9134   case eCSSProperty__moz_outline_radius_topLeft:
  9135   case eCSSProperty__moz_outline_radius_topRight:
  9136   case eCSSProperty__moz_outline_radius_bottomRight:
  9137   case eCSSProperty__moz_outline_radius_bottomLeft:
  9138     return ParseBoxCornerRadius(aPropID);
  9140   case eCSSProperty_box_shadow:
  9141   case eCSSProperty_text_shadow:
  9142     return ParseShadowList(aPropID);
  9144   case eCSSProperty_clip:
  9145     return ParseRect(eCSSProperty_clip);
  9146   case eCSSProperty__moz_columns:
  9147     return ParseColumns();
  9148   case eCSSProperty__moz_column_rule:
  9149     return ParseBorderSide(kColumnRuleIDs, false);
  9150   case eCSSProperty_content:
  9151     return ParseContent();
  9152   case eCSSProperty_counter_increment:
  9153   case eCSSProperty_counter_reset:
  9154     return ParseCounterData(aPropID);
  9155   case eCSSProperty_cursor:
  9156     return ParseCursor();
  9157   case eCSSProperty_filter:
  9158     return ParseFilter();
  9159   case eCSSProperty_flex:
  9160     return ParseFlex();
  9161   case eCSSProperty_flex_flow:
  9162     return ParseFlexFlow();
  9163   case eCSSProperty_font:
  9164     return ParseFont();
  9165   case eCSSProperty_grid_auto_flow:
  9166     return ParseGridAutoFlow();
  9167   case eCSSProperty_grid_auto_columns:
  9168   case eCSSProperty_grid_auto_rows:
  9169     return ParseGridAutoColumnsRows(aPropID);
  9170   case eCSSProperty_grid_template_areas:
  9171     return ParseGridTemplateAreas();
  9172   case eCSSProperty_grid_template_columns:
  9173   case eCSSProperty_grid_template_rows:
  9174     return ParseGridTemplateColumnsRows(aPropID);
  9175   case eCSSProperty_grid_template:
  9176     return ParseGridTemplate();
  9177   case eCSSProperty_grid:
  9178     return ParseGrid();
  9179   case eCSSProperty_grid_auto_position:
  9180     return ParseGridAutoPosition();
  9181   case eCSSProperty_grid_column_start:
  9182   case eCSSProperty_grid_column_end:
  9183   case eCSSProperty_grid_row_start:
  9184   case eCSSProperty_grid_row_end:
  9185     return ParseGridColumnRowStartEnd(aPropID);
  9186   case eCSSProperty_grid_column:
  9187     return ParseGridColumnRow(eCSSProperty_grid_column_start,
  9188                               eCSSProperty_grid_column_end);
  9189   case eCSSProperty_grid_row:
  9190     return ParseGridColumnRow(eCSSProperty_grid_row_start,
  9191                               eCSSProperty_grid_row_end);
  9192   case eCSSProperty_grid_area:
  9193     return ParseGridArea();
  9194   case eCSSProperty_image_region:
  9195     return ParseRect(eCSSProperty_image_region);
  9196   case eCSSProperty_list_style:
  9197     return ParseListStyle();
  9198   case eCSSProperty_margin:
  9199     return ParseMargin();
  9200   case eCSSProperty_margin_end:
  9201     return ParseDirectionalBoxProperty(eCSSProperty_margin_end,
  9202                                        NS_BOXPROP_SOURCE_LOGICAL);
  9203   case eCSSProperty_margin_left:
  9204     return ParseDirectionalBoxProperty(eCSSProperty_margin_left,
  9205                                        NS_BOXPROP_SOURCE_PHYSICAL);
  9206   case eCSSProperty_margin_right:
  9207     return ParseDirectionalBoxProperty(eCSSProperty_margin_right,
  9208                                        NS_BOXPROP_SOURCE_PHYSICAL);
  9209   case eCSSProperty_margin_start:
  9210     return ParseDirectionalBoxProperty(eCSSProperty_margin_start,
  9211                                        NS_BOXPROP_SOURCE_LOGICAL);
  9212   case eCSSProperty_outline:
  9213     return ParseOutline();
  9214   case eCSSProperty_overflow:
  9215     return ParseOverflow();
  9216   case eCSSProperty_padding:
  9217     return ParsePadding();
  9218   case eCSSProperty_padding_end:
  9219     return ParseDirectionalBoxProperty(eCSSProperty_padding_end,
  9220                                        NS_BOXPROP_SOURCE_LOGICAL);
  9221   case eCSSProperty_padding_left:
  9222     return ParseDirectionalBoxProperty(eCSSProperty_padding_left,
  9223                                        NS_BOXPROP_SOURCE_PHYSICAL);
  9224   case eCSSProperty_padding_right:
  9225     return ParseDirectionalBoxProperty(eCSSProperty_padding_right,
  9226                                        NS_BOXPROP_SOURCE_PHYSICAL);
  9227   case eCSSProperty_padding_start:
  9228     return ParseDirectionalBoxProperty(eCSSProperty_padding_start,
  9229                                        NS_BOXPROP_SOURCE_LOGICAL);
  9230   case eCSSProperty_quotes:
  9231     return ParseQuotes();
  9232   case eCSSProperty_size:
  9233     return ParseSize();
  9234   case eCSSProperty_text_decoration:
  9235     return ParseTextDecoration();
  9236   case eCSSProperty_will_change:
  9237     return ParseWillChange();
  9238   case eCSSProperty_transform:
  9239     return ParseTransform(false);
  9240   case eCSSProperty__moz_transform:
  9241     return ParseTransform(true);
  9242   case eCSSProperty_transform_origin:
  9243     return ParseTransformOrigin(false);
  9244   case eCSSProperty_perspective_origin:
  9245     return ParseTransformOrigin(true);
  9246   case eCSSProperty_transition:
  9247     return ParseTransition();
  9248   case eCSSProperty_animation:
  9249     return ParseAnimation();
  9250   case eCSSProperty_transition_property:
  9251     return ParseTransitionProperty();
  9252   case eCSSProperty_fill:
  9253   case eCSSProperty_stroke:
  9254     return ParsePaint(aPropID);
  9255   case eCSSProperty_stroke_dasharray:
  9256     return ParseDasharray();
  9257   case eCSSProperty_marker:
  9258     return ParseMarker();
  9259   case eCSSProperty_paint_order:
  9260     return ParsePaintOrder();
  9261   case eCSSProperty_all:
  9262     return ParseAll();
  9263   default:
  9264     NS_ABORT_IF_FALSE(false, "should not be called");
  9265     return false;
  9269 // Bits used in determining which background position info we have
  9270 #define BG_CENTER  NS_STYLE_BG_POSITION_CENTER
  9271 #define BG_TOP     NS_STYLE_BG_POSITION_TOP
  9272 #define BG_BOTTOM  NS_STYLE_BG_POSITION_BOTTOM
  9273 #define BG_LEFT    NS_STYLE_BG_POSITION_LEFT
  9274 #define BG_RIGHT   NS_STYLE_BG_POSITION_RIGHT
  9275 #define BG_CTB    (BG_CENTER | BG_TOP | BG_BOTTOM)
  9276 #define BG_TB     (BG_TOP | BG_BOTTOM)
  9277 #define BG_CLR    (BG_CENTER | BG_LEFT | BG_RIGHT)
  9278 #define BG_LR     (BG_LEFT | BG_RIGHT)
  9280 bool
  9281 CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
  9282                                         nsCSSProperty aPropID)
  9284   if (aPropID == eCSSPropertyExtra_x_none_value) {
  9285     return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nullptr);
  9288   if (aPropID == eCSSPropertyExtra_x_auto_value) {
  9289     return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nullptr);
  9292   if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
  9293     NS_ABORT_IF_FALSE(false, "not a single value property");
  9294     return false;
  9297   if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) {
  9298     switch (aPropID) {
  9299       case eCSSProperty_font_family:
  9300         return ParseFamily(aValue);
  9301       case eCSSProperty_font_synthesis:
  9302         return ParseFontSynthesis(aValue);
  9303       case eCSSProperty_font_variant_alternates:
  9304         return ParseFontVariantAlternates(aValue);
  9305       case eCSSProperty_font_variant_east_asian:
  9306         return ParseFontVariantEastAsian(aValue);
  9307       case eCSSProperty_font_variant_ligatures:
  9308         return ParseFontVariantLigatures(aValue);
  9309       case eCSSProperty_font_variant_numeric:
  9310         return ParseFontVariantNumeric(aValue);
  9311       case eCSSProperty_font_feature_settings:
  9312         return ParseFontFeatureSettings(aValue);
  9313       case eCSSProperty_font_weight:
  9314         return ParseFontWeight(aValue);
  9315       case eCSSProperty_image_orientation:
  9316         return ParseImageOrientation(aValue);
  9317       case eCSSProperty_marks:
  9318         return ParseMarks(aValue);
  9319       case eCSSProperty_text_align:
  9320         return ParseTextAlign(aValue);
  9321       case eCSSProperty_text_align_last:
  9322         return ParseTextAlignLast(aValue);
  9323       case eCSSProperty_text_decoration_line:
  9324         return ParseTextDecorationLine(aValue);
  9325       case eCSSProperty_text_combine_upright:
  9326         return ParseTextCombineUpright(aValue);
  9327       case eCSSProperty_text_overflow:
  9328         return ParseTextOverflow(aValue);
  9329       case eCSSProperty_touch_action:
  9330         return ParseTouchAction(aValue);
  9331       default:
  9332         NS_ABORT_IF_FALSE(false, "should not reach here");
  9333         return false;
  9337   uint32_t variant = nsCSSProps::ParserVariant(aPropID);
  9338   if (variant == 0) {
  9339     NS_ABORT_IF_FALSE(false, "not a single value property");
  9340     return false;
  9343   // We only allow 'script-level' when unsafe rules are enabled, because
  9344   // otherwise it could interfere with rulenode optimizations if used in
  9345   // a non-MathML-enabled document. We also only allow math-display when
  9346   // unsafe rules are enabled.
  9347   if (!mUnsafeRulesEnabled &&
  9348       (aPropID == eCSSProperty_script_level ||
  9349        aPropID == eCSSProperty_math_display))
  9350     return false;
  9352   const KTableValue *kwtable = nsCSSProps::kKeywordTableTable[aPropID];
  9353   switch (nsCSSProps::ValueRestrictions(aPropID)) {
  9354     default:
  9355       NS_ABORT_IF_FALSE(false, "should not be reached");
  9356     case 0:
  9357       return ParseVariant(aValue, variant, kwtable);
  9358     case CSS_PROPERTY_VALUE_NONNEGATIVE:
  9359       return ParseNonNegativeVariant(aValue, variant, kwtable);
  9360     case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
  9361       return ParseOneOrLargerVariant(aValue, variant, kwtable);
  9365 // nsFont::EnumerateFamilies callback for ParseFontDescriptorValue
  9366 struct MOZ_STACK_CLASS ExtractFirstFamilyData {
  9367   nsAutoString mFamilyName;
  9368   bool mGood;
  9369   ExtractFirstFamilyData() : mFamilyName(), mGood(false) {}
  9370 };
  9372 static bool
  9373 ExtractFirstFamily(const nsString& aFamily,
  9374                    bool aGeneric,
  9375                    void* aData)
  9377   ExtractFirstFamilyData* realData = (ExtractFirstFamilyData*) aData;
  9378   if (aGeneric || realData->mFamilyName.Length() > 0) {
  9379     realData->mGood = false;
  9380     return false;
  9382   realData->mFamilyName.Assign(aFamily);
  9383   realData->mGood = true;
  9384   return true;
  9387 // font-descriptor: descriptor ':' value ';'
  9388 // caller has advanced mToken to point at the descriptor
  9389 bool
  9390 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
  9391                                         nsCSSValue& aValue)
  9393   switch (aDescID) {
  9394     // These four are similar to the properties of the same name,
  9395     // possibly with more restrictions on the values they can take.
  9396   case eCSSFontDesc_Family: {
  9397     if (!ParseFamily(aValue) ||
  9398         aValue.GetUnit() != eCSSUnit_Families)
  9399       return false;
  9401     // the style parameters to the nsFont constructor are ignored,
  9402     // because it's only being used to call EnumerateFamilies
  9403     nsAutoString valueStr;
  9404     aValue.GetStringValue(valueStr);
  9405     nsFont font(valueStr, 0, 0, 0, 0, 0, 0);
  9406     ExtractFirstFamilyData dat;
  9408     font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
  9409     if (!dat.mGood)
  9410       return false;
  9412     aValue.SetStringValue(dat.mFamilyName, eCSSUnit_String);
  9413     return true;
  9416   case eCSSFontDesc_Style:
  9417     // property is VARIANT_HMK|VARIANT_SYSFONT
  9418     return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
  9419                         nsCSSProps::kFontStyleKTable);
  9421   case eCSSFontDesc_Weight:
  9422     return (ParseFontWeight(aValue) &&
  9423             aValue.GetUnit() != eCSSUnit_Inherit &&
  9424             aValue.GetUnit() != eCSSUnit_Initial &&
  9425             aValue.GetUnit() != eCSSUnit_Unset &&
  9426             (aValue.GetUnit() != eCSSUnit_Enumerated ||
  9427              (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
  9428               aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
  9430   case eCSSFontDesc_Stretch:
  9431     // property is VARIANT_HK|VARIANT_SYSFONT
  9432     return ParseVariant(aValue, VARIANT_KEYWORD,
  9433                         nsCSSProps::kFontStretchKTable);
  9435     // These two are unique to @font-face and have their own special grammar.
  9436   case eCSSFontDesc_Src:
  9437     return ParseFontSrc(aValue);
  9439   case eCSSFontDesc_UnicodeRange:
  9440     return ParseFontRanges(aValue);
  9442   case eCSSFontDesc_FontFeatureSettings:
  9443     return ParseFontFeatureSettings(aValue);
  9445   case eCSSFontDesc_FontLanguageOverride:
  9446     return ParseVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, nullptr);
  9448   case eCSSFontDesc_UNKNOWN:
  9449   case eCSSFontDesc_COUNT:
  9450     NS_NOTREACHED("bad nsCSSFontDesc code");
  9452   // explicitly do NOT have a default case to let the compiler
  9453   // help find missing descriptors
  9454   return false;
  9457 void
  9458 CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties)
  9460   nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
  9461   for (const nsCSSProperty *prop = aSourceProperties;
  9462        *prop != eCSSProperty_UNKNOWN; ++prop) {
  9463     AppendValue(*prop, physical);
  9467 static nsCSSValue
  9468 BoxPositionMaskToCSSValue(int32_t aMask, bool isX)
  9470   int32_t val = NS_STYLE_BG_POSITION_CENTER;
  9471   if (isX) {
  9472     if (aMask & BG_LEFT) {
  9473       val = NS_STYLE_BG_POSITION_LEFT;
  9475     else if (aMask & BG_RIGHT) {
  9476       val = NS_STYLE_BG_POSITION_RIGHT;
  9479   else {
  9480     if (aMask & BG_TOP) {
  9481       val = NS_STYLE_BG_POSITION_TOP;
  9483     else if (aMask & BG_BOTTOM) {
  9484       val = NS_STYLE_BG_POSITION_BOTTOM;
  9488   return nsCSSValue(val, eCSSUnit_Enumerated);
  9491 bool
  9492 CSSParserImpl::ParseBackground()
  9494   nsAutoParseCompoundProperty compound(this);
  9496   // background-color can only be set once, so it's not a list.
  9497   nsCSSValue color;
  9499   // Check first for inherit/initial/unset.
  9500   if (ParseVariant(color, VARIANT_INHERIT, nullptr)) {
  9501     // must be alone
  9502     for (const nsCSSProperty* subprops =
  9503            nsCSSProps::SubpropertyEntryFor(eCSSProperty_background);
  9504          *subprops != eCSSProperty_UNKNOWN; ++subprops) {
  9505       AppendValue(*subprops, color);
  9507     return true;
  9510   nsCSSValue image, repeat, attachment, clip, origin, position, size;
  9511   BackgroundParseState state(color, image.SetListValue(), 
  9512                              repeat.SetPairListValue(),
  9513                              attachment.SetListValue(), clip.SetListValue(),
  9514                              origin.SetListValue(), position.SetListValue(),
  9515                              size.SetPairListValue());
  9517   for (;;) {
  9518     if (!ParseBackgroundItem(state)) {
  9519       return false;
  9521     // If we saw a color, this must be the last item.
  9522     if (color.GetUnit() != eCSSUnit_Null) {
  9523       break;
  9525     // If there's a comma, expect another item.
  9526     if (!ExpectSymbol(',', true)) {
  9527       break;
  9529     // Chain another entry on all the lists.
  9530     state.mImage->mNext = new nsCSSValueList;
  9531     state.mImage = state.mImage->mNext;
  9532     state.mRepeat->mNext = new nsCSSValuePairList;
  9533     state.mRepeat = state.mRepeat->mNext;
  9534     state.mAttachment->mNext = new nsCSSValueList;
  9535     state.mAttachment = state.mAttachment->mNext;
  9536     state.mClip->mNext = new nsCSSValueList;
  9537     state.mClip = state.mClip->mNext;
  9538     state.mOrigin->mNext = new nsCSSValueList;
  9539     state.mOrigin = state.mOrigin->mNext;
  9540     state.mPosition->mNext = new nsCSSValueList;
  9541     state.mPosition = state.mPosition->mNext;
  9542     state.mSize->mNext = new nsCSSValuePairList;
  9543     state.mSize = state.mSize->mNext;
  9546   // If we get to this point without seeing a color, provide a default.
  9547   if (color.GetUnit() == eCSSUnit_Null) {
  9548     color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor);
  9551   AppendValue(eCSSProperty_background_image,      image);
  9552   AppendValue(eCSSProperty_background_repeat,     repeat);
  9553   AppendValue(eCSSProperty_background_attachment, attachment);
  9554   AppendValue(eCSSProperty_background_clip,       clip);
  9555   AppendValue(eCSSProperty_background_origin,     origin);
  9556   AppendValue(eCSSProperty_background_position,   position);
  9557   AppendValue(eCSSProperty_background_size,       size);
  9558   AppendValue(eCSSProperty_background_color,      color);
  9559   return true;
  9562 // Parse one item of the background shorthand property.
  9563 bool
  9564 CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState)
  9567   // Fill in the values that the shorthand will set if we don't find
  9568   // other values.
  9569   aState.mImage->mValue.SetNoneValue();
  9570   aState.mRepeat->mXValue.SetIntValue(NS_STYLE_BG_REPEAT_REPEAT,
  9571                                       eCSSUnit_Enumerated);
  9572   aState.mRepeat->mYValue.Reset();
  9573   aState.mAttachment->mValue.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL,
  9574                                          eCSSUnit_Enumerated);
  9575   aState.mClip->mValue.SetIntValue(NS_STYLE_BG_CLIP_BORDER,
  9576                                    eCSSUnit_Enumerated);
  9577   aState.mOrigin->mValue.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING,
  9578                                      eCSSUnit_Enumerated);
  9579   nsRefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4);
  9580   aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array);
  9581   positionArr->Item(1).SetPercentValue(0.0f);
  9582   positionArr->Item(3).SetPercentValue(0.0f);
  9583   aState.mSize->mXValue.SetAutoValue();
  9584   aState.mSize->mYValue.SetAutoValue();
  9586   bool haveColor = false,
  9587        haveImage = false,
  9588        haveRepeat = false,
  9589        haveAttach = false,
  9590        havePositionAndSize = false,
  9591        haveOrigin = false,
  9592        haveSomething = false;
  9594   while (GetToken(true)) {
  9595     nsCSSTokenType tt = mToken.mType;
  9596     UngetToken(); // ...but we'll still cheat and use mToken
  9597     if (tt == eCSSToken_Symbol) {
  9598       // ExpectEndProperty only looks for symbols, and nothing else will
  9599       // show up as one.
  9600       break;
  9603     if (tt == eCSSToken_Ident) {
  9604       nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
  9605       int32_t dummy;
  9606       if (keyword == eCSSKeyword_inherit ||
  9607           keyword == eCSSKeyword_initial ||
  9608           keyword == eCSSKeyword_unset) {
  9609         return false;
  9610       } else if (keyword == eCSSKeyword_none) {
  9611         if (haveImage)
  9612           return false;
  9613         haveImage = true;
  9614         if (!ParseSingleValueProperty(aState.mImage->mValue,
  9615                                       eCSSProperty_background_image)) {
  9616           NS_NOTREACHED("should be able to parse");
  9617           return false;
  9619       } else if (nsCSSProps::FindKeyword(keyword,
  9620                    nsCSSProps::kBackgroundAttachmentKTable, dummy)) {
  9621         if (haveAttach)
  9622           return false;
  9623         haveAttach = true;
  9624         if (!ParseSingleValueProperty(aState.mAttachment->mValue,
  9625                                       eCSSProperty_background_attachment)) {
  9626           NS_NOTREACHED("should be able to parse");
  9627           return false;
  9629       } else if (nsCSSProps::FindKeyword(keyword,
  9630                    nsCSSProps::kBackgroundRepeatKTable, dummy)) {
  9631         if (haveRepeat)
  9632           return false;
  9633         haveRepeat = true;
  9634         nsCSSValuePair scratch;
  9635         if (!ParseBackgroundRepeatValues(scratch)) {
  9636           NS_NOTREACHED("should be able to parse");
  9637           return false;
  9639         aState.mRepeat->mXValue = scratch.mXValue;
  9640         aState.mRepeat->mYValue = scratch.mYValue;
  9641       } else if (nsCSSProps::FindKeyword(keyword,
  9642                    nsCSSProps::kBackgroundPositionKTable, dummy)) {
  9643         if (havePositionAndSize)
  9644           return false;
  9645         havePositionAndSize = true;
  9646         if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) {
  9647           return false;
  9649         if (ExpectSymbol('/', true)) {
  9650           nsCSSValuePair scratch;
  9651           if (!ParseBackgroundSizeValues(scratch)) {
  9652             return false;
  9654           aState.mSize->mXValue = scratch.mXValue;
  9655           aState.mSize->mYValue = scratch.mYValue;
  9657       } else if (nsCSSProps::FindKeyword(keyword,
  9658                    nsCSSProps::kBackgroundOriginKTable, dummy)) {
  9659         if (haveOrigin)
  9660           return false;
  9661         haveOrigin = true;
  9662         if (!ParseSingleValueProperty(aState.mOrigin->mValue,
  9663                                       eCSSProperty_background_origin)) {
  9664           NS_NOTREACHED("should be able to parse");
  9665           return false;
  9668         // The spec allows a second box value (for background-clip),
  9669         // immediately following the first one (for background-origin).
  9671         // 'background-clip' and 'background-origin' use the same keyword table
  9672         MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
  9673                      eCSSProperty_background_origin] ==
  9674                    nsCSSProps::kBackgroundOriginKTable);
  9675         MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
  9676                      eCSSProperty_background_clip] ==
  9677                    nsCSSProps::kBackgroundOriginKTable);
  9678         static_assert(NS_STYLE_BG_CLIP_BORDER ==
  9679                       NS_STYLE_BG_ORIGIN_BORDER &&
  9680                       NS_STYLE_BG_CLIP_PADDING ==
  9681                       NS_STYLE_BG_ORIGIN_PADDING &&
  9682                       NS_STYLE_BG_CLIP_CONTENT ==
  9683                       NS_STYLE_BG_ORIGIN_CONTENT,
  9684                       "bg-clip and bg-origin style constants must agree");
  9686         if (!ParseSingleValueProperty(aState.mClip->mValue,
  9687                                       eCSSProperty_background_clip)) {
  9688           // When exactly one <box> value is set, it is used for both
  9689           // 'background-origin' and 'background-clip'.
  9690           // See assertions above showing these values are compatible.
  9691           aState.mClip->mValue = aState.mOrigin->mValue;
  9693       } else {
  9694         if (haveColor)
  9695           return false;
  9696         haveColor = true;
  9697         if (!ParseSingleValueProperty(aState.mColor,
  9698                                       eCSSProperty_background_color)) {
  9699           return false;
  9702     } else if (tt == eCSSToken_URL ||
  9703                (tt == eCSSToken_Function &&
  9704                 (mToken.mIdent.LowerCaseEqualsLiteral("linear-gradient") ||
  9705                  mToken.mIdent.LowerCaseEqualsLiteral("radial-gradient") ||
  9706                  mToken.mIdent.LowerCaseEqualsLiteral("repeating-linear-gradient") ||
  9707                  mToken.mIdent.LowerCaseEqualsLiteral("repeating-radial-gradient") ||
  9708                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
  9709                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
  9710                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
  9711                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
  9712                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect") ||
  9713                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-element")))) {
  9714       if (haveImage)
  9715         return false;
  9716       haveImage = true;
  9717       if (!ParseSingleValueProperty(aState.mImage->mValue,
  9718                                     eCSSProperty_background_image)) {
  9719         return false;
  9721     } else if (tt == eCSSToken_Dimension ||
  9722                tt == eCSSToken_Number ||
  9723                tt == eCSSToken_Percentage ||
  9724                (tt == eCSSToken_Function &&
  9725                 (mToken.mIdent.LowerCaseEqualsLiteral("calc") ||
  9726                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")))) {
  9727       if (havePositionAndSize)
  9728         return false;
  9729       havePositionAndSize = true;
  9730       if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) {
  9731         return false;
  9733       if (ExpectSymbol('/', true)) {
  9734         nsCSSValuePair scratch;
  9735         if (!ParseBackgroundSizeValues(scratch)) {
  9736           return false;
  9738         aState.mSize->mXValue = scratch.mXValue;
  9739         aState.mSize->mYValue = scratch.mYValue;
  9741     } else {
  9742       if (haveColor)
  9743         return false;
  9744       haveColor = true;
  9745       // Note: This parses 'inherit', 'initial' and 'unset', but
  9746       // we've already checked for them, so it's ok.
  9747       if (!ParseSingleValueProperty(aState.mColor,
  9748                                     eCSSProperty_background_color)) {
  9749         return false;
  9752     haveSomething = true;
  9755   return haveSomething;
  9758 // This function is very similar to ParseBackgroundPosition and
  9759 // ParseBackgroundSize.
  9760 bool
  9761 CSSParserImpl::ParseValueList(nsCSSProperty aPropID)
  9763   // aPropID is a single value prop-id
  9764   nsCSSValue value;
  9765   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
  9766   if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
  9767     nsCSSValueList* item = value.SetListValue();
  9768     for (;;) {
  9769       if (!ParseSingleValueProperty(item->mValue, aPropID)) {
  9770         return false;
  9772       if (!ExpectSymbol(',', true)) {
  9773         break;
  9775       item->mNext = new nsCSSValueList;
  9776       item = item->mNext;
  9779   AppendValue(aPropID, value);
  9780   return true;
  9783 bool
  9784 CSSParserImpl::ParseBackgroundRepeat()
  9786   nsCSSValue value;
  9787   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
  9788   if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
  9789     nsCSSValuePair valuePair;
  9790     if (!ParseBackgroundRepeatValues(valuePair)) {
  9791       return false;
  9793     nsCSSValuePairList* item = value.SetPairListValue();
  9794     for (;;) {
  9795       item->mXValue = valuePair.mXValue;
  9796       item->mYValue = valuePair.mYValue;
  9797       if (!ExpectSymbol(',', true)) {
  9798         break;
  9800       if (!ParseBackgroundRepeatValues(valuePair)) {
  9801         return false;
  9803       item->mNext = new nsCSSValuePairList;
  9804       item = item->mNext;
  9808   AppendValue(eCSSProperty_background_repeat, value);
  9809   return true;
  9812 bool
  9813 CSSParserImpl::ParseBackgroundRepeatValues(nsCSSValuePair& aValue) 
  9815   nsCSSValue& xValue = aValue.mXValue;
  9816   nsCSSValue& yValue = aValue.mYValue;
  9818   if (ParseEnum(xValue, nsCSSProps::kBackgroundRepeatKTable)) {
  9819     int32_t value = xValue.GetIntValue();
  9820     // For single values set yValue as eCSSUnit_Null.
  9821     if (value == NS_STYLE_BG_REPEAT_REPEAT_X ||
  9822         value == NS_STYLE_BG_REPEAT_REPEAT_Y ||
  9823         !ParseEnum(yValue, nsCSSProps::kBackgroundRepeatPartKTable)) {
  9824       // the caller will fail cases like "repeat-x no-repeat"
  9825       // by expecting a list separator or an end property.
  9826       yValue.Reset();
  9828     return true;
  9831   return false;
  9834 // This function is very similar to ParseBackgroundList and ParseBackgroundSize.
  9835 bool
  9836 CSSParserImpl::ParseBackgroundPosition()
  9838   nsCSSValue value;
  9839   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
  9840   if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
  9841     nsCSSValue itemValue;
  9842     if (!ParseBackgroundPositionValues(itemValue, false)) {
  9843       return false;
  9845     nsCSSValueList* item = value.SetListValue();
  9846     for (;;) {
  9847       item->mValue = itemValue;
  9848       if (!ExpectSymbol(',', true)) {
  9849         break;
  9851       if (!ParseBackgroundPositionValues(itemValue, false)) {
  9852         return false;
  9854       item->mNext = new nsCSSValueList;
  9855       item = item->mNext;
  9858   AppendValue(eCSSProperty_background_position, value);
  9859   return true;
  9862 /**
  9863  * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used
  9864  * for parsing the CSS 2.1 background-position syntax (which has at
  9865  * most two values).  (Compare to the css3-background syntax which
  9866  * takes up to four values.)  Some current CSS specifications that
  9867  * use background-position-like syntax still use this old syntax.
  9868  **
  9869  * Parses two values that correspond to positions in a box.  These can be
  9870  * values corresponding to percentages of the box, raw offsets, or keywords
  9871  * like "top," "left center," etc.
  9873  * @param aOut The nsCSSValuePair in which to place the result.
  9874  * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are
  9875  *   legal values
  9876  * @param aAllowExplicitCenter If true, 'center' is a legal value
  9877  * @return Whether or not the operation succeeded.
  9878  */
  9879 bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
  9880                                            bool aAcceptsInherit,
  9881                                            bool aAllowExplicitCenter)
  9883   // First try a percentage or a length value
  9884   nsCSSValue &xValue = aOut.mXValue,
  9885              &yValue = aOut.mYValue;
  9886   int32_t variantMask =
  9887     (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC;
  9888   if (ParseVariant(xValue, variantMask, nullptr)) {
  9889     if (eCSSUnit_Inherit == xValue.GetUnit() ||
  9890         eCSSUnit_Initial == xValue.GetUnit() ||
  9891         eCSSUnit_Unset == xValue.GetUnit()) {  // both are inherit, initial or unset
  9892       yValue = xValue;
  9893       return true;
  9895     // We have one percentage/length/calc. Get the optional second
  9896     // percentage/length/calc/keyword.
  9897     if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
  9898       // We have two numbers
  9899       return true;
  9902     if (ParseEnum(yValue, nsCSSProps::kBackgroundPositionKTable)) {
  9903       int32_t yVal = yValue.GetIntValue();
  9904       if (!(yVal & BG_CTB)) {
  9905         // The second keyword can only be 'center', 'top', or 'bottom'
  9906         return false;
  9908       yValue = BoxPositionMaskToCSSValue(yVal, false);
  9909       return true;
  9912     // If only one percentage or length value is given, it sets the
  9913     // horizontal position only, and the vertical position will be 50%.
  9914     yValue.SetPercentValue(0.5f);
  9915     return true;
  9918   // Now try keywords. We do this manually to allow for the first
  9919   // appearance of "center" to apply to the either the x or y
  9920   // position (it's ambiguous so we have to disambiguate). Each
  9921   // allowed keyword value is assigned it's own bit. We don't allow
  9922   // any duplicate keywords other than center. We try to get two
  9923   // keywords but it's okay if there is only one.
  9924   int32_t mask = 0;
  9925   if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
  9926     int32_t bit = xValue.GetIntValue();
  9927     mask |= bit;
  9928     if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
  9929       bit = xValue.GetIntValue();
  9930       if (mask & (bit & ~BG_CENTER)) {
  9931         // Only the 'center' keyword can be duplicated.
  9932         return false;
  9934       mask |= bit;
  9936     else {
  9937       // Only one keyword.  See if we have a length, percentage, or calc.
  9938       if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
  9939         if (!(mask & BG_CLR)) {
  9940           // The first keyword can only be 'center', 'left', or 'right'
  9941           return false;
  9944         xValue = BoxPositionMaskToCSSValue(mask, true);
  9945         return true;
  9950   // Check for bad input. Bad input consists of no matching keywords,
  9951   // or pairs of x keywords or pairs of y keywords.
  9952   if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
  9953       (mask == (BG_LEFT | BG_RIGHT)) ||
  9954       (!aAllowExplicitCenter && (mask & BG_CENTER))) {
  9955     return false;
  9958   // Create style values
  9959   xValue = BoxPositionMaskToCSSValue(mask, true);
  9960   yValue = BoxPositionMaskToCSSValue(mask, false);
  9961   return true;
  9964 bool CSSParserImpl::ParseBackgroundPositionValues(nsCSSValue& aOut,
  9965                                                   bool aAcceptsInherit)
  9967   // css3-background allows positions to be defined as offsets
  9968   // from an edge. There can be 2 keywords and 2 offsets given. These
  9969   // four 'values' are stored in an array in the following order:
  9970   // [keyword offset keyword offset]. If a keyword or offset isn't
  9971   // parsed the value of the corresponding array element is set
  9972   // to eCSSUnit_Null by a call to nsCSSValue::Reset().
  9973   if (aAcceptsInherit && ParseVariant(aOut, VARIANT_INHERIT, nullptr)) {
  9974     return true;
  9977   nsRefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(4);
  9978   aOut.SetArrayValue(value, eCSSUnit_Array);
  9980   // The following clarifies organisation of the array.
  9981   nsCSSValue &xEdge   = value->Item(0),
  9982              &xOffset = value->Item(1),
  9983              &yEdge   = value->Item(2),
  9984              &yOffset = value->Item(3);
  9986   // Parse all the values into the array.
  9987   uint32_t valueCount = 0;
  9988   for (int32_t i = 0; i < 4; i++) {
  9989     if (!ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD,
  9990                       nsCSSProps::kBackgroundPositionKTable)) {
  9991       break;
  9993     ++valueCount;
  9996   switch (valueCount) {
  9997     case 4:
  9998       // "If three or four values are given, then each <percentage> or <length>
  9999       // represents an offset and must be preceded by a keyword, which specifies
 10000       // from which edge the offset is given."
 10001       if (eCSSUnit_Enumerated != xEdge.GetUnit() ||
 10002           BG_CENTER == xEdge.GetIntValue() ||
 10003           eCSSUnit_Enumerated == xOffset.GetUnit() ||
 10004           eCSSUnit_Enumerated != yEdge.GetUnit() ||
 10005           BG_CENTER == yEdge.GetIntValue() ||
 10006           eCSSUnit_Enumerated == yOffset.GetUnit()) {
 10007         return false;
 10009       break;
 10010     case 3:
 10011       // "If three or four values are given, then each <percentage> or<length>
 10012       // represents an offset and must be preceded by a keyword, which specifies
 10013       // from which edge the offset is given." ... "If three values are given,
 10014       // the missing offset is assumed to be zero."
 10015       if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) {
 10016         // keyword offset keyword
 10017         // Second value is non-keyword, thus first value must be a non-center
 10018         // keyword.
 10019         if (eCSSUnit_Enumerated != value->Item(0).GetUnit() ||
 10020             BG_CENTER == value->Item(0).GetIntValue()) {
 10021           return false;
 10024         // Remaining value must be a keyword.
 10025         if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
 10026           return false;
 10029         yOffset.Reset(); // Everything else is in the correct position.
 10030       } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
 10031         // keyword keyword offset
 10032         // Third value is non-keyword, thus second value must be non-center
 10033         // keyword.
 10034         if (BG_CENTER == value->Item(1).GetIntValue()) {
 10035           return false;
 10038         // Remaining value must be a keyword.
 10039         if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) {
 10040           return false;
 10043         // Move the values to the correct position in the array.
 10044         value->Item(3) = value->Item(2); // yOffset
 10045         value->Item(2) = value->Item(1); // yEdge
 10046         value->Item(1).Reset(); // xOffset
 10047       } else {
 10048         return false;
 10050       break;
 10051     case 2:
 10052       // "If two values are given and at least one value is not a keyword, then
 10053       // the first value represents the horizontal position (or offset) and the
 10054       // second represents the vertical position (or offset)"
 10055       if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
 10056         if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
 10057           // keyword keyword
 10058           value->Item(2) = value->Item(1); // move yEdge to correct position
 10059           xOffset.Reset();
 10060           yOffset.Reset();
 10061         } else {
 10062           // keyword offset
 10063           // First value must represent horizontal position.
 10064           if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) {
 10065             return false;
 10067           value->Item(3) = value->Item(1); // move yOffset to correct position
 10068           xOffset.Reset();
 10069           yEdge.Reset();
 10071       } else {
 10072         if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
 10073           // offset keyword
 10074           // Second value must represent vertical position.
 10075           if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) {
 10076             return false;
 10078           value->Item(2) = value->Item(1); // move yEdge to correct position
 10079           value->Item(1) = value->Item(0); // move xOffset to correct position
 10080           xEdge.Reset();
 10081           yOffset.Reset();
 10082         } else {
 10083           // offset offset
 10084           value->Item(3) = value->Item(1); // move yOffset to correct position
 10085           value->Item(1) = value->Item(0); // move xOffset to correct position
 10086           xEdge.Reset();
 10087           yEdge.Reset();
 10090       break;
 10091     case 1:
 10092       // "If only one value is specified, the second value is assumed to be
 10093       // center."
 10094       if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
 10095         xOffset.Reset();
 10096       } else {
 10097         value->Item(1) = value->Item(0); // move xOffset to correct position
 10098         xEdge.Reset();
 10100       yEdge.SetIntValue(NS_STYLE_BG_POSITION_CENTER, eCSSUnit_Enumerated);
 10101       yOffset.Reset();
 10102       break;
 10103     default:
 10104       return false;
 10107   // For compatibility with CSS2.1 code the edges can be unspecified.
 10108   // Unspecified edges are recorded as nullptr.
 10109   NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit()  ||
 10110                 eCSSUnit_Null       == xEdge.GetUnit()) &&
 10111                (eCSSUnit_Enumerated == yEdge.GetUnit()  ||
 10112                 eCSSUnit_Null       == yEdge.GetUnit()) &&
 10113                 eCSSUnit_Enumerated != xOffset.GetUnit()  &&
 10114                 eCSSUnit_Enumerated != yOffset.GetUnit(),
 10115                 "Unexpected units");
 10117   // Keywords in first and second pairs can not both be vertical or
 10118   // horizontal keywords. (eg. left right, bottom top). Additionally,
 10119   // non-center keyword can not be duplicated (eg. left left).
 10120   int32_t xEdgeEnum =
 10121           xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0;
 10122   int32_t yEdgeEnum =
 10123           yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0;
 10124   if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) ||
 10125       (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) ||
 10126       (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) {
 10127     return false;
 10130   // The values could be in an order that is different than expected.
 10131   // eg. x contains vertical information, y contains horizontal information.
 10132   // Swap if incorrect order.
 10133   if (xEdgeEnum & (BG_TOP | BG_BOTTOM) ||
 10134       yEdgeEnum & (BG_LEFT | BG_RIGHT)) {
 10135     nsCSSValue swapEdge = xEdge;
 10136     nsCSSValue swapOffset = xOffset;
 10137     xEdge = yEdge;
 10138     xOffset = yOffset;
 10139     yEdge = swapEdge;
 10140     yOffset = swapOffset;
 10143   return true;
 10146 // This function is very similar to ParseBackgroundList and
 10147 // ParseBackgroundPosition.
 10148 bool
 10149 CSSParserImpl::ParseBackgroundSize()
 10151   nsCSSValue value;
 10152   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
 10153   if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
 10154     nsCSSValuePair valuePair;
 10155     if (!ParseBackgroundSizeValues(valuePair)) {
 10156       return false;
 10158     nsCSSValuePairList* item = value.SetPairListValue();
 10159     for (;;) {
 10160       item->mXValue = valuePair.mXValue;
 10161       item->mYValue = valuePair.mYValue;
 10162       if (!ExpectSymbol(',', true)) {
 10163         break;
 10165       if (!ParseBackgroundSizeValues(valuePair)) {
 10166         return false;
 10168       item->mNext = new nsCSSValuePairList;
 10169       item = item->mNext;
 10172   AppendValue(eCSSProperty_background_size, value);
 10173   return true;
 10176 /**
 10177  * Parses two values that correspond to lengths for the background-size
 10178  * property.  These can be one or two lengths (or the 'auto' keyword) or
 10179  * percentages corresponding to the element's dimensions or the single keywords
 10180  * 'contain' or 'cover'.  'initial', 'inherit' and 'unset' must be handled by
 10181  * the caller if desired.
 10183  * @param aOut The nsCSSValuePair in which to place the result.
 10184  * @return Whether or not the operation succeeded.
 10185  */
 10186 #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC)
 10187 bool CSSParserImpl::ParseBackgroundSizeValues(nsCSSValuePair &aOut)
 10189   // First try a percentage or a length value
 10190   nsCSSValue &xValue = aOut.mXValue,
 10191              &yValue = aOut.mYValue;
 10192   if (ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr)) {
 10193     // We have one percentage/length/calc/auto. Get the optional second
 10194     // percentage/length/calc/keyword.
 10195     if (ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr)) {
 10196       // We have a second percentage/length/calc/auto.
 10197       return true;
 10200     // If only one percentage or length value is given, it sets the
 10201     // horizontal size only, and the vertical size will be as if by 'auto'.
 10202     yValue.SetAutoValue();
 10203     return true;
 10206   // Now address 'contain' and 'cover'.
 10207   if (!ParseEnum(xValue, nsCSSProps::kBackgroundSizeKTable))
 10208     return false;
 10209   yValue.Reset();
 10210   return true;
 10212 #undef BG_SIZE_VARIANT
 10214 bool
 10215 CSSParserImpl::ParseBorderColor()
 10217   static const nsCSSProperty kBorderColorSources[] = {
 10218     eCSSProperty_border_left_color_ltr_source,
 10219     eCSSProperty_border_left_color_rtl_source,
 10220     eCSSProperty_border_right_color_ltr_source,
 10221     eCSSProperty_border_right_color_rtl_source,
 10222     eCSSProperty_UNKNOWN
 10223   };
 10225   // do this now, in case 4 values weren't specified
 10226   InitBoxPropsAsPhysical(kBorderColorSources);
 10227   return ParseBoxProperties(kBorderColorIDs);
 10230 void
 10231 CSSParserImpl::SetBorderImageInitialValues()
 10233   // border-image-source: none
 10234   nsCSSValue source;
 10235   source.SetNoneValue();
 10236   AppendValue(eCSSProperty_border_image_source, source);
 10238   // border-image-slice: 100%
 10239   nsCSSValue sliceBoxValue;
 10240   nsCSSRect& sliceBox = sliceBoxValue.SetRectValue();
 10241   sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent));
 10242   nsCSSValue slice;
 10243   nsCSSValueList* sliceList = slice.SetListValue();
 10244   sliceList->mValue = sliceBoxValue;
 10245   AppendValue(eCSSProperty_border_image_slice, slice);
 10247   // border-image-width: 1
 10248   nsCSSValue width;
 10249   nsCSSRect& widthBox = width.SetRectValue();
 10250   widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number));
 10251   AppendValue(eCSSProperty_border_image_width, width);
 10253   // border-image-outset: 0
 10254   nsCSSValue outset;
 10255   nsCSSRect& outsetBox = outset.SetRectValue();
 10256   outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number));
 10257   AppendValue(eCSSProperty_border_image_outset, outset);
 10259   // border-image-repeat: repeat
 10260   nsCSSValue repeat;
 10261   nsCSSValuePair repeatPair;
 10262   repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
 10263                                         eCSSUnit_Enumerated));
 10264   repeat.SetPairValue(&repeatPair);
 10265   AppendValue(eCSSProperty_border_image_repeat, repeat);
 10268 bool
 10269 CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
 10270                                      bool* aConsumedTokens)
 10272   // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
 10273   nsCSSValue value;
 10275   if (aConsumedTokens) {
 10276     *aConsumedTokens = true;
 10279   if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
 10280     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
 10281     // are done.
 10282     AppendValue(eCSSProperty_border_image_slice, value);
 10283     return true;
 10286   // Try parsing "fill" value.
 10287   nsCSSValue imageSliceFillValue;
 10288   bool hasFill = ParseEnum(imageSliceFillValue,
 10289                            nsCSSProps::kBorderImageSliceKTable);
 10291   // Parse the box dimensions.
 10292   nsCSSValue imageSliceBoxValue;
 10293   if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue)) {
 10294     if (!hasFill && aConsumedTokens) {
 10295       *aConsumedTokens = false;
 10298     return false;
 10301   // Try parsing "fill" keyword again if the first time failed because keyword
 10302   // and slice dimensions can be in any order.
 10303   if (!hasFill) {
 10304     hasFill = ParseEnum(imageSliceFillValue,
 10305                         nsCSSProps::kBorderImageSliceKTable);
 10308   nsCSSValueList* borderImageSlice = value.SetListValue();
 10309   // Put the box value into the list.
 10310   borderImageSlice->mValue = imageSliceBoxValue;
 10312   if (hasFill) {
 10313     // Put the "fill" value into the list.
 10314     borderImageSlice->mNext = new nsCSSValueList;
 10315     borderImageSlice->mNext->mValue = imageSliceFillValue;
 10318   AppendValue(eCSSProperty_border_image_slice, value);
 10319   return true;
 10322 bool
 10323 CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
 10325   // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
 10326   nsCSSValue value;
 10328   if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
 10329     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
 10330     // are done.
 10331     AppendValue(eCSSProperty_border_image_width, value);
 10332     return true;
 10335   // Parse the box dimensions.
 10336   if (!ParseGroupedBoxProperty(VARIANT_ALPN, value)) {
 10337     return false;
 10340   AppendValue(eCSSProperty_border_image_width, value);
 10341   return true;
 10344 bool
 10345 CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
 10347   // border-image-outset: initial | [<length>|<number>]{1,4}
 10348   nsCSSValue value;
 10350   if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
 10351     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
 10352     // are done.
 10353     AppendValue(eCSSProperty_border_image_outset, value);
 10354     return true;
 10357   // Parse the box dimensions.
 10358   if (!ParseGroupedBoxProperty(VARIANT_LN, value)) {
 10359     return false;
 10362   AppendValue(eCSSProperty_border_image_outset, value);
 10363   return true;
 10366 bool
 10367 CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit)
 10369   nsCSSValue value;
 10370   if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
 10371     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
 10372     // are done.
 10373     AppendValue(eCSSProperty_border_image_repeat, value);
 10374     return true;
 10377   nsCSSValuePair result;
 10378   if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) {
 10379     return false;
 10382   // optional second keyword, defaults to first
 10383   if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) {
 10384     result.mYValue = result.mXValue;
 10387   value.SetPairValue(&result);
 10388   AppendValue(eCSSProperty_border_image_repeat, value);
 10389   return true;
 10392 bool
 10393 CSSParserImpl::ParseBorderImage()
 10395   nsAutoParseCompoundProperty compound(this);
 10397   // border-image: inherit | initial |
 10398   // <border-image-source> ||
 10399   // <border-image-slice>
 10400   //   [ / <border-image-width> |
 10401   //     / <border-image-width>? / <border-image-outset> ]? ||
 10402   // <border-image-repeat>
 10404   nsCSSValue value;
 10405   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
 10406     AppendValue(eCSSProperty_border_image_source, value);
 10407     AppendValue(eCSSProperty_border_image_slice, value);
 10408     AppendValue(eCSSProperty_border_image_width, value);
 10409     AppendValue(eCSSProperty_border_image_outset, value);
 10410     AppendValue(eCSSProperty_border_image_repeat, value);
 10411     // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done.
 10412     return true;
 10415   // No empty property.
 10416   if (CheckEndProperty()) {
 10417     return false;
 10420   // Shorthand properties are required to set everything they can.
 10421   SetBorderImageInitialValues();
 10423   bool foundSource = false;
 10424   bool foundSliceWidthOutset = false;
 10425   bool foundRepeat = false;
 10427   // This loop is used to handle the parsing of border-image properties which
 10428   // can appear in any order.
 10429   nsCSSValue imageSourceValue;
 10430   while (!CheckEndProperty()) {
 10431     // <border-image-source>
 10432     if (!foundSource && ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) {
 10433       AppendValue(eCSSProperty_border_image_source, imageSourceValue);
 10434       foundSource = true;
 10435       continue;
 10438     // <border-image-slice>
 10439     // ParseBorderImageSlice is weird.  It may consume tokens and then return
 10440     // false, because it parses a property with two required components that
 10441     // can appear in either order.  Since the tokens that were consumed cannot
 10442     // parse as anything else we care about, this isn't a problem.
 10443     if (!foundSliceWidthOutset) {
 10444       bool sliceConsumedTokens = false;
 10445       if (ParseBorderImageSlice(false, &sliceConsumedTokens)) {
 10446         foundSliceWidthOutset = true;
 10448         // [ / <border-image-width>?
 10449         if (ExpectSymbol('/', true)) {
 10450           bool foundBorderImageWidth = ParseBorderImageWidth(false);
 10452           // [ / <border-image-outset>
 10453           if (ExpectSymbol('/', true)) {
 10454             if (!ParseBorderImageOutset(false)) {
 10455               return false;
 10457           } else if (!foundBorderImageWidth) {
 10458             // If this part has an trailing slash, the whole declaration is 
 10459             // invalid.
 10460             return false;
 10464         continue;
 10465       } else {
 10466         // If we consumed some tokens for <border-image-slice> but did not
 10467         // successfully parse it, we have an error.
 10468         if (sliceConsumedTokens) {
 10469           return false;
 10474     // <border-image-repeat>
 10475     if (!foundRepeat && ParseBorderImageRepeat(false)) {
 10476       foundRepeat = true;
 10477       continue;
 10480     return false;
 10483   return true;
 10486 bool
 10487 CSSParserImpl::ParseBorderSpacing()
 10489   nsCSSValue xValue, yValue;
 10490   if (!ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr)) {
 10491     return false;
 10494   // If we have one length, get the optional second length.
 10495   // set the second value equal to the first.
 10496   if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) {
 10497     ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC, nullptr);
 10500   if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) {
 10501     AppendValue(eCSSProperty_border_spacing, xValue);
 10502   } else {
 10503     nsCSSValue pair;
 10504     pair.SetPairValue(xValue, yValue);
 10505     AppendValue(eCSSProperty_border_spacing, pair);
 10507   return true;
 10510 bool
 10511 CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[],
 10512                                bool aSetAllSides)
 10514   const int32_t numProps = 3;
 10515   nsCSSValue  values[numProps];
 10517   int32_t found = ParseChoice(values, aPropIDs, numProps);
 10518   if (found < 1) {
 10519     return false;
 10522   if ((found & 1) == 0) { // Provide default border-width
 10523     values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
 10525   if ((found & 2) == 0) { // Provide default border-style
 10526     values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
 10528   if ((found & 4) == 0) { // text color will be used
 10529     values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
 10532   if (aSetAllSides) {
 10533     static const nsCSSProperty kBorderSources[] = {
 10534       eCSSProperty_border_left_color_ltr_source,
 10535       eCSSProperty_border_left_color_rtl_source,
 10536       eCSSProperty_border_right_color_ltr_source,
 10537       eCSSProperty_border_right_color_rtl_source,
 10538       eCSSProperty_border_left_style_ltr_source,
 10539       eCSSProperty_border_left_style_rtl_source,
 10540       eCSSProperty_border_right_style_ltr_source,
 10541       eCSSProperty_border_right_style_rtl_source,
 10542       eCSSProperty_border_left_width_ltr_source,
 10543       eCSSProperty_border_left_width_rtl_source,
 10544       eCSSProperty_border_right_width_ltr_source,
 10545       eCSSProperty_border_right_width_rtl_source,
 10546       eCSSProperty_UNKNOWN
 10547     };
 10549     InitBoxPropsAsPhysical(kBorderSources);
 10551     // Parsing "border" shorthand; set all four sides to the same thing
 10552     for (int32_t index = 0; index < 4; index++) {
 10553       NS_ASSERTION(numProps == 3, "This code needs updating");
 10554       AppendValue(kBorderWidthIDs[index], values[0]);
 10555       AppendValue(kBorderStyleIDs[index], values[1]);
 10556       AppendValue(kBorderColorIDs[index], values[2]);
 10559     static const nsCSSProperty kBorderColorsProps[] = {
 10560       eCSSProperty_border_top_colors,
 10561       eCSSProperty_border_right_colors,
 10562       eCSSProperty_border_bottom_colors,
 10563       eCSSProperty_border_left_colors
 10564     };
 10566     // Set the other properties that the border shorthand sets to their
 10567     // initial values.
 10568     nsCSSValue extraValue;
 10569     switch (values[0].GetUnit()) {
 10570     case eCSSUnit_Inherit:
 10571     case eCSSUnit_Initial:
 10572     case eCSSUnit_Unset:
 10573       extraValue = values[0];
 10574       // Set value of border-image properties to initial/inherit/unset
 10575       AppendValue(eCSSProperty_border_image_source, extraValue);
 10576       AppendValue(eCSSProperty_border_image_slice, extraValue);
 10577       AppendValue(eCSSProperty_border_image_width, extraValue);
 10578       AppendValue(eCSSProperty_border_image_outset, extraValue);
 10579       AppendValue(eCSSProperty_border_image_repeat, extraValue);
 10580       break;
 10581     default:
 10582       extraValue.SetNoneValue();
 10583       SetBorderImageInitialValues();
 10584       break;
 10586     NS_FOR_CSS_SIDES(side) {
 10587       AppendValue(kBorderColorsProps[side], extraValue);
 10590   else {
 10591     // Just set our one side
 10592     for (int32_t index = 0; index < numProps; index++) {
 10593       AppendValue(aPropIDs[index], values[index]);
 10596   return true;
 10599 bool
 10600 CSSParserImpl::ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
 10601                                           int32_t aSourceType)
 10603   const int32_t numProps = 3;
 10604   nsCSSValue  values[numProps];
 10606   int32_t found = ParseChoice(values, aPropIDs, numProps);
 10607   if (found < 1) {
 10608     return false;
 10611   if ((found & 1) == 0) { // Provide default border-width
 10612     values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
 10614   if ((found & 2) == 0) { // Provide default border-style
 10615     values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
 10617   if ((found & 4) == 0) { // text color will be used
 10618     values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
 10620   for (int32_t index = 0; index < numProps; index++) {
 10621     const nsCSSProperty* subprops =
 10622       nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]);
 10623     NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
 10624                  "not box property with physical vs. logical cascading");
 10625     AppendValue(subprops[0], values[index]);
 10626     nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
 10627     AppendValue(subprops[1], typeVal);
 10628     AppendValue(subprops[2], typeVal);
 10630   return true;
 10633 bool
 10634 CSSParserImpl::ParseBorderStyle()
 10636   static const nsCSSProperty kBorderStyleSources[] = {
 10637     eCSSProperty_border_left_style_ltr_source,
 10638     eCSSProperty_border_left_style_rtl_source,
 10639     eCSSProperty_border_right_style_ltr_source,
 10640     eCSSProperty_border_right_style_rtl_source,
 10641     eCSSProperty_UNKNOWN
 10642   };
 10644   // do this now, in case 4 values weren't specified
 10645   InitBoxPropsAsPhysical(kBorderStyleSources);
 10646   return ParseBoxProperties(kBorderStyleIDs);
 10649 bool
 10650 CSSParserImpl::ParseBorderWidth()
 10652   static const nsCSSProperty kBorderWidthSources[] = {
 10653     eCSSProperty_border_left_width_ltr_source,
 10654     eCSSProperty_border_left_width_rtl_source,
 10655     eCSSProperty_border_right_width_ltr_source,
 10656     eCSSProperty_border_right_width_rtl_source,
 10657     eCSSProperty_UNKNOWN
 10658   };
 10660   // do this now, in case 4 values weren't specified
 10661   InitBoxPropsAsPhysical(kBorderWidthSources);
 10662   return ParseBoxProperties(kBorderWidthIDs);
 10665 bool
 10666 CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty)
 10668   nsCSSValue value;
 10669   // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
 10670   if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
 10671     nsCSSValueList *cur = value.SetListValue();
 10672     for (;;) {
 10673       if (!ParseVariant(cur->mValue, VARIANT_COLOR | VARIANT_KEYWORD,
 10674                         nsCSSProps::kBorderColorKTable)) {
 10675         return false;
 10677       if (CheckEndProperty()) {
 10678         break;
 10680       cur->mNext = new nsCSSValueList;
 10681       cur = cur->mNext;
 10684   AppendValue(aProperty, value);
 10685   return true;
 10688 // Parse the top level of a calc() expression.
 10689 bool
 10690 CSSParserImpl::ParseCalc(nsCSSValue &aValue, int32_t aVariantMask)
 10692   // Parsing calc expressions requires, in a number of cases, looking
 10693   // for a token that is *either* a value of the property or a number.
 10694   // This can be done without lookahead when we assume that the property
 10695   // values cannot themselves be numbers.
 10696   NS_ASSERTION(!(aVariantMask & VARIANT_NUMBER), "unexpected variant mask");
 10697   NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
 10699   bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk;
 10700   mUnitlessLengthQuirk = false;
 10702   // One-iteration loop so we can break to the error-handling case.
 10703   do {
 10704     // The toplevel of a calc() is always an nsCSSValue::Array of length 1.
 10705     nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
 10707     if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask))
 10708       break;
 10710     if (!ExpectSymbol(')', true))
 10711       break;
 10713     aValue.SetArrayValue(arr, eCSSUnit_Calc);
 10714     mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
 10715     return true;
 10716   } while (false);
 10718   SkipUntil(')');
 10719   mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
 10720   return false;
 10723 // We optimize away the <value-expression> production given that
 10724 // ParseVariant consumes initial whitespace and we call
 10725 // ExpectSymbol(')') with true for aSkipWS.
 10726 //  * If aVariantMask is VARIANT_NUMBER, this function parses the
 10727 //    <number-additive-expression> production.
 10728 //  * If aVariantMask does not contain VARIANT_NUMBER, this function
 10729 //    parses the <value-additive-expression> production.
 10730 //  * Otherwise (VARIANT_NUMBER and other bits) this function parses
 10731 //    whichever one of the productions matches ***and modifies
 10732 //    aVariantMask*** to reflect which one it has parsed by either
 10733 //    removing VARIANT_NUMBER or removing all other bits.
 10734 // It does so iteratively, but builds the correct recursive
 10735 // data structure.
 10736 bool
 10737 CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue,
 10738                                            int32_t& aVariantMask)
 10740   NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
 10741   nsCSSValue *storage = &aValue;
 10742   for (;;) {
 10743     bool haveWS;
 10744     if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS))
 10745       return false;
 10747     if (!haveWS || !GetToken(false))
 10748       return true;
 10749     nsCSSUnit unit;
 10750     if (mToken.IsSymbol('+')) {
 10751       unit = eCSSUnit_Calc_Plus;
 10752     } else if (mToken.IsSymbol('-')) {
 10753       unit = eCSSUnit_Calc_Minus;
 10754     } else {
 10755       UngetToken();
 10756       return true;
 10758     if (!RequireWhitespace())
 10759       return false;
 10761     nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
 10762     arr->Item(0) = aValue;
 10763     storage = &arr->Item(1);
 10764     aValue.SetArrayValue(arr, unit);
 10768 struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
 10769                              public mozilla::css::CSSValueInputCalcOps
 10771   result_type ComputeLeafValue(const nsCSSValue& aValue)
 10773     NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
 10774     return aValue.GetFloatValue();
 10777   float ComputeNumber(const nsCSSValue& aValue)
 10779     return mozilla::css::ComputeCalc(aValue, *this);
 10781 };
 10783 //  * If aVariantMask is VARIANT_NUMBER, this function parses the
 10784 //    <number-multiplicative-expression> production.
 10785 //  * If aVariantMask does not contain VARIANT_NUMBER, this function
 10786 //    parses the <value-multiplicative-expression> production.
 10787 //  * Otherwise (VARIANT_NUMBER and other bits) this function parses
 10788 //    whichever one of the productions matches ***and modifies
 10789 //    aVariantMask*** to reflect which one it has parsed by either
 10790 //    removing VARIANT_NUMBER or removing all other bits.
 10791 // It does so iteratively, but builds the correct recursive data
 10792 // structure.
 10793 // This function always consumes *trailing* whitespace when it returns
 10794 // true; whether there was any such whitespace is returned in the
 10795 // aHadFinalWS parameter.
 10796 bool
 10797 CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
 10798                                                  int32_t& aVariantMask,
 10799                                                  bool *aHadFinalWS)
 10801   NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
 10802   bool gotValue = false; // already got the part with the unit
 10803   bool afterDivision = false;
 10805   nsCSSValue *storage = &aValue;
 10806   for (;;) {
 10807     int32_t variantMask;
 10808     if (afterDivision || gotValue) {
 10809       variantMask = VARIANT_NUMBER;
 10810     } else {
 10811       variantMask = aVariantMask | VARIANT_NUMBER;
 10813     if (!ParseCalcTerm(*storage, variantMask))
 10814       return false;
 10815     NS_ABORT_IF_FALSE(variantMask != 0,
 10816                       "ParseCalcTerm did not set variantMask appropriately");
 10817     NS_ABORT_IF_FALSE(!(variantMask & VARIANT_NUMBER) ||
 10818                       !(variantMask & ~int32_t(VARIANT_NUMBER)),
 10819                       "ParseCalcTerm did not set variantMask appropriately");
 10821     if (variantMask & VARIANT_NUMBER) {
 10822       // Simplify the value immediately so we can check for division by
 10823       // zero.
 10824       ReduceNumberCalcOps ops;
 10825       float number = mozilla::css::ComputeCalc(*storage, ops);
 10826       if (number == 0.0 && afterDivision)
 10827         return false;
 10828       storage->SetFloatValue(number, eCSSUnit_Number);
 10829     } else {
 10830       gotValue = true;
 10832       if (storage != &aValue) {
 10833         // Simplify any numbers in the Times_L position (which are
 10834         // not simplified by the check above).
 10835         NS_ABORT_IF_FALSE(storage == &aValue.GetArrayValue()->Item(1),
 10836                           "unexpected relationship to current storage");
 10837         nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
 10838         ReduceNumberCalcOps ops;
 10839         float number = mozilla::css::ComputeCalc(leftValue, ops);
 10840         leftValue.SetFloatValue(number, eCSSUnit_Number);
 10844     bool hadWS = RequireWhitespace();
 10845     if (!GetToken(false)) {
 10846       *aHadFinalWS = hadWS;
 10847       break;
 10849     nsCSSUnit unit;
 10850     if (mToken.IsSymbol('*')) {
 10851       unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
 10852       afterDivision = false;
 10853     } else if (mToken.IsSymbol('/')) {
 10854       unit = eCSSUnit_Calc_Divided;
 10855       afterDivision = true;
 10856     } else {
 10857       UngetToken();
 10858       *aHadFinalWS = hadWS;
 10859       break;
 10862     nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
 10863     arr->Item(0) = aValue;
 10864     storage = &arr->Item(1);
 10865     aValue.SetArrayValue(arr, unit);
 10868   // Adjust aVariantMask (see comments above function) to reflect which
 10869   // option we took.
 10870   if (aVariantMask & VARIANT_NUMBER) {
 10871     if (gotValue) {
 10872       aVariantMask &= ~int32_t(VARIANT_NUMBER);
 10873     } else {
 10874       aVariantMask = VARIANT_NUMBER;
 10876   } else {
 10877     if (!gotValue) {
 10878       // We had to find a value, but we didn't.
 10879       return false;
 10883   return true;
 10886 //  * If aVariantMask is VARIANT_NUMBER, this function parses the
 10887 //    <number-term> production.
 10888 //  * If aVariantMask does not contain VARIANT_NUMBER, this function
 10889 //    parses the <value-term> production.
 10890 //  * Otherwise (VARIANT_NUMBER and other bits) this function parses
 10891 //    whichever one of the productions matches ***and modifies
 10892 //    aVariantMask*** to reflect which one it has parsed by either
 10893 //    removing VARIANT_NUMBER or removing all other bits.
 10894 bool
 10895 CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask)
 10897   NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
 10898   if (!GetToken(true))
 10899     return false;
 10900   // Either an additive expression in parentheses...
 10901   if (mToken.IsSymbol('(')) {
 10902     if (!ParseCalcAdditiveExpression(aValue, aVariantMask) ||
 10903         !ExpectSymbol(')', true)) {
 10904       SkipUntil(')');
 10905       return false;
 10907     return true;
 10909   // ... or just a value
 10910   UngetToken();
 10911   // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
 10912   // always gets picked up
 10913   if (!ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr)) {
 10914     return false;
 10916   // ...and do the VARIANT_NUMBER check ourselves.
 10917   if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
 10918     return false;
 10920   // If we did the value parsing, we need to adjust aVariantMask to
 10921   // reflect which option we took (see above).
 10922   if (aVariantMask & VARIANT_NUMBER) {
 10923     if (aValue.GetUnit() == eCSSUnit_Number) {
 10924       aVariantMask = VARIANT_NUMBER;
 10925     } else {
 10926       aVariantMask &= ~int32_t(VARIANT_NUMBER);
 10929   return true;
 10932 // This function consumes all consecutive whitespace and returns whether
 10933 // there was any.
 10934 bool
 10935 CSSParserImpl::RequireWhitespace()
 10937   if (!GetToken(false))
 10938     return false;
 10939   if (mToken.mType != eCSSToken_Whitespace) {
 10940     UngetToken();
 10941     return false;
 10943   // Skip any additional whitespace tokens.
 10944   if (GetToken(true)) {
 10945     UngetToken();
 10947   return true;
 10950 bool
 10951 CSSParserImpl::ParseRect(nsCSSProperty aPropID)
 10953   nsCSSValue val;
 10954   if (ParseVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) {
 10955     AppendValue(aPropID, val);
 10956     return true;
 10959   if (! GetToken(true)) {
 10960     return false;
 10963   if (mToken.mType == eCSSToken_Function &&
 10964       mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
 10965     nsCSSRect& rect = val.SetRectValue();
 10966     bool useCommas;
 10967     NS_FOR_CSS_SIDES(side) {
 10968       if (! ParseVariant(rect.*(nsCSSRect::sides[side]),
 10969                          VARIANT_AL, nullptr)) {
 10970         return false;
 10972       if (side == 0) {
 10973         useCommas = ExpectSymbol(',', true);
 10974       } else if (useCommas && side < 3) {
 10975         // Skip optional commas between elements, but only if the first
 10976         // separator was a comma.
 10977         if (!ExpectSymbol(',', true)) {
 10978           return false;
 10982     if (!ExpectSymbol(')', true)) {
 10983       return false;
 10985   } else {
 10986     UngetToken();
 10987     return false;
 10990   AppendValue(aPropID, val);
 10991   return true;
 10994 bool
 10995 CSSParserImpl::ParseColumns()
 10997   // We use a similar "fake value" hack to ParseListStyle, because
 10998   // "auto" is acceptable for both column-count and column-width.
 10999   // If the fake "auto" value is found, and one of the real values isn't,
 11000   // that means the fake auto value is meant for the real value we didn't
 11001   // find.
 11002   static const nsCSSProperty columnIDs[] = {
 11003     eCSSPropertyExtra_x_auto_value,
 11004     eCSSProperty__moz_column_count,
 11005     eCSSProperty__moz_column_width
 11006   };
 11007   const int32_t numProps = MOZ_ARRAY_LENGTH(columnIDs);
 11009   nsCSSValue values[numProps];
 11010   int32_t found = ParseChoice(values, columnIDs, numProps);
 11011   if (found < 1) {
 11012     return false;
 11014   if ((found & (1|2|4)) == (1|2|4) &&
 11015       values[0].GetUnit() ==  eCSSUnit_Auto) {
 11016     // We filled all 3 values, which is invalid
 11017     return false;
 11020   if ((found & 2) == 0) {
 11021     // Provide auto column-count
 11022     values[1].SetAutoValue();
 11024   if ((found & 4) == 0) {
 11025     // Provide auto column-width
 11026     values[2].SetAutoValue();
 11029   // Start at index 1 to skip the fake auto value.
 11030   for (int32_t index = 1; index < numProps; index++) {
 11031     AppendValue(columnIDs[index], values[index]);
 11033   return true;
 11036 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
 11037                          VARIANT_KEYWORD)
 11038 bool
 11039 CSSParserImpl::ParseContent()
 11041   // We need to divide the 'content' keywords into two classes for
 11042   // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable.
 11043   static const KTableValue kContentListKWs[] = {
 11044     eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE,
 11045     eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE,
 11046     eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE,
 11047     eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE,
 11048     eCSSKeyword_UNKNOWN,-1
 11049   };
 11051   static const KTableValue kContentSolitaryKWs[] = {
 11052     eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT,
 11053     eCSSKeyword_UNKNOWN,-1
 11054   };
 11056   // Verify that these two lists add up to the size of
 11057   // nsCSSProps::kContentKTable.
 11058   NS_ABORT_IF_FALSE(nsCSSProps::kContentKTable[
 11059                       ArrayLength(kContentListKWs) +
 11060                       ArrayLength(kContentSolitaryKWs) - 4] ==
 11061                     eCSSKeyword_UNKNOWN &&
 11062                     nsCSSProps::kContentKTable[
 11063                       ArrayLength(kContentListKWs) +
 11064                       ArrayLength(kContentSolitaryKWs) - 3] == -1,
 11065                     "content keyword tables out of sync");
 11067   nsCSSValue value;
 11068   // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must
 11069   // be alone
 11070   if (!ParseVariant(value, VARIANT_HMK | VARIANT_NONE,
 11071                     kContentSolitaryKWs)) {
 11072     nsCSSValueList* cur = value.SetListValue();
 11073     for (;;) {
 11074       if (!ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs)) {
 11075         return false;
 11077       if (CheckEndProperty()) {
 11078         break;
 11080       cur->mNext = new nsCSSValueList;
 11081       cur = cur->mNext;
 11084   AppendValue(eCSSProperty_content, value);
 11085   return true;
 11088 bool
 11089 CSSParserImpl::ParseCounterData(nsCSSProperty aPropID)
 11091   static const nsCSSKeyword kCounterDataKTable[] = {
 11092     eCSSKeyword_none,
 11093     eCSSKeyword_UNKNOWN
 11094   };
 11095   nsCSSValue value;
 11096   if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
 11097     if (!GetToken(true)) {
 11098       return false;
 11100     if (mToken.mType != eCSSToken_Ident) {
 11101       UngetToken();
 11102       return false;
 11105     nsCSSValuePairList *cur = value.SetPairListValue();
 11106     for (;;) {
 11107       if (!ParseCustomIdent(cur->mXValue, mToken.mIdent, kCounterDataKTable)) {
 11108         return false;
 11110       if (!GetToken(true)) {
 11111         break;
 11113       if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
 11114         cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
 11115       } else {
 11116         UngetToken();
 11118       if (!GetToken(true)) {
 11119         break;
 11121       if (mToken.mType != eCSSToken_Ident) {
 11122         UngetToken();
 11123         break;
 11125       cur->mNext = new nsCSSValuePairList;
 11126       cur = cur->mNext;
 11129   AppendValue(aPropID, value);
 11130   return true;
 11133 bool
 11134 CSSParserImpl::ParseCursor()
 11136   nsCSSValue value;
 11137   // 'inherit', 'initial' and 'unset' must be alone
 11138   if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
 11139     nsCSSValueList* cur = value.SetListValue();
 11140     for (;;) {
 11141       if (!ParseVariant(cur->mValue, VARIANT_UK, nsCSSProps::kCursorKTable)) {
 11142         return false;
 11144       if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last
 11145         break;
 11148       // We have a URL, so make a value array with three values.
 11149       nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
 11150       val->Item(0) = cur->mValue;
 11152       // Parse optional x and y position of cursor hotspot (css3-ui).
 11153       if (ParseVariant(val->Item(1), VARIANT_NUMBER, nullptr)) {
 11154         // If we have one number, we must have two.
 11155         if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nullptr)) {
 11156           return false;
 11159       cur->mValue.SetArrayValue(val, eCSSUnit_Array);
 11161       if (!ExpectSymbol(',', true)) { // url must not be last
 11162         return false;
 11164       cur->mNext = new nsCSSValueList;
 11165       cur = cur->mNext;
 11168   AppendValue(eCSSProperty_cursor, value);
 11169   return true;
 11173 bool
 11174 CSSParserImpl::ParseFont()
 11176   static const nsCSSProperty fontIDs[] = {
 11177     eCSSProperty_font_style,
 11178     eCSSProperty_font_variant,
 11179     eCSSProperty_font_weight
 11180   };
 11182   // font-variant-alternates enabled ==> layout.css.font-features.enabled is true
 11183   bool featuresEnabled =
 11184     nsCSSProps::IsEnabled(eCSSProperty_font_variant_alternates);
 11185   nsCSSValue  family;
 11186   if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
 11187     if (eCSSUnit_Inherit == family.GetUnit() ||
 11188         eCSSUnit_Initial == family.GetUnit() ||
 11189         eCSSUnit_Unset == family.GetUnit()) {
 11190       AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
 11191       AppendValue(eCSSProperty_font_family, family);
 11192       AppendValue(eCSSProperty_font_style, family);
 11193       AppendValue(eCSSProperty_font_variant, family);
 11194       AppendValue(eCSSProperty_font_weight, family);
 11195       AppendValue(eCSSProperty_font_size, family);
 11196       AppendValue(eCSSProperty_line_height, family);
 11197       AppendValue(eCSSProperty_font_stretch, family);
 11198       AppendValue(eCSSProperty_font_size_adjust, family);
 11199       AppendValue(eCSSProperty_font_feature_settings, family);
 11200       AppendValue(eCSSProperty_font_language_override, family);
 11201       if (featuresEnabled) {
 11202         AppendValue(eCSSProperty_font_kerning, family);
 11203         AppendValue(eCSSProperty_font_synthesis, family);
 11204         AppendValue(eCSSProperty_font_variant_alternates, family);
 11205         AppendValue(eCSSProperty_font_variant_caps, family);
 11206         AppendValue(eCSSProperty_font_variant_east_asian, family);
 11207         AppendValue(eCSSProperty_font_variant_ligatures, family);
 11208         AppendValue(eCSSProperty_font_variant_numeric, family);
 11209         AppendValue(eCSSProperty_font_variant_position, family);
 11212     else {
 11213       AppendValue(eCSSProperty__x_system_font, family);
 11214       nsCSSValue systemFont(eCSSUnit_System_Font);
 11215       AppendValue(eCSSProperty_font_family, systemFont);
 11216       AppendValue(eCSSProperty_font_style, systemFont);
 11217       AppendValue(eCSSProperty_font_variant, systemFont);
 11218       AppendValue(eCSSProperty_font_weight, systemFont);
 11219       AppendValue(eCSSProperty_font_size, systemFont);
 11220       AppendValue(eCSSProperty_line_height, systemFont);
 11221       AppendValue(eCSSProperty_font_stretch, systemFont);
 11222       AppendValue(eCSSProperty_font_size_adjust, systemFont);
 11223       AppendValue(eCSSProperty_font_feature_settings, systemFont);
 11224       AppendValue(eCSSProperty_font_language_override, systemFont);
 11225       if (featuresEnabled) {
 11226         AppendValue(eCSSProperty_font_kerning, systemFont);
 11227         AppendValue(eCSSProperty_font_synthesis, systemFont);
 11228         AppendValue(eCSSProperty_font_variant_alternates, systemFont);
 11229         AppendValue(eCSSProperty_font_variant_caps, systemFont);
 11230         AppendValue(eCSSProperty_font_variant_east_asian, systemFont);
 11231         AppendValue(eCSSProperty_font_variant_ligatures, systemFont);
 11232         AppendValue(eCSSProperty_font_variant_numeric, systemFont);
 11233         AppendValue(eCSSProperty_font_variant_position, systemFont);
 11236     return true;
 11239   // Get optional font-style, font-variant and font-weight (in any order)
 11240   const int32_t numProps = 3;
 11241   nsCSSValue  values[numProps];
 11242   int32_t found = ParseChoice(values, fontIDs, numProps);
 11243   if (found < 0 ||
 11244       eCSSUnit_Inherit == values[0].GetUnit() ||
 11245       eCSSUnit_Initial == values[0].GetUnit() ||
 11246       eCSSUnit_Unset == values[0].GetUnit()) { // illegal data
 11247     return false;
 11249   if ((found & 1) == 0) {
 11250     // Provide default font-style
 11251     values[0].SetIntValue(NS_FONT_STYLE_NORMAL, eCSSUnit_Enumerated);
 11253   if ((found & 2) == 0) {
 11254     // Provide default font-variant
 11255     values[1].SetIntValue(NS_FONT_VARIANT_NORMAL, eCSSUnit_Enumerated);
 11257   if ((found & 4) == 0) {
 11258     // Provide default font-weight
 11259     values[2].SetIntValue(NS_FONT_WEIGHT_NORMAL, eCSSUnit_Enumerated);
 11262   // Get mandatory font-size
 11263   nsCSSValue  size;
 11264   if (! ParseNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP,
 11265                                 nsCSSProps::kFontSizeKTable)) {
 11266     return false;
 11269   // Get optional "/" line-height
 11270   nsCSSValue  lineHeight;
 11271   if (ExpectSymbol('/', true)) {
 11272     if (! ParseNonNegativeVariant(lineHeight,
 11273                                   VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
 11274                                   nullptr)) {
 11275       return false;
 11278   else {
 11279     lineHeight.SetNormalValue();
 11282   // Get final mandatory font-family
 11283   nsAutoParseCompoundProperty compound(this);
 11284   if (ParseFamily(family)) {
 11285     if (eCSSUnit_Inherit != family.GetUnit() &&
 11286         eCSSUnit_Initial != family.GetUnit() &&
 11287         eCSSUnit_Unset != family.GetUnit()) {
 11288       AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
 11289       AppendValue(eCSSProperty_font_family, family);
 11290       AppendValue(eCSSProperty_font_style, values[0]);
 11291       AppendValue(eCSSProperty_font_variant, values[1]);
 11292       AppendValue(eCSSProperty_font_weight, values[2]);
 11293       AppendValue(eCSSProperty_font_size, size);
 11294       AppendValue(eCSSProperty_line_height, lineHeight);
 11295       AppendValue(eCSSProperty_font_stretch,
 11296                   nsCSSValue(NS_FONT_STRETCH_NORMAL, eCSSUnit_Enumerated));
 11297       AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
 11298       AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal));
 11299       AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal));
 11300       if (featuresEnabled) {
 11301         AppendValue(eCSSProperty_font_kerning,
 11302                     nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated));
 11303         AppendValue(eCSSProperty_font_synthesis,
 11304                     nsCSSValue(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE,
 11305                                eCSSUnit_Enumerated));
 11306         AppendValue(eCSSProperty_font_variant_alternates,
 11307                     nsCSSValue(eCSSUnit_Normal));
 11308         AppendValue(eCSSProperty_font_variant_caps, nsCSSValue(eCSSUnit_Normal));
 11309         AppendValue(eCSSProperty_font_variant_east_asian,
 11310                     nsCSSValue(eCSSUnit_Normal));
 11311         AppendValue(eCSSProperty_font_variant_ligatures,
 11312                     nsCSSValue(eCSSUnit_Normal));
 11313         AppendValue(eCSSProperty_font_variant_numeric,
 11314                     nsCSSValue(eCSSUnit_Normal));
 11315         AppendValue(eCSSProperty_font_variant_position,
 11316                     nsCSSValue(eCSSUnit_Normal));
 11318       return true;
 11321   return false;
 11324 bool
 11325 CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue)
 11327   if (!ParseVariant(aValue, VARIANT_HK | VARIANT_NONE,
 11328                     nsCSSProps::kFontSynthesisKTable)) {
 11329     return false;
 11332   // first value 'none' ==> done
 11333   if (eCSSUnit_None == aValue.GetUnit() ||
 11334       eCSSUnit_Initial == aValue.GetUnit() ||
 11335       eCSSUnit_Inherit == aValue.GetUnit() ||
 11336       eCSSUnit_Unset == aValue.GetUnit())
 11338     return true;
 11341   // look for a second value
 11342   int32_t intValue = aValue.GetIntValue();
 11343   nsCSSValue nextValue;
 11345   if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) {
 11346     int32_t nextIntValue = nextValue.GetIntValue();
 11347     if (nextIntValue & intValue) {
 11348       return false;
 11350     aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
 11353   return true;
 11356 // font-variant-alternates allows for a combination of multiple
 11357 // simple enumerated values and functional values.  Functional values have
 11358 // parameter lists with one or more idents which are later resolved
 11359 // based on values defined in @font-feature-value rules.
 11360 //
 11361 // font-variant-alternates: swash(flowing), historical-forms, styleset(alt-g, alt-m);
 11362 //
 11363 // So for this the nsCSSValue is set to a pair value, with one
 11364 // value for a bitmask of both simple and functional property values
 11365 // and another value containing a ValuePairList with lists of idents
 11366 // for each functional property value.
 11367 //
 11368 // pairValue
 11369 //   o intValue
 11370 //       NS_FONT_VARIANT_ALTERNATES_SWASH |
 11371 //       NS_FONT_VARIANT_ALTERNATES_STYLESET
 11372 //   o valuePairList, each element with
 11373 //     - intValue - indicates which alternate
 11374 //     - string or valueList of strings
 11375 //
 11376 // Note: when only 'historical-forms' is specified, there are no
 11377 // functional values to store, in which case the valuePairList is a
 11378 // single element dummy list.  In all other cases, the length of the
 11379 // list will match the number of functional values.
 11381 #define MAX_ALLOWED_FEATURES 512
 11383 bool
 11384 CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature,
 11385                                     nsCSSValue& aValue)
 11387   if (!GetToken(true)) {
 11388     return false;
 11391   bool isIdent = (mToken.mType == eCSSToken_Ident);
 11392   if (mToken.mType != eCSSToken_Function && !isIdent) {
 11393     UngetToken();
 11394     return false;
 11397   // ident ==> simple enumerated prop val (e.g. historical-forms)
 11398   // function ==> e.g. swash(flowing) styleset(alt-g, alt-m)
 11400   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
 11401   if (!(eCSSKeyword_UNKNOWN < keyword &&
 11402         nsCSSProps::FindKeyword(keyword,
 11403                                 (isIdent ?
 11404                                  nsCSSProps::kFontVariantAlternatesKTable :
 11405                                  nsCSSProps::kFontVariantAlternatesFuncsKTable),
 11406                                 aWhichFeature)))
 11408     // failed, pop token
 11409     UngetToken();
 11410     return false;
 11413   if (isIdent) {
 11414     aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated);
 11415     return true;
 11418   uint16_t maxElems = 1;
 11419   if (keyword == eCSSKeyword_styleset ||
 11420       keyword == eCSSKeyword_character_variant) {
 11421     maxElems = MAX_ALLOWED_FEATURES;
 11423   return ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER,
 11424                        1, maxElems, aValue);
 11427 bool
 11428 CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue)
 11430   if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
 11431     return true;
 11434   // iterate through parameters
 11435   nsCSSValue listValue;
 11436   int32_t feature, featureFlags = 0;
 11438   // if no functional values, this may be a list with a single, unused element
 11439   listValue.SetListValue();
 11441   nsCSSValueList* list = nullptr;
 11442   nsCSSValue value;
 11443   while (ParseSingleAlternate(feature, value)) {
 11445     // check to make sure value not already set
 11446     if (feature == 0 ||
 11447         feature & featureFlags) {
 11448       return false;
 11451     featureFlags |= feature;
 11453     // if function, need to add to the list of functions
 11454     if (value.GetUnit() == eCSSUnit_Function) {
 11455       if (!list) {
 11456         list = listValue.GetListValue();
 11457       } else {
 11458         list->mNext = new nsCSSValueList;
 11459         list = list->mNext;
 11461       list->mValue = value;
 11465   if (featureFlags == 0) {
 11466     // ParseSingleAlternate failed the first time through the loop.
 11467     return false;
 11470   nsCSSValue featureValue;
 11471   featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated);
 11472   aValue.SetPairValue(featureValue, listValue);
 11474   return true;
 11477 // aMasks - array of masks for mutually-exclusive property values,
 11478 //          e.g. proportial-nums, tabular-nums
 11480 bool
 11481 CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue,
 11482                                   const KTableValue aKeywordTable[],
 11483                                   const int32_t aMasks[])
 11485   // Parse at least one keyword
 11486   if (!ParseEnum(aValue, aKeywordTable)) {
 11487     return false;
 11490   // look for more values
 11491   nsCSSValue nextValue;
 11492   int32_t mergedValue = aValue.GetIntValue();
 11494   while (ParseEnum(nextValue, aKeywordTable))
 11496     int32_t nextIntValue = nextValue.GetIntValue();
 11498     // check to make sure value not already set
 11499     if (nextIntValue & mergedValue) {
 11500       return false;
 11503     const int32_t *m = aMasks;
 11504     int32_t c = 0;
 11506     while (*m != MASK_END_VALUE) {
 11507       if (*m & nextIntValue) {
 11508         c = mergedValue & *m;
 11509         break;
 11511       m++;
 11514     if (c) {
 11515       return false;
 11518     mergedValue |= nextIntValue;
 11521   aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated);
 11523   return true;
 11526 static const int32_t maskEastAsian[] = {
 11527   NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK,
 11528   NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK,
 11529   MASK_END_VALUE
 11530 };
 11532 bool
 11533 CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue)
 11535   if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
 11536     return true;
 11539   NS_ASSERTION(maskEastAsian[ArrayLength(maskEastAsian) - 1] ==
 11540                  MASK_END_VALUE,
 11541                "incorrectly terminated array");
 11543   return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable,
 11544                             maskEastAsian);
 11547 static const int32_t maskLigatures[] = {
 11548   NS_FONT_VARIANT_LIGATURES_COMMON_MASK,
 11549   NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK,
 11550   NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK,
 11551   NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK,
 11552   MASK_END_VALUE
 11553 };
 11555 bool
 11556 CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue)
 11558   if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
 11559     return true;
 11562   NS_ASSERTION(maskLigatures[ArrayLength(maskLigatures) - 1] ==
 11563                  MASK_END_VALUE,
 11564                "incorrectly terminated array");
 11566   bool parsed =
 11567     ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable,
 11568                        maskLigatures);
 11570   // if none value included, no other values are possible
 11571   if (parsed && eCSSUnit_Enumerated == aValue.GetUnit()) {
 11572     int32_t val = aValue.GetIntValue();
 11573     if ((val & NS_FONT_VARIANT_LIGATURES_NONE) &&
 11574         (val & ~int32_t(NS_FONT_VARIANT_LIGATURES_NONE))) {
 11575       parsed = false;
 11579   return parsed;
 11582 static const int32_t maskNumeric[] = {
 11583   NS_FONT_VARIANT_NUMERIC_FIGURE_MASK,
 11584   NS_FONT_VARIANT_NUMERIC_SPACING_MASK,
 11585   NS_FONT_VARIANT_NUMERIC_FRACTION_MASK,
 11586   MASK_END_VALUE
 11587 };
 11589 bool
 11590 CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue)
 11592   if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
 11593     return true;
 11596   NS_ASSERTION(maskNumeric[ArrayLength(maskNumeric) - 1] ==
 11597                  MASK_END_VALUE,
 11598                "incorrectly terminated array");
 11600   return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable,
 11601                             maskNumeric);
 11604 bool
 11605 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
 11607   if (ParseVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT,
 11608                    nsCSSProps::kFontWeightKTable)) {
 11609     if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
 11610       int32_t intValue = aValue.GetIntValue();
 11611       if ((100 <= intValue) &&
 11612           (intValue <= 900) &&
 11613           (0 == (intValue % 100))) {
 11614         return true;
 11615       } else {
 11616         UngetToken();
 11617         return false;
 11620     return true;
 11622   return false;
 11625 bool
 11626 CSSParserImpl::ParseOneFamily(nsAString& aFamily, bool& aOneKeyword)
 11628   if (!GetToken(true))
 11629     return false;
 11631   nsCSSToken* tk = &mToken;
 11633   aOneKeyword = false;
 11634   if (eCSSToken_Ident == tk->mType) {
 11635     aOneKeyword = true;
 11636     aFamily.Append(tk->mIdent);
 11637     for (;;) {
 11638       if (!GetToken(false))
 11639         break;
 11641       if (eCSSToken_Ident == tk->mType) {
 11642         aOneKeyword = false;
 11643         // We had at least another keyword before.
 11644         // "If a sequence of identifiers is given as a font family name,
 11645         //  the computed value is the name converted to a string by joining
 11646         //  all the identifiers in the sequence by single spaces."
 11647         // -- CSS 2.1, section 15.3
 11648         // Whitespace tokens do not actually matter,
 11649         // identifier tokens can be separated by comments.
 11650         aFamily.Append(char16_t(' '));
 11651         aFamily.Append(tk->mIdent);
 11652       } else if (eCSSToken_Whitespace != tk->mType) {
 11653         UngetToken();
 11654         break;
 11657     return true;
 11659   } else if (eCSSToken_String == tk->mType) {
 11660     aFamily.Append(tk->mSymbol); // replace the quotes
 11661     aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
 11662     aFamily.Append(tk->mSymbol);
 11663     return true;
 11665   } else {
 11666     UngetToken();
 11667     return false;
 11671 bool
 11672 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
 11674   nsAutoString family;
 11675   bool single;
 11677   // keywords only have meaning in the first position
 11678   if (!ParseOneFamily(family, single))
 11679     return false;
 11681   // check for keywords, but only when keywords appear by themselves
 11682   // i.e. not in compounds such as font-family: default blah;
 11683   if (single) {
 11684     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
 11685     if (keyword == eCSSKeyword_inherit) {
 11686       aValue.SetInheritValue();
 11687       return true;
 11689     // 605231 - don't parse unquoted 'default' reserved keyword
 11690     if (keyword == eCSSKeyword_default) {
 11691       return false;
 11693     if (keyword == eCSSKeyword_initial) {
 11694       aValue.SetInitialValue();
 11695       return true;
 11697     if (keyword == eCSSKeyword_unset &&
 11698         nsLayoutUtils::UnsetValueEnabled()) {
 11699       aValue.SetUnsetValue();
 11700       return true;
 11702     if (keyword == eCSSKeyword__moz_use_system_font &&
 11703         !IsParsingCompoundProperty()) {
 11704       aValue.SetSystemFontValue();
 11705       return true;
 11709   for (;;) {
 11710     if (!ExpectSymbol(',', true))
 11711       break;
 11713     family.Append(char16_t(','));
 11715     nsAutoString nextFamily;
 11716     if (!ParseOneFamily(nextFamily, single))
 11717       return false;
 11719     // at this point unquoted keywords are not allowed
 11720     // as font family names but can appear within names
 11721     if (single) {
 11722       nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily);
 11723       switch (keyword) {
 11724         case eCSSKeyword_inherit:
 11725         case eCSSKeyword_initial:
 11726         case eCSSKeyword_default:
 11727         case eCSSKeyword__moz_use_system_font:
 11728           return false;
 11729         case eCSSKeyword_unset:
 11730           if (nsLayoutUtils::UnsetValueEnabled()) {
 11731             return false;
 11733           // fall through
 11734         default:
 11735           break;
 11739     family.Append(nextFamily);
 11742   if (family.IsEmpty()) {
 11743     return false;
 11745   aValue.SetStringValue(family, eCSSUnit_Families);
 11746   return true;
 11749 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
 11750 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
 11751 // local-src: 'local(' ( string | ident ) ')'
 11753 bool
 11754 CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
 11756   // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>?
 11757   InfallibleTArray<nsCSSValue> values;
 11758   nsCSSValue cur;
 11759   for (;;) {
 11760     if (!GetToken(true))
 11761       break;
 11763     if (mToken.mType == eCSSToken_URL) {
 11764       SetValueToURL(cur, mToken.mIdent);
 11765       values.AppendElement(cur);
 11766       if (!ParseFontSrcFormat(values))
 11767         return false;
 11769     } else if (mToken.mType == eCSSToken_Function &&
 11770                mToken.mIdent.LowerCaseEqualsLiteral("local")) {
 11771       // css3-fonts does not specify a formal grammar for local().
 11772       // The text permits both unquoted identifiers and quoted
 11773       // strings.  We resolve this ambiguity in the spec by
 11774       // assuming that the appropriate production is a single
 11775       // <family-name>, possibly surrounded by whitespace.
 11777       nsAutoString family;
 11778       bool single;
 11779       if (!ParseOneFamily(family, single)) {
 11780         SkipUntil(')');
 11781         return false;
 11783       if (!ExpectSymbol(')', true)) {
 11784         SkipUntil(')');
 11785         return false;
 11788       // XXX: Getting closer...
 11789       // the style parameters to the nsFont constructor are ignored,
 11790       // because it's only being used to call EnumerateFamilies
 11791       nsFont font(family, 0, 0, 0, 0, 0, 0);
 11792       ExtractFirstFamilyData dat;
 11794       font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
 11795       if (!dat.mGood)
 11796         return false;
 11798       cur.SetStringValue(dat.mFamilyName, eCSSUnit_Local_Font);
 11799       values.AppendElement(cur);
 11800     } else {
 11801       // We don't know what to do with this token; unget it and error out
 11802       UngetToken();
 11803       return false;
 11806     if (!ExpectSymbol(',', true))
 11807       break;
 11810   if (values.Length() == 0)
 11811     return false;
 11813   nsRefPtr<nsCSSValue::Array> srcVals
 11814     = nsCSSValue::Array::Create(values.Length());
 11816   uint32_t i;
 11817   for (i = 0; i < values.Length(); i++)
 11818     srcVals->Item(i) = values[i];
 11819   aValue.SetArrayValue(srcVals, eCSSUnit_Array);
 11820   return true;
 11823 bool
 11824 CSSParserImpl::ParseFontSrcFormat(InfallibleTArray<nsCSSValue> & values)
 11826   if (!GetToken(true))
 11827     return true; // EOF harmless here
 11828   if (mToken.mType != eCSSToken_Function ||
 11829       !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
 11830     UngetToken();
 11831     return true;
 11834   do {
 11835     if (!GetToken(true))
 11836       return false; // EOF - no need for SkipUntil
 11838     if (mToken.mType != eCSSToken_String) {
 11839       UngetToken();
 11840       SkipUntil(')');
 11841       return false;
 11844     nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
 11845     values.AppendElement(cur);
 11846   } while (ExpectSymbol(',', true));
 11848   if (!ExpectSymbol(')', true)) {
 11849     SkipUntil(')');
 11850     return false;
 11853   return true;
 11856 // font-ranges: urange ( ',' urange )*
 11857 bool
 11858 CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
 11860   InfallibleTArray<uint32_t> ranges;
 11861   for (;;) {
 11862     if (!GetToken(true))
 11863       break;
 11865     if (mToken.mType != eCSSToken_URange) {
 11866       UngetToken();
 11867       break;
 11870     // An invalid range token is a parsing error, causing the entire
 11871     // descriptor to be ignored.
 11872     if (!mToken.mIntegerValid)
 11873       return false;
 11875     uint32_t low = mToken.mInteger;
 11876     uint32_t high = mToken.mInteger2;
 11878     // A range that descends, or a range that is entirely outside the
 11879     // current range of Unicode (U+0-10FFFF) is ignored, but does not
 11880     // invalidate the descriptor.  A range that straddles the high end
 11881     // is clipped.
 11882     if (low <= 0x10FFFF && low <= high) {
 11883       if (high > 0x10FFFF)
 11884         high = 0x10FFFF;
 11886       ranges.AppendElement(low);
 11887       ranges.AppendElement(high);
 11889     if (!ExpectSymbol(',', true))
 11890       break;
 11893   if (ranges.Length() == 0)
 11894     return false;
 11896   nsRefPtr<nsCSSValue::Array> srcVals
 11897     = nsCSSValue::Array::Create(ranges.Length());
 11899   for (uint32_t i = 0; i < ranges.Length(); i++)
 11900     srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer);
 11901   aValue.SetArrayValue(srcVals, eCSSUnit_Array);
 11902   return true;
 11905 // font-feature-settings: normal | <feature-tag-value> [, <feature-tag-value>]*
 11906 // <feature-tag-value> = <string> [ <integer> | on | off ]?
 11908 // minimum - "tagx", "tagy", "tagz"
 11909 // edge error case - "tagx" on 1, "tagx" "tagy", "tagx" -1, "tagx" big
 11911 // pair value is always x = string, y = int
 11913 // font feature tags must be four ASCII characters
 11914 #define FEATURE_TAG_LENGTH   4
 11916 static bool
 11917 ValidFontFeatureTag(const nsString& aTag)
 11919   if (aTag.Length() != FEATURE_TAG_LENGTH) {
 11920     return false;
 11922   uint32_t i;
 11923   for (i = 0; i < FEATURE_TAG_LENGTH; i++) {
 11924     uint32_t ch = aTag[i];
 11925     if (ch < 0x20 || ch > 0x7e) {
 11926       return false;
 11929   return true;
 11932 bool
 11933 CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue)
 11935   if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
 11936     return true;
 11939   nsCSSValuePairList *cur = aValue.SetPairListValue();
 11940   for (;;) {
 11941     // feature tag
 11942     if (!GetToken(true)) {
 11943       return false;
 11946     if (mToken.mType != eCSSToken_String ||
 11947         !ValidFontFeatureTag(mToken.mIdent)) {
 11948       UngetToken();
 11949       return false;
 11951     cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
 11953     if (!GetToken(true)) {
 11954       cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
 11955       break;
 11958     // optional value or on/off keyword
 11959     if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid &&
 11960         mToken.mInteger >= 0) {
 11961       cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
 11962     } else if (mToken.mType == eCSSToken_Ident &&
 11963                mToken.mIdent.LowerCaseEqualsLiteral("on")) {
 11964       cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
 11965     } else if (mToken.mType == eCSSToken_Ident &&
 11966                mToken.mIdent.LowerCaseEqualsLiteral("off")) {
 11967       cur->mYValue.SetIntValue(0, eCSSUnit_Integer);
 11968     } else {
 11969       // something other than value/on/off, set default value
 11970       cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
 11971       UngetToken();
 11974     if (!ExpectSymbol(',', true)) {
 11975       break;
 11978     cur->mNext = new nsCSSValuePairList;
 11979     cur = cur->mNext;
 11982   return true;
 11985 bool
 11986 CSSParserImpl::ParseListStyle()
 11988   // 'list-style' can accept 'none' for two different subproperties,
 11989   // 'list-style-type' and 'list-style-position'.  In order to accept
 11990   // 'none' as the value of either but still allow another value for
 11991   // either, we need to ensure that the first 'none' we find gets
 11992   // allocated to a dummy property instead.
 11993   static const nsCSSProperty listStyleIDs[] = {
 11994     eCSSPropertyExtra_x_none_value,
 11995     eCSSProperty_list_style_type,
 11996     eCSSProperty_list_style_position,
 11997     eCSSProperty_list_style_image
 11998   };
 12000   nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)];
 12001   int32_t found =
 12002     ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs));
 12003   if (found < 1) {
 12004     return false;
 12007   if ((found & (1|2|8)) == (1|2|8)) {
 12008     if (values[0].GetUnit() == eCSSUnit_None) {
 12009       // We found a 'none' plus another value for both of
 12010       // 'list-style-type' and 'list-style-image'.  This is a parse
 12011       // error, since the 'none' has to count for at least one of them.
 12012       return false;
 12013     } else {
 12014       NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] &&
 12015                    values[0] == values[2] && values[0] == values[3],
 12016                    "should be a special value");
 12020   // Provide default values
 12021   if ((found & 2) == 0) {
 12022     if (found & 1) {
 12023       values[1].SetIntValue(NS_STYLE_LIST_STYLE_NONE, eCSSUnit_Enumerated);
 12024     } else {
 12025       values[1].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
 12028   if ((found & 4) == 0) {
 12029     values[2].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
 12030                           eCSSUnit_Enumerated);
 12032   if ((found & 8) == 0) {
 12033     values[3].SetNoneValue();
 12036   // Start at 1 to avoid appending fake value.
 12037   for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) {
 12038     AppendValue(listStyleIDs[index], values[index]);
 12040   return true;
 12043 bool
 12044 CSSParserImpl::ParseMargin()
 12046   static const nsCSSProperty kMarginSideIDs[] = {
 12047     eCSSProperty_margin_top,
 12048     eCSSProperty_margin_right_value,
 12049     eCSSProperty_margin_bottom,
 12050     eCSSProperty_margin_left_value
 12051   };
 12052   static const nsCSSProperty kMarginSources[] = {
 12053     eCSSProperty_margin_left_ltr_source,
 12054     eCSSProperty_margin_left_rtl_source,
 12055     eCSSProperty_margin_right_ltr_source,
 12056     eCSSProperty_margin_right_rtl_source,
 12057     eCSSProperty_UNKNOWN
 12058   };
 12060   // do this now, in case 4 values weren't specified
 12061   InitBoxPropsAsPhysical(kMarginSources);
 12062   return ParseBoxProperties(kMarginSideIDs);
 12065 bool
 12066 CSSParserImpl::ParseMarks(nsCSSValue& aValue)
 12068   if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPageMarksKTable)) {
 12069     if (eCSSUnit_Enumerated == aValue.GetUnit()) {
 12070       if (NS_STYLE_PAGE_MARKS_NONE != aValue.GetIntValue() &&
 12071           false == CheckEndProperty()) {
 12072         nsCSSValue second;
 12073         if (ParseEnum(second, nsCSSProps::kPageMarksKTable)) {
 12074           // 'none' keyword in conjuction with others is not allowed
 12075           if (NS_STYLE_PAGE_MARKS_NONE != second.GetIntValue()) {
 12076             aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(),
 12077                                eCSSUnit_Enumerated);
 12078             return true;
 12081         return false;
 12084     return true;
 12086   return false;
 12089 bool
 12090 CSSParserImpl::ParseOutline()
 12092   const int32_t numProps = 3;
 12093   static const nsCSSProperty kOutlineIDs[] = {
 12094     eCSSProperty_outline_color,
 12095     eCSSProperty_outline_style,
 12096     eCSSProperty_outline_width
 12097   };
 12099   nsCSSValue  values[numProps];
 12100   int32_t found = ParseChoice(values, kOutlineIDs, numProps);
 12101   if (found < 1) {
 12102     return false;
 12105   // Provide default values
 12106   if ((found & 1) == 0) { // Provide default outline-color
 12107     values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
 12109   if ((found & 2) == 0) { // Provide default outline-style
 12110     values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
 12112   if ((found & 4) == 0) { // Provide default outline-width
 12113     values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
 12116   int32_t index;
 12117   for (index = 0; index < numProps; index++) {
 12118     AppendValue(kOutlineIDs[index], values[index]);
 12120   return true;
 12123 bool
 12124 CSSParserImpl::ParseOverflow()
 12126   nsCSSValue overflow;
 12127   if (!ParseVariant(overflow, VARIANT_HK, nsCSSProps::kOverflowKTable)) {
 12128     return false;
 12131   nsCSSValue overflowX(overflow);
 12132   nsCSSValue overflowY(overflow);
 12133   if (eCSSUnit_Enumerated == overflow.GetUnit())
 12134     switch(overflow.GetIntValue()) {
 12135       case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
 12136         overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
 12137         overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
 12138         break;
 12139       case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
 12140         overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
 12141         overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
 12142         break;
 12144   AppendValue(eCSSProperty_overflow_x, overflowX);
 12145   AppendValue(eCSSProperty_overflow_y, overflowY);
 12146   return true;
 12149 bool
 12150 CSSParserImpl::ParsePadding()
 12152   static const nsCSSProperty kPaddingSideIDs[] = {
 12153     eCSSProperty_padding_top,
 12154     eCSSProperty_padding_right_value,
 12155     eCSSProperty_padding_bottom,
 12156     eCSSProperty_padding_left_value
 12157   };
 12158   static const nsCSSProperty kPaddingSources[] = {
 12159     eCSSProperty_padding_left_ltr_source,
 12160     eCSSProperty_padding_left_rtl_source,
 12161     eCSSProperty_padding_right_ltr_source,
 12162     eCSSProperty_padding_right_rtl_source,
 12163     eCSSProperty_UNKNOWN
 12164   };
 12166   // do this now, in case 4 values weren't specified
 12167   InitBoxPropsAsPhysical(kPaddingSources);
 12168   return ParseBoxProperties(kPaddingSideIDs);
 12171 bool
 12172 CSSParserImpl::ParseQuotes()
 12174   nsCSSValue value;
 12175   if (!ParseVariant(value, VARIANT_HOS, nullptr)) {
 12176     return false;
 12178   if (value.GetUnit() == eCSSUnit_String) {
 12179     nsCSSValue open = value;
 12180     nsCSSValuePairList* quotes = value.SetPairListValue();
 12181     for (;;) {
 12182       quotes->mXValue = open;
 12183       // get mandatory close
 12184       if (!ParseVariant(quotes->mYValue, VARIANT_STRING, nullptr)) {
 12185         return false;
 12187       // look for another open
 12188       if (!ParseVariant(open, VARIANT_STRING, nullptr)) {
 12189         break;
 12191       quotes->mNext = new nsCSSValuePairList;
 12192       quotes = quotes->mNext;
 12195   AppendValue(eCSSProperty_quotes, value);
 12196   return true;
 12199 bool
 12200 CSSParserImpl::ParseSize()
 12202   nsCSSValue width, height;
 12203   if (!ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
 12204     return false;
 12206   if (width.IsLengthUnit()) {
 12207     ParseVariant(height, VARIANT_LENGTH, nullptr);
 12210   if (width == height || height.GetUnit() == eCSSUnit_Null) {
 12211     AppendValue(eCSSProperty_size, width);
 12212   } else {
 12213     nsCSSValue pair;
 12214     pair.SetPairValue(width, height);
 12215     AppendValue(eCSSProperty_size, pair);
 12217   return true;
 12220 bool
 12221 CSSParserImpl::ParseTextDecoration()
 12223   enum {
 12224     eDecorationNone         = NS_STYLE_TEXT_DECORATION_LINE_NONE,
 12225     eDecorationUnderline    = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
 12226     eDecorationOverline     = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE,
 12227     eDecorationLineThrough  = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
 12228     eDecorationBlink        = NS_STYLE_TEXT_DECORATION_LINE_BLINK,
 12229     eDecorationPrefAnchors  = NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS
 12230   };
 12231   static_assert((eDecorationNone ^ eDecorationUnderline ^
 12232                  eDecorationOverline ^ eDecorationLineThrough ^
 12233                  eDecorationBlink ^ eDecorationPrefAnchors) ==
 12234                 (eDecorationNone | eDecorationUnderline |
 12235                  eDecorationOverline | eDecorationLineThrough |
 12236                  eDecorationBlink | eDecorationPrefAnchors),
 12237                 "text decoration constants need to be bitmasks");
 12239   static const KTableValue kTextDecorationKTable[] = {
 12240     eCSSKeyword_none,                   eDecorationNone,
 12241     eCSSKeyword_underline,              eDecorationUnderline,
 12242     eCSSKeyword_overline,               eDecorationOverline,
 12243     eCSSKeyword_line_through,           eDecorationLineThrough,
 12244     eCSSKeyword_blink,                  eDecorationBlink,
 12245     eCSSKeyword__moz_anchor_decoration, eDecorationPrefAnchors,
 12246     eCSSKeyword_UNKNOWN,-1
 12247   };
 12249   nsCSSValue value;
 12250   if (!ParseVariant(value, VARIANT_HK, kTextDecorationKTable)) {
 12251     return false;
 12254   nsCSSValue line, style, color;
 12255   switch (value.GetUnit()) {
 12256     case eCSSUnit_Enumerated: {
 12257       // We shouldn't accept decoration line style and color via
 12258       // text-decoration.
 12259       color.SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR,
 12260                         eCSSUnit_Enumerated);
 12261       style.SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
 12262                         eCSSUnit_Enumerated);
 12264       int32_t intValue = value.GetIntValue();
 12265       if (intValue == eDecorationNone) {
 12266         line.SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE,
 12267                          eCSSUnit_Enumerated);
 12268         break;
 12271       // look for more keywords
 12272       nsCSSValue keyword;
 12273       int32_t index;
 12274       for (index = 0; index < 3; index++) {
 12275         if (!ParseEnum(keyword, kTextDecorationKTable)) {
 12276           break;
 12278         int32_t newValue = keyword.GetIntValue();
 12279         if (newValue == eDecorationNone || newValue & intValue) {
 12280           // 'none' keyword in conjuction with others is not allowed, and
 12281           // duplicate keyword is not allowed.
 12282           return false;
 12284         intValue |= newValue;
 12287       line.SetIntValue(intValue, eCSSUnit_Enumerated);
 12288       break;
 12290     default:
 12291       line = color = style = value;
 12292       break;
 12295   AppendValue(eCSSProperty_text_decoration_line, line);
 12296   AppendValue(eCSSProperty_text_decoration_color, color);
 12297   AppendValue(eCSSProperty_text_decoration_style, style);
 12299   return true;
 12302 bool
 12303 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableValue aTable[])
 12305   if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
 12306     // 'inherit', 'initial' and 'unset' must be alone
 12307     return true;
 12310   nsCSSValue left;
 12311   if (!ParseVariant(left, VARIANT_KEYWORD, aTable)) {
 12312     return false;
 12315   if (!nsLayoutUtils::IsTextAlignTrueValueEnabled()) {
 12316     aValue = left;
 12317     return true;
 12320   nsCSSValue right;
 12321   if (ParseVariant(right, VARIANT_KEYWORD, aTable)) {
 12322     // 'true' must be combined with some other value than 'true'.
 12323     if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE &&
 12324         right.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
 12325       return false;
 12327     aValue.SetPairValue(left, right);
 12328   } else {
 12329     // Single value 'true' is not allowed.
 12330     if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
 12331       return false;
 12333     aValue = left;
 12335   return true;
 12338 bool
 12339 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue)
 12341   return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable);
 12344 bool
 12345 CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue)
 12347   return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable);
 12350 bool
 12351 CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
 12353   if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTextDecorationLineKTable)) {
 12354     if (eCSSUnit_Enumerated == aValue.GetUnit()) {
 12355       int32_t intValue = aValue.GetIntValue();
 12356       if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
 12357         // look for more keywords
 12358         nsCSSValue  keyword;
 12359         int32_t index;
 12360         for (index = 0; index < 3; index++) {
 12361           if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) {
 12362             int32_t newValue = keyword.GetIntValue();
 12363             if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE ||
 12364                 newValue & intValue) {
 12365               // 'none' keyword in conjuction with others is not allowed, and
 12366               // duplicate keyword is not allowed.
 12367               return false;
 12369             intValue |= newValue;
 12371           else {
 12372             break;
 12375         aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
 12378     return true;
 12380   return false;
 12383 bool
 12384 CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
 12386   if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
 12387     // 'inherit', 'initial' and 'unset' must be alone
 12388     return true;
 12391   nsCSSValue left;
 12392   if (!ParseVariant(left, VARIANT_KEYWORD | VARIANT_STRING,
 12393                     nsCSSProps::kTextOverflowKTable))
 12394     return false;
 12396   nsCSSValue right;
 12397   if (ParseVariant(right, VARIANT_KEYWORD | VARIANT_STRING,
 12398                     nsCSSProps::kTextOverflowKTable))
 12399     aValue.SetPairValue(left, right);
 12400   else {
 12401     aValue = left;
 12403   return true;
 12406 bool
 12407 CSSParserImpl::ParseTouchAction(nsCSSValue& aValue)
 12409   // Avaliable values of property touch-action:
 12410   // auto | none | [pan-x || pan-y] | manipulation
 12412   if (!ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTouchActionKTable)) {
 12413     return false;
 12416   // Auto and None keywords aren't allowed in conjunction with others.
 12417   // Also inherit, initial and unset values are available.
 12418   if (eCSSUnit_Enumerated != aValue.GetUnit()) {
 12419     return true;
 12422   int32_t intValue = aValue.GetIntValue();
 12423   nsCSSValue nextValue;
 12424   if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) {
 12425     int32_t nextIntValue = nextValue.GetIntValue();
 12427     // duplicates aren't allowed.
 12428     if (nextIntValue & intValue) {
 12429       return false;
 12432     // Auto and None and Manipulation is not allowed in conjunction with others.
 12433     if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE |
 12434                                      NS_STYLE_TOUCH_ACTION_AUTO |
 12435                                      NS_STYLE_TOUCH_ACTION_MANIPULATION)) {
 12436       return false;
 12439     aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
 12442   return true;
 12445 bool
 12446 CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue)
 12448   if (!ParseVariant(aValue, VARIANT_HK,
 12449                     nsCSSProps::kTextCombineUprightKTable)) {
 12450     return false;
 12453   // if 'digits', need to check for an explicit number [2, 3, 4]
 12454   if (eCSSUnit_Enumerated == aValue.GetUnit() &&
 12455       aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) {
 12456     if (!GetToken(true)) {
 12457       return true;
 12459     if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
 12460       switch (mToken.mInteger) {
 12461         case 2:  // already set, nothing to do
 12462           break;
 12463         case 3:
 12464           aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3,
 12465                              eCSSUnit_Enumerated);
 12466           break;
 12467         case 4:
 12468           aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4,
 12469                              eCSSUnit_Enumerated);
 12470           break;
 12471         default:
 12472           // invalid digits value
 12473           return false;
 12475     } else {
 12476       UngetToken();
 12479   return true;
 12482 ///////////////////////////////////////////////////////
 12483 // transform Parsing Implementation
 12485 /* Reads a function list of arguments and consumes the closing parenthesis.
 12486  * Do not call this function directly; it's meant to be called from
 12487  * ParseFunction.
 12488  */
 12489 bool
 12490 CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[],
 12491                                       int32_t aVariantMaskAll,
 12492                                       uint16_t aMinElems,
 12493                                       uint16_t aMaxElems,
 12494                                       InfallibleTArray<nsCSSValue> &aOutput)
 12496   NS_ASSERTION((aVariantMask && !aVariantMaskAll) ||
 12497                (!aVariantMask && aVariantMaskAll),
 12498                "only one of the two variant mask parameters can be set");
 12500   for (uint16_t index = 0; index < aMaxElems; ++index) {
 12501     nsCSSValue newValue;
 12502     int32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index];
 12503     if (!ParseVariant(newValue, m, nullptr)) {
 12504       break;
 12507     aOutput.AppendElement(newValue);
 12509     if (ExpectSymbol(',', true)) {
 12510       // Move on to the next argument if we see a comma.
 12511       continue;
 12514     if (ExpectSymbol(')', true)) {
 12515       // Make sure we've read enough symbols if we see a closing parenthesis.
 12516       return (index + 1) >= aMinElems;
 12519     // Only a comma or a closing parenthesis is valid after an argument.
 12520     break;
 12523   // If we're here, we've hit an error without seeing a closing parenthesis or
 12524   // we've read too many elements without seeing a closing parenthesis.
 12525   SkipUntil(')');
 12526   return false;
 12529 /* Parses a function [ input of the form (a [, b]*) ] and stores it
 12530  * as an nsCSSValue that holds a function of the form
 12531  * function-name arg1 arg2 ... argN
 12533  * On error, the return value is false.
 12535  * @param aFunction The name of the function that we're reading.
 12536  * @param aAllowedTypes An array of values corresponding to the legal
 12537  *        types for each element in the function.  The zeroth element in the
 12538  *        array corresponds to the first function parameter, etc.  The length
 12539  *        of this array _must_ be greater than or equal to aMaxElems or the
 12540  *        behavior is undefined.  If not null, aAllowTypesAll must be 0.
 12541  * @param aAllowedTypesAll If set, every element tested for these types
 12542  * @param aMinElems Minimum number of elements to read.  Reading fewer than
 12543  *        this many elements will result in the function failing.
 12544  * @param aMaxElems Maximum number of elements to read.  Reading more than
 12545  *        this many elements will result in the function failing.
 12546  * @param aValue (out) The value that was parsed.
 12547  */
 12548 bool
 12549 CSSParserImpl::ParseFunction(nsCSSKeyword aFunction,
 12550                              const int32_t aAllowedTypes[],
 12551                              int32_t aAllowedTypesAll,
 12552                              uint16_t aMinElems, uint16_t aMaxElems,
 12553                              nsCSSValue &aValue)
 12555   NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) ||
 12556                (!aAllowedTypes && aAllowedTypesAll),
 12557                "only one of the two allowed type parameter can be set");
 12558   typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
 12560   /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
 12561    * elements stored in the the nsCSSValue::Array.
 12562    */
 12563   static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
 12565   /* Read in a list of values as an array, failing if we can't or if
 12566    * it's out of bounds.
 12568    * We reserve 16 entries in the foundValues array in order to avoid
 12569    * having to resize the array dynamically when parsing some well-formed
 12570    * functions.  The number 16 is coming from the number of arguments that
 12571    * matrix3d() accepts.
 12572    */
 12573   AutoInfallibleTArray<nsCSSValue, 16> foundValues;
 12574   if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems,
 12575                               aMaxElems, foundValues)) {
 12576     return false;
 12579   /*
 12580    * In case the user has given us more than 2^16 - 2 arguments,
 12581    * we'll truncate them at 2^16 - 2 arguments.
 12582    */
 12583   uint16_t numArgs = std::min(foundValues.Length(), MAX_ALLOWED_ELEMS);
 12584   nsRefPtr<nsCSSValue::Array> convertedArray =
 12585     aValue.InitFunction(aFunction, numArgs);
 12587   /* Copy things over. */
 12588   for (uint16_t index = 0; index < numArgs; ++index)
 12589     convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
 12591   /* Return it! */
 12592   return true;
 12595 /**
 12596  * Given a token, determines the minimum and maximum number of function
 12597  * parameters to read, along with the mask that should be used to read
 12598  * those function parameters.  If the token isn't a transform function,
 12599  * returns an error.
 12601  * @param aToken The token identifying the function.
 12602  * @param aMinElems [out] The minimum number of elements to read.
 12603  * @param aMaxElems [out] The maximum number of elements to read
 12604  * @param aVariantMask [out] The variant mask to use during parsing
 12605  * @return Whether the information was loaded successfully.
 12606  */
 12607 static bool GetFunctionParseInformation(nsCSSKeyword aToken,
 12608                                         bool aIsPrefixed,
 12609                                         uint16_t &aMinElems,
 12610                                         uint16_t &aMaxElems,
 12611                                         const int32_t *& aVariantMask)
 12613 /* These types represent the common variant masks that will be used to
 12614    * parse out the individual functions.  The order in the enumeration
 12615    * must match the order in which the masks are declared.
 12616    */
 12617   enum { eLengthPercentCalc,
 12618          eLengthCalc,
 12619          eTwoLengthPercentCalcs,
 12620          eTwoLengthPercentCalcsOneLengthCalc,
 12621          eAngle,
 12622          eTwoAngles,
 12623          eNumber,
 12624          ePositiveLength,
 12625          eTwoNumbers,
 12626          eThreeNumbers,
 12627          eThreeNumbersOneAngle,
 12628          eMatrix,
 12629          eMatrixPrefixed,
 12630          eMatrix3d,
 12631          eMatrix3dPrefixed,
 12632          eNumVariantMasks };
 12633   static const int32_t kMaxElemsPerFunction = 16;
 12634   static const int32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
 12635     {VARIANT_LPCALC},
 12636     {VARIANT_LENGTH|VARIANT_CALC},
 12637     {VARIANT_LPCALC, VARIANT_LPCALC},
 12638     {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LENGTH|VARIANT_CALC},
 12639     {VARIANT_ANGLE_OR_ZERO},
 12640     {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
 12641     {VARIANT_NUMBER},
 12642     {VARIANT_LENGTH|VARIANT_POSITIVE_DIMENSION},
 12643     {VARIANT_NUMBER, VARIANT_NUMBER},
 12644     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
 12645     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
 12646     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
 12647      VARIANT_NUMBER, VARIANT_NUMBER},
 12648     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
 12649      VARIANT_LPNCALC, VARIANT_LPNCALC},
 12650     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
 12651      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
 12652      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
 12653      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
 12654     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
 12655      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
 12656      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
 12657      VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
 12659 #ifdef DEBUG
 12660   static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
 12661     {1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 6, 16, 16};
 12662 #endif
 12664   int32_t variantIndex = eNumVariantMasks;
 12666   switch (aToken) {
 12667   case eCSSKeyword_translatex:
 12668   case eCSSKeyword_translatey:
 12669     /* Exactly one length or percent. */
 12670     variantIndex = eLengthPercentCalc;
 12671     aMinElems = 1U;
 12672     aMaxElems = 1U;
 12673     break;
 12674   case eCSSKeyword_translatez:
 12675     /* Exactly one length */
 12676     variantIndex = eLengthCalc;
 12677     aMinElems = 1U;
 12678     aMaxElems = 1U;
 12679     break;
 12680   case eCSSKeyword_translate3d:
 12681     /* Exactly two lengthds or percents and a number */
 12682     variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
 12683     aMinElems = 3U;
 12684     aMaxElems = 3U;
 12685     break;
 12686   case eCSSKeyword_scalez:
 12687   case eCSSKeyword_scalex:
 12688   case eCSSKeyword_scaley:
 12689     /* Exactly one scale factor. */
 12690     variantIndex = eNumber;
 12691     aMinElems = 1U;
 12692     aMaxElems = 1U;
 12693     break;
 12694   case eCSSKeyword_scale3d:
 12695     /* Exactly three scale factors. */
 12696     variantIndex = eThreeNumbers;
 12697     aMinElems = 3U;
 12698     aMaxElems = 3U;
 12699     break;
 12700   case eCSSKeyword_rotatex:
 12701   case eCSSKeyword_rotatey:
 12702   case eCSSKeyword_rotate:
 12703   case eCSSKeyword_rotatez:
 12704     /* Exactly one angle. */
 12705     variantIndex = eAngle;
 12706     aMinElems = 1U;
 12707     aMaxElems = 1U;
 12708     break;
 12709   case eCSSKeyword_rotate3d:
 12710     variantIndex = eThreeNumbersOneAngle;
 12711     aMinElems = 4U;
 12712     aMaxElems = 4U;
 12713     break;
 12714   case eCSSKeyword_translate:
 12715     /* One or two lengths or percents. */
 12716     variantIndex = eTwoLengthPercentCalcs;
 12717     aMinElems = 1U;
 12718     aMaxElems = 2U;
 12719     break;
 12720   case eCSSKeyword_skew:
 12721     /* Exactly one or two angles. */
 12722     variantIndex = eTwoAngles;
 12723     aMinElems = 1U;
 12724     aMaxElems = 2U;
 12725     break;
 12726   case eCSSKeyword_scale:
 12727     /* One or two scale factors. */
 12728     variantIndex = eTwoNumbers;
 12729     aMinElems = 1U;
 12730     aMaxElems = 2U;
 12731     break;
 12732   case eCSSKeyword_skewx:
 12733     /* Exactly one angle. */
 12734     variantIndex = eAngle;
 12735     aMinElems = 1U;
 12736     aMaxElems = 1U;
 12737     break;
 12738   case eCSSKeyword_skewy:
 12739     /* Exactly one angle. */
 12740     variantIndex = eAngle;
 12741     aMinElems = 1U;
 12742     aMaxElems = 1U;
 12743     break;
 12744   case eCSSKeyword_matrix:
 12745     /* Six values, all numbers. */
 12746     variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
 12747     aMinElems = 6U;
 12748     aMaxElems = 6U;
 12749     break;
 12750   case eCSSKeyword_matrix3d:
 12751     /* 16 matrix values, all numbers */
 12752     variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
 12753     aMinElems = 16U;
 12754     aMaxElems = 16U;
 12755     break;
 12756   case eCSSKeyword_perspective:
 12757     /* Exactly one scale number. */
 12758     variantIndex = ePositiveLength;
 12759     aMinElems = 1U;
 12760     aMaxElems = 1U;
 12761     break;
 12762   default:
 12763     /* Oh dear, we didn't match.  Report an error. */
 12764     return false;
 12767   NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
 12768   NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
 12769   NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
 12770   NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
 12771   NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
 12772 #ifdef DEBUG
 12773   NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
 12774                "Invalid aMaxElems for this variant mask.");
 12775 #endif
 12777   // Convert the index into a mask.
 12778   aVariantMask = kVariantMasks[variantIndex];
 12780   return true;
 12783 bool CSSParserImpl::ParseWillChange()
 12785   nsCSSValue listValue;
 12786   nsCSSValueList* currentListValue = listValue.SetListValue();
 12787   bool first = true;
 12788   for (;;) {
 12789     const uint32_t variantMask = VARIANT_IDENTIFIER |
 12790                                  VARIANT_INHERIT |
 12791                                  VARIANT_NONE |
 12792                                  VARIANT_ALL |
 12793                                  VARIANT_AUTO;
 12794     nsCSSValue value;
 12795     if (!ParseVariant(value, variantMask, nullptr)) {
 12796       return false;
 12799     if (value.GetUnit() == eCSSUnit_None ||
 12800         value.GetUnit() == eCSSUnit_All)
 12802       return false;
 12805     if (value.GetUnit() != eCSSUnit_Ident) {
 12806       if (first) {
 12807         AppendValue(eCSSProperty_will_change, value);
 12808         return true;
 12809       } else {
 12810         return false;
 12814     nsString str;
 12815     value.GetStringValue(str);
 12816     if (str.LowerCaseEqualsLiteral("default")) {
 12817       return false;
 12820     currentListValue->mValue = value;
 12822     if (!ExpectSymbol(',', true)) {
 12823       break;
 12825     currentListValue->mNext = new nsCSSValueList;
 12826     currentListValue = currentListValue->mNext;
 12827     first = false;
 12830   AppendValue(eCSSProperty_will_change, listValue);
 12831   return true;
 12834 /* Reads a single transform function from the tokenizer stream, reporting an
 12835  * error if something goes wrong.
 12836  */
 12837 bool
 12838 CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue)
 12840   if (!GetToken(true))
 12841     return false;
 12843   if (mToken.mType != eCSSToken_Function) {
 12844     UngetToken();
 12845     return false;
 12848   const int32_t* variantMask;
 12849   uint16_t minElems, maxElems;
 12850   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
 12852   if (!GetFunctionParseInformation(keyword, aIsPrefixed,
 12853                                    minElems, maxElems, variantMask))
 12854     return false;
 12856   return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue);
 12859 /* Parses a transform property list by continuously reading in properties
 12860  * and constructing a matrix from it.
 12861  */
 12862 bool CSSParserImpl::ParseTransform(bool aIsPrefixed)
 12864   nsCSSValue value;
 12865   // 'inherit', 'initial', 'unset' and 'none' must be alone
 12866   if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
 12867     nsCSSValueSharedList* list = new nsCSSValueSharedList;
 12868     value.SetSharedListValue(list);
 12869     list->mHead = new nsCSSValueList;
 12870     nsCSSValueList* cur = list->mHead;
 12871     for (;;) {
 12872       if (!ParseSingleTransform(aIsPrefixed, cur->mValue)) {
 12873         return false;
 12875       if (CheckEndProperty()) {
 12876         break;
 12878       cur->mNext = new nsCSSValueList;
 12879       cur = cur->mNext;
 12882   AppendValue(eCSSProperty_transform, value);
 12883   return true;
 12886 bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
 12888   nsCSSValuePair position;
 12889   if (!ParseBoxPositionValues(position, true))
 12890     return false;
 12892   nsCSSProperty prop = eCSSProperty_transform_origin;
 12893   if (aPerspective) {
 12894     prop = eCSSProperty_perspective_origin;
 12897   // Unlike many other uses of pairs, this position should always be stored
 12898   // as a pair, even if the values are the same, so it always serializes as
 12899   // a pair, and to keep the computation code simple.
 12900   if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
 12901       position.mXValue.GetUnit() == eCSSUnit_Initial ||
 12902       position.mXValue.GetUnit() == eCSSUnit_Unset) {
 12903     NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
 12904                       "inherit/initial/unset only half?");
 12905     AppendValue(prop, position.mXValue);
 12906   } else {
 12907     nsCSSValue value;
 12908     if (aPerspective) {
 12909       value.SetPairValue(position.mXValue, position.mYValue);
 12910     } else {
 12911       nsCSSValue depth;
 12912       if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr)) {
 12913         depth.SetFloatValue(0.0f, eCSSUnit_Pixel);
 12915       value.SetTripletValue(position.mXValue, position.mYValue, depth);
 12918     AppendValue(prop, value);
 12920   return true;
 12923 /**
 12924  * Reads a drop-shadow value. At the moment the Filter Effects specification
 12925  * just expects one shadow item. Should this ever change to a list of shadow
 12926  * items, use ParseShadowList instead.
 12927  */
 12928 bool
 12929 CSSParserImpl::ParseDropShadow(nsCSSValue* aValue)
 12931   // Use nsCSSValueList to reuse the shadow resolving code in
 12932   // nsRuleNode and nsComputedDOMStyle.
 12933   nsCSSValue shadow;
 12934   nsCSSValueList* cur = shadow.SetListValue();
 12935   if (!ParseShadowItem(cur->mValue, false))
 12936     return false;
 12938   if (!ExpectSymbol(')', true))
 12939     return false;
 12941   nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1);
 12943   // Copy things over.
 12944   dropShadow->Item(1) = shadow;
 12946   return true;
 12949 /**
 12950  * Reads a single url or filter function from the tokenizer stream, reporting an
 12951  * error if something goes wrong.
 12952  */
 12953 bool
 12954 CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
 12956   if (ParseVariant(*aValue, VARIANT_URL, nullptr)) {
 12957     return true;
 12960   if (!nsLayoutUtils::CSSFiltersEnabled()) {
 12961     // With CSS Filters disabled, we should only accept an SVG reference filter.
 12962     REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
 12963     return false;
 12966   if (!GetToken(true)) {
 12967     REPORT_UNEXPECTED_EOF(PEFilterEOF);
 12968     return false;
 12971   if (mToken.mType != eCSSToken_Function) {
 12972     REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
 12973     return false;
 12976   nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
 12977   // Parse drop-shadow independently of the other filter functions
 12978   // because of its more complex characteristics.
 12979   if (functionName == eCSSKeyword_drop_shadow) {
 12980     if (ParseDropShadow(aValue)) {
 12981       return true;
 12982     } else {
 12983       // Unrecognized filter function.
 12984       REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
 12985       SkipUntil(')');
 12986       return false;
 12990   // Set up the parsing rules based on the filter function.
 12991   int32_t variantMask = VARIANT_PN;
 12992   bool rejectNegativeArgument = true;
 12993   bool clampArgumentToOne = false;
 12994   switch (functionName) {
 12995     case eCSSKeyword_blur:
 12996       variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
 12997       // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
 12998       rejectNegativeArgument = false;
 12999       break;
 13000     case eCSSKeyword_brightness:
 13001     case eCSSKeyword_contrast:
 13002     case eCSSKeyword_saturate:
 13003       break;
 13004     case eCSSKeyword_grayscale:
 13005     case eCSSKeyword_invert:
 13006     case eCSSKeyword_sepia:
 13007     case eCSSKeyword_opacity:
 13008       clampArgumentToOne = true;
 13009       break;
 13010     case eCSSKeyword_hue_rotate:
 13011       variantMask = VARIANT_ANGLE;
 13012       rejectNegativeArgument = false;
 13013       break;
 13014     default:
 13015       // Unrecognized filter function.
 13016       REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
 13017       SkipUntil(')');
 13018       return false;
 13021   // Parse the function.
 13022   uint16_t minElems = 1U;
 13023   uint16_t maxElems = 1U;
 13024   uint32_t allVariants = 0;
 13025   if (!ParseFunction(functionName, &variantMask, allVariants,
 13026                      minElems, maxElems, *aValue)) {
 13027     REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError);
 13028     return false;
 13031   // Get the first and only argument to the filter function.
 13032   NS_ABORT_IF_FALSE(aValue->GetUnit() == eCSSUnit_Function,
 13033                     "expected a filter function");
 13034   NS_ABORT_IF_FALSE(aValue->UnitHasArrayValue(),
 13035                     "filter function should be an array");
 13036   NS_ABORT_IF_FALSE(aValue->GetArrayValue()->Count() == 2,
 13037                     "filter function should have exactly one argument");
 13038   nsCSSValue& arg = aValue->GetArrayValue()->Item(1);
 13040   if (rejectNegativeArgument &&
 13041       ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) ||
 13042        (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) {
 13043     REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
 13044     return false;
 13047   if (clampArgumentToOne) {
 13048     if (arg.GetUnit() == eCSSUnit_Number &&
 13049         arg.GetFloatValue() > 1.0f) {
 13050       arg.SetFloatValue(1.0f, arg.GetUnit());
 13051     } else if (arg.GetUnit() == eCSSUnit_Percent &&
 13052                arg.GetPercentValue() > 1.0f) {
 13053       arg.SetPercentValue(1.0f);
 13057   return true;
 13060 /**
 13061  * Parses a filter property value by continuously reading in urls and/or filter
 13062  * functions and constructing a list.
 13064  * When CSS Filters are enabled, the filter property accepts one or more SVG
 13065  * reference filters and/or CSS filter functions.
 13066  * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%);
 13068  * When CSS Filters are disabled, the filter property only accepts one SVG
 13069  * reference filter.
 13070  * e.g. filter: url(#my-filter);
 13071  */
 13072 bool
 13073 CSSParserImpl::ParseFilter()
 13075   nsCSSValue value;
 13076   // 'inherit', 'initial', 'unset' and 'none' must be alone
 13077   if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
 13078     nsCSSValueList* cur = value.SetListValue();
 13079     while (cur) {
 13080       if (!ParseSingleFilter(&cur->mValue)) {
 13081         return false;
 13083       if (CheckEndProperty()) {
 13084         break;
 13086       if (!nsLayoutUtils::CSSFiltersEnabled()) {
 13087         // With CSS Filters disabled, we should only accept one SVG reference
 13088         // filter.
 13089         REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
 13090         return false;
 13092       cur->mNext = new nsCSSValueList;
 13093       cur = cur->mNext;
 13096   AppendValue(eCSSProperty_filter, value);
 13097   return true;
 13100 bool
 13101 CSSParserImpl::ParseTransitionProperty()
 13103   nsCSSValue value;
 13104   // 'inherit', 'initial', 'unset' and 'none' must be alone
 13105   if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
 13106     // Accept a list of arbitrary identifiers.  They should be
 13107     // CSS properties, but we want to accept any so that we
 13108     // accept properties that we don't know about yet, e.g.
 13109     // transition-property: invalid-property, left, opacity;
 13110     nsCSSValueList* cur = value.SetListValue();
 13111     for (;;) {
 13112       if (!ParseVariant(cur->mValue, VARIANT_IDENTIFIER | VARIANT_ALL, nullptr)) {
 13113         return false;
 13115       if (cur->mValue.GetUnit() == eCSSUnit_Ident) {
 13116         nsDependentString str(cur->mValue.GetStringBufferValue());
 13117         // Exclude 'none', 'inherit', 'initial' and 'unset' according to the
 13118         // same rules as for 'counter-reset' in CSS 2.1.
 13119         if (str.LowerCaseEqualsLiteral("none") ||
 13120             str.LowerCaseEqualsLiteral("inherit") ||
 13121             str.LowerCaseEqualsLiteral("initial") ||
 13122             (str.LowerCaseEqualsLiteral("unset") &&
 13123              nsLayoutUtils::UnsetValueEnabled())) {
 13124           return false;
 13127       if (!ExpectSymbol(',', true)) {
 13128         break;
 13130       cur->mNext = new nsCSSValueList;
 13131       cur = cur->mNext;
 13134   AppendValue(eCSSProperty_transition_property, value);
 13135   return true;
 13138 bool
 13139 CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue)
 13141   NS_ASSERTION(!mHavePushBack &&
 13142                mToken.mType == eCSSToken_Function &&
 13143                mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"),
 13144                "unexpected initial state");
 13146   nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(4);
 13148   float x1, x2, y1, y2;
 13149   if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) ||
 13150       !ParseTransitionTimingFunctionValueComponent(y1, ',', false) ||
 13151       !ParseTransitionTimingFunctionValueComponent(x2, ',', true) ||
 13152       !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) {
 13153     return false;
 13156   val->Item(0).SetFloatValue(x1, eCSSUnit_Number);
 13157   val->Item(1).SetFloatValue(y1, eCSSUnit_Number);
 13158   val->Item(2).SetFloatValue(x2, eCSSUnit_Number);
 13159   val->Item(3).SetFloatValue(y2, eCSSUnit_Number);
 13161   aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier);
 13163   return true;
 13166 bool
 13167 CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent,
 13168                                                            char aStop,
 13169                                                            bool aCheckRange)
 13171   if (!GetToken(true)) {
 13172     return false;
 13174   nsCSSToken* tk = &mToken;
 13175   if (tk->mType == eCSSToken_Number) {
 13176     float num = tk->mNumber;
 13177     if (aCheckRange && (num < 0.0 || num > 1.0)) {
 13178       return false;
 13180     aComponent = num;
 13181     if (ExpectSymbol(aStop, true)) {
 13182       return true;
 13185   return false;
 13188 bool
 13189 CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
 13191   NS_ASSERTION(!mHavePushBack &&
 13192                mToken.mType == eCSSToken_Function &&
 13193                mToken.mIdent.LowerCaseEqualsLiteral("steps"),
 13194                "unexpected initial state");
 13196   nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
 13198   if (!ParseOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, nullptr)) {
 13199     return false;
 13202   int32_t type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
 13203   if (ExpectSymbol(',', true)) {
 13204     if (!GetToken(true)) {
 13205       return false;
 13207     type = -1;
 13208     if (mToken.mType == eCSSToken_Ident) {
 13209       if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
 13210         type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
 13211       } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) {
 13212         type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
 13215     if (type == -1) {
 13216       UngetToken();
 13217       return false;
 13220   val->Item(1).SetIntValue(type, eCSSUnit_Enumerated);
 13222   if (!ExpectSymbol(')', true)) {
 13223     return false;
 13226   aValue.SetArrayValue(val, eCSSUnit_Steps);
 13227   return true;
 13230 static nsCSSValueList*
 13231 AppendValueToList(nsCSSValue& aContainer,
 13232                   nsCSSValueList* aTail,
 13233                   const nsCSSValue& aValue)
 13235   nsCSSValueList* entry;
 13236   if (aContainer.GetUnit() == eCSSUnit_Null) {
 13237     NS_ABORT_IF_FALSE(!aTail, "should not have an entry");
 13238     entry = aContainer.SetListValue();
 13239   } else {
 13240     NS_ABORT_IF_FALSE(!aTail->mNext, "should not have a next entry");
 13241     NS_ABORT_IF_FALSE(aContainer.GetUnit() == eCSSUnit_List, "not a list");
 13242     entry = new nsCSSValueList;
 13243     aTail->mNext = entry;
 13245   entry->mValue = aValue;
 13246   return entry;
 13249 CSSParserImpl::ParseAnimationOrTransitionShorthandResult
 13250 CSSParserImpl::ParseAnimationOrTransitionShorthand(
 13251                  const nsCSSProperty* aProperties,
 13252                  const nsCSSValue* aInitialValues,
 13253                  nsCSSValue* aValues,
 13254                  size_t aNumProperties)
 13256   nsCSSValue tempValue;
 13257   // first see if 'inherit', 'initial' or 'unset' is specified.  If one is,
 13258   // it can be the only thing specified, so don't attempt to parse any
 13259   // additional properties
 13260   if (ParseVariant(tempValue, VARIANT_INHERIT, nullptr)) {
 13261     for (uint32_t i = 0; i < aNumProperties; ++i) {
 13262       AppendValue(aProperties[i], tempValue);
 13264     return eParseAnimationOrTransitionShorthand_Inherit;
 13267   static const size_t maxNumProperties = 7;
 13268   NS_ABORT_IF_FALSE(aNumProperties <= maxNumProperties,
 13269                     "can't handle this many properties");
 13270   nsCSSValueList *cur[maxNumProperties];
 13271   bool parsedProperty[maxNumProperties];
 13273   for (size_t i = 0; i < aNumProperties; ++i) {
 13274     cur[i] = nullptr;
 13276   bool atEOP = false; // at end of property?
 13277   for (;;) { // loop over comma-separated transitions or animations
 13278     // whether a particular subproperty was specified for this
 13279     // transition or animation
 13280     bool haveAnyProperty = false;
 13281     for (size_t i = 0; i < aNumProperties; ++i) {
 13282       parsedProperty[i] = false;
 13284     for (;;) { // loop over values within a transition or animation
 13285       bool foundProperty = false;
 13286       // check to see if we're at the end of one full transition or
 13287       // animation definition (either because we hit a comma or because
 13288       // we hit the end of the property definition)
 13289       if (ExpectSymbol(',', true))
 13290         break;
 13291       if (CheckEndProperty()) {
 13292         atEOP = true;
 13293         break;
 13296       // else, try to parse the next transition or animation sub-property
 13297       for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) {
 13298         if (!parsedProperty[i]) {
 13299           // if we haven't found this property yet, try to parse it
 13300           if (ParseSingleValueProperty(tempValue, aProperties[i])) {
 13301             parsedProperty[i] = true;
 13302             cur[i] = AppendValueToList(aValues[i], cur[i], tempValue);
 13303             foundProperty = true;
 13304             haveAnyProperty = true;
 13305             break; // out of inner loop; continue looking for next sub-property
 13309       if (!foundProperty) {
 13310         // We're not at a ',' or at the end of the property, but we couldn't
 13311         // parse any of the sub-properties, so the declaration is invalid.
 13312         return eParseAnimationOrTransitionShorthand_Error;
 13316     if (!haveAnyProperty) {
 13317       // Got an empty item.
 13318       return eParseAnimationOrTransitionShorthand_Error;
 13321     // We hit the end of the property or the end of one transition
 13322     // or animation definition, add its components to the list.
 13323     for (uint32_t i = 0; i < aNumProperties; ++i) {
 13324       // If all of the subproperties were not explicitly specified, fill
 13325       // in the missing ones with initial values.
 13326       if (!parsedProperty[i]) {
 13327         cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]);
 13331     if (atEOP)
 13332       break;
 13333     // else we just hit a ',' so continue parsing the next compound transition
 13336   return eParseAnimationOrTransitionShorthand_Values;
 13339 bool
 13340 CSSParserImpl::ParseTransition()
 13342   static const nsCSSProperty kTransitionProperties[] = {
 13343     eCSSProperty_transition_duration,
 13344     eCSSProperty_transition_timing_function,
 13345     // Must check 'transition-delay' after 'transition-duration', since
 13346     // that's our assumption about what the spec means for the shorthand
 13347     // syntax (the first time given is the duration, and the second
 13348     // given is the delay).
 13349     eCSSProperty_transition_delay,
 13350     // Must check 'transition-property' after
 13351     // 'transition-timing-function' since 'transition-property' accepts
 13352     // any keyword.
 13353     eCSSProperty_transition_property
 13354   };
 13355   static const uint32_t numProps = MOZ_ARRAY_LENGTH(kTransitionProperties);
 13356   // this is a shorthand property that accepts -property, -delay,
 13357   // -duration, and -timing-function with some components missing.
 13358   // there can be multiple transitions, separated with commas
 13360   nsCSSValue initialValues[numProps];
 13361   initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
 13362   initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
 13363                                eCSSUnit_Enumerated);
 13364   initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
 13365   initialValues[3].SetAllValue();
 13367   nsCSSValue values[numProps];
 13369   ParseAnimationOrTransitionShorthandResult spres =
 13370     ParseAnimationOrTransitionShorthand(kTransitionProperties,
 13371                                         initialValues, values, numProps);
 13372   if (spres != eParseAnimationOrTransitionShorthand_Values) {
 13373     return spres != eParseAnimationOrTransitionShorthand_Error;
 13376   // Make two checks on the list for 'transition-property':
 13377   //   + If there is more than one item, then none of the items can be
 13378   //     'none'.
 13379   //   + None of the items can be 'inherit', 'initial' or 'unset'.
 13381     NS_ABORT_IF_FALSE(kTransitionProperties[3] ==
 13382                         eCSSProperty_transition_property,
 13383                       "array index mismatch");
 13384     nsCSSValueList *l = values[3].GetListValue();
 13385     bool multipleItems = !!l->mNext;
 13386     do {
 13387       const nsCSSValue& val = l->mValue;
 13388       if (val.GetUnit() == eCSSUnit_None) {
 13389         if (multipleItems) {
 13390           // This is a syntax error.
 13391           return false;
 13394         // Unbox a solitary 'none'.
 13395         values[3].SetNoneValue();
 13396         break;
 13398       if (val.GetUnit() == eCSSUnit_Ident) {
 13399         nsDependentString str(val.GetStringBufferValue());
 13400         if (str.EqualsLiteral("inherit") ||
 13401             str.EqualsLiteral("initial") ||
 13402             (str.EqualsLiteral("unset") &&
 13403              nsLayoutUtils::UnsetValueEnabled())) {
 13404           return false;
 13407     } while ((l = l->mNext));
 13410   // Save all parsed transition sub-properties in mTempData
 13411   for (uint32_t i = 0; i < numProps; ++i) {
 13412     AppendValue(kTransitionProperties[i], values[i]);
 13414   return true;
 13417 bool
 13418 CSSParserImpl::ParseAnimation()
 13420   static const nsCSSProperty kAnimationProperties[] = {
 13421     eCSSProperty_animation_duration,
 13422     eCSSProperty_animation_timing_function,
 13423     // Must check 'animation-delay' after 'animation-duration', since
 13424     // that's our assumption about what the spec means for the shorthand
 13425     // syntax (the first time given is the duration, and the second
 13426     // given is the delay).
 13427     eCSSProperty_animation_delay,
 13428     eCSSProperty_animation_direction,
 13429     eCSSProperty_animation_fill_mode,
 13430     eCSSProperty_animation_iteration_count,
 13431     // Must check 'animation-name' after 'animation-timing-function',
 13432     // 'animation-direction', 'animation-fill-mode',
 13433     // 'animation-iteration-count', and 'animation-play-state' since
 13434     // 'animation-name' accepts any keyword.
 13435     eCSSProperty_animation_name
 13436   };
 13437   static const uint32_t numProps = MOZ_ARRAY_LENGTH(kAnimationProperties);
 13438   // this is a shorthand property that accepts -property, -delay,
 13439   // -duration, and -timing-function with some components missing.
 13440   // there can be multiple animations, separated with commas
 13442   nsCSSValue initialValues[numProps];
 13443   initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
 13444   initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
 13445                                eCSSUnit_Enumerated);
 13446   initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
 13447   initialValues[3].SetIntValue(NS_STYLE_ANIMATION_DIRECTION_NORMAL, eCSSUnit_Enumerated);
 13448   initialValues[4].SetIntValue(NS_STYLE_ANIMATION_FILL_MODE_NONE, eCSSUnit_Enumerated);
 13449   initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
 13450   initialValues[6].SetNoneValue();
 13452   nsCSSValue values[numProps];
 13454   ParseAnimationOrTransitionShorthandResult spres =
 13455     ParseAnimationOrTransitionShorthand(kAnimationProperties,
 13456                                         initialValues, values, numProps);
 13457   if (spres != eParseAnimationOrTransitionShorthand_Values) {
 13458     return spres != eParseAnimationOrTransitionShorthand_Error;
 13461   // Save all parsed animation sub-properties in mTempData
 13462   for (uint32_t i = 0; i < numProps; ++i) {
 13463     AppendValue(kAnimationProperties[i], values[i]);
 13465   return true;
 13468 bool
 13469 CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow)
 13471   // A shadow list item is an array, with entries in this sequence:
 13472   enum {
 13473     IndexX,
 13474     IndexY,
 13475     IndexRadius,
 13476     IndexSpread,  // only for box-shadow
 13477     IndexColor,
 13478     IndexInset    // only for box-shadow
 13479   };
 13481   nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6);
 13483   if (aIsBoxShadow) {
 13484     // Optional inset keyword (ignore errors)
 13485     ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
 13486                  nsCSSProps::kBoxShadowTypeKTable);
 13489   nsCSSValue xOrColor;
 13490   bool haveColor = false;
 13491   if (!ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC,
 13492                     nullptr)) {
 13493     return false;
 13495   if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) {
 13496     val->Item(IndexX) = xOrColor;
 13497   } else {
 13498     // Must be a color (as string or color value)
 13499     NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident ||
 13500                  xOrColor.GetUnit() == eCSSUnit_EnumColor ||
 13501                  xOrColor.IsNumericColorUnit(),
 13502                  "Must be a color value");
 13503     val->Item(IndexColor) = xOrColor;
 13504     haveColor = true;
 13506     // X coordinate mandatory after color
 13507     if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC,
 13508                       nullptr)) {
 13509       return false;
 13513   // Y coordinate; mandatory
 13514   if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC,
 13515                     nullptr)) {
 13516     return false;
 13519   // Optional radius. Ignore errors except if they pass a negative
 13520   // value which we must reject. If we use ParseNonNegativeVariant
 13521   // we can't tell the difference between an unspecified radius
 13522   // and a negative radius.
 13523   if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC,
 13524                    nullptr) &&
 13525       val->Item(IndexRadius).IsLengthUnit() &&
 13526       val->Item(IndexRadius).GetFloatValue() < 0) {
 13527     return false;
 13530   if (aIsBoxShadow) {
 13531     // Optional spread
 13532     ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC, nullptr);
 13535   if (!haveColor) {
 13536     // Optional color
 13537     ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr);
 13540   if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) {
 13541     // Optional inset keyword
 13542     ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
 13543                  nsCSSProps::kBoxShadowTypeKTable);
 13546   aValue.SetArrayValue(val, eCSSUnit_Array);
 13547   return true;
 13550 bool
 13551 CSSParserImpl::ParseShadowList(nsCSSProperty aProperty)
 13553   nsAutoParseCompoundProperty compound(this);
 13554   bool isBoxShadow = aProperty == eCSSProperty_box_shadow;
 13556   nsCSSValue value;
 13557   // 'inherit', 'initial', 'unset' and 'none' must be alone
 13558   if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
 13559     nsCSSValueList* cur = value.SetListValue();
 13560     for (;;) {
 13561       if (!ParseShadowItem(cur->mValue, isBoxShadow)) {
 13562         return false;
 13564       if (!ExpectSymbol(',', true)) {
 13565         break;
 13567       cur->mNext = new nsCSSValueList;
 13568       cur = cur->mNext;
 13571   AppendValue(aProperty, value);
 13572   return true;
 13575 int32_t
 13576 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix)
 13578   NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
 13580   int32_t nameSpaceID = kNameSpaceID_Unknown;
 13581   if (mNameSpaceMap) {
 13582     // user-specified identifiers are case-sensitive (bug 416106)
 13583     nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
 13584     if (!prefix) {
 13585       NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
 13587     nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
 13589   // else no declared namespaces
 13591   if (nameSpaceID == kNameSpaceID_Unknown) {   // unknown prefix, dump it
 13592     REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, aPrefix);
 13595   return nameSpaceID;
 13598 void
 13599 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
 13601   if (mNameSpaceMap) {
 13602     aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nullptr));
 13603   } else {
 13604     aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
 13608 bool
 13609 CSSParserImpl::ParsePaint(nsCSSProperty aPropID)
 13611   nsCSSValue x, y;
 13613   if (!ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL |
 13614                        VARIANT_OPENTYPE_SVG_KEYWORD,
 13615                     nsCSSProps::kContextPatternKTable)) {
 13616     return false;
 13619   bool canHaveFallback = x.GetUnit() == eCSSUnit_URL ||
 13620                          x.GetUnit() == eCSSUnit_Enumerated;
 13621   if (canHaveFallback) {
 13622     if (!ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr))
 13623       y.SetNoneValue();
 13626   if (!canHaveFallback) {
 13627     AppendValue(aPropID, x);
 13628   } else {
 13629     nsCSSValue val;
 13630     val.SetPairValue(x, y);
 13631     AppendValue(aPropID, val);
 13633   return true;
 13636 bool
 13637 CSSParserImpl::ParseDasharray()
 13639   nsCSSValue value;
 13641   // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
 13642   if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE |
 13643                            VARIANT_OPENTYPE_SVG_KEYWORD,
 13644                     nsCSSProps::kStrokeContextValueKTable)) {
 13645     nsCSSValueList *cur = value.SetListValue();
 13646     for (;;) {
 13647       if (!ParseNonNegativeVariant(cur->mValue, VARIANT_LPN, nullptr)) {
 13648         return false;
 13650       if (CheckEndProperty()) {
 13651         break;
 13653       // skip optional commas between elements
 13654       (void)ExpectSymbol(',', true);
 13656       cur->mNext = new nsCSSValueList;
 13657       cur = cur->mNext;
 13660   AppendValue(eCSSProperty_stroke_dasharray, value);
 13661   return true;
 13664 bool
 13665 CSSParserImpl::ParseMarker()
 13667   nsCSSValue marker;
 13668   if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) {
 13669     AppendValue(eCSSProperty_marker_end, marker);
 13670     AppendValue(eCSSProperty_marker_mid, marker);
 13671     AppendValue(eCSSProperty_marker_start, marker);
 13672     return true;
 13674   return false;
 13677 bool
 13678 CSSParserImpl::ParsePaintOrder()
 13680   static_assert
 13681     ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) > NS_STYLE_PAINT_ORDER_LAST_VALUE,
 13682      "bitfield width insufficient for paint-order constants");
 13684   static const KTableValue kPaintOrderKTable[] = {
 13685     eCSSKeyword_normal,  NS_STYLE_PAINT_ORDER_NORMAL,
 13686     eCSSKeyword_fill,    NS_STYLE_PAINT_ORDER_FILL,
 13687     eCSSKeyword_stroke,  NS_STYLE_PAINT_ORDER_STROKE,
 13688     eCSSKeyword_markers, NS_STYLE_PAINT_ORDER_MARKERS,
 13689     eCSSKeyword_UNKNOWN,-1
 13690   };
 13692   static_assert(MOZ_ARRAY_LENGTH(kPaintOrderKTable) ==
 13693                   2 * (NS_STYLE_PAINT_ORDER_LAST_VALUE + 2),
 13694                 "missing paint-order values in kPaintOrderKTable");
 13696   nsCSSValue value;
 13697   if (!ParseVariant(value, VARIANT_HK, kPaintOrderKTable)) {
 13698     return false;
 13701   uint32_t seen = 0;
 13702   uint32_t order = 0;
 13703   uint32_t position = 0;
 13705   // Ensure that even cast to a signed int32_t when stored in CSSValue,
 13706   // we have enough space for the entire paint-order value.
 13707   static_assert
 13708     (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE < 32,
 13709      "seen and order not big enough");
 13711   if (value.GetUnit() == eCSSUnit_Enumerated) {
 13712     uint32_t component = static_cast<uint32_t>(value.GetIntValue());
 13713     if (component != NS_STYLE_PAINT_ORDER_NORMAL) {
 13714       bool parsedOK = true;
 13715       for (;;) {
 13716         if (seen & (1 << component)) {
 13717           // Already seen this component.
 13718           UngetToken();
 13719           parsedOK = false;
 13720           break;
 13722         seen |= (1 << component);
 13723         order |= (component << position);
 13724         position += NS_STYLE_PAINT_ORDER_BITWIDTH;
 13725         if (!ParseEnum(value, kPaintOrderKTable)) {
 13726           break;
 13728         component = value.GetIntValue();
 13729         if (component == NS_STYLE_PAINT_ORDER_NORMAL) {
 13730           // Can't have "normal" in the middle of the list of paint components.
 13731           UngetToken();
 13732           parsedOK = false;
 13733           break;
 13737       // Fill in the remaining paint-order components in the order of their
 13738       // constant values.
 13739       if (parsedOK) {
 13740         for (component = 1;
 13741              component <= NS_STYLE_PAINT_ORDER_LAST_VALUE;
 13742              component++) {
 13743           if (!(seen & (1 << component))) {
 13744             order |= (component << position);
 13745             position += NS_STYLE_PAINT_ORDER_BITWIDTH;
 13751     static_assert(NS_STYLE_PAINT_ORDER_NORMAL == 0,
 13752                   "unexpected value for NS_STYLE_PAINT_ORDER_NORMAL");
 13753     value.SetIntValue(static_cast<int32_t>(order), eCSSUnit_Enumerated);
 13756   AppendValue(eCSSProperty_paint_order, value);
 13757   return true;
 13760 bool
 13761 CSSParserImpl::BackslashDropped()
 13763   return mScanner->GetEOFCharacters() &
 13764          nsCSSScanner::eEOFCharacters_DropBackslash;
 13767 void
 13768 CSSParserImpl::AppendImpliedEOFCharacters(nsAString& aResult)
 13770   nsCSSScanner::AppendImpliedEOFCharacters(mScanner->GetEOFCharacters(),
 13771                                            aResult);
 13774 bool
 13775 CSSParserImpl::ParseAll()
 13777   nsCSSValue value;
 13778   if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
 13779     return false;
 13782   CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all) {
 13783     AppendValue(*p, value);
 13785   return true;
 13788 bool
 13789 CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
 13790                                         nsString& aValue)
 13792   CSSVariableDeclarations::Type type;
 13793   nsString variableValue;
 13794   bool dropBackslash;
 13795   nsString impliedCharacters;
 13797   // Record the token stream while parsing a variable value.
 13798   if (!mInSupportsCondition) {
 13799     mScanner->StartRecording();
 13801   if (!ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
 13802                                nullptr, nullptr)) {
 13803     if (!mInSupportsCondition) {
 13804       mScanner->StopRecording();
 13806     return false;
 13809   if (!mInSupportsCondition) {
 13810     if (type == CSSVariableDeclarations::eTokenStream) {
 13811       // This was indeed a token stream value, so store it in variableValue.
 13812       mScanner->StopRecording(variableValue);
 13813       if (dropBackslash) {
 13814         MOZ_ASSERT(!variableValue.IsEmpty() &&
 13815                    variableValue[variableValue.Length() - 1] == '\\');
 13816         variableValue.Truncate(variableValue.Length() - 1);
 13818       variableValue.Append(impliedCharacters);
 13819     } else {
 13820       // This was either 'inherit' or 'initial'; we don't need the recorded
 13821       // input.
 13822       mScanner->StopRecording();
 13826   if (mHavePushBack && type == CSSVariableDeclarations::eTokenStream) {
 13827     // If we came to the end of a valid variable declaration and a token was
 13828     // pushed back, then it would have been ended by '!', ')', ';', ']' or '}'.
 13829     // We need to remove it from the recorded variable value.
 13830     MOZ_ASSERT(mToken.IsSymbol('!') ||
 13831                mToken.IsSymbol(')') ||
 13832                mToken.IsSymbol(';') ||
 13833                mToken.IsSymbol(']') ||
 13834                mToken.IsSymbol('}'));
 13835     if (!mInSupportsCondition) {
 13836       MOZ_ASSERT(!variableValue.IsEmpty());
 13837       MOZ_ASSERT(variableValue[variableValue.Length() - 1] == mToken.mSymbol);
 13838       variableValue.Truncate(variableValue.Length() - 1);
 13842   *aType = type;
 13843   aValue = variableValue;
 13844   return true;
 13847 bool
 13848 CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
 13849                                        bool* aDropBackslash,
 13850                                        nsString& aImpliedCharacters,
 13851                                        void (*aFunc)(const nsAString&, void*),
 13852                                        void* aData)
 13854   // A property value is invalid if it contains variable references and also:
 13855   //
 13856   //   * has unbalanced parens, brackets or braces
 13857   //   * has any BAD_STRING or BAD_URL tokens
 13858   //   * has any ';' or '!' tokens at the top level of a variable reference's
 13859   //     fallback
 13860   //
 13861   // If the property is a custom property (i.e. a variable declaration), then
 13862   // it is also invalid if it consists of no tokens, such as:
 13863   //
 13864   //   --invalid:;
 13865   //
 13866   // Note that is valid for a custom property to have a value that consists
 13867   // solely of white space, such as:
 13868   //
 13869   //   --valid: ;
 13871   // Stack of closing characters for currently open constructs.
 13872   StopSymbolCharStack stack;
 13874   // Indexes into ')' characters in |stack| that correspond to "var(".  This
 13875   // is used to stop parsing when we encounter a '!' or ';' at the top level
 13876   // of a variable reference's fallback.
 13877   nsAutoTArray<uint32_t, 16> references;
 13879   if (!GetToken(false)) {
 13880     // Variable value was empty since we reached EOF.
 13881     REPORT_UNEXPECTED_EOF(PEVariableEOF);
 13882     return false;
 13885   if (mToken.mType == eCSSToken_Symbol &&
 13886       (mToken.mSymbol == '!' ||
 13887        mToken.mSymbol == ')' ||
 13888        mToken.mSymbol == ';' ||
 13889        mToken.mSymbol == ']' ||
 13890        mToken.mSymbol == '}')) {
 13891     // Variable value was empty since we reached the end of the construct.
 13892     UngetToken();
 13893     REPORT_UNEXPECTED_TOKEN(PEVariableEmpty);
 13894     return false;
 13897   if (mToken.mType == eCSSToken_Whitespace) {
 13898     if (!GetToken(true)) {
 13899       // Variable value was white space only.  This is valid.
 13900       MOZ_ASSERT(!BackslashDropped());
 13901       *aType = CSSVariableDeclarations::eTokenStream;
 13902       *aDropBackslash = false;
 13903       AppendImpliedEOFCharacters(aImpliedCharacters);
 13904       return true;
 13908   // Look for 'initial', 'inherit' or 'unset' as the first non-white space
 13909   // token.
 13910   CSSVariableDeclarations::Type type = CSSVariableDeclarations::eTokenStream;
 13911   if (mToken.mType == eCSSToken_Ident) {
 13912     if (mToken.mIdent.LowerCaseEqualsLiteral("initial")) {
 13913       type = CSSVariableDeclarations::eInitial;
 13914     } else if (mToken.mIdent.LowerCaseEqualsLiteral("inherit")) {
 13915       type = CSSVariableDeclarations::eInherit;
 13916     } else if (mToken.mIdent.LowerCaseEqualsLiteral("unset")) {
 13917       type = CSSVariableDeclarations::eUnset;
 13921   if (type != CSSVariableDeclarations::eTokenStream) {
 13922     if (!GetToken(true)) {
 13923       // Variable value was 'initial' or 'inherit' followed by EOF.
 13924       MOZ_ASSERT(!BackslashDropped());
 13925       *aType = type;
 13926       *aDropBackslash = false;
 13927       AppendImpliedEOFCharacters(aImpliedCharacters);
 13928       return true;
 13930     UngetToken();
 13931     if (mToken.mType == eCSSToken_Symbol &&
 13932         (mToken.mSymbol == '!' ||
 13933          mToken.mSymbol == ')' ||
 13934          mToken.mSymbol == ';' ||
 13935          mToken.mSymbol == ']' ||
 13936          mToken.mSymbol == '}')) {
 13937       // Variable value was 'initial' or 'inherit' followed by the end
 13938       // of the declaration.
 13939       MOZ_ASSERT(!BackslashDropped());
 13940       *aType = type;
 13941       *aDropBackslash = false;
 13942       return true;
 13946   do {
 13947     switch (mToken.mType) {
 13948       case eCSSToken_Symbol:
 13949         if (mToken.mSymbol == '(') {
 13950           stack.AppendElement(')');
 13951         } else if (mToken.mSymbol == '[') {
 13952           stack.AppendElement(']');
 13953         } else if (mToken.mSymbol == '{') {
 13954           stack.AppendElement('}');
 13955         } else if (mToken.mSymbol == ';' ||
 13956                    mToken.mSymbol == '!') {
 13957           if (stack.IsEmpty()) {
 13958             UngetToken();
 13959             MOZ_ASSERT(!BackslashDropped());
 13960             *aType = CSSVariableDeclarations::eTokenStream;
 13961             *aDropBackslash = false;
 13962             return true;
 13963           } else if (!references.IsEmpty() &&
 13964                      references.LastElement() == stack.Length() - 1) {
 13965             REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback);
 13966             SkipUntilAllOf(stack);
 13967             return false;
 13969         } else if (mToken.mSymbol == ')' ||
 13970                    mToken.mSymbol == ']' ||
 13971                    mToken.mSymbol == '}') {
 13972           for (;;) {
 13973             if (stack.IsEmpty()) {
 13974               UngetToken();
 13975               MOZ_ASSERT(!BackslashDropped());
 13976               *aType = CSSVariableDeclarations::eTokenStream;
 13977               *aDropBackslash = false;
 13978               return true;
 13980             char16_t c = stack.LastElement();
 13981             stack.TruncateLength(stack.Length() - 1);
 13982             if (!references.IsEmpty() &&
 13983                 references.LastElement() == stack.Length()) {
 13984               references.TruncateLength(references.Length() - 1);
 13986             if (mToken.mSymbol == c) {
 13987               break;
 13991         break;
 13993       case eCSSToken_Function:
 13994         if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
 13995           if (!GetToken(true)) {
 13996             // EOF directly after "var(".
 13997             REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF);
 13998             return false;
 14000           if (mToken.mType != eCSSToken_Ident ||
 14001               !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
 14002             // There must be an identifier directly after the "var(" and
 14003             // it must be a custom property name.
 14004             UngetToken();
 14005             REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName);
 14006             SkipUntil(')');
 14007             SkipUntilAllOf(stack);
 14008             return false;
 14010           if (aFunc) {
 14011             MOZ_ASSERT(Substring(mToken.mIdent, 0,
 14012                                  CSS_CUSTOM_NAME_PREFIX_LENGTH).
 14013                          EqualsLiteral("--"));
 14014             // remove '--'
 14015             const nsDependentSubstring varName =
 14016               Substring(mToken.mIdent, CSS_CUSTOM_NAME_PREFIX_LENGTH);
 14017             aFunc(varName, aData);
 14019           if (!GetToken(true)) {
 14020             // EOF right after "var(<ident>".
 14021             stack.AppendElement(')');
 14022           } else if (mToken.IsSymbol(',')) {
 14023             // Variable reference with fallback.
 14024             if (!GetToken(false) || mToken.IsSymbol(')')) {
 14025               // Comma must be followed by at least one fallback token.
 14026               REPORT_UNEXPECTED(PEExpectedVariableFallback);
 14027               SkipUntilAllOf(stack);
 14028               return false;
 14030             UngetToken();
 14031             references.AppendElement(stack.Length());
 14032             stack.AppendElement(')');
 14033           } else if (mToken.IsSymbol(')')) {
 14034             // Correctly closed variable reference.
 14035           } else {
 14036             // Malformed variable reference.
 14037             REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen);
 14038             SkipUntil(')');
 14039             SkipUntilAllOf(stack);
 14040             return false;
 14042         } else {
 14043           stack.AppendElement(')');
 14045         break;
 14047       case eCSSToken_Bad_String:
 14048         SkipUntilAllOf(stack);
 14049         return false;
 14051       case eCSSToken_Bad_URL:
 14052         SkipUntil(')');
 14053         SkipUntilAllOf(stack);
 14054         return false;
 14056       default:
 14057         break;
 14059   } while (GetToken(true));
 14061   // Append any implied closing characters.
 14062   *aDropBackslash = BackslashDropped();
 14063   AppendImpliedEOFCharacters(aImpliedCharacters);
 14064   uint32_t i = stack.Length();
 14065   while (i--) {
 14066     aImpliedCharacters.Append(stack[i]);
 14069   *aType = type;
 14070   return true;
 14073 } // anonymous namespace
 14075 // Recycling of parser implementation objects
 14077 static CSSParserImpl* gFreeList = nullptr;
 14079 nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
 14080                          nsCSSStyleSheet* aSheet)
 14082   CSSParserImpl *impl = gFreeList;
 14083   if (impl) {
 14084     gFreeList = impl->mNextFree;
 14085     impl->mNextFree = nullptr;
 14086   } else {
 14087     impl = new CSSParserImpl();
 14090   if (aLoader) {
 14091     impl->SetChildLoader(aLoader);
 14092     impl->SetQuirkMode(aLoader->GetCompatibilityMode() ==
 14093                        eCompatibility_NavQuirks);
 14095   if (aSheet) {
 14096     impl->SetStyleSheet(aSheet);
 14099   mImpl = static_cast<void*>(impl);
 14102 nsCSSParser::~nsCSSParser()
 14104   CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl);
 14105   impl->Reset();
 14106   impl->mNextFree = gFreeList;
 14107   gFreeList = impl;
 14110 /* static */ void
 14111 nsCSSParser::Shutdown()
 14113   CSSParserImpl *tofree = gFreeList;
 14114   CSSParserImpl *next;
 14115   while (tofree)
 14117       next = tofree->mNextFree;
 14118       delete tofree;
 14119       tofree = next;
 14123 // Wrapper methods
 14125 nsresult
 14126 nsCSSParser::SetStyleSheet(nsCSSStyleSheet* aSheet)
 14128   return static_cast<CSSParserImpl*>(mImpl)->
 14129     SetStyleSheet(aSheet);
 14132 nsresult
 14133 nsCSSParser::SetQuirkMode(bool aQuirkMode)
 14135   return static_cast<CSSParserImpl*>(mImpl)->
 14136     SetQuirkMode(aQuirkMode);
 14139 nsresult
 14140 nsCSSParser::SetChildLoader(mozilla::css::Loader* aChildLoader)
 14142   return static_cast<CSSParserImpl*>(mImpl)->
 14143     SetChildLoader(aChildLoader);
 14146 nsresult
 14147 nsCSSParser::ParseSheet(const nsAString& aInput,
 14148                         nsIURI*          aSheetURI,
 14149                         nsIURI*          aBaseURI,
 14150                         nsIPrincipal*    aSheetPrincipal,
 14151                         uint32_t         aLineNumber,
 14152                         bool             aAllowUnsafeRules)
 14154   return static_cast<CSSParserImpl*>(mImpl)->
 14155     ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
 14156                aAllowUnsafeRules);
 14159 nsresult
 14160 nsCSSParser::ParseStyleAttribute(const nsAString&  aAttributeValue,
 14161                                  nsIURI*           aDocURI,
 14162                                  nsIURI*           aBaseURI,
 14163                                  nsIPrincipal*     aNodePrincipal,
 14164                                  css::StyleRule**  aResult)
 14166   return static_cast<CSSParserImpl*>(mImpl)->
 14167     ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI,
 14168                         aNodePrincipal, aResult);
 14171 nsresult
 14172 nsCSSParser::ParseDeclarations(const nsAString&  aBuffer,
 14173                                nsIURI*           aSheetURI,
 14174                                nsIURI*           aBaseURI,
 14175                                nsIPrincipal*     aSheetPrincipal,
 14176                                css::Declaration* aDeclaration,
 14177                                bool*           aChanged)
 14179   return static_cast<CSSParserImpl*>(mImpl)->
 14180     ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal,
 14181                       aDeclaration, aChanged);
 14184 nsresult
 14185 nsCSSParser::ParseRule(const nsAString&        aRule,
 14186                        nsIURI*                 aSheetURI,
 14187                        nsIURI*                 aBaseURI,
 14188                        nsIPrincipal*           aSheetPrincipal,
 14189                        css::Rule**             aResult)
 14191   return static_cast<CSSParserImpl*>(mImpl)->
 14192     ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult);
 14195 nsresult
 14196 nsCSSParser::ParseProperty(const nsCSSProperty aPropID,
 14197                            const nsAString&    aPropValue,
 14198                            nsIURI*             aSheetURI,
 14199                            nsIURI*             aBaseURI,
 14200                            nsIPrincipal*       aSheetPrincipal,
 14201                            css::Declaration*   aDeclaration,
 14202                            bool*               aChanged,
 14203                            bool                aIsImportant,
 14204                            bool                aIsSVGMode)
 14206   return static_cast<CSSParserImpl*>(mImpl)->
 14207     ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
 14208                   aSheetPrincipal, aDeclaration, aChanged,
 14209                   aIsImportant, aIsSVGMode);
 14212 nsresult
 14213 nsCSSParser::ParseVariable(const nsAString&    aVariableName,
 14214                            const nsAString&    aPropValue,
 14215                            nsIURI*             aSheetURI,
 14216                            nsIURI*             aBaseURI,
 14217                            nsIPrincipal*       aSheetPrincipal,
 14218                            css::Declaration*   aDeclaration,
 14219                            bool*               aChanged,
 14220                            bool                aIsImportant)
 14222   return static_cast<CSSParserImpl*>(mImpl)->
 14223     ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI,
 14224                   aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
 14227 void
 14228 nsCSSParser::ParseMediaList(const nsSubstring& aBuffer,
 14229                             nsIURI*            aURI,
 14230                             uint32_t           aLineNumber,
 14231                             nsMediaList*       aMediaList,
 14232                             bool               aHTMLMode)
 14234   static_cast<CSSParserImpl*>(mImpl)->
 14235     ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode);
 14238 bool
 14239 nsCSSParser::ParseColorString(const nsSubstring& aBuffer,
 14240                               nsIURI*            aURI,
 14241                               uint32_t           aLineNumber,
 14242                               nsCSSValue&        aValue)
 14244   return static_cast<CSSParserImpl*>(mImpl)->
 14245     ParseColorString(aBuffer, aURI, aLineNumber, aValue);
 14248 nsresult
 14249 nsCSSParser::ParseSelectorString(const nsSubstring&  aSelectorString,
 14250                                  nsIURI*             aURI,
 14251                                  uint32_t            aLineNumber,
 14252                                  nsCSSSelectorList** aSelectorList)
 14254   return static_cast<CSSParserImpl*>(mImpl)->
 14255     ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList);
 14258 already_AddRefed<nsCSSKeyframeRule>
 14259 nsCSSParser::ParseKeyframeRule(const nsSubstring& aBuffer,
 14260                                nsIURI*            aURI,
 14261                                uint32_t           aLineNumber)
 14263   return static_cast<CSSParserImpl*>(mImpl)->
 14264     ParseKeyframeRule(aBuffer, aURI, aLineNumber);
 14267 bool
 14268 nsCSSParser::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
 14269                                          nsIURI*            aURI,
 14270                                          uint32_t           aLineNumber,
 14271                                          InfallibleTArray<float>& aSelectorList)
 14273   return static_cast<CSSParserImpl*>(mImpl)->
 14274     ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber,
 14275                                 aSelectorList);
 14278 bool
 14279 nsCSSParser::EvaluateSupportsDeclaration(const nsAString& aProperty,
 14280                                          const nsAString& aValue,
 14281                                          nsIURI* aDocURL,
 14282                                          nsIURI* aBaseURL,
 14283                                          nsIPrincipal* aDocPrincipal)
 14285   return static_cast<CSSParserImpl*>(mImpl)->
 14286     EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL,
 14287                                 aDocPrincipal);
 14290 bool
 14291 nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition,
 14292                                        nsIURI* aDocURL,
 14293                                        nsIURI* aBaseURL,
 14294                                        nsIPrincipal* aDocPrincipal)
 14296   return static_cast<CSSParserImpl*>(mImpl)->
 14297     EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL, aDocPrincipal);
 14300 bool
 14301 nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue,
 14302                                          VariableEnumFunc aFunc,
 14303                                          void* aData)
 14305   return static_cast<CSSParserImpl*>(mImpl)->
 14306     EnumerateVariableReferences(aPropertyValue, aFunc, aData);
 14309 bool
 14310 nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue,
 14311                                   const CSSVariableValues* aVariables,
 14312                                   nsString& aResult,
 14313                                   nsCSSTokenSerializationType& aFirstToken,
 14314                                   nsCSSTokenSerializationType& aLastToken)
 14316   return static_cast<CSSParserImpl*>(mImpl)->
 14317     ResolveVariableValue(aPropertyValue, aVariables,
 14318                          aResult, aFirstToken, aLastToken);
 14321 void
 14322 nsCSSParser::ParsePropertyWithVariableReferences(
 14323                                             nsCSSProperty aPropertyID,
 14324                                             nsCSSProperty aShorthandPropertyID,
 14325                                             const nsAString& aValue,
 14326                                             const CSSVariableValues* aVariables,
 14327                                             nsRuleData* aRuleData,
 14328                                             nsIURI* aDocURL,
 14329                                             nsIURI* aBaseURL,
 14330                                             nsIPrincipal* aDocPrincipal,
 14331                                             nsCSSStyleSheet* aSheet,
 14332                                             uint32_t aLineNumber,
 14333                                             uint32_t aLineOffset)
 14335   static_cast<CSSParserImpl*>(mImpl)->
 14336     ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID,
 14337                                         aValue, aVariables, aRuleData, aDocURL,
 14338                                         aBaseURL, aDocPrincipal, aSheet,
 14339                                         aLineNumber, aLineOffset);

mercurial