LCOV - code coverage report
Current view: top level - layout/style - nsCSSParser.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 3761 8206 45.8 %
Date: 2017-07-14 16:53:18 Functions: 242 391 61.9 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
       8             : 
       9             : #include "nsCSSParser.h"
      10             : 
      11             : #include "mozilla/Attributes.h"
      12             : #include "mozilla/ArrayUtils.h"
      13             : #include "mozilla/DebugOnly.h"
      14             : #include "mozilla/Maybe.h"
      15             : #include "mozilla/Move.h"
      16             : #include "mozilla/MathAlgorithms.h"
      17             : #include "mozilla/TypedEnumBits.h"
      18             : #include "mozilla/Unused.h"
      19             : 
      20             : #include <algorithm> // for std::stable_sort
      21             : #include <limits> // for std::numeric_limits
      22             : 
      23             : #include "nsAlgorithm.h"
      24             : #include "nsCSSProps.h"
      25             : #include "nsCSSKeywords.h"
      26             : #include "nsCSSScanner.h"
      27             : #include "mozilla/css/ErrorReporter.h"
      28             : #include "mozilla/css/Loader.h"
      29             : #include "mozilla/css/StyleRule.h"
      30             : #include "mozilla/css/ImportRule.h"
      31             : #include "mozilla/css/URLMatchingFunction.h"
      32             : #include "nsCSSRules.h"
      33             : #include "nsCSSCounterStyleRule.h"
      34             : #include "nsCSSFontFaceRule.h"
      35             : #include "mozilla/css/NameSpaceRule.h"
      36             : #include "nsTArray.h"
      37             : #include "mozilla/StyleSheetInlines.h"
      38             : #include "mozilla/css/Declaration.h"
      39             : #include "nsStyleConsts.h"
      40             : #include "nsNetUtil.h"
      41             : #include "nsCOMPtr.h"
      42             : #include "nsString.h"
      43             : #include "nsIAtom.h"
      44             : #include "nsColor.h"
      45             : #include "nsCSSPseudoClasses.h"
      46             : #include "nsCSSPseudoElements.h"
      47             : #include "nsCSSAnonBoxes.h"
      48             : #include "nsNameSpaceManager.h"
      49             : #include "nsXMLNameSpaceMap.h"
      50             : #include "nsError.h"
      51             : #include "nsMediaList.h"
      52             : #include "nsStyleUtil.h"
      53             : #include "nsIPrincipal.h"
      54             : #include "mozilla/Sprintf.h"
      55             : #include "nsContentUtils.h"
      56             : #include "nsAutoPtr.h"
      57             : #include "CSSCalc.h"
      58             : #include "nsMediaFeatures.h"
      59             : #include "nsLayoutUtils.h"
      60             : #include "mozilla/LookAndFeel.h"
      61             : #include "mozilla/Preferences.h"
      62             : #include "nsRuleData.h"
      63             : #include "mozilla/CSSVariableValues.h"
      64             : #include "mozilla/dom/AnimationEffectReadOnlyBinding.h"
      65             : #include "mozilla/dom/URL.h"
      66             : #include "gfxFontFamilyList.h"
      67             : 
      68             : using namespace mozilla;
      69             : using namespace mozilla::css;
      70             : 
      71             : typedef nsCSSProps::KTableEntry KTableEntry;
      72             : 
      73             : // pref-backed bool values (hooked up in nsCSSParser::Startup)
      74             : static bool sOpentypeSVGEnabled;
      75             : static bool sWebkitPrefixedAliasesEnabled;
      76             : static bool sWebkitDevicePixelRatioEnabled;
      77             : static bool sMozGradientsEnabled;
      78             : static bool sControlCharVisibility;
      79             : static bool sFramesTimingFunctionEnabled;
      80             : 
      81             : const uint32_t
      82             : nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
      83             : #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \
      84             :                  stylestruct_, stylestructoffset_, animtype_)                 \
      85             :   parsevariant_,
      86             : #define CSS_PROP_LIST_INCLUDE_LOGICAL
      87             : #include "nsCSSPropList.h"
      88             : #undef CSS_PROP_LIST_INCLUDE_LOGICAL
      89             : #undef CSS_PROP
      90             : };
      91             : 
      92             : // Maximum number of repetitions for the repeat() function
      93             : // in the grid-template-rows and grid-template-columns properties,
      94             : // to limit high memory usage from small stylesheets.
      95             : // Must be a positive integer. Should be large-ish.
      96             : #define GRID_TEMPLATE_MAX_REPETITIONS 10000
      97             : 
      98             : // End-of-array marker for mask arguments to ParseBitmaskValues
      99             : #define MASK_END_VALUE  (-1)
     100             : 
     101             : enum class CSSParseResult : int32_t {
     102             :   // Parsed something successfully:
     103             :   Ok,
     104             :   // Did not find what we were looking for, but did not consume any token:
     105             :   NotFound,
     106             :   // Unexpected token or token value, too late for UngetToken() to be enough:
     107             :   Error
     108             : };
     109             : 
     110             : enum class GridTrackSizeFlags {
     111             :   eDefaultTrackSize = 0x0,
     112             :   eFixedTrackSize   = 0x1, // parse a <fixed-size> instead of <track-size>
     113             : };
     114           0 : MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackSizeFlags)
     115             : 
     116             : enum class GridTrackListFlags {
     117             :   eDefaultTrackList  = 0x0, // parse a <track-list>
     118             :   eExplicitTrackList = 0x1, // parse an <explicit-track-list> instead
     119             : };
     120           0 : MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackListFlags)
     121             : 
     122             : namespace {
     123             : 
     124             : // Rule processing function
     125             : typedef void (* RuleAppendFunc) (css::Rule* aRule, void* aData);
     126             : static void AssignRuleToPointer(css::Rule* aRule, void* aPointer);
     127             : static void AppendRuleToSheet(css::Rule* aRule, void* aParser);
     128             : 
     129       14916 : struct CSSParserInputState {
     130             :   nsCSSScannerPosition mPosition;
     131             :   nsCSSToken mToken;
     132             :   bool mHavePushBack;
     133             : };
     134             : 
     135             : static_assert(css::eAuthorSheetFeatures == 0 &&
     136             :               css::eUserSheetFeatures == 1 &&
     137             :               css::eAgentSheetFeatures == 2,
     138             :               "sheet parsing mode constants won't fit "
     139             :               "in CSSParserImpl::mParsingMode");
     140             : 
     141             : // Your basic top-down recursive descent style parser
     142             : // The exposed methods and members of this class are precisely those
     143             : // needed by nsCSSParser, far below.
     144             : class CSSParserImpl {
     145             : public:
     146             :   CSSParserImpl();
     147             :   ~CSSParserImpl();
     148             : 
     149             :   nsresult SetStyleSheet(CSSStyleSheet* aSheet);
     150             : 
     151             :   nsIDocument* GetDocument();
     152             : 
     153             :   nsresult SetQuirkMode(bool aQuirkMode);
     154             : 
     155             :   nsresult SetChildLoader(mozilla::css::Loader* aChildLoader);
     156             : 
     157             :   // Clears everything set by the above Set*() functions.
     158             :   void Reset();
     159             : 
     160             :   nsresult ParseSheet(const nsAString& aInput,
     161             :                       nsIURI*          aSheetURI,
     162             :                       nsIURI*          aBaseURI,
     163             :                       nsIPrincipal*    aSheetPrincipal,
     164             :                       uint32_t         aLineNumber,
     165             :                       css::LoaderReusableStyleSheets* aReusableSheets);
     166             : 
     167             :   already_AddRefed<css::Declaration>
     168             :            ParseStyleAttribute(const nsAString&  aAttributeValue,
     169             :                                nsIURI*           aDocURL,
     170             :                                nsIURI*           aBaseURL,
     171             :                                nsIPrincipal*     aNodePrincipal);
     172             : 
     173             :   nsresult ParseDeclarations(const nsAString&  aBuffer,
     174             :                              nsIURI*           aSheetURL,
     175             :                              nsIURI*           aBaseURL,
     176             :                              nsIPrincipal*     aSheetPrincipal,
     177             :                              css::Declaration* aDeclaration,
     178             :                              bool*           aChanged);
     179             : 
     180             :   nsresult ParseRule(const nsAString&        aRule,
     181             :                      nsIURI*                 aSheetURL,
     182             :                      nsIURI*                 aBaseURL,
     183             :                      nsIPrincipal*           aSheetPrincipal,
     184             :                      css::Rule**             aResult);
     185             : 
     186             :   void ParseProperty(const nsCSSPropertyID aPropID,
     187             :                      const nsAString& aPropValue,
     188             :                      nsIURI* aSheetURL,
     189             :                      nsIURI* aBaseURL,
     190             :                      nsIPrincipal* aSheetPrincipal,
     191             :                      css::Declaration* aDeclaration,
     192             :                      bool* aChanged,
     193             :                      bool aIsImportant,
     194             :                      bool aIsSVGMode);
     195             :   void ParseLonghandProperty(const nsCSSPropertyID aPropID,
     196             :                              const nsAString& aPropValue,
     197             :                              nsIURI* aSheetURL,
     198             :                              nsIURI* aBaseURL,
     199             :                              nsIPrincipal* aSheetPrincipal,
     200             :                              nsCSSValue& aValue);
     201             : 
     202             :   bool ParseTransformProperty(const nsAString& aPropValue,
     203             :                               bool aDisallowRelativeValues,
     204             :                               nsCSSValue& aResult);
     205             : 
     206             :   void ParseMediaList(const nsAString& aBuffer,
     207             :                       nsIURI* aURL, // for error reporting
     208             :                       uint32_t aLineNumber, // for error reporting
     209             :                       nsMediaList* aMediaList);
     210             : 
     211             :   bool ParseSourceSizeList(const nsAString& aBuffer,
     212             :                            nsIURI* aURI, // for error reporting
     213             :                            uint32_t aLineNumber, // for error reporting
     214             :                            InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
     215             :                            InfallibleTArray<nsCSSValue>& aValues);
     216             : 
     217             :   void ParseVariable(const nsAString& aVariableName,
     218             :                      const nsAString& aPropValue,
     219             :                      nsIURI* aSheetURL,
     220             :                      nsIURI* aBaseURL,
     221             :                      nsIPrincipal* aSheetPrincipal,
     222             :                      css::Declaration* aDeclaration,
     223             :                      bool* aChanged,
     224             :                      bool aIsImportant);
     225             : 
     226             :   bool ParseFontFamilyListString(const nsAString& aBuffer,
     227             :                                  nsIURI* aURL, // for error reporting
     228             :                                  uint32_t aLineNumber, // for error reporting
     229             :                                  nsCSSValue& aValue);
     230             : 
     231             :   bool ParseColorString(const nsAString& aBuffer,
     232             :                         nsIURI* aURL, // for error reporting
     233             :                         uint32_t aLineNumber, // for error reporting
     234             :                         nsCSSValue& aValue,
     235             :                         bool aSuppressErrors /* false */);
     236             : 
     237             :   bool ParseMarginString(const nsAString& aBuffer,
     238             :                          nsIURI* aURL, // for error reporting
     239             :                          uint32_t aLineNumber, // for error reporting
     240             :                          nsCSSValue& aValue,
     241             :                          bool aSuppressErrors /* false */);
     242             : 
     243             :   nsresult ParseSelectorString(const nsAString& aSelectorString,
     244             :                                nsIURI* aURL, // for error reporting
     245             :                                uint32_t aLineNumber, // for error reporting
     246             :                                nsCSSSelectorList **aSelectorList);
     247             : 
     248             :   already_AddRefed<nsCSSKeyframeRule>
     249             :   ParseKeyframeRule(const nsAString& aBuffer,
     250             :                     nsIURI*            aURL,
     251             :                     uint32_t           aLineNumber);
     252             : 
     253             :   bool ParseKeyframeSelectorString(const nsAString& aSelectorString,
     254             :                                    nsIURI* aURL, // for error reporting
     255             :                                    uint32_t aLineNumber, // for error reporting
     256             :                                    InfallibleTArray<float>& aSelectorList);
     257             : 
     258             :   bool EvaluateSupportsDeclaration(const nsAString& aProperty,
     259             :                                    const nsAString& aValue,
     260             :                                    nsIURI* aDocURL,
     261             :                                    nsIURI* aBaseURL,
     262             :                                    nsIPrincipal* aDocPrincipal);
     263             : 
     264             :   bool EvaluateSupportsCondition(const nsAString& aCondition,
     265             :                                  nsIURI* aDocURL,
     266             :                                  nsIURI* aBaseURL,
     267             :                                  nsIPrincipal* aDocPrincipal,
     268             :                                  SupportsParsingSettings aSettings
     269             :                                   = SupportsParsingSettings::Normal);
     270             : 
     271             :   already_AddRefed<nsIAtom> ParseCounterStyleName(const nsAString& aBuffer,
     272             :                                                   nsIURI* aURL);
     273             : 
     274             :   bool ParseCounterDescriptor(nsCSSCounterDesc aDescID,
     275             :                               const nsAString& aBuffer,
     276             :                               nsIURI* aSheetURL,
     277             :                               nsIURI* aBaseURL,
     278             :                               nsIPrincipal* aSheetPrincipal,
     279             :                               nsCSSValue& aValue);
     280             : 
     281             :   bool ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
     282             :                                const nsAString& aBuffer,
     283             :                                nsIURI* aSheetURL,
     284             :                                nsIURI* aBaseURL,
     285             :                                nsIPrincipal* aSheetPrincipal,
     286             :                                nsCSSValue& aValue);
     287             : 
     288             :   bool IsValueValidForProperty(const nsCSSPropertyID aPropID,
     289             :                                const nsAString& aPropValue);
     290             : 
     291             :   typedef nsCSSParser::VariableEnumFunc VariableEnumFunc;
     292             : 
     293             :   /**
     294             :    * Parses a CSS token stream value and invokes a callback function for each
     295             :    * variable reference that is encountered.
     296             :    *
     297             :    * @param aPropertyValue The CSS token stream value.
     298             :    * @param aFunc The callback function to invoke; its parameters are the
     299             :    *   variable name found and the aData argument passed in to this function.
     300             :    * @param aData User data to pass in to the callback.
     301             :    * @return Whether aPropertyValue could be parsed as a valid CSS token stream
     302             :    *   value (e.g., without syntactic errors in variable references).
     303             :    */
     304             :   bool EnumerateVariableReferences(const nsAString& aPropertyValue,
     305             :                                    VariableEnumFunc aFunc,
     306             :                                    void* aData);
     307             : 
     308             :   /**
     309             :    * Parses aPropertyValue as a CSS token stream value and resolves any
     310             :    * variable references using the variables in aVariables.
     311             :    *
     312             :    * @param aPropertyValue The CSS token stream value.
     313             :    * @param aVariables The set of variable values to use when resolving variable
     314             :    *   references.
     315             :    * @param aResult Out parameter that gets the resolved value.
     316             :    * @param aFirstToken Out parameter that gets the type of the first token in
     317             :    *   aResult.
     318             :    * @param aLastToken Out parameter that gets the type of the last token in
     319             :    *   aResult.
     320             :    * @return Whether aResult could be parsed successfully and variable reference
     321             :    *   substitution succeeded.
     322             :    */
     323             :   bool ResolveVariableValue(const nsAString& aPropertyValue,
     324             :                             const CSSVariableValues* aVariables,
     325             :                             nsString& aResult,
     326             :                             nsCSSTokenSerializationType& aFirstToken,
     327             :                             nsCSSTokenSerializationType& aLastToken);
     328             : 
     329             :   /**
     330             :    * Parses a string as a CSS token stream value for particular property,
     331             :    * resolving any variable references.  The parsed property value is stored
     332             :    * in the specified nsRuleData object.  If aShorthandPropertyID has a value
     333             :    * other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
     334             :    * otherwise, aPropertyID will be parsed.  Either way, only aPropertyID,
     335             :    * a longhand property, will be copied over to the rule data.
     336             :    *
     337             :    * If the property cannot be parsed, it will be treated as if 'initial' or
     338             :    * 'inherit' were specified, for non-inherited and inherited properties
     339             :    * respectively.
     340             :    *
     341             :    * @param aPropertyID The ID of the longhand property whose value is to be
     342             :    *   copied to the rule data.
     343             :    * @param aShorthandPropertyID The ID of the shorthand property to be parsed.
     344             :    *   If a longhand property is to be parsed, aPropertyID is that property,
     345             :    *   and aShorthandPropertyID must be eCSSProperty_UNKNOWN.
     346             :    * @param aValue The CSS token stream value.
     347             :    * @param aVariables The set of variable values to use when resolving variable
     348             :    *   references.
     349             :    * @param aRuleData The rule data object into which parsed property value for
     350             :    *   aPropertyID will be stored.
     351             :    */
     352             :   void ParsePropertyWithVariableReferences(nsCSSPropertyID aPropertyID,
     353             :                                            nsCSSPropertyID aShorthandPropertyID,
     354             :                                            const nsAString& aValue,
     355             :                                            const CSSVariableValues* aVariables,
     356             :                                            nsRuleData* aRuleData,
     357             :                                            nsIURI* aDocURL,
     358             :                                            nsIURI* aBaseURL,
     359             :                                            nsIPrincipal* aDocPrincipal,
     360             :                                            CSSStyleSheet* aSheet,
     361             :                                            uint32_t aLineNumber,
     362             :                                            uint32_t aLineOffset);
     363             : 
     364       16350 :   bool AgentRulesEnabled() const {
     365       16350 :     return mParsingMode == css::eAgentSheetFeatures;
     366             :   }
     367       16258 :   bool ChromeRulesEnabled() const {
     368       16258 :     return mIsChrome;
     369             :   }
     370             : 
     371       16258 :   CSSEnabledState EnabledState() const {
     372             :     static_assert(int(CSSEnabledState::eForAllContent) == 0,
     373             :                   "CSSEnabledState::eForAllContent should be zero for "
     374             :                   "this bitfield to work");
     375       16258 :     CSSEnabledState enabledState = CSSEnabledState::eForAllContent;
     376       16258 :     if (AgentRulesEnabled()) {
     377        6578 :       enabledState |= CSSEnabledState::eInUASheets;
     378             :     }
     379       16258 :     if (ChromeRulesEnabled()) {
     380       10274 :       enabledState |= CSSEnabledState::eInChrome;
     381             :     }
     382       16258 :     return enabledState;
     383             :   }
     384             : 
     385        6472 :   nsCSSPropertyID LookupEnabledProperty(const nsAString& aProperty) {
     386        6472 :     return nsCSSProps::LookupProperty(aProperty, EnabledState());
     387             :   }
     388             : 
     389             : protected:
     390             :   class nsAutoParseCompoundProperty;
     391             :   friend class nsAutoParseCompoundProperty;
     392             : 
     393             :   class nsAutoFailingSupportsRule;
     394             :   friend class nsAutoFailingSupportsRule;
     395             : 
     396             :   class nsAutoSuppressErrors;
     397             :   friend class nsAutoSuppressErrors;
     398             : 
     399             :   void AppendRule(css::Rule* aRule);
     400             :   friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule
     401             : 
     402             :   /**
     403             :    * This helper class automatically calls SetParsingCompoundProperty in its
     404             :    * constructor and takes care of resetting it to false in its destructor.
     405             :    */
     406             :   class nsAutoParseCompoundProperty {
     407             :     public:
     408         560 :       explicit nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
     409             :       {
     410         560 :         NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
     411             :                      "already parsing compound property");
     412         560 :         NS_ASSERTION(aParser, "Null parser?");
     413         560 :         aParser->SetParsingCompoundProperty(true);
     414         560 :       }
     415             : 
     416         560 :       ~nsAutoParseCompoundProperty()
     417         560 :       {
     418         560 :         mParser->SetParsingCompoundProperty(false);
     419         560 :       }
     420             :     private:
     421             :       CSSParserImpl* mParser;
     422             :   };
     423             : 
     424             :   /**
     425             :    * This helper class conditionally sets mInFailingSupportsRule to
     426             :    * true if aCondition = false, and resets it to its original value in its
     427             :    * destructor.  If we are already somewhere within a failing @supports
     428             :    * rule, passing in aCondition = true does not change mInFailingSupportsRule.
     429             :    */
     430             :   class nsAutoFailingSupportsRule {
     431             :     public:
     432           0 :       nsAutoFailingSupportsRule(CSSParserImpl* aParser,
     433             :                                 bool aCondition)
     434           0 :         : mParser(aParser),
     435           0 :           mOriginalValue(aParser->mInFailingSupportsRule)
     436             :       {
     437           0 :         if (!aCondition) {
     438           0 :           mParser->mInFailingSupportsRule = true;
     439             :         }
     440           0 :       }
     441             : 
     442           0 :       ~nsAutoFailingSupportsRule()
     443           0 :       {
     444           0 :         mParser->mInFailingSupportsRule = mOriginalValue;
     445           0 :       }
     446             : 
     447             :     private:
     448             :       CSSParserImpl* mParser;
     449             :       bool mOriginalValue;
     450             :   };
     451             : 
     452             :   /**
     453             :    * Auto class to set aParser->mSuppressErrors to the specified value
     454             :    * and restore it to its original value later.
     455             :    */
     456             :   class nsAutoSuppressErrors {
     457             :     public:
     458        6839 :       explicit nsAutoSuppressErrors(CSSParserImpl* aParser,
     459             :                                     bool aSuppressErrors = true)
     460        6839 :         : mParser(aParser),
     461        6839 :           mOriginalValue(aParser->mSuppressErrors)
     462             :       {
     463        6839 :         mParser->mSuppressErrors = aSuppressErrors;
     464        6839 :       }
     465             : 
     466        6839 :       ~nsAutoSuppressErrors()
     467        6839 :       {
     468        6839 :         mParser->mSuppressErrors = mOriginalValue;
     469        6839 :       }
     470             : 
     471             :     private:
     472             :       CSSParserImpl* mParser;
     473             :       bool mOriginalValue;
     474             :   };
     475             : 
     476             :   /**
     477             :    * RAII class to set aParser->mInSupportsCondition to true and restore it
     478             :    * to false later.
     479             :    */
     480             :   class MOZ_RAII nsAutoInSupportsCondition
     481             :   {
     482             :   public:
     483           0 :     explicit nsAutoInSupportsCondition(CSSParserImpl* aParser)
     484           0 :       : mParser(aParser)
     485             :     {
     486           0 :       MOZ_ASSERT(!aParser->mInSupportsCondition,
     487             :                  "nsAutoInSupportsCondition is not designed to be used "
     488             :                  "re-entrantly");
     489           0 :       mParser->mInSupportsCondition = true;
     490           0 :     }
     491             : 
     492           0 :     ~nsAutoInSupportsCondition()
     493           0 :     {
     494           0 :       mParser->mInSupportsCondition = false;
     495           0 :     }
     496             : 
     497             :   private:
     498             :     CSSParserImpl* const mParser;
     499             :   };
     500             : 
     501             :   // the caller must hold on to aString until parsing is done
     502             :   void InitScanner(nsCSSScanner& aScanner,
     503             :                    css::ErrorReporter& aReporter,
     504             :                    nsIURI* aSheetURI, nsIURI* aBaseURI,
     505             :                    nsIPrincipal* aSheetPrincipal);
     506             :   void ReleaseScanner(void);
     507             : 
     508             :   /**
     509             :    * Saves the current input state, which includes any currently pushed
     510             :    * back token, and the current position of the scanner.
     511             :    */
     512             :   void SaveInputState(CSSParserInputState& aState);
     513             : 
     514             :   /**
     515             :    * Restores the saved input state by pushing back any saved pushback
     516             :    * token and calling RestoreSavedPosition on the scanner.
     517             :    */
     518             :   void RestoreSavedInputState(const CSSParserInputState& aState);
     519             : 
     520             :   bool GetToken(bool aSkipWS);
     521             :   void UngetToken();
     522             :   bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum);
     523        3056 :   void AssertNextTokenAt(uint32_t aLine, uint32_t aCol)
     524             :   {
     525             :     // Beware that this method will call GetToken/UngetToken (in
     526             :     // GetNextTokenLocation) in DEBUG builds, but not in non-DEBUG builds.
     527        6112 :     DebugOnly<uint32_t> lineAfter, colAfter;
     528        3056 :     MOZ_ASSERT(GetNextTokenLocation(true, &lineAfter, &colAfter) &&
     529             :                lineAfter == aLine && colAfter == aCol,
     530             :                "shouldn't have consumed any tokens");
     531        3056 :   }
     532             : 
     533             :   bool ExpectSymbol(char16_t aSymbol, bool aSkipWS);
     534             :   bool ExpectEndProperty();
     535             :   bool CheckEndProperty();
     536             :   nsAString* NextIdent();
     537             : 
     538             :   // returns true when the stop symbol is found, and false for EOF
     539             :   bool SkipUntil(char16_t aStopSymbol);
     540             :   void SkipUntilOneOf(const char16_t* aStopSymbolChars);
     541             :   // For each character in aStopSymbolChars from the end of the array
     542             :   // to the start, calls SkipUntil with that character.
     543             :   typedef AutoTArray<char16_t, 16> StopSymbolCharStack;
     544             :   void SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars);
     545             :   // returns true if the stop symbol or EOF is found, and false for an
     546             :   // unexpected ')', ']' or '}'; this not safe to call outside variable
     547             :   // resolution, as it doesn't handle mismatched content
     548             :   bool SkipBalancedContentUntil(char16_t aStopSymbol);
     549             : 
     550             :   void SkipRuleSet(bool aInsideBraces);
     551             :   bool SkipAtRule(bool aInsideBlock);
     552             :   MOZ_MUST_USE bool SkipDeclaration(bool aCheckForBraces);
     553             : 
     554             :   void PushGroup(css::GroupRule* aRule);
     555             :   void PopGroup();
     556             : 
     557             :   bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData,
     558             :                     bool aInsideBraces = false);
     559             :   bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData,
     560             :                    bool aInAtRule);
     561             :   bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
     562             :   bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
     563             :   bool ParseURLOrString(nsString& aURL);
     564             :   bool GatherMedia(nsMediaList* aMedia, bool aInAtRule);
     565             : 
     566             :   enum eMediaQueryType { eMediaQueryNormal,
     567             :                          // Parsing an at rule
     568             :                          eMediaQueryAtRule,
     569             :                          // Attempt to consume a single media-condition and
     570             :                          // stop. Note that the spec defines "expression and/or
     571             :                          // expression" as one condition but "expression,
     572             :                          // expression" as two.
     573             :                          eMediaQuerySingleCondition };
     574             :   bool ParseMediaQuery(eMediaQueryType aMode, nsMediaQuery **aQuery,
     575             :                        bool *aHitStop);
     576             :   bool ParseMediaQueryExpression(nsMediaQuery* aQuery);
     577             :   void ProcessImport(const nsString& aURLSpec,
     578             :                      nsMediaList* aMedia,
     579             :                      RuleAppendFunc aAppendFunc,
     580             :                      void* aProcessData,
     581             :                      uint32_t aLineNumber,
     582             :                      uint32_t aColumnNumber);
     583             :   bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc,
     584             :                       void* aProcessData);
     585             :   bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
     586             :   bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
     587             :   bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
     588             :   void ProcessNameSpace(const nsString& aPrefix,
     589             :                         const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
     590             :                         void* aProcessData,
     591             :                         uint32_t aLineNumber, uint32_t aColumnNumber);
     592             : 
     593             :   bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
     594             :   bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
     595             :                                   void* aProcessData);
     596             :   bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule);
     597             :   bool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
     598             :   bool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
     599             :                                 nsCSSValue& aValue);
     600             : 
     601             :   bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
     602             :   bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData);
     603             :   already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule();
     604             :   bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList);
     605             : 
     606             :   bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData);
     607             :   bool ParseSupportsCondition(bool& aConditionMet);
     608             :   bool ParseSupportsConditionNegation(bool& aConditionMet);
     609             :   bool ParseSupportsConditionInParens(bool& aConditionMet);
     610             :   bool ParseSupportsMozBoolPrefName(bool& aConditionMet);
     611             :   bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet);
     612             :   bool ParseSupportsConditionTerms(bool& aConditionMet);
     613             :   enum SupportsConditionTermOperator { eAnd, eOr };
     614             :   bool ParseSupportsConditionTermsAfterOperator(
     615             :                                        bool& aConditionMet,
     616             :                                        SupportsConditionTermOperator aOperator);
     617             : 
     618             :   bool ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aProcessData);
     619             :   already_AddRefed<nsIAtom> ParseCounterStyleName(bool aForDefinition);
     620             :   bool ParseCounterStyleNameValue(nsCSSValue& aValue);
     621             :   bool ParseCounterDescriptor(nsCSSCounterStyleRule *aRule);
     622             :   bool ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
     623             :                                    nsCSSValue& aValue);
     624             :   bool ParseCounterRange(nsCSSValuePair& aPair);
     625             : 
     626             :   /**
     627             :    * Parses the current input stream for a CSS token stream value and resolves
     628             :    * any variable references using the variables in aVariables.
     629             :    *
     630             :    * @param aVariables The set of variable values to use when resolving variable
     631             :    *   references.
     632             :    * @param aResult Out parameter that, if the function returns true, will be
     633             :    *   replaced with the resolved value.
     634             :    * @return Whether aResult could be parsed successfully and variable reference
     635             :    *   substitution succeeded.
     636             :    */
     637             :   bool ResolveValueWithVariableReferences(
     638             :                               const CSSVariableValues* aVariables,
     639             :                               nsString& aResult,
     640             :                               nsCSSTokenSerializationType& aResultFirstToken,
     641             :                               nsCSSTokenSerializationType& aResultLastToken);
     642             :   // Helper function for ResolveValueWithVariableReferences.
     643             :   bool ResolveValueWithVariableReferencesRec(
     644             :                              nsString& aResult,
     645             :                              nsCSSTokenSerializationType& aResultFirstToken,
     646             :                              nsCSSTokenSerializationType& aResultLastToken,
     647             :                              const CSSVariableValues* aVariables);
     648             : 
     649             :   enum nsSelectorParsingStatus {
     650             :     // we have parsed a selector and we saw a token that cannot be
     651             :     // part of a selector:
     652             :     eSelectorParsingStatus_Done,
     653             :     // we should continue parsing the selector:
     654             :     eSelectorParsingStatus_Continue,
     655             :     // we saw an unexpected token or token value,
     656             :     // or we saw end-of-file with an unfinished selector:
     657             :     eSelectorParsingStatus_Error
     658             :   };
     659             :   nsSelectorParsingStatus ParseIDSelector(int32_t&       aDataMask,
     660             :                                           nsCSSSelector& aSelector);
     661             : 
     662             :   nsSelectorParsingStatus ParseClassSelector(int32_t&       aDataMask,
     663             :                                              nsCSSSelector& aSelector);
     664             : 
     665             :   // aPseudoElement and aPseudoElementArgs are the location where
     666             :   // pseudo-elements (as opposed to pseudo-classes) are stored;
     667             :   // pseudo-classes are stored on aSelector.  aPseudoElement and
     668             :   // aPseudoElementArgs must be non-null iff !aIsNegated.
     669             :   nsSelectorParsingStatus ParsePseudoSelector(int32_t&       aDataMask,
     670             :                                               nsCSSSelector& aSelector,
     671             :                                               bool           aIsNegated,
     672             :                                               nsIAtom**      aPseudoElement,
     673             :                                               nsAtomList**   aPseudoElementArgs,
     674             :                                               CSSPseudoElementType* aPseudoElementType);
     675             : 
     676             :   nsSelectorParsingStatus ParseAttributeSelector(int32_t&       aDataMask,
     677             :                                                  nsCSSSelector& aSelector);
     678             : 
     679             :   nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t&       aDataMask,
     680             :                                                        nsCSSSelector& aSelector,
     681             :                                                        bool           aIsNegated);
     682             : 
     683             :   nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
     684             :                                                        CSSPseudoClassType aType);
     685             : 
     686             :   nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
     687             :                                                          CSSPseudoClassType aType);
     688             : 
     689             :   nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
     690             :                                                               CSSPseudoClassType aType);
     691             : 
     692             :   nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t&       aDataMask,
     693             :                                                      nsCSSSelector& aSelector);
     694             : 
     695             :   // If aStopChar is non-zero, the selector list is done when we hit
     696             :   // aStopChar.  Otherwise, it's done when we hit EOF.
     697             :   bool ParseSelectorList(nsCSSSelectorList*& aListHead,
     698             :                            char16_t aStopChar);
     699             :   bool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
     700             :   bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator);
     701             : 
     702             :   enum {
     703             :     eParseDeclaration_InBraces           = 1 << 0,
     704             :     eParseDeclaration_AllowImportant     = 1 << 1
     705             :   };
     706             :   enum nsCSSContextType {
     707             :     eCSSContext_General,
     708             :     eCSSContext_Page
     709             :   };
     710             : 
     711             :   already_AddRefed<css::Declaration>
     712             :     ParseDeclarationBlock(uint32_t aFlags,
     713             :                           nsCSSContextType aContext = eCSSContext_General);
     714             :   bool ParseDeclaration(css::Declaration* aDeclaration,
     715             :                         uint32_t aFlags,
     716             :                         bool aMustCallValueAppended,
     717             :                         bool* aChanged,
     718             :                         nsCSSContextType aContext = eCSSContext_General);
     719             : 
     720             :   // A "prefix-aware" wrapper for nsCSSKeywords::LookupKeyword().
     721             :   // Use this instead of LookupKeyword() if you might be parsing an unprefixed
     722             :   // property (like "display") for which we emulate a vendor-prefixed value
     723             :   // (like "-webkit-box").
     724             :   nsCSSKeyword LookupKeywordPrefixAware(nsAString& aKeywordStr,
     725             :                                         const KTableEntry aKeywordTable[]);
     726             : 
     727             :   bool ParseProperty(nsCSSPropertyID aPropID);
     728             :   bool ParsePropertyByFunction(nsCSSPropertyID aPropID);
     729             :   CSSParseResult ParseSingleValueProperty(nsCSSValue& aValue,
     730             :                                           nsCSSPropertyID aPropID);
     731             :   bool ParseSingleValuePropertyByFunction(nsCSSValue& aValue,
     732             :                                           nsCSSPropertyID aPropID);
     733             : 
     734             :   // This is similar to ParseSingleValueProperty but only works for
     735             :   // properties that are parsed with ParseBoxProperties or
     736             :   // ParseGroupedBoxProperty.
     737             :   //
     738             :   // Only works with variants with the following flags:
     739             :   // A, C, H, K, L, N, P, CALC.
     740             :   CSSParseResult ParseBoxProperty(nsCSSValue& aValue,
     741             :                                   nsCSSPropertyID aPropID);
     742             : 
     743             :   enum PriorityParsingStatus {
     744             :     ePriority_None,
     745             :     ePriority_Important,
     746             :     ePriority_Error
     747             :   };
     748             :   PriorityParsingStatus ParsePriority();
     749             : 
     750             : #ifdef MOZ_XUL
     751             :   bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs);
     752             : #endif
     753             : 
     754             :   // Property specific parsing routines
     755             :   bool ParseImageLayers(const nsCSSPropertyID aTable[]);
     756             : 
     757             :   struct ImageLayersShorthandParseState {
     758             :     nsCSSValue&  mColor;
     759             :     nsCSSValueList* mImage;
     760             :     nsCSSValuePairList* mRepeat;
     761             :     nsCSSValueList* mAttachment;   // A property for background layer only
     762             :     nsCSSValueList* mClip;
     763             :     nsCSSValueList* mOrigin;
     764             :     nsCSSValueList* mPositionX;
     765             :     nsCSSValueList* mPositionY;
     766             :     nsCSSValuePairList* mSize;
     767             :     nsCSSValueList* mComposite;    // A property for mask layer only
     768             :     nsCSSValueList* mMode;         // A property for mask layer only
     769          66 :     ImageLayersShorthandParseState(
     770             :         nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
     771             :         nsCSSValueList* aAttachment, nsCSSValueList* aClip,
     772             :         nsCSSValueList* aOrigin,
     773             :         nsCSSValueList* aPositionX, nsCSSValueList* aPositionY,
     774             :         nsCSSValuePairList* aSize, nsCSSValueList* aComposite,
     775          66 :         nsCSSValueList* aMode) :
     776             :         mColor(aColor), mImage(aImage), mRepeat(aRepeat),
     777             :         mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
     778             :         mPositionX(aPositionX), mPositionY(aPositionY),
     779             :         mSize(aSize), mComposite(aComposite),
     780          66 :         mMode(aMode) {};
     781             :   };
     782             : 
     783             :   bool IsFunctionTokenValidForImageLayerImage(const nsCSSToken& aToken) const;
     784             :   bool ParseImageLayersItem(ImageLayersShorthandParseState& aState,
     785             :                             const nsCSSPropertyID aTable[]);
     786             : 
     787             :   bool ParseValueList(nsCSSPropertyID aPropID); // a single value prop-id
     788             :   bool ParseImageLayerRepeat(nsCSSPropertyID aPropID);
     789             :   bool ParseImageLayerRepeatValues(nsCSSValuePair& aValue);
     790             :   bool ParseImageLayerPosition(const nsCSSPropertyID aTable[]);
     791             :   bool ParseImageLayerPositionCoord(nsCSSPropertyID aPropID, bool aIsHorizontal);
     792             : 
     793             :   // ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
     794             :   // which is still used by some properties. See ParsePositionValue
     795             :   // for the css3-background syntax.
     796             :   bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit,
     797             :                               bool aAllowExplicitCenter = true); // deprecated
     798             : 
     799             :   // ParsePositionValue parses a CSS <position> value, which is used by
     800             :   // the 'background-position' property.
     801             :   bool ParsePositionValue(nsCSSValue& aOut);
     802             :   bool ParsePositionValueSeparateCoords(nsCSSValue& aOutX, nsCSSValue& aOutY);
     803             : 
     804             :   bool ParseImageLayerPositionCoordItem(nsCSSValue& aOut, bool aIsHorizontal);
     805             :   bool ParseImageLayerSize(nsCSSPropertyID aPropID);
     806             :   bool ParseImageLayerSizeValues(nsCSSValuePair& aOut);
     807             :   bool ParseBorderColor();
     808             :   bool ParseBorderColors(nsCSSPropertyID aProperty);
     809             :   void SetBorderImageInitialValues();
     810             :   bool ParseBorderImageRepeat(bool aAcceptsInherit);
     811             :   // If ParseBorderImageSlice returns false, aConsumedTokens indicates
     812             :   // whether or not any tokens were consumed (in other words, was the property
     813             :   // in error or just not present).  If ParseBorderImageSlice returns true
     814             :   // aConsumedTokens is always true.
     815             :   bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens);
     816             :   bool ParseBorderImageWidth(bool aAcceptsInherit);
     817             :   bool ParseBorderImageOutset(bool aAcceptsInherit);
     818             :   bool ParseBorderImage();
     819             :   bool ParseBorderSpacing();
     820             :   bool ParseBorderSide(const nsCSSPropertyID aPropIDs[],
     821             :                          bool aSetAllSides);
     822             :   bool ParseBorderStyle();
     823             :   bool ParseBorderWidth();
     824             : 
     825             :   bool ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask);
     826             :   bool ParseCalcAdditiveExpression(nsCSSValue& aValue,
     827             :                                    uint32_t& aVariantMask);
     828             :   bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
     829             :                                          uint32_t& aVariantMask,
     830             :                                          bool *aHadFinalWS);
     831             :   bool ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask);
     832             :   bool ParseContextProperties();
     833             :   bool RequireWhitespace();
     834             : 
     835             :   // For "flex" shorthand property, defined in CSS Flexbox spec
     836             :   bool ParseFlex();
     837             :   // For "flex-flow" shorthand property, defined in CSS Flexbox spec
     838             :   bool ParseFlexFlow();
     839             : 
     840             :   // CSS Grid
     841             :   bool ParseGridAutoFlow();
     842             : 
     843             :   // Parse a <line-names> expression.
     844             :   // If successful, either leave aValue untouched,
     845             :   // to indicate that we parsed the empty list,
     846             :   // or set it to a eCSSUnit_List of eCSSUnit_Ident.
     847             :   //
     848             :   // To parse an optional <line-names> (ie. if not finding an open bracket
     849             :   // is considered the same as an empty list),
     850             :   // treat CSSParseResult::NotFound the same as CSSParseResult::Ok.
     851             :   //
     852             :   // If aValue is already a eCSSUnit_List, append to that list.
     853             :   CSSParseResult ParseGridLineNames(nsCSSValue& aValue);
     854             :   bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr);
     855             :   bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue);
     856             : 
     857             :   CSSParseResult ParseGridTrackBreadth(nsCSSValue& aValue);
     858             :   // eFixedTrackSize in aFlags makes it parse a <fixed-size>.
     859             :   CSSParseResult ParseGridTrackSize(nsCSSValue& aValue,
     860             :     GridTrackSizeFlags aFlags = GridTrackSizeFlags::eDefaultTrackSize);
     861             : 
     862             :   bool ParseGridAutoColumnsRows(nsCSSPropertyID aPropID);
     863             :   bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr);
     864             :   bool ParseGridTrackRepeatIntro(bool            aForSubgrid,
     865             :                                  int32_t*        aRepetitions,
     866             :                                  Maybe<int32_t>* aRepeatAutoEnum);
     867             : 
     868             :   // Assuming a [ <line-names>? ] has already been parsed,
     869             :   // parse the rest of a <track-list>.
     870             :   //
     871             :   // This exists because [ <line-names>? ] is ambiguous in the 'grid-template'
     872             :   // shorthand: it can be either the start of a <track-list> (in
     873             :   // a <'grid-template-rows'>) or of the intertwined syntax that sets both
     874             :   // grid-template-rows and grid-template-areas.
     875             :   //
     876             :   // On success, |aValue| will be a list of odd length >= 3,
     877             :   // starting with a <line-names> (which is itself a list)
     878             :   // and alternating between that and <track-size>.
     879             :   bool ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
     880             :     const nsCSSValue& aFirstLineNames,
     881             :     GridTrackListFlags aFlags = GridTrackListFlags::eDefaultTrackList);
     882             : 
     883             :   bool ParseGridTrackList(nsCSSPropertyID aPropID,
     884             :     GridTrackListFlags aFlags = GridTrackListFlags::eDefaultTrackList);
     885             :   bool ParseGridTemplateColumnsRows(nsCSSPropertyID aPropID);
     886             : 
     887             :   // |aAreaIndices| is a lookup table to help us parse faster,
     888             :   // mapping area names to indices in |aResult.mNamedAreas|.
     889             :   bool ParseGridTemplateAreasLine(const nsAutoString& aInput,
     890             :                                   css::GridTemplateAreasValue* aResult,
     891             :                                   nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices);
     892             :   bool ParseGridTemplateAreas();
     893             :   bool ParseGridTemplateColumnsOrAutoFlow(bool aForGridShorthand);
     894             :   bool ParseGridTemplate(bool aForGridShorthand = false);
     895             :   bool ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames);
     896             :   bool ParseGrid();
     897             :   CSSParseResult ParseGridShorthandAutoProps(int32_t aAutoFlowAxis);
     898             :   bool ParseGridLine(nsCSSValue& aValue);
     899             :   bool ParseGridColumnRowStartEnd(nsCSSPropertyID aPropID);
     900             :   bool ParseGridColumnRow(nsCSSPropertyID aStartPropID,
     901             :                           nsCSSPropertyID aEndPropID);
     902             :   bool ParseGridArea();
     903             :   bool ParseGridGap();
     904             : 
     905             :   bool ParseInitialLetter();
     906             : 
     907             :   // parsing 'align/justify-items/self' from the css-align spec
     908             :   bool ParseAlignJustifyPosition(nsCSSValue& aResult,
     909             :                                  const KTableEntry aTable[]);
     910             :   bool ParseJustifyItems();
     911             :   bool ParseAlignItems();
     912             :   bool ParseAlignJustifySelf(nsCSSPropertyID aPropID);
     913             :   // parsing 'align/justify-content' from the css-align spec
     914             :   bool ParseAlignJustifyContent(nsCSSPropertyID aPropID);
     915             :   bool ParsePlaceContent();
     916             :   bool ParsePlaceItems();
     917             :   bool ParsePlaceSelf();
     918             : 
     919             :   // for 'clip' and '-moz-image-region'
     920             :   bool ParseRect(nsCSSPropertyID aPropID);
     921             :   bool ParseColumns();
     922             :   bool ParseContain(nsCSSValue& aValue);
     923             :   bool ParseContent();
     924             :   bool ParseCounterData(nsCSSPropertyID aPropID);
     925             :   bool ParseCursor();
     926             :   bool ParseFont();
     927             :   bool ParseFontSynthesis(nsCSSValue& aValue);
     928             :   bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue);
     929             :   bool ParseFontVariantAlternates(nsCSSValue& aValue);
     930             :   bool MergeBitmaskValue(int32_t aNewValue, const int32_t aMasks[],
     931             :                          int32_t& aMergedValue);
     932             :   bool ParseBitmaskValues(nsCSSValue& aValue,
     933             :                           const KTableEntry aKeywordTable[],
     934             :                           const int32_t aMasks[]);
     935             :   bool ParseFontVariantEastAsian(nsCSSValue& aValue);
     936             :   bool ParseFontVariantLigatures(nsCSSValue& aValue);
     937             :   bool ParseFontVariantNumeric(nsCSSValue& aValue);
     938             :   bool ParseFontVariant();
     939             :   bool ParseFontWeight(nsCSSValue& aValue);
     940             :   bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword, bool& aQuoted);
     941             :   bool ParseFamily(nsCSSValue& aValue);
     942             :   bool ParseFontFeatureSettings(nsCSSValue& aValue);
     943             :   bool ParseFontVariationSettings(nsCSSValue& aValue);
     944             :   bool ParseFontSrc(nsCSSValue& aValue);
     945             :   bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
     946             :   bool ParseFontRanges(nsCSSValue& aValue);
     947             :   bool ParseListStyle();
     948             :   bool ParseListStyleType(nsCSSValue& aValue);
     949             :   bool ParseMargin();
     950             :   bool ParseClipPath(nsCSSValue& aValue);
     951             :   bool ParseTransform(bool aIsPrefixed, nsCSSPropertyID aProperty,
     952             :                       bool aDisallowRelativeValues = false);
     953             :   bool ParseObjectPosition();
     954             :   bool ParseOutline();
     955             :   bool ParseOverflow();
     956             :   bool ParsePadding();
     957             :   bool ParseQuotes();
     958             :   bool ParseTextAlign(nsCSSValue& aValue,
     959             :                       const KTableEntry aTable[]);
     960             :   bool ParseTextAlign(nsCSSValue& aValue);
     961             :   bool ParseTextAlignLast(nsCSSValue& aValue);
     962             :   bool ParseTextDecoration();
     963             :   bool ParseTextDecorationLine(nsCSSValue& aValue);
     964             :   bool ParseTextEmphasis();
     965             :   bool ParseTextEmphasisPosition(nsCSSValue& aValue);
     966             :   bool ParseTextEmphasisStyle(nsCSSValue& aValue);
     967             :   bool ParseTextCombineUpright(nsCSSValue& aValue);
     968             :   bool ParseTextOverflow(nsCSSValue& aValue);
     969             :   bool ParseTouchAction(nsCSSValue& aValue);
     970             : 
     971             :   bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow);
     972             :   bool ParseShadowList(nsCSSPropertyID aProperty);
     973             :   bool ParseShapeOutside(nsCSSValue& aValue);
     974             :   bool ParseTransitionProperty();
     975             :   bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
     976             :   bool ParseTransitionTimingFunctionValueComponent(float& aComponent,
     977             :                                                      char aStop,
     978             :                                                      bool aIsXPoint);
     979             :   bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
     980             :   bool ParseTransitionFramesTimingFunctionValues(nsCSSValue& aValue);
     981             :   enum ParseAnimationOrTransitionShorthandResult {
     982             :     eParseAnimationOrTransitionShorthand_Values,
     983             :     eParseAnimationOrTransitionShorthand_Inherit,
     984             :     eParseAnimationOrTransitionShorthand_Error
     985             :   };
     986             :   ParseAnimationOrTransitionShorthandResult
     987             :     ParseAnimationOrTransitionShorthand(const nsCSSPropertyID* aProperties,
     988             :                                         const nsCSSValue* aInitialValues,
     989             :                                         nsCSSValue* aValues,
     990             :                                         size_t aNumProperties);
     991             :   bool ParseTransition();
     992             :   bool ParseAnimation();
     993             :   bool ParseWillChange();
     994             : 
     995             :   bool ParsePaint(nsCSSPropertyID aPropID);
     996             :   bool ParseDasharray();
     997             :   bool ParseMarker();
     998             :   bool ParsePaintOrder();
     999             :   bool ParseAll();
    1000             :   bool ParseScrollSnapType();
    1001             :   bool ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSPropertyID aPropID);
    1002             :   bool ParseScrollSnapDestination(nsCSSValue& aValue);
    1003             :   bool ParseScrollSnapCoordinate(nsCSSValue& aValue);
    1004             :   bool ParseWebkitTextStroke();
    1005             : 
    1006             :   /**
    1007             :    * Parses a variable value from a custom property declaration.
    1008             :    *
    1009             :    * @param aType Out parameter into which will be stored the type of variable
    1010             :    *   value, indicating whether the parsed value was a token stream or one of
    1011             :    *   the CSS-wide keywords.
    1012             :    * @param aValue Out parameter into which will be stored the token stream
    1013             :    *   as a string, if the parsed custom property did take a token stream.
    1014             :    * @return Whether parsing succeeded.
    1015             :    */
    1016             :   bool ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
    1017             :                                 nsString& aValue);
    1018             : 
    1019             :   /**
    1020             :    * Parses a CSS variable value.  This could be 'initial', 'inherit', 'unset'
    1021             :    * or a token stream, which may or may not include variable references.
    1022             :    *
    1023             :    * @param aType Out parameter into which the type of the variable value
    1024             :    *   will be stored.
    1025             :    * @param aDropBackslash Out parameter indicating whether during variable
    1026             :    *   value parsing there was a trailing backslash before EOF that must
    1027             :    *   be dropped when serializing the variable value.
    1028             :    * @param aImpliedCharacters Out parameter appended to which will be any
    1029             :    *   characters that were implied when encountering EOF and which
    1030             :    *   must be included at the end of the serialized variable value.
    1031             :    * @param aFunc A callback function to invoke when a variable reference
    1032             :    *   is encountered.  May be null.  Arguments are the variable name
    1033             :    *   and the aData argument passed in to this function.
    1034             :    * @param User data to pass in to the callback.
    1035             :    * @return Whether parsing succeeded.
    1036             :    */
    1037             :   bool ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
    1038             :                                bool* aDropBackslash,
    1039             :                                nsString& aImpliedCharacters,
    1040             :                                void (*aFunc)(const nsAString&, void*),
    1041             :                                void* aData);
    1042             : 
    1043             :   /**
    1044             :    * Returns whether the scanner dropped a backslash just before EOF.
    1045             :    */
    1046             :   bool BackslashDropped();
    1047             : 
    1048             :   /**
    1049             :    * Calls AppendImpliedEOFCharacters on mScanner.
    1050             :    */
    1051             :   void AppendImpliedEOFCharacters(nsAString& aResult);
    1052             : 
    1053             :   // Reused utility parsing routines
    1054             :   void AppendValue(nsCSSPropertyID aPropID, const nsCSSValue& aValue);
    1055             :   bool ParseBoxProperties(const nsCSSPropertyID aPropIDs[]);
    1056             :   bool ParseGroupedBoxProperty(int32_t aVariantMask,
    1057             :                                nsCSSValue& aValue,
    1058             :                                uint32_t aRestrictions);
    1059             :   bool ParseBoxCornerRadius(const nsCSSPropertyID aPropID);
    1060             :   bool ParseBoxCornerRadiiInternals(nsCSSValue array[]);
    1061             :   bool ParseBoxCornerRadii(const nsCSSPropertyID aPropIDs[]);
    1062             : 
    1063             :   int32_t ParseChoice(nsCSSValue aValues[],
    1064             :                       const nsCSSPropertyID aPropIDs[], int32_t aNumIDs);
    1065             : 
    1066             :   CSSParseResult ParseColor(nsCSSValue& aValue);
    1067             : 
    1068             :   template<typename ComponentType>
    1069             :   bool ParseRGBColor(ComponentType& aR,
    1070             :                      ComponentType& aG,
    1071             :                      ComponentType& aB,
    1072             :                      ComponentType& aA);
    1073             :   bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
    1074             :                      float& aOpacity);
    1075             : 
    1076             :   // The ParseColorOpacityAndCloseParen methods below attempt to parse an
    1077             :   // optional [ separator <alpha-value> ] expression, followed by a
    1078             :   // close-parenthesis, at the end of a css color function (e.g. "rgba()" or
    1079             :   // "hsla()"). If these functions simply encounter a close-parenthesis (without
    1080             :   // any [separator <alpha-value>]), they will still succeed (i.e. return true),
    1081             :   // with outparam 'aOpacity' set to a default opacity value (fully-opaque).
    1082             :   //
    1083             :   // The range of opacity component is [0, 255], and the default opacity value
    1084             :   // is 255 (fully-opaque) for this function.
    1085             :   bool ParseColorOpacityAndCloseParen(uint8_t& aOpacity,
    1086             :                                       char aSeparator);
    1087             :   // Similar to the previous one, but the range of opacity component is
    1088             :   // [0.0f, 1.0f] and the default opacity value is 1.0f (fully-opaque).
    1089             :   bool ParseColorOpacityAndCloseParen(float& aOpacity,
    1090             :                                       char aSeparator);
    1091             : 
    1092             :   // Parse a <number> color component. The range of color component is [0, 255].
    1093             :   // If |aSeparator| is provided, this function will also attempt to parse that
    1094             :   // character after parsing the color component.
    1095             :   bool ParseColorComponent(uint8_t& aComponent, const Maybe<char>& aSeparator);
    1096             :   // Similar to the previous one, but parse a <percentage> color component.
    1097             :   // The range of color component is [0.0f, 1.0f].
    1098             :   bool ParseColorComponent(float& aComponent, const Maybe<char>& aSeparator);
    1099             : 
    1100             :   // Parse a <hue> component.
    1101             :   //   <hue> = <number> | <angle>
    1102             :   // The unit of outparam 'aAngle' is degree. Assume the unit to be degree if an
    1103             :   // unitless <number> is parsed.
    1104             :   bool ParseHue(float& aAngle);
    1105             : 
    1106             :   bool ParseEnum(nsCSSValue& aValue,
    1107             :                  const KTableEntry aKeywordTable[]);
    1108             : 
    1109             :   // A special ParseEnum for the CSS Box Alignment properties that have
    1110             :   // 'baseline' values.  In addition to the keywords in aKeywordTable, it also
    1111             :   // parses 'first baseline' and 'last baseline' as a single value.
    1112             :   // (aKeywordTable must contain 'baseline')
    1113             :   bool ParseAlignEnum(nsCSSValue& aValue, const KTableEntry aKeywordTable[]);
    1114             : 
    1115             :   // Variant parsing methods
    1116             :   CSSParseResult ParseVariant(nsCSSValue& aValue,
    1117             :                               uint32_t aVariantMask,
    1118             :                               const KTableEntry aKeywordTable[]);
    1119             :   CSSParseResult ParseVariantWithRestrictions(nsCSSValue& aValue,
    1120             :                                               int32_t aVariantMask,
    1121             :                                               const KTableEntry aKeywordTable[],
    1122             :                                               uint32_t aRestrictions);
    1123             :   CSSParseResult ParseNonNegativeVariant(nsCSSValue& aValue,
    1124             :                                          int32_t aVariantMask,
    1125             :                                          const KTableEntry aKeywordTable[]);
    1126             :   CSSParseResult ParseOneOrLargerVariant(nsCSSValue& aValue,
    1127             :                                          int32_t aVariantMask,
    1128             :                                          const KTableEntry aKeywordTable[]);
    1129             : 
    1130             :   // Variant parsing methods that are guaranteed to UngetToken any token
    1131             :   // consumed on failure
    1132             : 
    1133        3864 :   MOZ_MUST_USE bool ParseSingleTokenVariant(nsCSSValue& aValue,
    1134             :                                             int32_t aVariantMask,
    1135             :                                             const KTableEntry aKeywordTable[])
    1136             :   {
    1137        3864 :     MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS),
    1138             :                "use ParseVariant for variants in VARIANT_MULTIPLE_TOKENS");
    1139        3864 :     CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable);
    1140        3864 :     MOZ_ASSERT(result != CSSParseResult::Error);
    1141        3864 :     return result == CSSParseResult::Ok;
    1142             :   }
    1143         439 :   bool ParseSingleTokenNonNegativeVariant(nsCSSValue& aValue,
    1144             :                                           int32_t aVariantMask,
    1145             :                                           const KTableEntry aKeywordTable[])
    1146             :   {
    1147         439 :     MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS),
    1148             :                "use ParseNonNegativeVariant for variants in "
    1149             :                "VARIANT_MULTIPLE_TOKENS");
    1150             :     CSSParseResult result =
    1151         439 :       ParseNonNegativeVariant(aValue, aVariantMask, aKeywordTable);
    1152         439 :     MOZ_ASSERT(result != CSSParseResult::Error);
    1153         439 :     return result == CSSParseResult::Ok;
    1154             :   }
    1155           3 :   bool ParseSingleTokenOneOrLargerVariant(nsCSSValue& aValue,
    1156             :                                           int32_t aVariantMask,
    1157             :                                           const KTableEntry aKeywordTable[])
    1158             :   {
    1159           3 :     MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS),
    1160             :                "use ParseOneOrLargerVariant for variants in "
    1161             :                "VARIANT_MULTIPLE_TOKENS");
    1162             :     CSSParseResult result =
    1163           3 :       ParseOneOrLargerVariant(aValue, aVariantMask, aKeywordTable);
    1164           3 :     MOZ_ASSERT(result != CSSParseResult::Error);
    1165           3 :     return result == CSSParseResult::Ok;
    1166             :   }
    1167             : 
    1168             :   // Helpers for some common ParseSingleTokenNonNegativeVariant calls.
    1169         344 :   bool ParseNonNegativeInteger(nsCSSValue& aValue)
    1170             :   {
    1171         344 :     return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_INTEGER, nullptr);
    1172             :   }
    1173          16 :   bool ParseNonNegativeNumber(nsCSSValue& aValue)
    1174             :   {
    1175          16 :     return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_NUMBER, nullptr);
    1176             :   }
    1177             : 
    1178             :   // Helpers for some common ParseSingleTokenOneOrLargerVariant calls.
    1179           0 :   bool ParseOneOrLargerInteger(nsCSSValue& aValue)
    1180             :   {
    1181           0 :     return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_INTEGER, nullptr);
    1182             :   }
    1183           0 :   bool ParseOneOrLargerNumber(nsCSSValue& aValue)
    1184             :   {
    1185           0 :     return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_NUMBER, nullptr);
    1186             :   }
    1187             : 
    1188             :   // http://dev.w3.org/csswg/css-values/#custom-idents
    1189             :   // Parse an identifier that is none of:
    1190             :   // * a CSS-wide keyword
    1191             :   // * "default"
    1192             :   // * a keyword in |aExcludedKeywords|
    1193             :   // * a keyword in |aPropertyKTable|
    1194             :   //
    1195             :   // |aExcludedKeywords| is an array of nsCSSKeyword
    1196             :   // that ends with a eCSSKeyword_UNKNOWN marker.
    1197             :   //
    1198             :   // |aPropertyKTable| can be used if some of the keywords to exclude
    1199             :   // also appear in an existing nsCSSProps::KTableEntry,
    1200             :   // to avoid duplicating them.
    1201             :   bool ParseCustomIdent(nsCSSValue& aValue,
    1202             :                         const nsAutoString& aIdentValue,
    1203             :                         const nsCSSKeyword aExcludedKeywords[] = nullptr,
    1204             :                         const nsCSSProps::KTableEntry aPropertyKTable[] = nullptr);
    1205             :   bool ParseCounter(nsCSSValue& aValue);
    1206             :   bool ParseAttr(nsCSSValue& aValue);
    1207             :   bool ParseSymbols(nsCSSValue& aValue);
    1208             :   bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL);
    1209             :   bool TranslateDimension(nsCSSValue& aValue, uint32_t aVariantMask,
    1210             :                             float aNumber, const nsString& aUnit);
    1211             :   bool ParseImageOrientation(nsCSSValue& aAngle);
    1212             :   bool ParseImageRect(nsCSSValue& aImage);
    1213             :   bool ParseElement(nsCSSValue& aValue);
    1214             :   bool ParseColorStop(nsCSSValueGradient* aGradient);
    1215             : 
    1216             :   enum GradientParsingFlags {
    1217             :     eGradient_Repeating    = 1 << 0, // repeating-{linear|radial}-gradient
    1218             :     eGradient_MozLegacy    = 1 << 1, // -moz-{linear|radial}-gradient
    1219             :     eGradient_WebkitLegacy = 1 << 2, // -webkit-{linear|radial}-gradient
    1220             : 
    1221             :     // Mask to catch both "legacy" flags:
    1222             :     eGradient_AnyLegacy = eGradient_MozLegacy | eGradient_WebkitLegacy
    1223             :   };
    1224             :   bool ParseLinearGradient(nsCSSValue& aValue, uint8_t aFlags);
    1225             :   bool ParseRadialGradient(nsCSSValue& aValue, uint8_t aFlags);
    1226             :   bool IsLegacyGradientLine(const nsCSSTokenType& aType,
    1227             :                             const nsString& aId);
    1228             :   bool ParseGradientColorStops(nsCSSValueGradient* aGradient,
    1229             :                                nsCSSValue& aValue);
    1230             : 
    1231             :   // For the ancient "-webkit-gradient(linear|radial, ...)" syntax:
    1232             :   bool ParseWebkitGradientPointComponent(nsCSSValue& aComponent,
    1233             :                                          bool aIsHorizontal);
    1234             :   bool ParseWebkitGradientPoint(nsCSSValuePair& aPoint);
    1235             :   bool ParseWebkitGradientRadius(float& aRadius);
    1236             :   bool ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient);
    1237             :   bool ParseWebkitGradientColorStops(nsCSSValueGradient* aGradient);
    1238             :   void FinalizeLinearWebkitGradient(nsCSSValueGradient* aGradient,
    1239             :                                     const nsCSSValuePair& aStartPoint,
    1240             :                                     const nsCSSValuePair& aSecondPoint);
    1241             :   void FinalizeRadialWebkitGradient(nsCSSValueGradient* aGradient,
    1242             :                                     const nsCSSValuePair& aFirstCenter,
    1243             :                                     const nsCSSValuePair& aSecondCenter,
    1244             :                                     const float aFirstRadius,
    1245             :                                     const float aSecondRadius);
    1246             :   bool ParseWebkitGradient(nsCSSValue& aValue);
    1247             : 
    1248        1120 :   void SetParsingCompoundProperty(bool aBool) {
    1249        1120 :     mParsingCompoundProperty = aBool;
    1250        1120 :   }
    1251         614 :   bool IsParsingCompoundProperty(void) const {
    1252         614 :     return mParsingCompoundProperty;
    1253             :   }
    1254             : 
    1255             :   /* Functions for basic shapes */
    1256             :   bool ParseReferenceBoxAndBasicShape(nsCSSValue& aValue,
    1257             :                                       const KTableEntry aBoxKeywordTable[]);
    1258             :   bool ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens);
    1259             :   bool ParsePolygonFunction(nsCSSValue& aValue);
    1260             :   bool ParseCircleOrEllipseFunction(nsCSSKeyword, nsCSSValue& aValue);
    1261             :   bool ParseInsetFunction(nsCSSValue& aValue);
    1262             :   // We parse position values differently for basic-shape, by expanding defaults
    1263             :   // and replacing keywords with percentages
    1264             :   bool ParsePositionValueForBasicShape(nsCSSValue& aOut);
    1265             : 
    1266             : 
    1267             :   /* Functions for transform Parsing */
    1268             :   bool ParseSingleTransform(bool aIsPrefixed, bool aDisallowRelativeValues,
    1269             :                             nsCSSValue& aValue);
    1270             :   bool ParseFunction(nsCSSKeyword aFunction, const uint32_t aAllowedTypes[],
    1271             :                      uint32_t aVariantMaskAll, uint16_t aMinElems,
    1272             :                      uint16_t aMaxElems, nsCSSValue &aValue);
    1273             :   bool ParseFunctionInternals(const uint32_t aVariantMask[],
    1274             :                               uint32_t aVariantMaskAll,
    1275             :                               uint16_t aMinElems,
    1276             :                               uint16_t aMaxElems,
    1277             :                               InfallibleTArray<nsCSSValue>& aOutput);
    1278             : 
    1279             :   /* Functions for transform-origin/perspective-origin Parsing */
    1280             :   bool ParseTransformOrigin(nsCSSPropertyID aProperty);
    1281             : 
    1282             :   /* Functions for filter parsing */
    1283             :   bool ParseFilter();
    1284             :   bool ParseSingleFilter(nsCSSValue* aValue);
    1285             :   bool ParseDropShadow(nsCSSValue* aValue);
    1286             : 
    1287             :   /* Find and return the namespace ID associated with aPrefix.
    1288             :      If aPrefix has not been declared in an @namespace rule, returns
    1289             :      kNameSpaceID_Unknown. */
    1290             :   int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
    1291             : 
    1292             :   /* Find the correct default namespace, and set it on aSelector. */
    1293             :   void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
    1294             : 
    1295             :   // Current token. The value is valid after calling GetToken and invalidated
    1296             :   // by UngetToken.
    1297             :   nsCSSToken mToken;
    1298             : 
    1299             :   // Our scanner.
    1300             :   nsCSSScanner* mScanner;
    1301             : 
    1302             :   // Our error reporter.
    1303             :   css::ErrorReporter* mReporter;
    1304             : 
    1305             :   // The URI to be used as a base for relative URIs.
    1306             :   nsCOMPtr<nsIURI> mBaseURI;
    1307             : 
    1308             :   // The URI to be used as an HTTP "Referer" and for error reporting.
    1309             :   nsCOMPtr<nsIURI> mSheetURI;
    1310             : 
    1311             :   // The principal of the sheet involved
    1312             :   nsCOMPtr<nsIPrincipal> mSheetPrincipal;
    1313             : 
    1314             :   // The sheet we're parsing into
    1315             :   RefPtr<CSSStyleSheet> mSheet;
    1316             : 
    1317             :   // Used for @import rules
    1318             :   css::Loader* mChildLoader; // not ref counted, it owns us
    1319             : 
    1320             :   // Any sheets we may reuse when parsing an @import.
    1321             :   css::LoaderReusableStyleSheets* mReusableSheets;
    1322             : 
    1323             :   // Sheet section we're in.  This is used to enforce correct ordering of the
    1324             :   // various rule types (eg the fact that a @charset rule must come before
    1325             :   // anything else).  Note that there are checks of similar things in various
    1326             :   // places in CSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
    1327             :   enum nsCSSSection {
    1328             :     eCSSSection_Charset,
    1329             :     eCSSSection_Import,
    1330             :     eCSSSection_NameSpace,
    1331             :     eCSSSection_General
    1332             :   };
    1333             :   nsCSSSection  mSection;
    1334             : 
    1335             :   nsXMLNameSpaceMap *mNameSpaceMap;  // weak, mSheet owns it
    1336             : 
    1337             :   // After an UngetToken is done this flag is true. The next call to
    1338             :   // GetToken clears the flag.
    1339             :   bool mHavePushBack : 1;
    1340             : 
    1341             :   // True if we are in quirks mode; false in standards or almost standards mode
    1342             :   bool          mNavQuirkMode : 1;
    1343             : 
    1344             :   // True when the hashless color quirk applies.
    1345             :   bool mHashlessColorQuirk : 1;
    1346             : 
    1347             :   // True when the unitless length quirk applies.
    1348             :   bool mUnitlessLengthQuirk : 1;
    1349             : 
    1350             :   // True if we are in parsing rules for the chrome.
    1351             :   bool mIsChrome : 1;
    1352             : 
    1353             :   // True if we're parsing SVG presentation attributes
    1354             :   // These attributes allow non-calc lengths to be unitless (mapping to px)
    1355             :   bool mIsSVGMode : 1;
    1356             : 
    1357             :   // True if viewport units should be allowed.
    1358             :   bool mViewportUnitsEnabled : 1;
    1359             : 
    1360             :   // This flag is set when parsing a non-box shorthand; it's used to not apply
    1361             :   // some quirks during shorthand parsing
    1362             :   bool          mParsingCompoundProperty : 1;
    1363             : 
    1364             :   // True if we are in the middle of parsing an @supports condition.
    1365             :   // This is used to avoid recording the input stream when variable references
    1366             :   // are encountered in a property declaration in the @supports condition.
    1367             :   bool mInSupportsCondition : 1;
    1368             : 
    1369             :   // True if we are somewhere within a @supports rule whose condition is
    1370             :   // false.
    1371             :   bool mInFailingSupportsRule : 1;
    1372             : 
    1373             :   // True if we will suppress all parse errors (except unexpected EOFs).
    1374             :   // This is used to prevent errors for declarations inside a failing
    1375             :   // @supports rule.
    1376             :   bool mSuppressErrors : 1;
    1377             : 
    1378             :   // True if any parsing of URL values requires a sheet principal to have
    1379             :   // been passed in the nsCSSScanner constructor.  This is usually the case.
    1380             :   // It can be set to false, for example, when we create an nsCSSParser solely
    1381             :   // to parse a property value to test it for syntactic correctness.  When
    1382             :   // false, an assertion that mSheetPrincipal is non-null is skipped.  Should
    1383             :   // not be set to false if any nsCSSValues created during parsing can escape
    1384             :   // out of the parser.
    1385             :   bool mSheetPrincipalRequired;
    1386             : 
    1387             :   // Controls access to nonstandard style constructs that are not safe
    1388             :   // for use on the public Web but necessary in UA sheets and/or
    1389             :   // useful in user sheets.
    1390             :   css::SheetParsingMode mParsingMode;
    1391             : 
    1392             :   // This enum helps us track whether we've unprefixed "display: -webkit-box"
    1393             :   // (treating it as "display: flex") in an earlier declaration within a series
    1394             :   // of declarations.  (This only impacts behavior if
    1395             :   // sWebkitPrefixedAliasesEnabled is true.)
    1396             :   enum WebkitBoxUnprefixState : uint8_t {
    1397             :     eNotParsingDecls, // We are *not* currently parsing a sequence of
    1398             :                       // CSS declarations. (default state)
    1399             : 
    1400             :     // The next two enum values indicate that we *are* currently parsing a
    1401             :     // sequence of declarations (in ParseDeclarations or ParseDeclarationBlock)
    1402             :     // and...
    1403             :     eHaveNotUnprefixed, // ...we have not unprefixed 'display:-webkit-box' in
    1404             :                         // this sequence of CSS declarations.
    1405             :     eHaveUnprefixed // ...we *have* unprefixed 'display:-webkit-box' earlier in
    1406             :                     // this sequence of CSS declarations.
    1407             :   };
    1408             :   WebkitBoxUnprefixState mWebkitBoxUnprefixState;
    1409             : 
    1410             :   // Stack of rule groups; used for @media and such.
    1411             :   InfallibleTArray<RefPtr<css::GroupRule> > mGroupStack;
    1412             : 
    1413             :   // During the parsing of a property (which may be a shorthand), the data
    1414             :   // are stored in |mTempData|.  (It is needed to ensure that parser
    1415             :   // errors cause the data to be ignored, and to ensure that a
    1416             :   // non-'!important' declaration does not override an '!important'
    1417             :   // one.)
    1418             :   nsCSSExpandedDataBlock mTempData;
    1419             : 
    1420             :   // All data from successfully parsed properties are placed into |mData|.
    1421             :   nsCSSExpandedDataBlock mData;
    1422             : 
    1423             : public:
    1424             :   // Used from nsCSSParser constructors and destructors
    1425             :   CSSParserImpl* mNextFree;
    1426             : };
    1427             : 
    1428           0 : static void AssignRuleToPointer(css::Rule* aRule, void* aPointer)
    1429             : {
    1430           0 :   css::Rule **pointer = static_cast<css::Rule**>(aPointer);
    1431           0 :   NS_ADDREF(*pointer = aRule);
    1432           0 : }
    1433             : 
    1434        3203 : static void AppendRuleToSheet(css::Rule* aRule, void* aParser)
    1435             : {
    1436        3203 :   CSSParserImpl* parser = (CSSParserImpl*) aParser;
    1437        3203 :   parser->AppendRule(aRule);
    1438        3203 : }
    1439             : 
    1440             : #define REPORT_UNEXPECTED(msg_) \
    1441             :   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_); }
    1442             : 
    1443             : #define REPORT_UNEXPECTED_P(msg_, param_) \
    1444             :   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_); }
    1445             : 
    1446             : #define REPORT_UNEXPECTED_P_V(msg_, param_, value_) \
    1447             :   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_, value_); }
    1448             : 
    1449             : #define REPORT_UNEXPECTED_TOKEN(msg_) \
    1450             :   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken); }
    1451             : 
    1452             : #define REPORT_UNEXPECTED_TOKEN_CHAR(msg_, ch_) \
    1453             :   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken, ch_); }
    1454             : 
    1455             : #define REPORT_UNEXPECTED_EOF(lf_) \
    1456             :   mReporter->ReportUnexpectedEOF(#lf_)
    1457             : 
    1458             : #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
    1459             :   mReporter->ReportUnexpectedEOF(ch_)
    1460             : 
    1461             : #define OUTPUT_ERROR() \
    1462             :   mReporter->OutputError()
    1463             : 
    1464             : #define OUTPUT_ERROR_WITH_POSITION(linenum_, lineoff_) \
    1465             :   mReporter->OutputError(linenum_, lineoff_)
    1466             : 
    1467             : #define CLEAR_ERROR() \
    1468             :   mReporter->ClearError()
    1469             : 
    1470           4 : CSSParserImpl::CSSParserImpl()
    1471             :   : mToken(),
    1472             :     mScanner(nullptr),
    1473             :     mReporter(nullptr),
    1474             :     mChildLoader(nullptr),
    1475             :     mReusableSheets(nullptr),
    1476             :     mSection(eCSSSection_Charset),
    1477             :     mNameSpaceMap(nullptr),
    1478             :     mHavePushBack(false),
    1479             :     mNavQuirkMode(false),
    1480             :     mHashlessColorQuirk(false),
    1481             :     mUnitlessLengthQuirk(false),
    1482             :     mIsChrome(false),
    1483             :     mIsSVGMode(false),
    1484             :     mViewportUnitsEnabled(true),
    1485             :     mParsingCompoundProperty(false),
    1486             :     mInSupportsCondition(false),
    1487             :     mInFailingSupportsRule(false),
    1488             :     mSuppressErrors(false),
    1489             :     mSheetPrincipalRequired(true),
    1490             :     mParsingMode(css::eAuthorSheetFeatures),
    1491             :     mWebkitBoxUnprefixState(eNotParsingDecls),
    1492           4 :     mNextFree(nullptr)
    1493             : {
    1494           4 : }
    1495             : 
    1496           0 : CSSParserImpl::~CSSParserImpl()
    1497             : {
    1498           0 :   mData.AssertInitialState();
    1499           0 :   mTempData.AssertInitialState();
    1500           0 : }
    1501             : 
    1502             : nsresult
    1503       12085 : CSSParserImpl::SetStyleSheet(CSSStyleSheet* aSheet)
    1504             : {
    1505       12085 :   if (aSheet != mSheet) {
    1506             :     // Switch to using the new sheet, if any
    1507         120 :     mGroupStack.Clear();
    1508         120 :     mSheet = aSheet;
    1509         120 :     if (mSheet) {
    1510          60 :       mNameSpaceMap = mSheet->GetNameSpaceMap();
    1511             :     } else {
    1512          60 :       mNameSpaceMap = nullptr;
    1513             :     }
    1514       11965 :   } else if (mSheet) {
    1515           0 :     mNameSpaceMap = mSheet->GetNameSpaceMap();
    1516             :   }
    1517             : 
    1518       12085 :   return NS_OK;
    1519             : }
    1520             : 
    1521             : nsIDocument*
    1522        6571 : CSSParserImpl::GetDocument()
    1523             : {
    1524        6571 :   if (!mSheet) {
    1525         111 :     return nullptr;
    1526             :   }
    1527        6460 :   return mSheet->GetAssociatedDocument();
    1528             : }
    1529             : 
    1530             : nsresult
    1531       12325 : CSSParserImpl::SetQuirkMode(bool aQuirkMode)
    1532             : {
    1533       12325 :   mNavQuirkMode = aQuirkMode;
    1534       12325 :   return NS_OK;
    1535             : }
    1536             : 
    1537             : nsresult
    1538       12325 : CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader)
    1539             : {
    1540       12325 :   mChildLoader = aChildLoader;  // not ref counted, it owns us
    1541       12325 :   return NS_OK;
    1542             : }
    1543             : 
    1544             : void
    1545       12025 : CSSParserImpl::Reset()
    1546             : {
    1547       12025 :   NS_ASSERTION(!mScanner, "resetting with scanner active");
    1548       12025 :   SetStyleSheet(nullptr);
    1549       12025 :   SetQuirkMode(false);
    1550       12025 :   SetChildLoader(nullptr);
    1551       12025 : }
    1552             : 
    1553             : void
    1554        2380 : CSSParserImpl::InitScanner(nsCSSScanner& aScanner,
    1555             :                            css::ErrorReporter& aReporter,
    1556             :                            nsIURI* aSheetURI, nsIURI* aBaseURI,
    1557             :                            nsIPrincipal* aSheetPrincipal)
    1558             : {
    1559        2380 :   NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
    1560        2380 :   NS_PRECONDITION(!mScanner, "already have scanner");
    1561             : 
    1562        2380 :   mScanner = &aScanner;
    1563        2380 :   mReporter = &aReporter;
    1564        2380 :   mScanner->SetErrorReporter(mReporter);
    1565             : 
    1566        2380 :   mBaseURI = aBaseURI;
    1567        2380 :   mSheetURI = aSheetURI;
    1568        2380 :   mSheetPrincipal = aSheetPrincipal;
    1569        2380 :   mHavePushBack = false;
    1570        2380 : }
    1571             : 
    1572             : void
    1573        2380 : CSSParserImpl::ReleaseScanner()
    1574             : {
    1575        2380 :   mScanner = nullptr;
    1576        2380 :   mReporter = nullptr;
    1577        2380 :   mBaseURI = nullptr;
    1578        2380 :   mSheetURI = nullptr;
    1579        2380 :   mSheetPrincipal = nullptr;
    1580        2380 : }
    1581             : 
    1582             : nsresult
    1583          60 : CSSParserImpl::ParseSheet(const nsAString& aInput,
    1584             :                           nsIURI*          aSheetURI,
    1585             :                           nsIURI*          aBaseURI,
    1586             :                           nsIPrincipal*    aSheetPrincipal,
    1587             :                           uint32_t         aLineNumber,
    1588             :                           css::LoaderReusableStyleSheets* aReusableSheets)
    1589             : {
    1590          60 :   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
    1591          60 :   NS_PRECONDITION(aBaseURI, "need base URI");
    1592          60 :   NS_PRECONDITION(aSheetURI, "need sheet URI");
    1593          60 :   NS_PRECONDITION(mSheet, "Must have sheet to parse into");
    1594          60 :   NS_ENSURE_STATE(mSheet);
    1595             : 
    1596             : #ifdef DEBUG
    1597          60 :   nsIURI* uri = mSheet->GetSheetURI();
    1598             :   bool equal;
    1599          60 :   NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
    1600             :                "Sheet URI does not match passed URI");
    1601          60 :   NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
    1602             :                                                         &equal)) &&
    1603             :                equal,
    1604             :                "Sheet principal does not match passed principal");
    1605             : #endif
    1606             : 
    1607         120 :   nsCSSScanner scanner(aInput, aLineNumber);
    1608         120 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
    1609          60 :   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
    1610             : 
    1611          60 :   int32_t ruleCount = mSheet->StyleRuleCount();
    1612          60 :   if (0 < ruleCount) {
    1613           0 :     const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1);
    1614           0 :     if (lastRule) {
    1615           0 :       switch (lastRule->GetType()) {
    1616             :         case css::Rule::CHARSET_RULE:
    1617             :         case css::Rule::IMPORT_RULE:
    1618           0 :           mSection = eCSSSection_Import;
    1619           0 :           break;
    1620             :         case css::Rule::NAMESPACE_RULE:
    1621           0 :           mSection = eCSSSection_NameSpace;
    1622           0 :           break;
    1623             :         default:
    1624           0 :           mSection = eCSSSection_General;
    1625           0 :           break;
    1626             :       }
    1627             :     }
    1628             :   }
    1629             :   else {
    1630          60 :     mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
    1631             :   }
    1632             : 
    1633          60 :   mParsingMode = mSheet->ParsingMode();
    1634          60 :   mIsChrome = dom::IsChromeURI(aSheetURI);
    1635          60 :   mReusableSheets = aReusableSheets;
    1636             : 
    1637          60 :   nsCSSToken* tk = &mToken;
    1638             :   for (;;) {
    1639             :     // Get next non-whitespace token
    1640        6208 :     if (!GetToken(true)) {
    1641          60 :       OUTPUT_ERROR();
    1642          60 :       break;
    1643             :     }
    1644        3074 :     if (eCSSToken_HTMLComment == tk->mType) {
    1645           0 :       continue; // legal here only
    1646             :     }
    1647        3074 :     if (eCSSToken_AtKeyword == tk->mType) {
    1648         286 :       ParseAtRule(AppendRuleToSheet, this, false);
    1649         286 :       continue;
    1650             :     }
    1651        2788 :     UngetToken();
    1652        2788 :     if (ParseRuleSet(AppendRuleToSheet, this)) {
    1653        2788 :       mSection = eCSSSection_General;
    1654             :     }
    1655             :   }
    1656          60 :   ReleaseScanner();
    1657             : 
    1658          60 :   mParsingMode = css::eAuthorSheetFeatures;
    1659          60 :   mIsChrome = false;
    1660          60 :   mReusableSheets = nullptr;
    1661             : 
    1662          60 :   return NS_OK;
    1663             : }
    1664             : 
    1665             : /**
    1666             :  * Determines whether the identifier contained in the given string is a
    1667             :  * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
    1668             :  */
    1669             : static bool
    1670           0 : NonMozillaVendorIdentifier(const nsAString& ident)
    1671             : {
    1672           0 :   return (ident.First() == char16_t('-') &&
    1673           0 :           !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
    1674           0 :          ident.First() == char16_t('_');
    1675             : 
    1676             : }
    1677             : 
    1678             : already_AddRefed<css::Declaration>
    1679           7 : CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
    1680             :                                    nsIURI*          aDocURI,
    1681             :                                    nsIURI*          aBaseURI,
    1682             :                                    nsIPrincipal*    aNodePrincipal)
    1683             : {
    1684           7 :   NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
    1685           7 :   NS_PRECONDITION(aBaseURI, "need base URI");
    1686             : 
    1687             :   // XXX line number?
    1688          14 :   nsCSSScanner scanner(aAttributeValue, 0);
    1689          14 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI);
    1690           7 :   InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal);
    1691             : 
    1692           7 :   mSection = eCSSSection_General;
    1693             : 
    1694           7 :   uint32_t parseFlags = eParseDeclaration_AllowImportant;
    1695             : 
    1696          14 :   RefPtr<css::Declaration> declaration = ParseDeclarationBlock(parseFlags);
    1697             : 
    1698           7 :   ReleaseScanner();
    1699             : 
    1700          14 :   return declaration.forget();
    1701             : }
    1702             : 
    1703             : nsresult
    1704           0 : CSSParserImpl::ParseDeclarations(const nsAString&  aBuffer,
    1705             :                                  nsIURI*           aSheetURI,
    1706             :                                  nsIURI*           aBaseURI,
    1707             :                                  nsIPrincipal*     aSheetPrincipal,
    1708             :                                  css::Declaration* aDeclaration,
    1709             :                                  bool*           aChanged)
    1710             : {
    1711           0 :   *aChanged = false;
    1712             : 
    1713           0 :   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
    1714             : 
    1715           0 :   nsCSSScanner scanner(aBuffer, 0);
    1716           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
    1717           0 :   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
    1718             : 
    1719           0 :   MOZ_ASSERT(mWebkitBoxUnprefixState == eNotParsingDecls,
    1720             :              "Someone forgot to clear mWebkitBoxUnprefixState!");
    1721           0 :   AutoRestore<WebkitBoxUnprefixState> autoRestore(mWebkitBoxUnprefixState);
    1722           0 :   mWebkitBoxUnprefixState = eHaveNotUnprefixed;
    1723             : 
    1724           0 :   mSection = eCSSSection_General;
    1725             : 
    1726           0 :   mData.AssertInitialState();
    1727           0 :   aDeclaration->ClearData();
    1728             :   // We could check if it was already empty, but...
    1729           0 :   *aChanged = true;
    1730             : 
    1731             :   for (;;) {
    1732             :     // If we cleared the old decl, then we want to be calling
    1733             :     // ValueAppended as we parse.
    1734           0 :     if (!ParseDeclaration(aDeclaration, eParseDeclaration_AllowImportant,
    1735             :                           true, aChanged)) {
    1736           0 :       if (!SkipDeclaration(false)) {
    1737           0 :         break;
    1738             :       }
    1739             :     }
    1740             :   }
    1741             : 
    1742           0 :   aDeclaration->CompressFrom(&mData);
    1743           0 :   ReleaseScanner();
    1744           0 :   return NS_OK;
    1745             : }
    1746             : 
    1747             : nsresult
    1748           0 : CSSParserImpl::ParseRule(const nsAString&        aRule,
    1749             :                          nsIURI*                 aSheetURI,
    1750             :                          nsIURI*                 aBaseURI,
    1751             :                          nsIPrincipal*           aSheetPrincipal,
    1752             :                          css::Rule**             aResult)
    1753             : {
    1754           0 :   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
    1755           0 :   NS_PRECONDITION(aBaseURI, "need base URI");
    1756             : 
    1757           0 :   *aResult = nullptr;
    1758             : 
    1759           0 :   nsCSSScanner scanner(aRule, 0);
    1760           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
    1761           0 :   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
    1762             : 
    1763           0 :   mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
    1764             : 
    1765           0 :   nsCSSToken* tk = &mToken;
    1766             :   // Get first non-whitespace token
    1767           0 :   nsresult rv = NS_OK;
    1768           0 :   if (!GetToken(true)) {
    1769           0 :     REPORT_UNEXPECTED(PEParseRuleWSOnly);
    1770           0 :     OUTPUT_ERROR();
    1771           0 :     rv = NS_ERROR_DOM_SYNTAX_ERR;
    1772             :   } else {
    1773           0 :     if (eCSSToken_AtKeyword == tk->mType) {
    1774             :       // FIXME: perhaps aInsideBlock should be true when we are?
    1775           0 :       ParseAtRule(AssignRuleToPointer, aResult, false);
    1776             :     } else {
    1777           0 :       UngetToken();
    1778           0 :       ParseRuleSet(AssignRuleToPointer, aResult);
    1779             :     }
    1780             : 
    1781           0 :     if (*aResult && GetToken(true)) {
    1782             :       // garbage after rule
    1783           0 :       REPORT_UNEXPECTED_TOKEN(PERuleTrailing);
    1784           0 :       NS_RELEASE(*aResult);
    1785             :     }
    1786             : 
    1787           0 :     if (!*aResult) {
    1788           0 :       rv = NS_ERROR_DOM_SYNTAX_ERR;
    1789           0 :       OUTPUT_ERROR();
    1790             :     }
    1791             :   }
    1792             : 
    1793           0 :   ReleaseScanner();
    1794           0 :   return rv;
    1795             : }
    1796             : 
    1797             : void
    1798           0 : CSSParserImpl::ParseLonghandProperty(const nsCSSPropertyID aPropID,
    1799             :                                      const nsAString& aPropValue,
    1800             :                                      nsIURI* aSheetURL,
    1801             :                                      nsIURI* aBaseURL,
    1802             :                                      nsIPrincipal* aSheetPrincipal,
    1803             :                                      nsCSSValue& aValue)
    1804             : {
    1805           0 :   MOZ_ASSERT(aPropID < eCSSProperty_COUNT_no_shorthands,
    1806             :              "ParseLonghandProperty must only take a longhand property");
    1807             : 
    1808           0 :   RefPtr<css::Declaration> declaration = new css::Declaration;
    1809           0 :   declaration->InitializeEmpty();
    1810             : 
    1811             :   bool changed;
    1812           0 :   ParseProperty(aPropID, aPropValue, aSheetURL, aBaseURL, aSheetPrincipal,
    1813             :                 declaration, &changed,
    1814             :                 /* aIsImportant */ false,
    1815           0 :                 /* aIsSVGMode */ false);
    1816             : 
    1817           0 :   if (changed) {
    1818           0 :     aValue = *declaration->GetNormalBlock()->ValueFor(aPropID);
    1819             :   } else {
    1820           0 :     aValue.Reset();
    1821             :   }
    1822           0 : }
    1823             : 
    1824             : bool
    1825           0 : CSSParserImpl::ParseTransformProperty(const nsAString& aPropValue,
    1826             :                                       bool aDisallowRelativeValues,
    1827             :                                       nsCSSValue& aValue)
    1828             : {
    1829           0 :   RefPtr<css::Declaration> declaration = new css::Declaration();
    1830           0 :   declaration->InitializeEmpty();
    1831             : 
    1832           0 :   mData.AssertInitialState();
    1833           0 :   mTempData.AssertInitialState();
    1834             : 
    1835           0 :   nsCSSScanner scanner(aPropValue, 0);
    1836           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr);
    1837           0 :   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
    1838             : 
    1839           0 :   bool parsedOK = ParseTransform(false, eCSSProperty_transform,
    1840           0 :                                  aDisallowRelativeValues);
    1841             :   // We should now be at EOF
    1842           0 :   if (parsedOK && GetToken(true)) {
    1843           0 :     parsedOK = false;
    1844             :   }
    1845             : 
    1846           0 :   bool changed = false;
    1847           0 :   if (parsedOK) {
    1848           0 :     declaration->ExpandTo(&mData);
    1849           0 :     changed = mData.TransferFromBlock(mTempData, eCSSProperty_transform,
    1850             :                                       EnabledState(), false,
    1851             :                                       true, false, declaration,
    1852           0 :                                       GetDocument());
    1853           0 :     declaration->CompressFrom(&mData);
    1854             :   }
    1855             : 
    1856           0 :   if (changed) {
    1857           0 :     aValue = *declaration->GetNormalBlock()->ValueFor(eCSSProperty_transform);
    1858             :   } else {
    1859           0 :     aValue.Reset();
    1860             :   }
    1861             : 
    1862           0 :   ReleaseScanner();
    1863             : 
    1864           0 :   return parsedOK;
    1865             : }
    1866             : 
    1867             : void
    1868         103 : CSSParserImpl::ParseProperty(const nsCSSPropertyID aPropID,
    1869             :                              const nsAString& aPropValue,
    1870             :                              nsIURI* aSheetURI,
    1871             :                              nsIURI* aBaseURI,
    1872             :                              nsIPrincipal* aSheetPrincipal,
    1873             :                              css::Declaration* aDeclaration,
    1874             :                              bool* aChanged,
    1875             :                              bool aIsImportant,
    1876             :                              bool aIsSVGMode)
    1877             : {
    1878         103 :   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
    1879         103 :   NS_PRECONDITION(aBaseURI, "need base URI");
    1880         103 :   NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
    1881         103 :   MOZ_ASSERT(aPropID != eCSSPropertyExtra_variable);
    1882             : 
    1883         103 :   mData.AssertInitialState();
    1884         103 :   mTempData.AssertInitialState();
    1885         103 :   aDeclaration->AssertMutable();
    1886             : 
    1887         206 :   nsCSSScanner scanner(aPropValue, 0);
    1888         206 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
    1889         103 :   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
    1890         103 :   mSection = eCSSSection_General;
    1891             : 
    1892         103 :   *aChanged = false;
    1893             : 
    1894             :   // Check for unknown or preffed off properties
    1895         206 :   if (eCSSProperty_UNKNOWN == aPropID ||
    1896         103 :       !nsCSSProps::IsEnabled(aPropID, EnabledState())) {
    1897           0 :     NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
    1898           0 :     REPORT_UNEXPECTED_P(PEUnknownProperty, propName);
    1899           0 :     REPORT_UNEXPECTED(PEDeclDropped);
    1900           0 :     OUTPUT_ERROR();
    1901           0 :     ReleaseScanner();
    1902           0 :     return;
    1903             :   }
    1904             : 
    1905         103 :   mIsSVGMode = aIsSVGMode;
    1906         103 :   bool parsedOK = ParseProperty(aPropID);
    1907             :   // We should now be at EOF
    1908         103 :   if (parsedOK && GetToken(true)) {
    1909           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
    1910           0 :     parsedOK = false;
    1911             :   }
    1912             : 
    1913         103 :   if (!parsedOK) {
    1914           0 :     NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
    1915           0 :     REPORT_UNEXPECTED_P(PEValueParsingError, propName);
    1916           0 :     REPORT_UNEXPECTED(PEDeclDropped);
    1917           0 :     OUTPUT_ERROR();
    1918           0 :     mTempData.ClearProperty(aPropID);
    1919             :   } else {
    1920             : 
    1921             :     // We know we don't need to force a ValueAppended call for the new
    1922             :     // value.  So if we are not processing a shorthand, and there's
    1923             :     // already a value for this property in the declaration at the
    1924             :     // same importance level, then we can just copy our parsed value
    1925             :     // directly into the declaration without going through the whole
    1926             :     // expand/compress thing.
    1927         103 :     if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData,
    1928             :                                        aChanged)) {
    1929             :       // Do it the slow way
    1930          99 :       aDeclaration->ExpandTo(&mData);
    1931          99 :       *aChanged = mData.TransferFromBlock(mTempData, aPropID,
    1932             :                                           EnabledState(), aIsImportant,
    1933             :                                           true, false, aDeclaration,
    1934             :                                           GetDocument());
    1935          99 :       aDeclaration->CompressFrom(&mData);
    1936             :     }
    1937         103 :     CLEAR_ERROR();
    1938             :   }
    1939             : 
    1940         103 :   mTempData.AssertInitialState();
    1941         103 :   mIsSVGMode = false;
    1942             : 
    1943         103 :   ReleaseScanner();
    1944             : }
    1945             : 
    1946             : void
    1947           0 : CSSParserImpl::ParseVariable(const nsAString& aVariableName,
    1948             :                              const nsAString& aPropValue,
    1949             :                              nsIURI* aSheetURI,
    1950             :                              nsIURI* aBaseURI,
    1951             :                              nsIPrincipal* aSheetPrincipal,
    1952             :                              css::Declaration* aDeclaration,
    1953             :                              bool* aChanged,
    1954             :                              bool aIsImportant)
    1955             : {
    1956           0 :   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
    1957           0 :   NS_PRECONDITION(aBaseURI, "need base URI");
    1958           0 :   NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
    1959             : 
    1960           0 :   mData.AssertInitialState();
    1961           0 :   mTempData.AssertInitialState();
    1962           0 :   aDeclaration->AssertMutable();
    1963             : 
    1964           0 :   nsCSSScanner scanner(aPropValue, 0);
    1965           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
    1966           0 :   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
    1967           0 :   mSection = eCSSSection_General;
    1968             : 
    1969           0 :   *aChanged = false;
    1970             : 
    1971             :   CSSVariableDeclarations::Type variableType;
    1972           0 :   nsString variableValue;
    1973             : 
    1974           0 :   bool parsedOK = ParseVariableDeclaration(&variableType, variableValue);
    1975             : 
    1976             :   // We should now be at EOF
    1977           0 :   if (parsedOK && GetToken(true)) {
    1978           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
    1979           0 :     parsedOK = false;
    1980             :   }
    1981             : 
    1982           0 :   if (!parsedOK) {
    1983           0 :     REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("--") +
    1984             :                                              aVariableName);
    1985           0 :     REPORT_UNEXPECTED(PEDeclDropped);
    1986           0 :     OUTPUT_ERROR();
    1987             :   } else {
    1988           0 :     CLEAR_ERROR();
    1989           0 :     aDeclaration->AddVariable(aVariableName, variableType,
    1990           0 :                               variableValue, aIsImportant, true);
    1991           0 :     *aChanged = true;
    1992             :   }
    1993             : 
    1994           0 :   mTempData.AssertInitialState();
    1995             : 
    1996           0 :   ReleaseScanner();
    1997           0 : }
    1998             : 
    1999             : void
    2000           3 : CSSParserImpl::ParseMediaList(const nsAString& aBuffer,
    2001             :                               nsIURI* aURI, // for error reporting
    2002             :                               uint32_t aLineNumber, // for error reporting
    2003             :                               nsMediaList* aMediaList)
    2004             : {
    2005             :   // XXX Are there cases where the caller wants to keep what it already
    2006             :   // has in case of parser error?  If GatherMedia ever changes to return
    2007             :   // a value other than true, we probably should avoid modifying aMediaList.
    2008           3 :   aMediaList->Clear();
    2009             : 
    2010             :   // fake base URI since media lists don't have URIs in them
    2011           6 :   nsCSSScanner scanner(aBuffer, aLineNumber);
    2012           6 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
    2013           3 :   InitScanner(scanner, reporter, aURI, aURI, nullptr);
    2014             : 
    2015           6 :   DebugOnly<bool> parsedOK = GatherMedia(aMediaList, false);
    2016           3 :   NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid "
    2017             :                          "trashing aMediaList");
    2018             : 
    2019           3 :   CLEAR_ERROR();
    2020           3 :   ReleaseScanner();
    2021           3 : }
    2022             : 
    2023             : //  <source-size-list> = <source-size>#?
    2024             : //  <source-size> = <media-condition>? <length>
    2025             : bool
    2026           0 : CSSParserImpl::ParseSourceSizeList(const nsAString& aBuffer,
    2027             :                                    nsIURI* aURI, // for error reporting
    2028             :                                    uint32_t aLineNumber, // for error reporting
    2029             :                                    InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
    2030             :                                    InfallibleTArray<nsCSSValue>& aValues)
    2031             : {
    2032           0 :   aQueries.Clear();
    2033           0 :   aValues.Clear();
    2034             : 
    2035             :   // fake base URI since media value lists don't have URIs in them
    2036           0 :   nsCSSScanner scanner(aBuffer, aLineNumber);
    2037           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
    2038           0 :   InitScanner(scanner, reporter, aURI, aURI, nullptr);
    2039             : 
    2040             :   // https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-sizes-attribute
    2041           0 :   bool hitEnd = false;
    2042           0 :   do {
    2043           0 :     bool hitError = false;
    2044             :     // Parse single <media-condition> <source-size-value>
    2045             :     do {
    2046           0 :       nsAutoPtr<nsMediaQuery> query;
    2047           0 :       nsCSSValue value;
    2048             : 
    2049             :       bool hitStop;
    2050           0 :       if (!ParseMediaQuery(eMediaQuerySingleCondition, getter_Transfers(query),
    2051             :                            &hitStop)) {
    2052           0 :         NS_ASSERTION(!hitStop, "should return true when hit stop");
    2053           0 :         hitError = true;
    2054           0 :         break;
    2055             :       }
    2056             : 
    2057           0 :       if (!query) {
    2058           0 :         REPORT_UNEXPECTED_EOF(PEParseSourceSizeListEOF);
    2059           0 :         NS_ASSERTION(hitStop,
    2060             :                      "should return hitStop or an error if returning no query");
    2061           0 :         hitError = true;
    2062           0 :         break;
    2063             :       }
    2064             : 
    2065           0 :       if (hitStop) {
    2066             :         // Empty conditions (e.g. just a bare value) should be treated as always
    2067             :         // matching (a query with no expressions fails to match, so a negated one
    2068             :         // always matches.)
    2069           0 :         query->SetNegated();
    2070             :       }
    2071             : 
    2072             :       // https://html.spec.whatwg.org/multipage/embedded-content.html#source-size-value
    2073             :       // Percentages are not allowed in a <source-size-value>, to avoid
    2074             :       // confusion about what it would be relative to.
    2075           0 :       if (ParseNonNegativeVariant(value, VARIANT_LCALC, nullptr) !=
    2076             :           CSSParseResult::Ok) {
    2077           0 :         hitError = true;
    2078           0 :         break;
    2079             :       }
    2080             : 
    2081           0 :       if (GetToken(true)) {
    2082           0 :         if (!mToken.IsSymbol(',')) {
    2083           0 :           REPORT_UNEXPECTED_TOKEN(PEParseSourceSizeListNotComma);
    2084           0 :           hitError = true;
    2085           0 :           break;
    2086             :         }
    2087             :       } else {
    2088           0 :         hitEnd = true;
    2089             :       }
    2090             : 
    2091           0 :       aQueries.AppendElement(query.forget());
    2092           0 :       aValues.AppendElement(value);
    2093             :     } while(0);
    2094             : 
    2095           0 :     if (hitError) {
    2096           0 :       OUTPUT_ERROR();
    2097             : 
    2098             :       // Per spec, we just skip the current entry if there was a parse error.
    2099             :       // Jumps to next entry of <source-size-list> which is a comma-separated list.
    2100           0 :       if (!SkipUntil(',')) {
    2101           0 :         hitEnd = true;
    2102             :       }
    2103             :     }
    2104           0 :   } while (!hitEnd);
    2105             : 
    2106           0 :   CLEAR_ERROR();
    2107           0 :   ReleaseScanner();
    2108             : 
    2109           0 :   return !aQueries.IsEmpty();
    2110             : }
    2111             : 
    2112             : bool
    2113         152 : CSSParserImpl::ParseColorString(const nsAString& aBuffer,
    2114             :                                 nsIURI* aURI, // for error reporting
    2115             :                                 uint32_t aLineNumber, // for error reporting
    2116             :                                 nsCSSValue& aValue,
    2117             :                                 bool aSuppressErrors /* false */)
    2118             : {
    2119         304 :   nsCSSScanner scanner(aBuffer, aLineNumber);
    2120         304 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
    2121         152 :   InitScanner(scanner, reporter, aURI, aURI, nullptr);
    2122             : 
    2123         304 :   nsAutoSuppressErrors suppressErrors(this, aSuppressErrors);
    2124             : 
    2125             :   // Parse a color, and check that there's nothing else after it.
    2126         304 :   bool colorParsed = ParseColor(aValue) == CSSParseResult::Ok &&
    2127         304 :                      !GetToken(true);
    2128             : 
    2129         152 :   if (aSuppressErrors) {
    2130           0 :     CLEAR_ERROR();
    2131             :   } else {
    2132         152 :     OUTPUT_ERROR();
    2133             :   }
    2134             : 
    2135         152 :   ReleaseScanner();
    2136         304 :   return colorParsed;
    2137             : }
    2138             : 
    2139             : bool
    2140           0 : CSSParserImpl::ParseMarginString(const nsAString& aBuffer,
    2141             :                                  nsIURI* aURI, // for error reporting
    2142             :                                  uint32_t aLineNumber, // for error reporting
    2143             :                                  nsCSSValue& aValue,
    2144             :                                  bool aSuppressErrors /* false */)
    2145             : {
    2146           0 :   nsCSSScanner scanner(aBuffer, aLineNumber);
    2147           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
    2148           0 :   InitScanner(scanner, reporter, aURI, aURI, nullptr);
    2149             : 
    2150           0 :   nsAutoSuppressErrors suppressErrors(this, aSuppressErrors);
    2151             : 
    2152             :   // Parse a margin, and check that there's nothing else after it.
    2153           0 :   bool marginParsed = ParseGroupedBoxProperty(VARIANT_LP, aValue, 0) && !GetToken(true);
    2154             : 
    2155           0 :   if (aSuppressErrors) {
    2156           0 :     CLEAR_ERROR();
    2157             :   } else {
    2158           0 :     OUTPUT_ERROR();
    2159             :   }
    2160             : 
    2161           0 :   ReleaseScanner();
    2162           0 :   return marginParsed;
    2163             : }
    2164             : 
    2165             : bool
    2166           0 : CSSParserImpl::ParseFontFamilyListString(const nsAString& aBuffer,
    2167             :                                          nsIURI* aURI, // for error reporting
    2168             :                                          uint32_t aLineNumber, // for error reporting
    2169             :                                          nsCSSValue& aValue)
    2170             : {
    2171           0 :   nsCSSScanner scanner(aBuffer, aLineNumber);
    2172           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
    2173           0 :   InitScanner(scanner, reporter, aURI, aURI, nullptr);
    2174             : 
    2175             :   // Parse a font family list, and check that there's nothing else after it.
    2176           0 :   bool familyParsed = ParseFamily(aValue) && !GetToken(true);
    2177           0 :   OUTPUT_ERROR();
    2178           0 :   ReleaseScanner();
    2179           0 :   return familyParsed;
    2180             : }
    2181             : 
    2182             : nsresult
    2183           7 : CSSParserImpl::ParseSelectorString(const nsAString& aSelectorString,
    2184             :                                    nsIURI* aURI, // for error reporting
    2185             :                                    uint32_t aLineNumber, // for error reporting
    2186             :                                    nsCSSSelectorList **aSelectorList)
    2187             : {
    2188          14 :   nsCSSScanner scanner(aSelectorString, aLineNumber);
    2189          14 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
    2190           7 :   InitScanner(scanner, reporter, aURI, aURI, nullptr);
    2191             : 
    2192           7 :   bool success = ParseSelectorList(*aSelectorList, char16_t(0));
    2193             : 
    2194             :   // We deliberately do not call OUTPUT_ERROR here, because all our
    2195             :   // callers map a failure return to a JS exception, and if that JS
    2196             :   // exception is caught, people don't want to see parser diagnostics;
    2197             :   // see e.g. http://bugs.jquery.com/ticket/7535
    2198             :   // It would be nice to be able to save the parser diagnostics into
    2199             :   // the exception, so that if it _isn't_ caught we can report them
    2200             :   // along with the usual uncaught-exception message, but we don't
    2201             :   // have any way to do that at present; see bug 631621.
    2202           7 :   CLEAR_ERROR();
    2203           7 :   ReleaseScanner();
    2204             : 
    2205           7 :   if (success) {
    2206           7 :     NS_ASSERTION(*aSelectorList, "Should have list!");
    2207           7 :     return NS_OK;
    2208             :   }
    2209             : 
    2210           0 :   NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
    2211             : 
    2212           0 :   return NS_ERROR_DOM_SYNTAX_ERR;
    2213             : }
    2214             : 
    2215             : 
    2216             : already_AddRefed<nsCSSKeyframeRule>
    2217           0 : CSSParserImpl::ParseKeyframeRule(const nsAString&  aBuffer,
    2218             :                                  nsIURI*             aURI,
    2219             :                                  uint32_t            aLineNumber)
    2220             : {
    2221           0 :   nsCSSScanner scanner(aBuffer, aLineNumber);
    2222           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
    2223           0 :   InitScanner(scanner, reporter, aURI, aURI, nullptr);
    2224             : 
    2225           0 :   RefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule();
    2226           0 :   if (GetToken(true)) {
    2227             :     // extra garbage at the end
    2228           0 :     result = nullptr;
    2229             :   }
    2230             : 
    2231           0 :   OUTPUT_ERROR();
    2232           0 :   ReleaseScanner();
    2233             : 
    2234           0 :   return result.forget();
    2235             : }
    2236             : 
    2237             : bool
    2238           0 : CSSParserImpl::ParseKeyframeSelectorString(const nsAString& aSelectorString,
    2239             :                                            nsIURI* aURI, // for error reporting
    2240             :                                            uint32_t aLineNumber, // for error reporting
    2241             :                                            InfallibleTArray<float>& aSelectorList)
    2242             : {
    2243           0 :   MOZ_ASSERT(aSelectorList.IsEmpty(), "given list should start empty");
    2244             : 
    2245           0 :   nsCSSScanner scanner(aSelectorString, aLineNumber);
    2246           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
    2247           0 :   InitScanner(scanner, reporter, aURI, aURI, nullptr);
    2248             : 
    2249           0 :   bool success = ParseKeyframeSelectorList(aSelectorList) &&
    2250             :                  // must consume entire input string
    2251           0 :                  !GetToken(true);
    2252             : 
    2253           0 :   OUTPUT_ERROR();
    2254           0 :   ReleaseScanner();
    2255             : 
    2256           0 :   if (success) {
    2257           0 :     NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty");
    2258             :   } else {
    2259           0 :     aSelectorList.Clear();
    2260             :   }
    2261             : 
    2262           0 :   return success;
    2263             : }
    2264             : 
    2265             : bool
    2266           0 : CSSParserImpl::EvaluateSupportsDeclaration(const nsAString& aProperty,
    2267             :                                            const nsAString& aValue,
    2268             :                                            nsIURI* aDocURL,
    2269             :                                            nsIURI* aBaseURL,
    2270             :                                            nsIPrincipal* aDocPrincipal)
    2271             : {
    2272           0 :   nsCSSPropertyID propID = LookupEnabledProperty(aProperty);
    2273           0 :   if (propID == eCSSProperty_UNKNOWN) {
    2274           0 :     return false;
    2275             :   }
    2276             : 
    2277           0 :   nsCSSScanner scanner(aValue, 0);
    2278           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
    2279           0 :   InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
    2280           0 :   nsAutoSuppressErrors suppressErrors(this);
    2281             : 
    2282             :   bool parsedOK;
    2283             : 
    2284           0 :   if (propID == eCSSPropertyExtra_variable) {
    2285           0 :     MOZ_ASSERT(Substring(aProperty, 0,
    2286             :                          CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
    2287             :     const nsDependentSubstring varName =
    2288           0 :       Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH);  // remove '--'
    2289             :     CSSVariableDeclarations::Type variableType;
    2290           0 :     nsString variableValue;
    2291           0 :     parsedOK = ParseVariableDeclaration(&variableType, variableValue) &&
    2292           0 :                !GetToken(true);
    2293             :   } else {
    2294           0 :     parsedOK = ParseProperty(propID) && !GetToken(true);
    2295             : 
    2296           0 :     mTempData.ClearProperty(propID);
    2297           0 :     mTempData.AssertInitialState();
    2298             :   }
    2299             : 
    2300           0 :   CLEAR_ERROR();
    2301           0 :   ReleaseScanner();
    2302             : 
    2303           0 :   return parsedOK;
    2304             : }
    2305             : 
    2306             : bool
    2307           0 : CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration,
    2308             :                                          nsIURI* aDocURL,
    2309             :                                          nsIURI* aBaseURL,
    2310             :                                          nsIPrincipal* aDocPrincipal,
    2311             :                                          SupportsParsingSettings aSettings)
    2312             : {
    2313           0 :   nsCSSScanner scanner(aDeclaration, 0);
    2314           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
    2315           0 :   InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
    2316           0 :   nsAutoSuppressErrors suppressErrors(this);
    2317             : 
    2318             :   bool conditionMet;
    2319             :   bool parsedOK;
    2320             : 
    2321           0 :   if (aSettings == SupportsParsingSettings::ImpliedParentheses) {
    2322           0 :     parsedOK = ParseSupportsConditionInParensInsideParens(conditionMet) && !GetToken(true);
    2323             :   } else {
    2324           0 :     parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true);
    2325             :   }
    2326             : 
    2327           0 :   CLEAR_ERROR();
    2328           0 :   ReleaseScanner();
    2329             : 
    2330           0 :   return parsedOK && conditionMet;
    2331             : }
    2332             : 
    2333             : bool
    2334         141 : CSSParserImpl::EnumerateVariableReferences(const nsAString& aPropertyValue,
    2335             :                                            VariableEnumFunc aFunc,
    2336             :                                            void* aData)
    2337             : {
    2338         282 :   nsCSSScanner scanner(aPropertyValue, 0);
    2339         282 :   css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
    2340         141 :   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
    2341         282 :   nsAutoSuppressErrors suppressErrors(this);
    2342             : 
    2343             :   CSSVariableDeclarations::Type type;
    2344             :   bool dropBackslash;
    2345         282 :   nsString impliedCharacters;
    2346         141 :   bool result = ParseValueWithVariables(&type, &dropBackslash,
    2347         282 :                                         impliedCharacters, aFunc, aData) &&
    2348         282 :                 !GetToken(true);
    2349             : 
    2350         141 :   ReleaseScanner();
    2351             : 
    2352         282 :   return result;
    2353             : }
    2354             : 
    2355             : static bool
    2356        3132 : SeparatorRequiredBetweenTokens(nsCSSTokenSerializationType aToken1,
    2357             :                                nsCSSTokenSerializationType aToken2)
    2358             : {
    2359             :   // The two lines marked with (*) do not correspond to entries in
    2360             :   // the table in the css-syntax spec but which we need to handle,
    2361             :   // as we treat them as whole tokens.
    2362        3132 :   switch (aToken1) {
    2363             :     case eCSSTokenSerialization_Ident:
    2364           0 :       return aToken2 == eCSSTokenSerialization_Ident ||
    2365           0 :              aToken2 == eCSSTokenSerialization_Function ||
    2366           0 :              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
    2367           0 :              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
    2368           0 :              aToken2 == eCSSTokenSerialization_Number ||
    2369           0 :              aToken2 == eCSSTokenSerialization_Percentage ||
    2370           0 :              aToken2 == eCSSTokenSerialization_Dimension ||
    2371           0 :              aToken2 == eCSSTokenSerialization_URange ||
    2372           0 :              aToken2 == eCSSTokenSerialization_CDC ||
    2373           0 :              aToken2 == eCSSTokenSerialization_Symbol_OpenParen;
    2374             :     case eCSSTokenSerialization_AtKeyword_or_Hash:
    2375             :     case eCSSTokenSerialization_Dimension:
    2376         953 :       return aToken2 == eCSSTokenSerialization_Ident ||
    2377         953 :              aToken2 == eCSSTokenSerialization_Function ||
    2378         953 :              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
    2379         953 :              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
    2380         953 :              aToken2 == eCSSTokenSerialization_Number ||
    2381         953 :              aToken2 == eCSSTokenSerialization_Percentage ||
    2382         953 :              aToken2 == eCSSTokenSerialization_Dimension ||
    2383        1906 :              aToken2 == eCSSTokenSerialization_URange ||
    2384         953 :              aToken2 == eCSSTokenSerialization_CDC;
    2385             :     case eCSSTokenSerialization_Symbol_Hash:
    2386             :     case eCSSTokenSerialization_Symbol_Minus:
    2387           0 :       return aToken2 == eCSSTokenSerialization_Ident ||
    2388           0 :              aToken2 == eCSSTokenSerialization_Function ||
    2389           0 :              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
    2390           0 :              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
    2391           0 :              aToken2 == eCSSTokenSerialization_Number ||
    2392           0 :              aToken2 == eCSSTokenSerialization_Percentage ||
    2393           0 :              aToken2 == eCSSTokenSerialization_Dimension ||
    2394           0 :              aToken2 == eCSSTokenSerialization_URange;
    2395             :     case eCSSTokenSerialization_Number:
    2396           0 :       return aToken2 == eCSSTokenSerialization_Ident ||
    2397           0 :              aToken2 == eCSSTokenSerialization_Function ||
    2398           0 :              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
    2399           0 :              aToken2 == eCSSTokenSerialization_Number ||
    2400           0 :              aToken2 == eCSSTokenSerialization_Percentage ||
    2401           0 :              aToken2 == eCSSTokenSerialization_Dimension ||
    2402           0 :              aToken2 == eCSSTokenSerialization_URange;
    2403             :     case eCSSTokenSerialization_Symbol_At:
    2404           0 :       return aToken2 == eCSSTokenSerialization_Ident ||
    2405           0 :              aToken2 == eCSSTokenSerialization_Function ||
    2406           0 :              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
    2407           0 :              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
    2408           0 :              aToken2 == eCSSTokenSerialization_URange;
    2409             :     case eCSSTokenSerialization_URange:
    2410           0 :       return aToken2 == eCSSTokenSerialization_Ident ||
    2411           0 :              aToken2 == eCSSTokenSerialization_Function ||
    2412           0 :              aToken2 == eCSSTokenSerialization_Number ||
    2413           0 :              aToken2 == eCSSTokenSerialization_Percentage ||
    2414           0 :              aToken2 == eCSSTokenSerialization_Dimension ||
    2415           0 :              aToken2 == eCSSTokenSerialization_Symbol_Question;
    2416             :     case eCSSTokenSerialization_Symbol_Dot_or_Plus:
    2417           0 :       return aToken2 == eCSSTokenSerialization_Number ||
    2418           0 :              aToken2 == eCSSTokenSerialization_Percentage ||
    2419           0 :              aToken2 == eCSSTokenSerialization_Dimension;
    2420             :     case eCSSTokenSerialization_Symbol_Assorted:
    2421             :     case eCSSTokenSerialization_Symbol_Asterisk:
    2422           0 :       return aToken2 == eCSSTokenSerialization_Symbol_Equals;
    2423             :     case eCSSTokenSerialization_Symbol_Bar:
    2424           0 :       return aToken2 == eCSSTokenSerialization_Symbol_Equals ||
    2425           0 :              aToken2 == eCSSTokenSerialization_Symbol_Bar ||
    2426           0 :              aToken2 == eCSSTokenSerialization_DashMatch;              // (*)
    2427             :     case eCSSTokenSerialization_Symbol_Slash:
    2428           0 :       return aToken2 == eCSSTokenSerialization_Symbol_Asterisk ||
    2429           0 :              aToken2 == eCSSTokenSerialization_ContainsMatch;          // (*)
    2430             :     default:
    2431        2179 :       MOZ_ASSERT(aToken1 == eCSSTokenSerialization_Nothing ||
    2432             :                  aToken1 == eCSSTokenSerialization_Whitespace ||
    2433             :                  aToken1 == eCSSTokenSerialization_Percentage ||
    2434             :                  aToken1 == eCSSTokenSerialization_URL_or_BadURL ||
    2435             :                  aToken1 == eCSSTokenSerialization_Function ||
    2436             :                  aToken1 == eCSSTokenSerialization_CDC ||
    2437             :                  aToken1 == eCSSTokenSerialization_Symbol_OpenParen ||
    2438             :                  aToken1 == eCSSTokenSerialization_Symbol_Question ||
    2439             :                  aToken1 == eCSSTokenSerialization_Symbol_Assorted ||
    2440             :                  aToken1 == eCSSTokenSerialization_Symbol_Asterisk ||
    2441             :                  aToken1 == eCSSTokenSerialization_Symbol_Equals ||
    2442             :                  aToken1 == eCSSTokenSerialization_Symbol_Bar ||
    2443             :                  aToken1 == eCSSTokenSerialization_Symbol_Slash ||
    2444             :                  aToken1 == eCSSTokenSerialization_Other,
    2445             :                  "unexpected nsCSSTokenSerializationType value");
    2446        2179 :       return false;
    2447             :   }
    2448             : }
    2449             : 
    2450             : /**
    2451             :  * Appends aValue to aResult, possibly inserting an empty CSS
    2452             :  * comment between the two to ensure that tokens from both strings
    2453             :  * remain separated.
    2454             :  */
    2455             : static void
    2456        2108 : AppendTokens(nsAString& aResult,
    2457             :              nsCSSTokenSerializationType& aResultFirstToken,
    2458             :              nsCSSTokenSerializationType& aResultLastToken,
    2459             :              nsCSSTokenSerializationType aValueFirstToken,
    2460             :              nsCSSTokenSerializationType aValueLastToken,
    2461             :              const nsAString& aValue)
    2462             : {
    2463        2108 :   if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
    2464           0 :     aResult.AppendLiteral("/**/");
    2465             :   }
    2466        2108 :   aResult.Append(aValue);
    2467        2108 :   if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
    2468         894 :     aResultFirstToken = aValueFirstToken;
    2469             :   }
    2470        2108 :   if (aValueLastToken != eCSSTokenSerialization_Nothing) {
    2471        2108 :     aResultLastToken = aValueLastToken;
    2472             :   }
    2473        2108 : }
    2474             : 
    2475             : /**
    2476             :  * Stops the given scanner recording, and appends the recorded result
    2477             :  * to aResult, possibly inserting an empty CSS comment between the two to
    2478             :  * ensure that tokens from both strings remain separated.
    2479             :  */
    2480             : static void
    2481        1024 : StopRecordingAndAppendTokens(nsString& aResult,
    2482             :                              nsCSSTokenSerializationType& aResultFirstToken,
    2483             :                              nsCSSTokenSerializationType& aResultLastToken,
    2484             :                              nsCSSTokenSerializationType aValueFirstToken,
    2485             :                              nsCSSTokenSerializationType aValueLastToken,
    2486             :                              nsCSSScanner* aScanner)
    2487             : {
    2488        1024 :   if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
    2489           0 :     aResult.AppendLiteral("/**/");
    2490             :   }
    2491        1024 :   aScanner->StopRecording(aResult);
    2492        1024 :   if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
    2493         130 :     aResultFirstToken = aValueFirstToken;
    2494             :   }
    2495        1024 :   if (aValueLastToken != eCSSTokenSerialization_Nothing) {
    2496         442 :     aResultLastToken = aValueLastToken;
    2497             :   }
    2498        1024 : }
    2499             : 
    2500             : bool
    2501        1024 : CSSParserImpl::ResolveValueWithVariableReferencesRec(
    2502             :                                      nsString& aResult,
    2503             :                                      nsCSSTokenSerializationType& aResultFirstToken,
    2504             :                                      nsCSSTokenSerializationType& aResultLastToken,
    2505             :                                      const CSSVariableValues* aVariables)
    2506             : {
    2507             :   // This function assumes we are already recording, and will leave the scanner
    2508             :   // recording when it returns.
    2509        1024 :   MOZ_ASSERT(mScanner->IsRecording());
    2510        1024 :   MOZ_ASSERT(aResult.IsEmpty());
    2511             : 
    2512             :   // Stack of closing characters for currently open constructs.
    2513        2048 :   AutoTArray<char16_t, 16> stack;
    2514             : 
    2515             :   // The resolved value for this ResolveValueWithVariableReferencesRec call.
    2516        2048 :   nsString value;
    2517             : 
    2518             :   // The length of the scanner's recording before the currently parsed token.
    2519             :   // This is used so that when we encounter a "var(" token, we can strip
    2520             :   // it off the end of the recording, regardless of how long the token was.
    2521             :   // (With escapes, it could be longer than four characters.)
    2522        1024 :   uint32_t lengthBeforeVar = 0;
    2523             : 
    2524             :   // Tracking the type of token that appears at the start and end of |value|
    2525             :   // and that appears at the start and end of the scanner recording.  These are
    2526             :   // used to determine whether we need to insert "/**/" when pasting token
    2527             :   // streams together.
    2528        1024 :   nsCSSTokenSerializationType valueFirstToken = eCSSTokenSerialization_Nothing,
    2529        1024 :                               valueLastToken  = eCSSTokenSerialization_Nothing,
    2530        1024 :                               recFirstToken   = eCSSTokenSerialization_Nothing,
    2531        1024 :                               recLastToken    = eCSSTokenSerialization_Nothing;
    2532             : 
    2533             : #define UPDATE_RECORDING_TOKENS(type)                    \
    2534             :   if (recFirstToken == eCSSTokenSerialization_Nothing) { \
    2535             :     recFirstToken = type;                                \
    2536             :   }                                                      \
    2537             :   recLastToken = type;
    2538             : 
    2539       11932 :   while (GetToken(false)) {
    2540        5454 :     switch (mToken.mType) {
    2541             :       case eCSSToken_Symbol: {
    2542         577 :         nsCSSTokenSerializationType type = eCSSTokenSerialization_Other;
    2543         577 :         if (mToken.mSymbol == '(') {
    2544          13 :           stack.AppendElement(')');
    2545          13 :           type = eCSSTokenSerialization_Symbol_OpenParen;
    2546         564 :         } else if (mToken.mSymbol == '[') {
    2547           0 :           stack.AppendElement(']');
    2548         564 :         } else if (mToken.mSymbol == '{') {
    2549           0 :           stack.AppendElement('}');
    2550         564 :         } else if (mToken.mSymbol == ';') {
    2551           0 :           if (stack.IsEmpty()) {
    2552             :             // A ';' that is at the top level of the value or at the top level
    2553             :             // of a variable reference's fallback is invalid.
    2554           0 :             return false;
    2555             :           }
    2556         564 :         } else if (mToken.mSymbol == '!') {
    2557           0 :           if (stack.IsEmpty()) {
    2558             :             // An '!' that is at the top level of the value or at the top level
    2559             :             // of a variable reference's fallback is invalid.
    2560           0 :             return false;
    2561             :           }
    2562         788 :         } else if (mToken.mSymbol == ')' &&
    2563         224 :                    stack.IsEmpty()) {
    2564             :           // We're closing a "var(".
    2565           0 :           nsString finalTokens;
    2566           0 :           mScanner->StopRecording(finalTokens);
    2567           0 :           MOZ_ASSERT(finalTokens[finalTokens.Length() - 1] == ')');
    2568           0 :           finalTokens.Truncate(finalTokens.Length() - 1);
    2569           0 :           aResult.Append(value);
    2570             : 
    2571           0 :           AppendTokens(aResult, valueFirstToken, valueLastToken,
    2572           0 :                        recFirstToken, recLastToken, finalTokens);
    2573             : 
    2574           0 :           mScanner->StartRecording();
    2575           0 :           UngetToken();
    2576           0 :           aResultFirstToken = valueFirstToken;
    2577           0 :           aResultLastToken = valueLastToken;
    2578           0 :           return true;
    2579         904 :         } else if (mToken.mSymbol == ')' ||
    2580         680 :                    mToken.mSymbol == ']' ||
    2581         340 :                    mToken.mSymbol == '}') {
    2582         448 :           if (stack.IsEmpty() ||
    2583         224 :               stack.LastElement() != mToken.mSymbol) {
    2584             :             // A mismatched closing bracket is invalid.
    2585           0 :             return false;
    2586             :           }
    2587         224 :           stack.TruncateLength(stack.Length() - 1);
    2588         340 :         } else if (mToken.mSymbol == '#') {
    2589           0 :           type = eCSSTokenSerialization_Symbol_Hash;
    2590         340 :         } else if (mToken.mSymbol == '@') {
    2591           0 :           type = eCSSTokenSerialization_Symbol_At;
    2592         680 :         } else if (mToken.mSymbol == '.' ||
    2593         340 :                    mToken.mSymbol == '+') {
    2594         109 :           type = eCSSTokenSerialization_Symbol_Dot_or_Plus;
    2595         231 :         } else if (mToken.mSymbol == '-') {
    2596          28 :           type = eCSSTokenSerialization_Symbol_Minus;
    2597         203 :         } else if (mToken.mSymbol == '?') {
    2598           0 :           type = eCSSTokenSerialization_Symbol_Question;
    2599         406 :         } else if (mToken.mSymbol == '$' ||
    2600         406 :                    mToken.mSymbol == '^' ||
    2601         203 :                    mToken.mSymbol == '~') {
    2602           0 :           type = eCSSTokenSerialization_Symbol_Assorted;
    2603         203 :         } else if (mToken.mSymbol == '=') {
    2604           0 :           type = eCSSTokenSerialization_Symbol_Equals;
    2605         203 :         } else if (mToken.mSymbol == '|') {
    2606           0 :           type = eCSSTokenSerialization_Symbol_Bar;
    2607         203 :         } else if (mToken.mSymbol == '/') {
    2608          13 :           type = eCSSTokenSerialization_Symbol_Slash;
    2609         190 :         } else if (mToken.mSymbol == '*') {
    2610          98 :           type = eCSSTokenSerialization_Symbol_Asterisk;
    2611             :         }
    2612         577 :         UPDATE_RECORDING_TOKENS(type);
    2613         577 :         break;
    2614             :       }
    2615             : 
    2616             :       case eCSSToken_Function:
    2617        1265 :         if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
    2618             :           // Save the tokens before the "var(" to our resolved value.
    2619        2108 :           nsString recording;
    2620        1054 :           mScanner->StopRecording(recording);
    2621        1054 :           recording.Truncate(lengthBeforeVar);
    2622             :           AppendTokens(value, valueFirstToken, valueLastToken,
    2623        1054 :                        recFirstToken, recLastToken, recording);
    2624        1054 :           recFirstToken = eCSSTokenSerialization_Nothing;
    2625        1054 :           recLastToken = eCSSTokenSerialization_Nothing;
    2626             : 
    2627        3162 :           if (!GetToken(true) ||
    2628        2108 :               mToken.mType != eCSSToken_Ident ||
    2629        1054 :               !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
    2630             :             // "var(" must be followed by an identifier, and it must be a
    2631             :             // custom property name.
    2632           0 :             return false;
    2633             :           }
    2634             : 
    2635             :           // Turn the custom property name into a variable name by removing the
    2636             :           // '--' prefix.
    2637        1054 :           MOZ_ASSERT(Substring(mToken.mIdent, 0,
    2638             :                                CSS_CUSTOM_NAME_PREFIX_LENGTH).
    2639             :                        EqualsLiteral("--"));
    2640             :           nsDependentString variableName(mToken.mIdent,
    2641        2108 :                                          CSS_CUSTOM_NAME_PREFIX_LENGTH);
    2642             : 
    2643             :           // Get the value of the identified variable.  Note that we
    2644             :           // check if the variable value is the empty string, as that means
    2645             :           // that the variable was invalid at computed value time due to
    2646             :           // unresolveable variable references or cycles.
    2647        2108 :           nsString variableValue;
    2648             :           nsCSSTokenSerializationType varFirstToken, varLastToken;
    2649        1054 :           bool valid = aVariables->Get(variableName, variableValue,
    2650        2108 :                                        varFirstToken, varLastToken) &&
    2651        2108 :                        !variableValue.IsEmpty();
    2652             : 
    2653        2108 :           if (!GetToken(true) ||
    2654        1054 :               mToken.IsSymbol(')')) {
    2655        1054 :             mScanner->StartRecording();
    2656        1054 :             if (!valid) {
    2657             :               // Invalid variable with no fallback.
    2658           0 :               return false;
    2659             :             }
    2660             :             // Valid variable with no fallback.
    2661             :             AppendTokens(value, valueFirstToken, valueLastToken,
    2662        1054 :                          varFirstToken, varLastToken, variableValue);
    2663           0 :           } else if (mToken.IsSymbol(',')) {
    2664           0 :             mScanner->StartRecording();
    2665           0 :             if (!GetToken(false) ||
    2666           0 :                 mToken.IsSymbol(')')) {
    2667             :               // Comma must be followed by at least one fallback token.
    2668           0 :               return false;
    2669             :             }
    2670           0 :             UngetToken();
    2671           0 :             if (valid) {
    2672             :               // Valid variable with ignored fallback.
    2673           0 :               mScanner->StopRecording();
    2674             :               AppendTokens(value, valueFirstToken, valueLastToken,
    2675           0 :                            varFirstToken, varLastToken, variableValue);
    2676           0 :               bool ok = SkipBalancedContentUntil(')');
    2677           0 :               mScanner->StartRecording();
    2678           0 :               if (!ok) {
    2679           0 :                 return false;
    2680             :               }
    2681             :             } else {
    2682           0 :               nsString fallback;
    2683           0 :               if (!ResolveValueWithVariableReferencesRec(fallback,
    2684             :                                                          varFirstToken,
    2685             :                                                          varLastToken,
    2686             :                                                          aVariables)) {
    2687             :                 // Fallback value had invalid tokens or an invalid variable reference
    2688             :                 // that itself had no fallback.
    2689           0 :                 return false;
    2690             :               }
    2691             :               AppendTokens(value, valueFirstToken, valueLastToken,
    2692           0 :                            varFirstToken, varLastToken, fallback);
    2693             :               // Now we're either at the pushed back ')' that finished the
    2694             :               // fallback or at EOF.
    2695           0 :               DebugOnly<bool> gotToken = GetToken(false);
    2696           0 :               MOZ_ASSERT(!gotToken || mToken.IsSymbol(')'));
    2697             :             }
    2698             :           } else {
    2699             :             // Expected ',' or ')' after the variable name.
    2700           0 :             mScanner->StartRecording();
    2701           0 :             return false;
    2702             :           }
    2703             :         } else {
    2704         211 :           stack.AppendElement(')');
    2705         211 :           UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function);
    2706             :         }
    2707        1265 :         break;
    2708             : 
    2709             :       case eCSSToken_Bad_String:
    2710             :       case eCSSToken_Bad_URL:
    2711           0 :         return false;
    2712             : 
    2713             :       case eCSSToken_Whitespace:
    2714        2421 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace);
    2715        2421 :         break;
    2716             : 
    2717             :       case eCSSToken_AtKeyword:
    2718             :       case eCSSToken_Hash:
    2719           6 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash);
    2720           6 :         break;
    2721             : 
    2722             :       case eCSSToken_Number:
    2723         514 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number);
    2724         514 :         break;
    2725             : 
    2726             :       case eCSSToken_Dimension:
    2727         268 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension);
    2728         268 :         break;
    2729             : 
    2730             :       case eCSSToken_Ident:
    2731         323 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident);
    2732         323 :         break;
    2733             : 
    2734             :       case eCSSToken_Percentage:
    2735          64 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage);
    2736          64 :         break;
    2737             : 
    2738             :       case eCSSToken_URange:
    2739           0 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange);
    2740           0 :         break;
    2741             : 
    2742             :       case eCSSToken_URL:
    2743           4 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL);
    2744           4 :         break;
    2745             : 
    2746             :       case eCSSToken_HTMLComment:
    2747           0 :         if (mToken.mIdent[0] == '-') {
    2748           0 :           UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC);
    2749             :         } else {
    2750           0 :           UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
    2751             :         }
    2752           0 :         break;
    2753             : 
    2754             :       case eCSSToken_Dashmatch:
    2755           0 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch);
    2756           0 :         break;
    2757             : 
    2758             :       case eCSSToken_Containsmatch:
    2759           0 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch);
    2760           0 :         break;
    2761             : 
    2762             :       default:
    2763           0 :         MOZ_FALLTHROUGH_ASSERT("unexpected token type");
    2764             :       case eCSSToken_ID:
    2765             :       case eCSSToken_String:
    2766             :       case eCSSToken_Includes:
    2767             :       case eCSSToken_Beginsmatch:
    2768             :       case eCSSToken_Endsmatch:
    2769          12 :         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
    2770          12 :         break;
    2771             :     }
    2772             : 
    2773        5454 :     lengthBeforeVar = mScanner->RecordingLength();
    2774             :   }
    2775             : 
    2776             : #undef UPDATE_RECORDING_TOKENS
    2777             : 
    2778        1024 :   aResult.Append(value);
    2779        1024 :   StopRecordingAndAppendTokens(aResult, valueFirstToken, valueLastToken,
    2780        1024 :                                recFirstToken, recLastToken, mScanner);
    2781             : 
    2782             :   // Append any implicitly closed brackets.
    2783        1024 :   if (!stack.IsEmpty()) {
    2784           0 :     do {
    2785           0 :       aResult.Append(stack.LastElement());
    2786           0 :       stack.TruncateLength(stack.Length() - 1);
    2787           0 :     } while (!stack.IsEmpty());
    2788           0 :     valueLastToken = eCSSTokenSerialization_Other;
    2789             :   }
    2790             : 
    2791        1024 :   mScanner->StartRecording();
    2792        1024 :   aResultFirstToken = valueFirstToken;
    2793        1024 :   aResultLastToken = valueLastToken;
    2794        1024 :   return true;
    2795             : }
    2796             : 
    2797             : bool
    2798        1024 : CSSParserImpl::ResolveValueWithVariableReferences(
    2799             :                                         const CSSVariableValues* aVariables,
    2800             :                                         nsString& aResult,
    2801             :                                         nsCSSTokenSerializationType& aFirstToken,
    2802             :                                         nsCSSTokenSerializationType& aLastToken)
    2803             : {
    2804        1024 :   aResult.Truncate(0);
    2805             : 
    2806             :   // Start recording before we read the first token.
    2807        1024 :   mScanner->StartRecording();
    2808             : 
    2809        1024 :   if (!GetToken(false)) {
    2810             :     // Value was empty since we reached EOF.
    2811           0 :     mScanner->StopRecording();
    2812           0 :     return false;
    2813             :   }
    2814             : 
    2815        1024 :   UngetToken();
    2816             : 
    2817        2048 :   nsString value;
    2818             :   nsCSSTokenSerializationType firstToken, lastToken;
    2819        2048 :   bool ok = ResolveValueWithVariableReferencesRec(value, firstToken, lastToken, aVariables) &&
    2820        2048 :             !GetToken(true);
    2821             : 
    2822        1024 :   mScanner->StopRecording();
    2823             : 
    2824        1024 :   if (ok) {
    2825        1024 :     aResult = value;
    2826        1024 :     aFirstToken = firstToken;
    2827        1024 :     aLastToken = lastToken;
    2828             :   }
    2829        1024 :   return ok;
    2830             : }
    2831             : 
    2832             : bool
    2833         141 : CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue,
    2834             :                                     const CSSVariableValues* aVariables,
    2835             :                                     nsString& aResult,
    2836             :                                     nsCSSTokenSerializationType& aFirstToken,
    2837             :                                     nsCSSTokenSerializationType& aLastToken)
    2838             : {
    2839         282 :   nsCSSScanner scanner(aPropertyValue, 0);
    2840             : 
    2841             :   // At this point, we know that aPropertyValue is syntactically correct
    2842             :   // for a token stream that has variable references.  We also won't be
    2843             :   // interpreting any of the stream as we parse it, apart from expanding
    2844             :   // var() references, so we don't need a base URL etc. or any useful
    2845             :   // error reporting.
    2846         282 :   css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
    2847         141 :   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
    2848             : 
    2849             :   bool valid = ResolveValueWithVariableReferences(aVariables, aResult,
    2850         141 :                                                   aFirstToken, aLastToken);
    2851             : 
    2852         141 :   ReleaseScanner();
    2853         282 :   return valid;
    2854             : }
    2855             : 
    2856             : void
    2857         883 : CSSParserImpl::ParsePropertyWithVariableReferences(
    2858             :                                             nsCSSPropertyID aPropertyID,
    2859             :                                             nsCSSPropertyID aShorthandPropertyID,
    2860             :                                             const nsAString& aValue,
    2861             :                                             const CSSVariableValues* aVariables,
    2862             :                                             nsRuleData* aRuleData,
    2863             :                                             nsIURI* aDocURL,
    2864             :                                             nsIURI* aBaseURL,
    2865             :                                             nsIPrincipal* aDocPrincipal,
    2866             :                                             CSSStyleSheet* aSheet,
    2867             :                                             uint32_t aLineNumber,
    2868             :                                             uint32_t aLineOffset)
    2869             : {
    2870         883 :   mTempData.AssertInitialState();
    2871             : 
    2872             :   bool valid;
    2873        1766 :   nsString expandedValue;
    2874             : 
    2875             :   // Resolve any variable references in the property value.
    2876             :   {
    2877        1766 :     nsCSSScanner scanner(aValue, 0);
    2878        1766 :     css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
    2879         883 :     InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
    2880             : 
    2881             :     nsCSSTokenSerializationType firstToken, lastToken;
    2882             :     valid = ResolveValueWithVariableReferences(aVariables, expandedValue,
    2883         883 :                                                firstToken, lastToken);
    2884         883 :     if (!valid) {
    2885           0 :       NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropertyID));
    2886           0 :       REPORT_UNEXPECTED(PEInvalidVariableReference);
    2887           0 :       REPORT_UNEXPECTED_P(PEValueParsingError, propName);
    2888           0 :       if (nsCSSProps::IsInherited(aPropertyID)) {
    2889           0 :         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
    2890             :       } else {
    2891           0 :         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
    2892             :       }
    2893           0 :       OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
    2894             :     }
    2895         883 :     ReleaseScanner();
    2896             :   }
    2897             : 
    2898             :   nsCSSPropertyID propertyToParse =
    2899         883 :     aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID :
    2900         883 :                                                    aPropertyID;
    2901             : 
    2902             :   // Parse the property with that resolved value.
    2903         883 :   if (valid) {
    2904        1766 :     nsCSSScanner scanner(expandedValue, 0);
    2905        1766 :     css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
    2906         883 :     InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
    2907         883 :     valid = ParseProperty(propertyToParse);
    2908         883 :     if (valid && GetToken(true)) {
    2909           0 :       REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
    2910           0 :       valid = false;
    2911             :     }
    2912         883 :     if (!valid) {
    2913             :       NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(
    2914           0 :                                                               propertyToParse));
    2915           0 :       REPORT_UNEXPECTED_P_V(PEValueWithVariablesParsingErrorInValue,
    2916             :                             propName, expandedValue);
    2917           0 :       if (nsCSSProps::IsInherited(aPropertyID)) {
    2918           0 :         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
    2919             :       } else {
    2920           0 :         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
    2921             :       }
    2922           0 :       OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
    2923             :     }
    2924         883 :     ReleaseScanner();
    2925             :   }
    2926             : 
    2927             :   // If the property could not be parsed with the resolved value, then we
    2928             :   // treat it as if the value were 'initial' or 'inherit', depending on whether
    2929             :   // the property is an inherited property.
    2930         883 :   if (!valid) {
    2931           0 :     nsCSSValue defaultValue;
    2932           0 :     if (nsCSSProps::IsInherited(aPropertyID)) {
    2933           0 :       defaultValue.SetInheritValue();
    2934             :     } else {
    2935           0 :       defaultValue.SetInitialValue();
    2936             :     }
    2937           0 :     mTempData.AddLonghandProperty(aPropertyID, defaultValue);
    2938             :   }
    2939             : 
    2940             :   // Copy the property value into the rule data.
    2941         883 :   mTempData.MapRuleInfoInto(aPropertyID, aRuleData);
    2942             : 
    2943         883 :   mTempData.ClearProperty(propertyToParse);
    2944         883 :   mTempData.AssertInitialState();
    2945         883 : }
    2946             : 
    2947             : already_AddRefed<nsIAtom>
    2948           0 : CSSParserImpl::ParseCounterStyleName(const nsAString& aBuffer, nsIURI* aURL)
    2949             : {
    2950           0 :   nsCSSScanner scanner(aBuffer, 0);
    2951           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURL);
    2952           0 :   InitScanner(scanner, reporter, aURL, aURL, nullptr);
    2953             : 
    2954           0 :   nsCOMPtr<nsIAtom> name = ParseCounterStyleName(true);
    2955           0 :   bool success = name && !GetToken(true);
    2956             : 
    2957           0 :   OUTPUT_ERROR();
    2958           0 :   ReleaseScanner();
    2959             : 
    2960           0 :   return success ? name.forget() : nullptr;
    2961             : }
    2962             : 
    2963             : bool
    2964           0 : CSSParserImpl::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
    2965             :                                       const nsAString& aBuffer,
    2966             :                                       nsIURI* aSheetURL,
    2967             :                                       nsIURI* aBaseURL,
    2968             :                                       nsIPrincipal* aSheetPrincipal,
    2969             :                                       nsCSSValue& aValue)
    2970             : {
    2971           0 :   nsCSSScanner scanner(aBuffer, 0);
    2972           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
    2973           0 :   InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
    2974             : 
    2975           0 :   bool success = ParseCounterDescriptorValue(aDescID, aValue) &&
    2976           0 :                  !GetToken(true);
    2977             : 
    2978           0 :   OUTPUT_ERROR();
    2979           0 :   ReleaseScanner();
    2980             : 
    2981           0 :   return success;
    2982             : }
    2983             : 
    2984             : bool
    2985           0 : CSSParserImpl::ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
    2986             :                                        const nsAString& aBuffer,
    2987             :                                        nsIURI* aSheetURL,
    2988             :                                        nsIURI* aBaseURL,
    2989             :                                        nsIPrincipal* aSheetPrincipal,
    2990             :                                        nsCSSValue& aValue)
    2991             : {
    2992           0 :   nsCSSScanner scanner(aBuffer, 0);
    2993           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
    2994           0 :   InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
    2995             : 
    2996           0 :   bool success = ParseFontDescriptorValue(aDescID, aValue) &&
    2997           0 :                  !GetToken(true);
    2998             : 
    2999           0 :   OUTPUT_ERROR();
    3000           0 :   ReleaseScanner();
    3001             : 
    3002           0 :   return success;
    3003             : }
    3004             : 
    3005             : //----------------------------------------------------------------------
    3006             : 
    3007             : bool
    3008      177452 : CSSParserImpl::GetToken(bool aSkipWS)
    3009             : {
    3010      177452 :   if (mHavePushBack) {
    3011       76929 :     mHavePushBack = false;
    3012       76929 :     if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) {
    3013       76911 :       return true;
    3014             :     }
    3015             :   }
    3016      100541 :   return mScanner->Next(mToken, aSkipWS ?
    3017             :                         eCSSScannerExclude_WhitespaceAndComments :
    3018      100541 :                         eCSSScannerExclude_Comments);
    3019             : }
    3020             : 
    3021             : void
    3022       77160 : CSSParserImpl::UngetToken()
    3023             : {
    3024       77160 :   NS_PRECONDITION(!mHavePushBack, "double pushback");
    3025       77160 :   mHavePushBack = true;
    3026       77160 : }
    3027             : 
    3028             : bool
    3029       22370 : CSSParserImpl::GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum)
    3030             : {
    3031             :   // Peek at next token so that mScanner updates line and column vals
    3032       22370 :   if (!GetToken(aSkipWS)) {
    3033         454 :     return false;
    3034             :   }
    3035       21916 :   UngetToken();
    3036             :   // The scanner uses one-indexing for line numbers but zero-indexing
    3037             :   // for column numbers.
    3038       21916 :   *linenum = mScanner->GetLineNumber();
    3039       21916 :   *colnum = 1 + mScanner->GetColumnNumber();
    3040       21916 :   return true;
    3041             : }
    3042             : 
    3043             : bool
    3044       24841 : CSSParserImpl::ExpectSymbol(char16_t aSymbol,
    3045             :                             bool aSkipWS)
    3046             : {
    3047       24841 :   if (!GetToken(aSkipWS)) {
    3048             :     // CSS2.1 specifies that all "open constructs" are to be closed at
    3049             :     // EOF.  It simplifies higher layers if we claim to have found an
    3050             :     // ), ], }, or ; if we encounter EOF while looking for one of them.
    3051             :     // Do still issue a diagnostic, to aid debugging.
    3052         372 :     if (aSymbol == ')' || aSymbol == ']' ||
    3053         372 :         aSymbol == '}' || aSymbol == ';') {
    3054           0 :       REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
    3055           0 :       return true;
    3056             :     }
    3057             :     else
    3058         372 :       return false;
    3059             :   }
    3060       24469 :   if (mToken.IsSymbol(aSymbol)) {
    3061       16687 :     return true;
    3062             :   }
    3063        7782 :   UngetToken();
    3064        7782 :   return false;
    3065             : }
    3066             : 
    3067             : // Checks to see if we're at the end of a property.  If an error occurs during
    3068             : // the check, does not signal a parse error.
    3069             : bool
    3070        8295 : CSSParserImpl::CheckEndProperty()
    3071             : {
    3072        8295 :   if (!GetToken(true)) {
    3073        1057 :     return true; // properties may end with eof
    3074             :   }
    3075       13889 :   if ((eCSSToken_Symbol == mToken.mType) &&
    3076        7140 :       ((';' == mToken.mSymbol) ||
    3077         496 :        ('!' == mToken.mSymbol) ||
    3078           7 :        ('}' == mToken.mSymbol) ||
    3079           0 :        (')' == mToken.mSymbol))) {
    3080             :     // XXX need to verify that ! is only followed by "important [;|}]
    3081             :     // XXX this requires a multi-token pushback buffer
    3082        6651 :     UngetToken();
    3083        6651 :     return true;
    3084             :   }
    3085         587 :   UngetToken();
    3086         587 :   return false;
    3087             : }
    3088             : 
    3089             : // Checks if we're at the end of a property, raising an error if we're not.
    3090             : bool
    3091        7498 : CSSParserImpl::ExpectEndProperty()
    3092             : {
    3093        7498 :   if (CheckEndProperty())
    3094        7454 :     return true;
    3095             : 
    3096             :   // If we're here, we read something incorrect, so we should report it.
    3097          44 :   REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
    3098          44 :   return false;
    3099             : }
    3100             : 
    3101             : // Parses the priority suffix on a property, which at present may be
    3102             : // either '!important' or nothing.
    3103             : CSSParserImpl::PriorityParsingStatus
    3104        6448 : CSSParserImpl::ParsePriority()
    3105             : {
    3106        6448 :   if (!GetToken(true)) {
    3107           3 :     return ePriority_None; // properties may end with EOF
    3108             :   }
    3109        6445 :   if (!mToken.IsSymbol('!')) {
    3110        5928 :     UngetToken();
    3111        5928 :     return ePriority_None; // dunno what it is, but it's not a priority
    3112             :   }
    3113             : 
    3114         517 :   if (!GetToken(true)) {
    3115             :     // EOF is not ok after !
    3116           0 :     REPORT_UNEXPECTED_EOF(PEImportantEOF);
    3117           0 :     return ePriority_Error;
    3118             :   }
    3119             : 
    3120        1034 :   if (mToken.mType != eCSSToken_Ident ||
    3121         517 :       !mToken.mIdent.LowerCaseEqualsLiteral("important")) {
    3122           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
    3123           0 :     UngetToken();
    3124           0 :     return ePriority_Error;
    3125             :   }
    3126             : 
    3127         517 :   return ePriority_Important;
    3128             : }
    3129             : 
    3130             : nsAString*
    3131         325 : CSSParserImpl::NextIdent()
    3132             : {
    3133             :   // XXX Error reporting?
    3134         325 :   if (!GetToken(true)) {
    3135           0 :     return nullptr;
    3136             :   }
    3137         325 :   if (eCSSToken_Ident != mToken.mType) {
    3138          88 :     UngetToken();
    3139          88 :     return nullptr;
    3140             :   }
    3141         237 :   return &mToken.mIdent;
    3142             : }
    3143             : 
    3144             : bool
    3145           0 : CSSParserImpl::SkipAtRule(bool aInsideBlock)
    3146             : {
    3147             :   for (;;) {
    3148           0 :     if (!GetToken(true)) {
    3149           0 :       REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2);
    3150           0 :       return false;
    3151             :     }
    3152           0 :     if (eCSSToken_Symbol == mToken.mType) {
    3153           0 :       char16_t symbol = mToken.mSymbol;
    3154           0 :       if (symbol == ';') {
    3155           0 :         break;
    3156             :       }
    3157           0 :       if (aInsideBlock && symbol == '}') {
    3158             :         // The closing } doesn't belong to us.
    3159           0 :         UngetToken();
    3160           0 :         break;
    3161             :       }
    3162           0 :       if (symbol == '{') {
    3163           0 :         SkipUntil('}');
    3164           0 :         break;
    3165           0 :       } else if (symbol == '(') {
    3166           0 :         SkipUntil(')');
    3167           0 :       } else if (symbol == '[') {
    3168           0 :         SkipUntil(']');
    3169             :       }
    3170           0 :     } else if (eCSSToken_Function == mToken.mType ||
    3171           0 :                eCSSToken_Bad_URL == mToken.mType) {
    3172           0 :       SkipUntil(')');
    3173             :     }
    3174           0 :   }
    3175           0 :   return true;
    3176             : }
    3177             : 
    3178             : bool
    3179         286 : CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
    3180             :                            void* aData,
    3181             :                            bool aInAtRule)
    3182             : {
    3183             : 
    3184             :   nsCSSSection newSection;
    3185             :   bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*);
    3186             : 
    3187         328 :   if ((mSection <= eCSSSection_Charset) &&
    3188          42 :       (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
    3189           0 :     parseFunc = &CSSParserImpl::ParseCharsetRule;
    3190           0 :     newSection = eCSSSection_Import;  // only one charset allowed
    3191             : 
    3192         330 :   } else if ((mSection <= eCSSSection_Import) &&
    3193          44 :              mToken.mIdent.LowerCaseEqualsLiteral("import")) {
    3194           3 :     parseFunc = &CSSParserImpl::ParseImportRule;
    3195           3 :     newSection = eCSSSection_Import;
    3196             : 
    3197         350 :   } else if ((mSection <= eCSSSection_NameSpace) &&
    3198          67 :              mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
    3199          64 :     parseFunc = &CSSParserImpl::ParseNameSpaceRule;
    3200          64 :     newSection = eCSSSection_NameSpace;
    3201             : 
    3202         219 :   } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
    3203          48 :     parseFunc = &CSSParserImpl::ParseMediaRule;
    3204          48 :     newSection = eCSSSection_General;
    3205             : 
    3206         171 :   } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
    3207           2 :     parseFunc = &CSSParserImpl::ParseMozDocumentRule;
    3208           2 :     newSection = eCSSSection_General;
    3209             : 
    3210         169 :   } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
    3211           0 :     parseFunc = &CSSParserImpl::ParseFontFaceRule;
    3212           0 :     newSection = eCSSSection_General;
    3213             : 
    3214         169 :   } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values")) {
    3215           0 :     parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule;
    3216           0 :     newSection = eCSSSection_General;
    3217             : 
    3218         169 :   } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
    3219           0 :     parseFunc = &CSSParserImpl::ParsePageRule;
    3220           0 :     newSection = eCSSSection_General;
    3221             : 
    3222         338 :   } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation,
    3223         169 :                                     EnabledState()) &&
    3224         338 :               mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) ||
    3225         338 :              (nsCSSProps::IsEnabled(eCSSPropertyAlias_WebkitAnimation) &&
    3226         507 :               mToken.mIdent.LowerCaseEqualsLiteral("-webkit-keyframes")) ||
    3227         169 :              mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) {
    3228          31 :     parseFunc = &CSSParserImpl::ParseKeyframesRule;
    3229          31 :     newSection = eCSSSection_General;
    3230             : 
    3231         138 :   } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports")) {
    3232           0 :     parseFunc = &CSSParserImpl::ParseSupportsRule;
    3233           0 :     newSection = eCSSSection_General;
    3234             : 
    3235         138 :   } else if (mToken.mIdent.LowerCaseEqualsLiteral("counter-style")) {
    3236         138 :     parseFunc = &CSSParserImpl::ParseCounterStyleRule;
    3237         138 :     newSection = eCSSSection_General;
    3238             : 
    3239             :   } else {
    3240           0 :     if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
    3241           0 :       REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
    3242           0 :       OUTPUT_ERROR();
    3243             :     }
    3244             :     // Skip over unsupported at rule, don't advance section
    3245           0 :     return SkipAtRule(aInAtRule);
    3246             :   }
    3247             : 
    3248             :   // Inside of @-rules, only the rules that can occur anywhere
    3249             :   // are allowed.
    3250         286 :   bool unnestable = aInAtRule && newSection != eCSSSection_General;
    3251         286 :   if (unnestable) {
    3252           0 :     REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule);
    3253             :   }
    3254             : 
    3255         286 :   if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) {
    3256             :     // Skip over invalid at rule, don't advance section
    3257           0 :     OUTPUT_ERROR();
    3258           0 :     return SkipAtRule(aInAtRule);
    3259             :   }
    3260             : 
    3261             :   // Nested @-rules don't affect the top-level rule chain requirement
    3262         286 :   if (!aInAtRule) {
    3263         286 :     mSection = newSection;
    3264             :   }
    3265             : 
    3266         286 :   return true;
    3267             : }
    3268             : 
    3269             : bool
    3270           0 : CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
    3271             :                                 void* aData)
    3272             : {
    3273             :   uint32_t linenum, colnum;
    3274           0 :   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
    3275           0 :       !GetToken(true)) {
    3276           0 :     REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
    3277           0 :     return false;
    3278             :   }
    3279             : 
    3280           0 :   if (eCSSToken_String != mToken.mType) {
    3281           0 :     UngetToken();
    3282           0 :     REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
    3283           0 :     return false;
    3284             :   }
    3285             : 
    3286           0 :   nsAutoString charset = mToken.mIdent;
    3287             : 
    3288           0 :   if (!ExpectSymbol(';', true)) {
    3289           0 :     return false;
    3290             :   }
    3291             : 
    3292             :   // It's intentional that we don't create a rule object for @charset rules.
    3293             : 
    3294           0 :   return true;
    3295             : }
    3296             : 
    3297             : bool
    3298          83 : CSSParserImpl::ParseURLOrString(nsString& aURL)
    3299             : {
    3300          83 :   if (!GetToken(true)) {
    3301           0 :     return false;
    3302             :   }
    3303          83 :   if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) {
    3304          83 :     aURL = mToken.mIdent;
    3305          83 :     return true;
    3306             :   }
    3307           0 :   UngetToken();
    3308           0 :   return false;
    3309             : }
    3310             : 
    3311             : bool
    3312          51 : CSSParserImpl::ParseMediaQuery(eMediaQueryType aQueryType,
    3313             :                                nsMediaQuery **aQuery,
    3314             :                                bool *aHitStop)
    3315             : {
    3316          51 :   *aQuery = nullptr;
    3317          51 :   *aHitStop = false;
    3318          51 :   bool inAtRule = aQueryType == eMediaQueryAtRule;
    3319             :   // Attempt to parse a single condition and stop
    3320          51 :   bool singleCondition = aQueryType == eMediaQuerySingleCondition;
    3321             : 
    3322             :   // "If the comma-separated list is the empty list it is assumed to
    3323             :   // specify the media query 'all'."  (css3-mediaqueries, section
    3324             :   // "Media Queries")
    3325          51 :   if (!GetToken(true)) {
    3326           0 :     *aHitStop = true;
    3327             :     // expected termination by EOF
    3328           0 :     if (!inAtRule)
    3329           0 :       return true;
    3330             : 
    3331             :     // unexpected termination by EOF
    3332           0 :     REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
    3333           0 :     return true;
    3334             :   }
    3335             : 
    3336          83 :   if (eCSSToken_Symbol == mToken.mType && inAtRule &&
    3337          64 :       (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) {
    3338           0 :     *aHitStop = true;
    3339           0 :     UngetToken();
    3340           0 :     return true;
    3341             :   }
    3342          51 :   UngetToken();
    3343             : 
    3344          51 :   nsMediaQuery* query = new nsMediaQuery;
    3345          51 :   *aQuery = query;
    3346             : 
    3347          51 :   if (ExpectSymbol('(', true)) {
    3348             :     // we got an expression without a media type
    3349          35 :     UngetToken(); // so ParseMediaQueryExpression can handle it
    3350          35 :     query->SetType(nsGkAtoms::all);
    3351          35 :     query->SetTypeOmitted();
    3352             :     // Just parse the first expression here.
    3353          35 :     if (!ParseMediaQueryExpression(query)) {
    3354           0 :       OUTPUT_ERROR();
    3355           0 :       query->SetHadUnknownExpression();
    3356             :     }
    3357          16 :   } else if (singleCondition) {
    3358             :     // Since we are only trying to consume a single condition, which precludes
    3359             :     // media types and not/only, this should be the same as reaching immediate
    3360             :     // EOF (no condition to parse)
    3361           0 :     *aHitStop = true;
    3362           0 :     return true;
    3363             :   } else {
    3364          32 :     nsCOMPtr<nsIAtom> mediaType;
    3365          16 :     bool gotNotOrOnly = false;
    3366             :     for (;;) {
    3367          20 :       if (!GetToken(true)) {
    3368           0 :         REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
    3369           0 :         return false;
    3370             :       }
    3371          18 :       if (eCSSToken_Ident != mToken.mType) {
    3372           0 :         REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
    3373           0 :         UngetToken();
    3374           0 :         return false;
    3375             :       }
    3376             :       // case insensitive from CSS - must be lower cased
    3377          18 :       nsContentUtils::ASCIIToLower(mToken.mIdent);
    3378          18 :       mediaType = NS_Atomize(mToken.mIdent);
    3379          18 :       if (!gotNotOrOnly && mediaType == nsGkAtoms::_not) {
    3380           2 :         gotNotOrOnly = true;
    3381           2 :         query->SetNegated();
    3382          16 :       } else if (!gotNotOrOnly && mediaType == nsGkAtoms::only) {
    3383           0 :         gotNotOrOnly = true;
    3384           0 :         query->SetHasOnly();
    3385          48 :       } else if (mediaType == nsGkAtoms::_not ||
    3386          32 :                  mediaType == nsGkAtoms::only ||
    3387          48 :                  mediaType == nsGkAtoms::_and ||
    3388          16 :                  mediaType == nsGkAtoms::_or) {
    3389           0 :         REPORT_UNEXPECTED_TOKEN(PEGatherMediaReservedMediaType);
    3390           0 :         UngetToken();
    3391           0 :         return false;
    3392             :       } else {
    3393             :         // valid media type
    3394          16 :         break;
    3395             :       }
    3396             :     }
    3397          16 :     query->SetType(mediaType);
    3398             :   }
    3399             : 
    3400             :   for (;;) {
    3401          71 :     if (!GetToken(true)) {
    3402           3 :       *aHitStop = true;
    3403             :       // expected termination by EOF
    3404           3 :       if (!inAtRule)
    3405           3 :         break;
    3406             : 
    3407             :       // unexpected termination by EOF
    3408           0 :       REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
    3409           0 :       break;
    3410             :     }
    3411             : 
    3412         106 :     if (eCSSToken_Symbol == mToken.mType && inAtRule &&
    3413          96 :         (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) {
    3414          48 :       *aHitStop = true;
    3415          48 :       UngetToken();
    3416          48 :       break;
    3417             :     }
    3418          20 :     if (!singleCondition &&
    3419          10 :         eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
    3420             :       // Done with the expressions for this query
    3421           0 :       break;
    3422             :     }
    3423          20 :     if (eCSSToken_Ident != mToken.mType ||
    3424          10 :         !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
    3425           0 :       if (singleCondition) {
    3426             :         // We have a condition at this point -- if we're not chained to other
    3427             :         // conditions with and/or, we're done.
    3428           0 :         UngetToken();
    3429           0 :         break;
    3430             :       } else {
    3431           0 :         REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
    3432           0 :         UngetToken();
    3433           0 :         return false;
    3434             :       }
    3435             :     }
    3436          10 :     if (!ParseMediaQueryExpression(query)) {
    3437           0 :       OUTPUT_ERROR();
    3438           0 :       query->SetHadUnknownExpression();
    3439             :     }
    3440             :   }
    3441          51 :   return true;
    3442             : }
    3443             : 
    3444             : // Returns false only when there is a low-level error in the scanner
    3445             : // (out-of-memory).
    3446             : bool
    3447          51 : CSSParserImpl::GatherMedia(nsMediaList* aMedia,
    3448             :                            bool aInAtRule)
    3449             : {
    3450          51 :   eMediaQueryType type = aInAtRule ? eMediaQueryAtRule : eMediaQueryNormal;
    3451             :   for (;;) {
    3452          51 :     nsAutoPtr<nsMediaQuery> query;
    3453             :     bool hitStop;
    3454          51 :     if (!ParseMediaQuery(type, getter_Transfers(query), &hitStop)) {
    3455           0 :       NS_ASSERTION(!hitStop, "should return true when hit stop");
    3456           0 :       OUTPUT_ERROR();
    3457           0 :       if (query) {
    3458           0 :         query->SetHadUnknownExpression();
    3459             :       }
    3460           0 :       if (aInAtRule) {
    3461             :         const char16_t stopChars[] =
    3462           0 :           { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) };
    3463           0 :         SkipUntilOneOf(stopChars);
    3464             :       } else {
    3465           0 :         SkipUntil(',');
    3466             :       }
    3467             :       // Rely on SkipUntilOneOf leaving mToken around as the last token read.
    3468           0 :       if (mToken.mType == eCSSToken_Symbol && aInAtRule &&
    3469           0 :           (mToken.mSymbol == '{' || mToken.mSymbol == ';'  || mToken.mSymbol == '}')) {
    3470           0 :         UngetToken();
    3471           0 :         hitStop = true;
    3472             :       }
    3473             :     }
    3474          51 :     if (query) {
    3475          51 :       aMedia->AppendQuery(query);
    3476             :     }
    3477          51 :     if (hitStop) {
    3478          51 :       break;
    3479             :     }
    3480           0 :   }
    3481          51 :   return true;
    3482             : }
    3483             : 
    3484             : bool
    3485          45 : CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
    3486             : {
    3487          45 :   if (!ExpectSymbol('(', true)) {
    3488           0 :     REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
    3489           0 :     return false;
    3490             :   }
    3491          45 :   if (! GetToken(true)) {
    3492           0 :     REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
    3493           0 :     return false;
    3494             :   }
    3495          45 :   if (eCSSToken_Ident != mToken.mType) {
    3496           0 :     REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
    3497           0 :     UngetToken();
    3498           0 :     SkipUntil(')');
    3499           0 :     return false;
    3500             :   }
    3501             : 
    3502          45 :   nsMediaExpression *expr = aQuery->NewExpression();
    3503             : 
    3504             :   // case insensitive from CSS - must be lower cased
    3505          45 :   nsContentUtils::ASCIIToLower(mToken.mIdent);
    3506          90 :   nsDependentString featureString(mToken.mIdent, 0);
    3507          45 :   uint8_t satisfiedReqFlags = 0;
    3508             : 
    3509             :   // Strip off "-webkit-" prefix from featureString:
    3510         180 :   if (sWebkitPrefixedAliasesEnabled &&
    3511         180 :       StringBeginsWith(featureString, NS_LITERAL_STRING("-webkit-"))) {
    3512           0 :     satisfiedReqFlags |= nsMediaFeature::eHasWebkitPrefix;
    3513           0 :     featureString.Rebind(featureString, 8);
    3514             :   }
    3515          45 :   if (sWebkitDevicePixelRatioEnabled) {
    3516           0 :     satisfiedReqFlags |= nsMediaFeature::eWebkitDevicePixelRatioPrefEnabled;
    3517             :   }
    3518             : 
    3519             :   // Strip off "min-"/"max-" prefix from featureString:
    3520          45 :   if (StringBeginsWith(featureString, NS_LITERAL_STRING("min-"))) {
    3521          23 :     expr->mRange = nsMediaExpression::eMin;
    3522          23 :     featureString.Rebind(featureString, 4);
    3523          22 :   } else if (StringBeginsWith(featureString, NS_LITERAL_STRING("max-"))) {
    3524           6 :     expr->mRange = nsMediaExpression::eMax;
    3525           6 :     featureString.Rebind(featureString, 4);
    3526             :   } else {
    3527          16 :     expr->mRange = nsMediaExpression::eEqual;
    3528             :   }
    3529             : 
    3530          90 :   nsCOMPtr<nsIAtom> mediaFeatureAtom = NS_Atomize(featureString);
    3531          45 :   const nsMediaFeature *feature = nsMediaFeatures::features;
    3532        1169 :   for (; feature->mName; ++feature) {
    3533             :     // See if name matches & all requirement flags are satisfied:
    3534             :     // (We check requirements by turning off all of the flags that have been
    3535             :     // satisfied, and then see if the result is 0.)
    3536         652 :     if (*(feature->mName) == mediaFeatureAtom &&
    3537          45 :         !(feature->mReqFlags & ~satisfiedReqFlags)) {
    3538          45 :       break;
    3539             :     }
    3540             :   }
    3541          90 :   if (!feature->mName ||
    3542          74 :       (expr->mRange != nsMediaExpression::eEqual &&
    3543          29 :        feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
    3544           0 :     REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
    3545           0 :     SkipUntil(')');
    3546           0 :     return false;
    3547             :   }
    3548          45 :   expr->mFeature = feature;
    3549             : 
    3550          45 :   if (!GetToken(true) || mToken.IsSymbol(')')) {
    3551             :     // Query expressions for any feature can be given without a value.
    3552             :     // However, min/max prefixes are not allowed.
    3553           8 :     if (expr->mRange != nsMediaExpression::eEqual) {
    3554           0 :       REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
    3555           0 :       return false;
    3556             :     }
    3557           8 :     expr->mValue.Reset();
    3558           8 :     return true;
    3559             :   }
    3560             : 
    3561          37 :   if (!mToken.IsSymbol(':')) {
    3562           0 :     REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
    3563           0 :     UngetToken();
    3564           0 :     SkipUntil(')');
    3565           0 :     return false;
    3566             :   }
    3567             : 
    3568          37 :   bool rv = false;
    3569          37 :   switch (feature->mValueType) {
    3570             :     case nsMediaFeature::eLength:
    3571           6 :       rv = ParseSingleTokenNonNegativeVariant(expr->mValue, VARIANT_LENGTH,
    3572           6 :                                               nullptr);
    3573           6 :       break;
    3574             :     case nsMediaFeature::eInteger:
    3575             :     case nsMediaFeature::eBoolInteger:
    3576           0 :       rv = ParseNonNegativeInteger(expr->mValue);
    3577             :       // Enforce extra restrictions for eBoolInteger
    3578           0 :       if (rv &&
    3579           0 :           feature->mValueType == nsMediaFeature::eBoolInteger &&
    3580           0 :           expr->mValue.GetIntValue() > 1)
    3581           0 :         rv = false;
    3582           0 :       break;
    3583             :     case nsMediaFeature::eFloat:
    3584           0 :       rv = ParseNonNegativeNumber(expr->mValue);
    3585           0 :       break;
    3586             :     case nsMediaFeature::eIntRatio:
    3587             :       {
    3588             :         // Two integers separated by '/', with optional whitespace on
    3589             :         // either side of the '/'.
    3590           0 :         RefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
    3591           0 :         expr->mValue.SetArrayValue(a, eCSSUnit_Array);
    3592             :         // We don't bother with ParseNonNegativeVariant since we have to
    3593             :         // check for != 0 as well; no need to worry about the UngetToken
    3594             :         // since we're throwing out up to the next ')' anyway.
    3595           0 :         rv = ParseSingleTokenVariant(a->Item(0), VARIANT_INTEGER, nullptr) &&
    3596           0 :              a->Item(0).GetIntValue() > 0 &&
    3597           0 :              ExpectSymbol('/', true) &&
    3598           0 :              ParseSingleTokenVariant(a->Item(1), VARIANT_INTEGER, nullptr) &&
    3599           0 :              a->Item(1).GetIntValue() > 0;
    3600             :       }
    3601           0 :       break;
    3602             :     case nsMediaFeature::eResolution:
    3603          30 :       rv = GetToken(true);
    3604          30 :       if (!rv)
    3605           0 :         break;
    3606          30 :       rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f;
    3607          30 :       if (!rv) {
    3608           0 :         UngetToken();
    3609           0 :         break;
    3610             :       }
    3611             :       // No worries about whether unitless zero is allowed, since the
    3612             :       // value must be positive (and we checked that above).
    3613          30 :       NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
    3614          30 :       if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
    3615           0 :         expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
    3616          30 :       } else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) {
    3617          30 :         expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
    3618           0 :       } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
    3619           0 :         expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
    3620             :       } else {
    3621           0 :         rv = false;
    3622             :       }
    3623          30 :       break;
    3624             :     case nsMediaFeature::eEnumerated:
    3625           0 :       rv = ParseSingleTokenVariant(expr->mValue, VARIANT_KEYWORD,
    3626           0 :                                    feature->mData.mKeywordTable);
    3627           0 :       break;
    3628             :     case nsMediaFeature::eIdent:
    3629           1 :       rv = ParseSingleTokenVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr);
    3630           1 :       break;
    3631             :   }
    3632          37 :   if (!rv || !ExpectSymbol(')', true)) {
    3633           0 :     REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
    3634           0 :     SkipUntil(')');
    3635           0 :     return false;
    3636             :   }
    3637             : 
    3638          37 :   return true;
    3639             : }
    3640             : 
    3641             : // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
    3642             : bool
    3643           3 : CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
    3644             : {
    3645           6 :   RefPtr<nsMediaList> media = new nsMediaList();
    3646             : 
    3647             :   uint32_t linenum, colnum;
    3648           6 :   nsAutoString url;
    3649           6 :   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
    3650           3 :       !ParseURLOrString(url)) {
    3651           0 :     REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
    3652           0 :     return false;
    3653             :   }
    3654             : 
    3655           3 :   if (!ExpectSymbol(';', true)) {
    3656           0 :     if (!GatherMedia(media, true) ||
    3657           0 :         !ExpectSymbol(';', true)) {
    3658           0 :       REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
    3659             :       // don't advance section, simply ignore invalid @import
    3660           0 :       return false;
    3661             :     }
    3662             : 
    3663             :     // Safe to assert this, since we ensured that there is something
    3664             :     // other than the ';' coming after the @import's url() token.
    3665           0 :     NS_ASSERTION(media->Length() != 0, "media list must be nonempty");
    3666             :   }
    3667             : 
    3668           3 :   ProcessImport(url, media, aAppendFunc, aData, linenum, colnum);
    3669           3 :   return true;
    3670             : }
    3671             : 
    3672             : void
    3673           3 : CSSParserImpl::ProcessImport(const nsString& aURLSpec,
    3674             :                              nsMediaList* aMedia,
    3675             :                              RuleAppendFunc aAppendFunc,
    3676             :                              void* aData,
    3677             :                              uint32_t aLineNumber,
    3678             :                              uint32_t aColumnNumber)
    3679             : {
    3680             :   RefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec,
    3681             :                                                      aLineNumber,
    3682           6 :                                                      aColumnNumber);
    3683           3 :   (*aAppendFunc)(rule, aData);
    3684             : 
    3685             :   // Diagnose bad URIs even if we don't have a child loader.
    3686           6 :   nsCOMPtr<nsIURI> url;
    3687             :   // Charset will be deduced from mBaseURI, which is more or less correct.
    3688           3 :   nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI);
    3689             : 
    3690           3 :   if (NS_FAILED(rv)) {
    3691           0 :     if (rv == NS_ERROR_MALFORMED_URI) {
    3692             :       // import url is bad
    3693           0 :       REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
    3694           0 :       OUTPUT_ERROR();
    3695             :     }
    3696           0 :     return;
    3697             :   }
    3698             : 
    3699           3 :   if (mChildLoader) {
    3700           3 :     mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule, mReusableSheets);
    3701             :   }
    3702             : }
    3703             : 
    3704             : // Parse the {} part of an @media or @-moz-document rule.
    3705             : bool
    3706          50 : CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
    3707             :                               RuleAppendFunc aAppendFunc,
    3708             :                               void* aData)
    3709             : {
    3710             :   // XXXbz this could use better error reporting throughout the method
    3711          50 :   if (!ExpectSymbol('{', true)) {
    3712           0 :     return false;
    3713             :   }
    3714             : 
    3715             :   // push rule on stack, loop over children
    3716          50 :   PushGroup(aRule);
    3717          50 :   nsCSSSection holdSection = mSection;
    3718          50 :   mSection = eCSSSection_General;
    3719             : 
    3720             :   for (;;) {
    3721             :     // Get next non-whitespace token
    3722         308 :     if (! GetToken(true)) {
    3723           0 :       REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2);
    3724           0 :       break;
    3725             :     }
    3726         179 :     if (mToken.IsSymbol('}')) { // done!
    3727          50 :       UngetToken();
    3728          50 :       break;
    3729             :     }
    3730         129 :     if (eCSSToken_AtKeyword == mToken.mType) {
    3731             :       // Parse for nested rules
    3732           0 :       ParseAtRule(aAppendFunc, aData, true);
    3733           0 :       continue;
    3734             :     }
    3735         129 :     UngetToken();
    3736         129 :     ParseRuleSet(AppendRuleToSheet, this, true);
    3737             :   }
    3738          50 :   PopGroup();
    3739             : 
    3740          50 :   if (!ExpectSymbol('}', true)) {
    3741           0 :     mSection = holdSection;
    3742           0 :     return false;
    3743             :   }
    3744          50 :   (*aAppendFunc)(aRule, aData);
    3745          50 :   return true;
    3746             : }
    3747             : 
    3748             : // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
    3749             : bool
    3750          48 : CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
    3751             : {
    3752          96 :   RefPtr<nsMediaList> media = new nsMediaList();
    3753             :   uint32_t linenum, colnum;
    3754          96 :   if (GetNextTokenLocation(true, &linenum, &colnum) &&
    3755          48 :       GatherMedia(media, true)) {
    3756             :     // XXXbz this could use better error reporting throughout the method
    3757          48 :     RefPtr<css::MediaRule> rule = new css::MediaRule(linenum, colnum);
    3758             :     // Append first, so when we do SetMedia() the rule
    3759             :     // knows what its stylesheet is.
    3760          48 :     if (ParseGroupRule(rule, aAppendFunc, aData)) {
    3761          48 :       rule->SetMedia(media);
    3762          48 :       return true;
    3763             :     }
    3764             :   }
    3765             : 
    3766           0 :   return false;
    3767             : }
    3768             : 
    3769             : // Parse a @-moz-document rule.  This is like an @media rule, but instead
    3770             : // of a medium it has a nonempty list of items where each item is either
    3771             : // url(), url-prefix(), or domain().
    3772             : bool
    3773           2 : CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
    3774             : {
    3775           2 :   css::DocumentRule::URL *urls = nullptr;
    3776           2 :   css::DocumentRule::URL **next = &urls;
    3777             : 
    3778             :   uint32_t linenum, colnum;
    3779           2 :   if (!GetNextTokenLocation(true, &linenum, &colnum)) {
    3780           0 :     return false;
    3781             :   }
    3782             : 
    3783           2 :   do {
    3784           2 :     if (!GetToken(true)) {
    3785           0 :       REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF);
    3786           0 :       delete urls;
    3787           0 :       return false;
    3788             :     }
    3789             : 
    3790           4 :     if (!(eCSSToken_URL == mToken.mType ||
    3791           4 :           (eCSSToken_Function == mToken.mType &&
    3792           2 :            (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
    3793           0 :             mToken.mIdent.LowerCaseEqualsLiteral("domain") ||
    3794           0 :             mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) {
    3795           0 :       REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc2);
    3796           0 :       UngetToken();
    3797           0 :       delete urls;
    3798           0 :       return false;
    3799             :     }
    3800           2 :     css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL;
    3801           2 :     next = &cur->next;
    3802           2 :     if (mToken.mType == eCSSToken_URL) {
    3803           0 :       cur->func = URLMatchingFunction::eURL;
    3804           0 :       CopyUTF16toUTF8(mToken.mIdent, cur->url);
    3805           2 :     } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) {
    3806             :       // regexp() is different from url-prefix() and domain() (but
    3807             :       // probably the way they *should* have been* in that it requires a
    3808             :       // string argument, and doesn't try to behave like url().
    3809           0 :       cur->func = URLMatchingFunction::eRegExp;
    3810           0 :       GetToken(true);
    3811             :       // copy before we know it's valid (but before ExpectSymbol changes
    3812             :       // mToken.mIdent)
    3813           0 :       CopyUTF16toUTF8(mToken.mIdent, cur->url);
    3814           0 :       if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) {
    3815           0 :         REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString);
    3816           0 :         SkipUntil(')');
    3817           0 :         delete urls;
    3818           0 :         return false;
    3819             :       }
    3820             :     } else {
    3821           2 :       if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
    3822           2 :         cur->func = URLMatchingFunction::eURLPrefix;
    3823           0 :       } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
    3824           0 :         cur->func = URLMatchingFunction::eDomain;
    3825             :       }
    3826             : 
    3827           2 :       NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point");
    3828           2 :       mScanner->NextURL(mToken);
    3829           2 :       if (mToken.mType != eCSSToken_URL) {
    3830           0 :         REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
    3831           0 :         SkipUntil(')');
    3832           0 :         delete urls;
    3833           0 :         return false;
    3834             :       }
    3835             : 
    3836             :       // We could try to make the URL (as long as it's not domain())
    3837             :       // canonical and absolute with NS_NewURI and GetSpec, but I'm
    3838             :       // inclined to think we shouldn't.
    3839           2 :       CopyUTF16toUTF8(mToken.mIdent, cur->url);
    3840             :     }
    3841             :   } while (ExpectSymbol(',', true));
    3842             : 
    3843           4 :   RefPtr<css::DocumentRule> rule = new css::DocumentRule(linenum, colnum);
    3844           2 :   rule->SetURLs(urls);
    3845             : 
    3846           2 :   return ParseGroupRule(rule, aAppendFunc, aData);
    3847             : }
    3848             : 
    3849             : // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
    3850             : bool
    3851          64 : CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
    3852             : {
    3853             :   uint32_t linenum, colnum;
    3854         128 :   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
    3855          64 :       !GetToken(true)) {
    3856           0 :     REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
    3857           0 :     return false;
    3858             :   }
    3859             : 
    3860         128 :   nsAutoString  prefix;
    3861         128 :   nsAutoString  url;
    3862             : 
    3863          64 :   if (eCSSToken_Ident == mToken.mType) {
    3864          28 :     prefix = mToken.mIdent;
    3865             :     // user-specified identifiers are case-sensitive (bug 416106)
    3866             :   } else {
    3867          36 :     UngetToken();
    3868             :   }
    3869             : 
    3870          64 :   if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) {
    3871           0 :     if (mHavePushBack) {
    3872           0 :       REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
    3873             :     } else {
    3874           0 :       REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
    3875             :     }
    3876           0 :     return false;
    3877             :   }
    3878             : 
    3879          64 :   ProcessNameSpace(prefix, url, aAppendFunc, aData, linenum, colnum);
    3880          64 :   return true;
    3881             : }
    3882             : 
    3883             : void
    3884          64 : CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
    3885             :                                 const nsString& aURLSpec,
    3886             :                                 RuleAppendFunc aAppendFunc,
    3887             :                                 void* aData,
    3888             :                                 uint32_t aLineNumber,
    3889             :                                 uint32_t aColumnNumber)
    3890             : {
    3891         128 :   nsCOMPtr<nsIAtom> prefix;
    3892             : 
    3893          64 :   if (!aPrefix.IsEmpty()) {
    3894          28 :     prefix = NS_Atomize(aPrefix);
    3895             :   }
    3896             : 
    3897             :   RefPtr<css::NameSpaceRule> rule = new css::NameSpaceRule(prefix, aURLSpec,
    3898             :                                                              aLineNumber,
    3899         192 :                                                              aColumnNumber);
    3900          64 :   (*aAppendFunc)(rule, aData);
    3901             : 
    3902             :   // If this was the first namespace rule encountered, it will trigger
    3903             :   // creation of a namespace map.
    3904          64 :   if (!mNameSpaceMap) {
    3905          39 :     mNameSpaceMap = mSheet->GetNameSpaceMap();
    3906             :   }
    3907          64 : }
    3908             : 
    3909             : // font-face-rule: '@font-face' '{' font-description '}'
    3910             : // font-description: font-descriptor+
    3911             : bool
    3912           0 : CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
    3913             : {
    3914             :   uint32_t linenum, colnum;
    3915           0 :   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
    3916           0 :       !ExpectSymbol('{', true)) {
    3917           0 :     REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart);
    3918           0 :     return false;
    3919             :   }
    3920             : 
    3921           0 :   RefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule(linenum, colnum));
    3922             : 
    3923             :   for (;;) {
    3924           0 :     if (!GetToken(true)) {
    3925           0 :       REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
    3926           0 :       break;
    3927             :     }
    3928           0 :     if (mToken.IsSymbol('}')) { // done!
    3929           0 :       UngetToken();
    3930           0 :       break;
    3931             :     }
    3932             : 
    3933             :     // ignore extra semicolons
    3934           0 :     if (mToken.IsSymbol(';'))
    3935           0 :       continue;
    3936             : 
    3937           0 :     if (!ParseFontDescriptor(rule)) {
    3938           0 :       REPORT_UNEXPECTED(PEDeclSkipped);
    3939           0 :       OUTPUT_ERROR();
    3940           0 :       if (!SkipDeclaration(true))
    3941           0 :         break;
    3942             :     }
    3943             :   }
    3944           0 :   if (!ExpectSymbol('}', true)) {
    3945           0 :     REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd);
    3946           0 :     return false;
    3947             :   }
    3948           0 :   (*aAppendFunc)(rule, aData);
    3949           0 :   return true;
    3950             : }
    3951             : 
    3952             : // font-descriptor: font-family-desc
    3953             : //                | font-style-desc
    3954             : //                | font-weight-desc
    3955             : //                | font-stretch-desc
    3956             : //                | font-src-desc
    3957             : //                | unicode-range-desc
    3958             : //
    3959             : // All font-*-desc productions follow the pattern
    3960             : //    IDENT ':' value ';'
    3961             : //
    3962             : // On entry to this function, mToken is the IDENT.
    3963             : 
    3964             : bool
    3965           0 : CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
    3966             : {
    3967           0 :   if (eCSSToken_Ident != mToken.mType) {
    3968           0 :     REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
    3969           0 :     return false;
    3970             :   }
    3971             : 
    3972           0 :   nsString descName = mToken.mIdent;
    3973           0 :   if (!ExpectSymbol(':', true)) {
    3974           0 :     REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
    3975           0 :     OUTPUT_ERROR();
    3976           0 :     return false;
    3977             :   }
    3978             : 
    3979           0 :   nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
    3980           0 :   nsCSSValue value;
    3981             : 
    3982           0 :   if (descID == eCSSFontDesc_UNKNOWN ||
    3983           0 :       (descID == eCSSFontDesc_Display &&
    3984           0 :        !Preferences::GetBool("layout.css.font-display.enabled"))) {
    3985           0 :     if (NonMozillaVendorIdentifier(descName)) {
    3986             :       // silently skip other vendors' extensions
    3987           0 :       Unused << SkipDeclaration(true);
    3988           0 :       return true;
    3989             :     } else {
    3990           0 :       REPORT_UNEXPECTED_P(PEUnknownFontDesc, descName);
    3991           0 :       return false;
    3992             :     }
    3993             :   }
    3994             : 
    3995           0 :   if (!ParseFontDescriptorValue(descID, value)) {
    3996           0 :     REPORT_UNEXPECTED_P(PEValueParsingError, descName);
    3997           0 :     return false;
    3998             :   }
    3999             : 
    4000             :   // Expect termination by ;, }, or EOF.
    4001           0 :   if (GetToken(true)) {
    4002           0 :     if (!mToken.IsSymbol(';') &&
    4003           0 :         !mToken.IsSymbol('}')) {
    4004           0 :       UngetToken();
    4005           0 :       REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
    4006           0 :       return false;
    4007             :     }
    4008           0 :     UngetToken();
    4009             :   }
    4010             : 
    4011           0 :   aRule->SetDesc(descID, value);
    4012           0 :   return true;
    4013             : }
    4014             : 
    4015             : // @font-feature-values <font-family># {
    4016             : //   @<feature-type> {
    4017             : //     <feature-ident> : <feature-index>+;
    4018             : //     <feature-ident> : <feature-index>+;
    4019             : //     ...
    4020             : //   }
    4021             : //   ...
    4022             : // }
    4023             : 
    4024             : bool
    4025           0 : CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
    4026             :                                           void* aData)
    4027             : {
    4028             :   uint32_t linenum, colnum;
    4029           0 :   if (!GetNextTokenLocation(true, &linenum, &colnum)) {
    4030           0 :     return false;
    4031             :   }
    4032             : 
    4033             :   RefPtr<nsCSSFontFeatureValuesRule>
    4034           0 :                valuesRule(new nsCSSFontFeatureValuesRule(linenum, colnum));
    4035             : 
    4036             :   // parse family list
    4037           0 :   nsCSSValue fontlistValue;
    4038             : 
    4039           0 :   if (!ParseFamily(fontlistValue) ||
    4040           0 :       fontlistValue.GetUnit() != eCSSUnit_FontFamilyList)
    4041             :   {
    4042           0 :     REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily);
    4043           0 :     return false;
    4044             :   }
    4045             : 
    4046             :   // add family to rule
    4047           0 :   const FontFamilyList* fontlist = fontlistValue.GetFontFamilyListValue();
    4048             : 
    4049             :   // family list has generic ==> parse error
    4050           0 :   if (fontlist->HasGeneric()) {
    4051           0 :     REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList);
    4052           0 :     return false;
    4053             :   }
    4054             : 
    4055           0 :   valuesRule->SetFamilyList(*fontlist);
    4056             : 
    4057             :   // open brace
    4058           0 :   if (!ExpectSymbol('{', true)) {
    4059           0 :     REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart);
    4060           0 :     return false;
    4061             :   }
    4062             : 
    4063             :   // list of sets of feature values, each set bound to a specific
    4064             :   // feature-type (e.g. swash, annotation)
    4065             :   for (;;) {
    4066           0 :     if (!GetToken(true)) {
    4067           0 :       REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
    4068           0 :       break;
    4069             :     }
    4070           0 :     if (mToken.IsSymbol('}')) { // done!
    4071           0 :       UngetToken();
    4072           0 :       break;
    4073             :     }
    4074             : 
    4075           0 :     if (!ParseFontFeatureValueSet(valuesRule)) {
    4076           0 :       if (!SkipAtRule(false)) {
    4077           0 :         break;
    4078             :       }
    4079             :     }
    4080             :   }
    4081           0 :   if (!ExpectSymbol('}', true)) {
    4082           0 :     REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd);
    4083           0 :     SkipUntil('}');
    4084           0 :     return false;
    4085             :   }
    4086             : 
    4087           0 :   (*aAppendFunc)(valuesRule, aData);
    4088           0 :   return true;
    4089             : }
    4090             : 
    4091             : #define NUMVALUES_NO_LIMIT  0xFFFF
    4092             : 
    4093             : // parse a single value set containing name-value pairs for a single feature type
    4094             : //   @<feature-type> { [ <feature-ident> : <feature-index>+ ; ]* }
    4095             : //   Ex: @swash { flowing: 1; delicate: 2; }
    4096             : bool
    4097           0 : CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule
    4098             :                                                             *aFeatureValuesRule)
    4099             : {
    4100             :   // -- @keyword (e.g. swash, styleset)
    4101           0 :   if (eCSSToken_AtKeyword != mToken.mType) {
    4102           0 :     REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt);
    4103           0 :     OUTPUT_ERROR();
    4104           0 :     UngetToken();
    4105           0 :     return false;
    4106             :   }
    4107             : 
    4108             :   // which font-specific variant of font-variant-alternates
    4109             :   int32_t whichVariant;
    4110           0 :   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
    4111           0 :   if (!nsCSSProps::FindKeyword(keyword,
    4112             :                                nsCSSProps::kFontVariantAlternatesFuncsKTable,
    4113             :                                whichVariant))
    4114             :   {
    4115           0 :     if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
    4116           0 :       REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue);
    4117           0 :       OUTPUT_ERROR();
    4118             :     }
    4119           0 :     UngetToken();
    4120           0 :     return false;
    4121             :   }
    4122             : 
    4123           0 :   nsAutoString featureType(mToken.mIdent);
    4124             : 
    4125             :   // open brace
    4126           0 :   if (!ExpectSymbol('{', true)) {
    4127           0 :     REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart);
    4128           0 :     return false;
    4129             :   }
    4130             : 
    4131             :   // styleset and character-variant can be multi-valued, otherwise single value
    4132             :   int32_t limitNumValues;
    4133             : 
    4134           0 :   switch (keyword) {
    4135             :     case eCSSKeyword_styleset:
    4136           0 :       limitNumValues = NUMVALUES_NO_LIMIT;
    4137           0 :       break;
    4138             :     case eCSSKeyword_character_variant:
    4139           0 :       limitNumValues = 2;
    4140           0 :       break;
    4141             :     default:
    4142           0 :       limitNumValues = 1;
    4143           0 :       break;
    4144             :   }
    4145             : 
    4146             :   // -- ident integer+ [, ident integer+]
    4147           0 :   AutoTArray<gfxFontFeatureValueSet::ValueList, 5> values;
    4148             : 
    4149             :   // list of font-feature-values-declaration's
    4150             :   for (;;) {
    4151           0 :     nsAutoString valueId;
    4152             : 
    4153           0 :     if (!GetToken(true)) {
    4154           0 :       REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
    4155           0 :       break;
    4156             :     }
    4157             : 
    4158             :     // ignore extra semicolons
    4159           0 :     if (mToken.IsSymbol(';')) {
    4160           0 :       continue;
    4161             :     }
    4162             : 
    4163             :     // close brace ==> done
    4164           0 :     if (mToken.IsSymbol('}')) {
    4165           0 :       break;
    4166             :     }
    4167             : 
    4168             :     // ident
    4169           0 :     if (eCSSToken_Ident != mToken.mType) {
    4170           0 :       REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent);
    4171           0 :       if (!SkipDeclaration(true)) {
    4172           0 :         break;
    4173             :       }
    4174           0 :       continue;
    4175             :     }
    4176             : 
    4177           0 :     valueId.Assign(mToken.mIdent);
    4178             : 
    4179             :     // colon
    4180           0 :     if (!ExpectSymbol(':', true)) {
    4181           0 :       REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
    4182           0 :       OUTPUT_ERROR();
    4183           0 :       if (!SkipDeclaration(true)) {
    4184           0 :         break;
    4185             :       }
    4186           0 :       continue;
    4187             :     }
    4188             : 
    4189             :     // value list
    4190           0 :     AutoTArray<uint32_t,4>   featureSelectors;
    4191             : 
    4192           0 :     nsCSSValue intValue;
    4193           0 :     while (ParseNonNegativeInteger(intValue)) {
    4194           0 :       featureSelectors.AppendElement(uint32_t(intValue.GetIntValue()));
    4195             :     }
    4196             : 
    4197           0 :     int32_t numValues = featureSelectors.Length();
    4198             : 
    4199           0 :     if (numValues == 0) {
    4200           0 :       REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue);
    4201           0 :       OUTPUT_ERROR();
    4202           0 :       if (!SkipDeclaration(true)) {
    4203           0 :         break;
    4204             :       }
    4205           0 :       continue;
    4206             :     }
    4207             : 
    4208           0 :     if (numValues > limitNumValues) {
    4209           0 :       REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType);
    4210           0 :       OUTPUT_ERROR();
    4211           0 :       if (!SkipDeclaration(true)) {
    4212           0 :         break;
    4213             :       }
    4214           0 :       continue;
    4215             :     }
    4216             : 
    4217           0 :     if (!GetToken(true)) {
    4218           0 :       REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
    4219           0 :       gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
    4220           0 :       values.AppendElement(v);
    4221           0 :       break;
    4222             :     }
    4223             : 
    4224             :     // ';' or '}' to end definition
    4225           0 :     if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) {
    4226           0 :       REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing);
    4227           0 :       OUTPUT_ERROR();
    4228           0 :       if (!SkipDeclaration(true)) {
    4229           0 :         break;
    4230             :       }
    4231           0 :       continue;
    4232             :     }
    4233             : 
    4234           0 :     gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
    4235           0 :     values.AppendElement(v);
    4236             : 
    4237           0 :     if (mToken.IsSymbol('}')) {
    4238           0 :       break;
    4239             :     }
    4240           0 :  }
    4241             : 
    4242           0 :   aFeatureValuesRule->AddValueList(whichVariant, values);
    4243           0 :   return true;
    4244             : }
    4245             : 
    4246             : bool
    4247          31 : CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData)
    4248             : {
    4249             :   uint32_t linenum, colnum;
    4250          62 :   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
    4251          31 :       !GetToken(true)) {
    4252           0 :     REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF);
    4253           0 :     return false;
    4254             :   }
    4255             : 
    4256          31 :   if (mToken.mType != eCSSToken_Ident && mToken.mType != eCSSToken_String) {
    4257           0 :     REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
    4258           0 :     UngetToken();
    4259           0 :     return false;
    4260             :   }
    4261             : 
    4262          31 :   if (mToken.mType == eCSSToken_Ident) {
    4263             :     // Check for keywords that are not allowed as custom-ident for the
    4264             :     // keyframes-name: standard CSS-wide keywords, plus 'none'.
    4265             :     static const nsCSSKeyword excludedKeywords[] = {
    4266             :       eCSSKeyword_none,
    4267             :       eCSSKeyword_UNKNOWN
    4268             :     };
    4269          62 :     nsCSSValue value;
    4270          31 :     if (!ParseCustomIdent(value, mToken.mIdent, excludedKeywords)) {
    4271           0 :       REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
    4272           0 :       UngetToken();
    4273           0 :       return false;
    4274             :     }
    4275             :   }
    4276             : 
    4277          62 :   nsString name(mToken.mIdent);
    4278             : 
    4279          31 :   if (!ExpectSymbol('{', true)) {
    4280           0 :     REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace);
    4281           0 :     return false;
    4282             :   }
    4283             : 
    4284             :   RefPtr<nsCSSKeyframesRule> rule = new nsCSSKeyframesRule(name,
    4285          62 :                                                              linenum, colnum);
    4286             : 
    4287         187 :   while (!ExpectSymbol('}', true)) {
    4288         156 :     RefPtr<nsCSSKeyframeRule> kid = ParseKeyframeRule();
    4289          78 :     if (kid) {
    4290          78 :       rule->AppendStyleRule(kid);
    4291             :     } else {
    4292           0 :       OUTPUT_ERROR();
    4293           0 :       SkipRuleSet(true);
    4294             :     }
    4295             :   }
    4296             : 
    4297          31 :   (*aAppendFunc)(rule, aData);
    4298          31 :   return true;
    4299             : }
    4300             : 
    4301             : bool
    4302           0 : CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
    4303             : {
    4304             :   uint32_t linenum, colnum;
    4305           0 :   if (!GetNextTokenLocation(true, &linenum, &colnum)) {
    4306           0 :     return false;
    4307             :   }
    4308             : 
    4309             :   // TODO: There can be page selectors after @page such as ":first", ":left".
    4310             :   uint32_t parseFlags = eParseDeclaration_InBraces |
    4311           0 :                         eParseDeclaration_AllowImportant;
    4312             : 
    4313             :   // Forbid viewport units in @page rules. See bug 811391.
    4314           0 :   MOZ_ASSERT(mViewportUnitsEnabled,
    4315             :              "Viewport units should be enabled outside of @page rules.");
    4316           0 :   mViewportUnitsEnabled = false;
    4317             :   RefPtr<css::Declaration> declaration =
    4318           0 :     ParseDeclarationBlock(parseFlags, eCSSContext_Page);
    4319           0 :   mViewportUnitsEnabled = true;
    4320             : 
    4321           0 :   if (!declaration) {
    4322           0 :     return false;
    4323             :   }
    4324             : 
    4325             :   RefPtr<nsCSSPageRule> rule =
    4326           0 :     new nsCSSPageRule(declaration, linenum, colnum);
    4327             : 
    4328           0 :   (*aAppendFunc)(rule, aData);
    4329           0 :   return true;
    4330             : }
    4331             : 
    4332             : already_AddRefed<nsCSSKeyframeRule>
    4333          78 : CSSParserImpl::ParseKeyframeRule()
    4334             : {
    4335         156 :   InfallibleTArray<float> selectorList;
    4336             :   uint32_t linenum, colnum;
    4337         156 :   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
    4338          78 :       !ParseKeyframeSelectorList(selectorList)) {
    4339           0 :     REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
    4340           0 :     return nullptr;
    4341             :   }
    4342             : 
    4343             :   // Ignore !important in keyframe rules
    4344          78 :   uint32_t parseFlags = eParseDeclaration_InBraces;
    4345         156 :   RefPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags));
    4346          78 :   if (!declaration) {
    4347           0 :     return nullptr;
    4348             :   }
    4349             : 
    4350             :   // Takes ownership of declaration
    4351             :   RefPtr<nsCSSKeyframeRule> rule =
    4352         156 :     new nsCSSKeyframeRule(Move(selectorList), declaration.forget(),
    4353         312 :                           linenum, colnum);
    4354          78 :   return rule.forget();
    4355             : }
    4356             : 
    4357             : bool
    4358          86 : CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList)
    4359             : {
    4360             :   for (;;) {
    4361          86 :     if (!GetToken(true)) {
    4362             :       // The first time through the loop, this means we got an empty
    4363             :       // list.  Otherwise, it means we have a trailing comma.
    4364          78 :       return false;
    4365             :     }
    4366             :     float value;
    4367          86 :     switch (mToken.mType) {
    4368             :       case eCSSToken_Percentage:
    4369          41 :         value = mToken.mNumber;
    4370          41 :         break;
    4371             :       case eCSSToken_Ident:
    4372          45 :         if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
    4373          22 :           value = 0.0f;
    4374          22 :           break;
    4375             :         }
    4376          23 :         if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
    4377          23 :           value = 1.0f;
    4378          23 :           break;
    4379             :         }
    4380             :         MOZ_FALLTHROUGH;
    4381             :       default:
    4382           0 :         UngetToken();
    4383             :         // The first time through the loop, this means we got an empty
    4384             :         // list.  Otherwise, it means we have a trailing comma.
    4385           0 :         return false;
    4386             :     }
    4387          86 :     aSelectorList.AppendElement(value);
    4388          86 :     if (!ExpectSymbol(',', true)) {
    4389          78 :       return true;
    4390             :     }
    4391           8 :   }
    4392             : }
    4393             : 
    4394             : // supports_rule
    4395             : //   : "@supports" supports_condition group_rule_body
    4396             : //   ;
    4397             : bool
    4398           0 : CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData)
    4399             : {
    4400           0 :   bool conditionMet = false;
    4401           0 :   nsString condition;
    4402             : 
    4403           0 :   mScanner->StartRecording();
    4404             : 
    4405             :   uint32_t linenum, colnum;
    4406           0 :   if (!GetNextTokenLocation(true, &linenum, &colnum)) {
    4407           0 :     return false;
    4408             :   }
    4409             : 
    4410           0 :   bool parsed = ParseSupportsCondition(conditionMet);
    4411             : 
    4412           0 :   if (!parsed) {
    4413           0 :     mScanner->StopRecording();
    4414           0 :     return false;
    4415             :   }
    4416             : 
    4417           0 :   if (!ExpectSymbol('{', true)) {
    4418           0 :     REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart);
    4419           0 :     mScanner->StopRecording();
    4420           0 :     return false;
    4421             :   }
    4422             : 
    4423           0 :   UngetToken();
    4424           0 :   mScanner->StopRecording(condition);
    4425             : 
    4426             :   // Remove the "{" that would follow the condition.
    4427           0 :   if (condition.Length() != 0) {
    4428           0 :     condition.Truncate(condition.Length() - 1);
    4429             :   }
    4430             : 
    4431             :   // Remove spaces from the start and end of the recorded supports condition.
    4432           0 :   condition.Trim(" ", true, true, false);
    4433             : 
    4434             :   // Record whether we are in a failing @supports, so that property parse
    4435             :   // errors don't get reported.
    4436           0 :   nsAutoFailingSupportsRule failing(this, conditionMet);
    4437             : 
    4438             :   RefPtr<css::GroupRule> rule =
    4439           0 :     new mozilla::CSSSupportsRule(conditionMet, condition, linenum, colnum);
    4440           0 :   return ParseGroupRule(rule, aAppendFunc, aProcessData);
    4441             : }
    4442             : 
    4443             : // supports_condition
    4444             : //   : supports_condition_in_parens supports_condition_terms
    4445             : //   | supports_condition_negation
    4446             : //   ;
    4447             : bool
    4448           0 : CSSParserImpl::ParseSupportsCondition(bool& aConditionMet)
    4449             : {
    4450           0 :   nsAutoInSupportsCondition aisc(this);
    4451             : 
    4452           0 :   if (!GetToken(true)) {
    4453           0 :     REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2);
    4454           0 :     return false;
    4455             :   }
    4456             : 
    4457           0 :   UngetToken();
    4458             : 
    4459           0 :   mScanner->ClearSeenBadToken();
    4460             : 
    4461           0 :   if (mToken.IsSymbol('(') ||
    4462           0 :       mToken.mType == eCSSToken_Function ||
    4463           0 :       mToken.mType == eCSSToken_URL ||
    4464           0 :       mToken.mType == eCSSToken_Bad_URL) {
    4465           0 :     bool result = ParseSupportsConditionInParens(aConditionMet) &&
    4466           0 :                   ParseSupportsConditionTerms(aConditionMet) &&
    4467           0 :                   !mScanner->SeenBadToken();
    4468           0 :     return result;
    4469             :   }
    4470             : 
    4471           0 :   if (mToken.mType == eCSSToken_Ident &&
    4472           0 :       mToken.mIdent.LowerCaseEqualsLiteral("not")) {
    4473           0 :     bool result = ParseSupportsConditionNegation(aConditionMet) &&
    4474           0 :                   !mScanner->SeenBadToken();
    4475           0 :     return result;
    4476             :   }
    4477             : 
    4478           0 :   REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart);
    4479           0 :   return false;
    4480             : }
    4481             : 
    4482             : // supports_condition_negation
    4483             : //   : 'not' S+ supports_condition_in_parens
    4484             : //   ;
    4485             : bool
    4486           0 : CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet)
    4487             : {
    4488           0 :   if (!GetToken(true)) {
    4489           0 :     REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF);
    4490           0 :     return false;
    4491             :   }
    4492             : 
    4493           0 :   if (mToken.mType != eCSSToken_Ident ||
    4494           0 :       !mToken.mIdent.LowerCaseEqualsLiteral("not")) {
    4495           0 :     REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot);
    4496           0 :     return false;
    4497             :   }
    4498             : 
    4499           0 :   if (!RequireWhitespace()) {
    4500           0 :     REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
    4501           0 :     return false;
    4502             :   }
    4503             : 
    4504           0 :   if (ParseSupportsConditionInParens(aConditionMet)) {
    4505           0 :     aConditionMet = !aConditionMet;
    4506           0 :     return true;
    4507             :   }
    4508             : 
    4509           0 :   return false;
    4510             : }
    4511             : 
    4512             : // supports_condition_in_parens
    4513             : //   : '(' S* supports_condition_in_parens_inside_parens ')' S*
    4514             : //   | supports_condition_pref
    4515             : //   | general_enclosed
    4516             : //   ;
    4517             : bool
    4518           0 : CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet)
    4519             : {
    4520           0 :   if (!GetToken(true)) {
    4521           0 :     REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF);
    4522           0 :     return false;
    4523             :   }
    4524             : 
    4525           0 :   if (mToken.mType == eCSSToken_URL) {
    4526           0 :     aConditionMet = false;
    4527           0 :     return true;
    4528             :   }
    4529             : 
    4530           0 :   if (AgentRulesEnabled() &&
    4531           0 :       mToken.mType == eCSSToken_Function &&
    4532           0 :       mToken.mIdent.LowerCaseEqualsLiteral("-moz-bool-pref")) {
    4533           0 :     return ParseSupportsMozBoolPrefName(aConditionMet);
    4534             :   }
    4535             : 
    4536           0 :   if (mToken.mType == eCSSToken_Function ||
    4537           0 :       mToken.mType == eCSSToken_Bad_URL) {
    4538           0 :     if (!SkipUntil(')')) {
    4539           0 :       REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
    4540           0 :       return false;
    4541             :     }
    4542           0 :     aConditionMet = false;
    4543           0 :     return true;
    4544             :   }
    4545             : 
    4546           0 :   if (!mToken.IsSymbol('(')) {
    4547           0 :     REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction);
    4548           0 :     UngetToken();
    4549           0 :     return false;
    4550             :   }
    4551             : 
    4552           0 :   if (!ParseSupportsConditionInParensInsideParens(aConditionMet)) {
    4553           0 :     if (!SkipUntil(')')) {
    4554           0 :       REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
    4555           0 :       return false;
    4556             :     }
    4557           0 :     aConditionMet = false;
    4558           0 :     return true;
    4559             :   }
    4560             : 
    4561           0 :   if (!(ExpectSymbol(')', true))) {
    4562           0 :     SkipUntil(')');
    4563           0 :     aConditionMet = false;
    4564           0 :     return true;
    4565             :   }
    4566             : 
    4567           0 :   return true;
    4568             : }
    4569             : 
    4570             : // supports_condition_pref
    4571             : //   : '-moz-bool-pref(' bool_pref_name ')'
    4572             : //   ;
    4573             : bool
    4574           0 : CSSParserImpl::ParseSupportsMozBoolPrefName(bool& aConditionMet)
    4575             : {
    4576           0 :   if (!GetToken(true)) {
    4577           0 :     return false;
    4578             :   }
    4579             : 
    4580           0 :   if (mToken.mType != eCSSToken_String) {
    4581           0 :     SkipUntil(')');
    4582           0 :     return false;
    4583             :   }
    4584             : 
    4585           0 :   aConditionMet = Preferences::GetBool(
    4586           0 :     NS_ConvertUTF16toUTF8(mToken.mIdent).get());
    4587             : 
    4588           0 :   if (!ExpectSymbol(')', true)) {
    4589           0 :     SkipUntil(')');
    4590           0 :     return false;
    4591             :   }
    4592             : 
    4593           0 :   return true;
    4594             : }
    4595             : 
    4596             : // supports_condition_in_parens_inside_parens
    4597             : //   : core_declaration
    4598             : //   | supports_condition_negation
    4599             : //   | supports_condition_in_parens supports_condition_terms
    4600             : //   ;
    4601             : bool
    4602           0 : CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet)
    4603             : {
    4604           0 :   if (!GetToken(true)) {
    4605           0 :     return false;
    4606             :   }
    4607             : 
    4608           0 :   if (mToken.mType == eCSSToken_Ident) {
    4609           0 :     if (!mToken.mIdent.LowerCaseEqualsLiteral("not")) {
    4610           0 :       nsAutoString propertyName = mToken.mIdent;
    4611           0 :       if (!ExpectSymbol(':', true)) {
    4612           0 :         return false;
    4613             :       }
    4614             : 
    4615           0 :       nsCSSPropertyID propID = LookupEnabledProperty(propertyName);
    4616           0 :       if (propID == eCSSProperty_UNKNOWN) {
    4617           0 :         if (ExpectSymbol(')', true)) {
    4618           0 :           UngetToken();
    4619           0 :           return false;
    4620             :         }
    4621           0 :         aConditionMet = false;
    4622           0 :         SkipUntil(')');
    4623           0 :         UngetToken();
    4624           0 :       } else if (propID == eCSSPropertyExtra_variable) {
    4625           0 :         if (ExpectSymbol(')', false)) {
    4626           0 :           UngetToken();
    4627           0 :           return false;
    4628             :         }
    4629             :         CSSVariableDeclarations::Type variableType;
    4630           0 :         nsString variableValue;
    4631           0 :         aConditionMet =
    4632           0 :           ParseVariableDeclaration(&variableType, variableValue) &&
    4633           0 :           ParsePriority() != ePriority_Error;
    4634           0 :         if (!aConditionMet) {
    4635           0 :           SkipUntil(')');
    4636           0 :           UngetToken();
    4637             :         }
    4638             :       } else {
    4639           0 :         if (ExpectSymbol(')', true)) {
    4640           0 :           UngetToken();
    4641           0 :           return false;
    4642             :         }
    4643           0 :         aConditionMet = ParseProperty(propID) &&
    4644           0 :                         ParsePriority() != ePriority_Error;
    4645           0 :         if (!aConditionMet) {
    4646           0 :           SkipUntil(')');
    4647           0 :           UngetToken();
    4648             :         }
    4649           0 :         mTempData.ClearProperty(propID);
    4650           0 :         mTempData.AssertInitialState();
    4651             :       }
    4652           0 :       return true;
    4653             :     }
    4654             : 
    4655           0 :     UngetToken();
    4656           0 :     return ParseSupportsConditionNegation(aConditionMet);
    4657             :   }
    4658             : 
    4659           0 :   UngetToken();
    4660           0 :   return ParseSupportsConditionInParens(aConditionMet) &&
    4661           0 :          ParseSupportsConditionTerms(aConditionMet);
    4662             : }
    4663             : 
    4664             : // supports_condition_terms
    4665             : //   : S+ 'and' supports_condition_terms_after_operator('and')
    4666             : //   | S+ 'or' supports_condition_terms_after_operator('or')
    4667             : //   |
    4668             : //   ;
    4669             : bool
    4670           0 : CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet)
    4671             : {
    4672           0 :   if (!RequireWhitespace() || !GetToken(false)) {
    4673           0 :     return true;
    4674             :   }
    4675             : 
    4676           0 :   if (mToken.mType != eCSSToken_Ident) {
    4677           0 :     UngetToken();
    4678           0 :     return true;
    4679             :   }
    4680             : 
    4681           0 :   if (mToken.mIdent.LowerCaseEqualsLiteral("and")) {
    4682           0 :     return ParseSupportsConditionTermsAfterOperator(aConditionMet, eAnd);
    4683             :   }
    4684             : 
    4685           0 :   if (mToken.mIdent.LowerCaseEqualsLiteral("or")) {
    4686           0 :     return ParseSupportsConditionTermsAfterOperator(aConditionMet, eOr);
    4687             :   }
    4688             : 
    4689           0 :   UngetToken();
    4690           0 :   return true;
    4691             : }
    4692             : 
    4693             : // supports_condition_terms_after_operator(operator)
    4694             : //   : S+ supports_condition_in_parens ( <operator> supports_condition_in_parens )*
    4695             : //   ;
    4696             : bool
    4697           0 : CSSParserImpl::ParseSupportsConditionTermsAfterOperator(
    4698             :                          bool& aConditionMet,
    4699             :                          CSSParserImpl::SupportsConditionTermOperator aOperator)
    4700             : {
    4701           0 :   if (!RequireWhitespace()) {
    4702           0 :     REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
    4703           0 :     return false;
    4704             :   }
    4705             : 
    4706           0 :   const char* token = aOperator == eAnd ? "and" : "or";
    4707             :   for (;;) {
    4708           0 :     bool termConditionMet = false;
    4709           0 :     if (!ParseSupportsConditionInParens(termConditionMet)) {
    4710           0 :       return false;
    4711             :     }
    4712           0 :     aConditionMet = aOperator == eAnd ? aConditionMet && termConditionMet :
    4713           0 :                                         aConditionMet || termConditionMet;
    4714             : 
    4715           0 :     if (!GetToken(true)) {
    4716           0 :       return true;
    4717             :     }
    4718             : 
    4719           0 :     if (mToken.mType != eCSSToken_Ident ||
    4720           0 :         !mToken.mIdent.LowerCaseEqualsASCII(token)) {
    4721           0 :       UngetToken();
    4722           0 :       return true;
    4723             :     }
    4724           0 :   }
    4725             : }
    4726             : 
    4727             : bool
    4728         138 : CSSParserImpl::ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aData)
    4729             : {
    4730         276 :   nsCOMPtr<nsIAtom> name;
    4731             :   uint32_t linenum, colnum;
    4732         552 :   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
    4733         552 :       !(name = ParseCounterStyleName(true))) {
    4734           0 :     REPORT_UNEXPECTED_TOKEN(PECounterStyleNotIdent);
    4735           0 :     return false;
    4736             :   }
    4737             : 
    4738         138 :   if (!ExpectSymbol('{', true)) {
    4739           0 :     REPORT_UNEXPECTED_TOKEN(PECounterStyleBadBlockStart);
    4740           0 :     return false;
    4741             :   }
    4742             : 
    4743             :   RefPtr<nsCSSCounterStyleRule> rule = new nsCSSCounterStyleRule(name,
    4744             :                                                                    linenum,
    4745         414 :                                                                    colnum);
    4746             :   for (;;) {
    4747        1154 :     if (!GetToken(true)) {
    4748           0 :       REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
    4749           0 :       break;
    4750             :     }
    4751         646 :     if (mToken.IsSymbol('}')) {
    4752         138 :       break;
    4753             :     }
    4754         508 :     if (mToken.IsSymbol(';')) {
    4755         254 :       continue;
    4756             :     }
    4757             : 
    4758         254 :     if (!ParseCounterDescriptor(rule)) {
    4759           0 :       REPORT_UNEXPECTED(PEDeclSkipped);
    4760           0 :       OUTPUT_ERROR();
    4761           0 :       if (!SkipDeclaration(true)) {
    4762           0 :         REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
    4763           0 :         break;
    4764             :       }
    4765             :     }
    4766             :   }
    4767             : 
    4768         138 :   int32_t system = rule->GetSystem();
    4769         138 :   bool isCorrect = false;
    4770         138 :   switch (system) {
    4771             :     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
    4772             :     case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
    4773             :     case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
    4774             :     case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
    4775             :     case NS_STYLE_COUNTER_SYSTEM_FIXED: {
    4776             :       // check whether symbols is set and the length is sufficient
    4777          66 :       const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
    4778         132 :       if (symbols.GetUnit() == eCSSUnit_List &&
    4779          66 :           nsCSSCounterStyleRule::CheckDescValue(
    4780             :               system, eCSSCounterDesc_Symbols, symbols)) {
    4781          66 :         isCorrect = true;
    4782             :       }
    4783          66 :       break;
    4784             :     }
    4785             :     case NS_STYLE_COUNTER_SYSTEM_ADDITIVE: {
    4786             :       // for additive system, additive-symbols must be set
    4787             :       const nsCSSValue& symbols =
    4788          12 :         rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
    4789          12 :       if (symbols.GetUnit() == eCSSUnit_PairList) {
    4790          12 :         isCorrect = true;
    4791             :       }
    4792          12 :       break;
    4793             :     }
    4794             :     case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
    4795             :       // for extends system, symbols & additive-symbols must not be set
    4796          60 :       const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
    4797             :       const nsCSSValue& additiveSymbols =
    4798          60 :         rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
    4799         120 :       if (symbols.GetUnit() == eCSSUnit_Null &&
    4800          60 :           additiveSymbols.GetUnit() == eCSSUnit_Null) {
    4801          60 :         isCorrect = true;
    4802             :       }
    4803          60 :       break;
    4804             :     }
    4805             :     default:
    4806           0 :       NS_NOTREACHED("unknown system");
    4807             :   }
    4808             : 
    4809         138 :   if (isCorrect) {
    4810         138 :     (*aAppendFunc)(rule, aData);
    4811             :   }
    4812         138 :   return true;
    4813             : }
    4814             : 
    4815             : already_AddRefed<nsIAtom>
    4816         214 : CSSParserImpl::ParseCounterStyleName(bool aForDefinition)
    4817             : {
    4818         214 :   if (!GetToken(true)) {
    4819           0 :     return nullptr;
    4820             :   }
    4821             : 
    4822         214 :   if (mToken.mType != eCSSToken_Ident) {
    4823           0 :     UngetToken();
    4824           0 :     return nullptr;
    4825             :   }
    4826             : 
    4827             :   static const nsCSSKeyword kReservedNames[] = {
    4828             :     eCSSKeyword_none,
    4829             :     eCSSKeyword_decimal,
    4830             :     eCSSKeyword_disc,
    4831             :     eCSSKeyword_UNKNOWN
    4832             :   };
    4833             : 
    4834         428 :   nsCSSValue value; // we don't actually care about the value
    4835         214 :   if (!ParseCustomIdent(value, mToken.mIdent,
    4836             :                         aForDefinition ? kReservedNames : nullptr)) {
    4837           0 :     REPORT_UNEXPECTED_TOKEN(PECounterStyleBadName);
    4838           0 :     UngetToken();
    4839           0 :     return nullptr;
    4840             :   }
    4841             : 
    4842         428 :   nsString name = mToken.mIdent;
    4843         214 :   if (nsCSSProps::IsPredefinedCounterStyle(name)) {
    4844         152 :     ToLowerCase(name);
    4845             :   }
    4846         214 :   return NS_Atomize(name);
    4847             : }
    4848             : 
    4849             : bool
    4850          76 : CSSParserImpl::ParseCounterStyleNameValue(nsCSSValue& aValue)
    4851             : {
    4852          76 :   if (nsCOMPtr<nsIAtom> name = ParseCounterStyleName(false)) {
    4853          76 :     aValue.SetAtomIdentValue(name.forget());
    4854          76 :     return true;
    4855             :   }
    4856           0 :   return false;
    4857             : }
    4858             : 
    4859             : bool
    4860         254 : CSSParserImpl::ParseCounterDescriptor(nsCSSCounterStyleRule* aRule)
    4861             : {
    4862         254 :   if (eCSSToken_Ident != mToken.mType) {
    4863           0 :     REPORT_UNEXPECTED_TOKEN(PECounterDescExpected);
    4864           0 :     return false;
    4865             :   }
    4866             : 
    4867         508 :   nsString descName = mToken.mIdent;
    4868         254 :   if (!ExpectSymbol(':', true)) {
    4869           0 :     REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
    4870           0 :     OUTPUT_ERROR();
    4871           0 :     return false;
    4872             :   }
    4873             : 
    4874         254 :   nsCSSCounterDesc descID = nsCSSProps::LookupCounterDesc(descName);
    4875         508 :   nsCSSValue value;
    4876             : 
    4877         254 :   if (descID == eCSSCounterDesc_UNKNOWN) {
    4878           0 :     REPORT_UNEXPECTED_P(PEUnknownCounterDesc, descName);
    4879           0 :     return false;
    4880             :   }
    4881             : 
    4882         254 :   if (!ParseCounterDescriptorValue(descID, value)) {
    4883           0 :     REPORT_UNEXPECTED_P(PEValueParsingError, descName);
    4884           0 :     return false;
    4885             :   }
    4886             : 
    4887         254 :   if (!ExpectEndProperty()) {
    4888           0 :     return false;
    4889             :   }
    4890             : 
    4891         254 :   aRule->SetDesc(descID, value);
    4892         254 :   return true;
    4893             : }
    4894             : 
    4895             : bool
    4896         254 : CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
    4897             :                                            nsCSSValue& aValue)
    4898             : {
    4899             :   // Should also include VARIANT_IMAGE, but it is not supported currently.
    4900             :   // See bug 1024179.
    4901             :   static const int32_t VARIANT_COUNTER_SYMBOL =
    4902             :     VARIANT_STRING | VARIANT_IDENTIFIER;
    4903             : 
    4904         254 :   switch (aDescID) {
    4905             :     case eCSSCounterDesc_System: {
    4906         276 :       nsCSSValue system;
    4907         138 :       if (!ParseEnum(system, nsCSSProps::kCounterSystemKTable)) {
    4908           0 :         return false;
    4909             :       }
    4910         138 :       switch (system.GetIntValue()) {
    4911             :         case NS_STYLE_COUNTER_SYSTEM_FIXED: {
    4912           0 :           nsCSSValue start;
    4913           0 :           if (!ParseSingleTokenVariant(start, VARIANT_INTEGER, nullptr)) {
    4914           0 :             start.SetIntValue(1, eCSSUnit_Integer);
    4915             :           }
    4916           0 :           aValue.SetPairValue(system, start);
    4917           0 :           return true;
    4918             :         }
    4919             :         case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
    4920         120 :           nsCSSValue name;
    4921          60 :           if (!ParseCounterStyleNameValue(name)) {
    4922           0 :             REPORT_UNEXPECTED_TOKEN(PECounterExtendsNotIdent);
    4923           0 :             return false;
    4924             :           }
    4925          60 :           aValue.SetPairValue(system, name);
    4926          60 :           return true;
    4927             :         }
    4928             :         default:
    4929          78 :           aValue = system;
    4930          78 :           return true;
    4931             :       }
    4932             :     }
    4933             : 
    4934             :     case eCSSCounterDesc_Negative: {
    4935           0 :       nsCSSValue first, second;
    4936           0 :       if (!ParseSingleTokenVariant(first, VARIANT_COUNTER_SYMBOL, nullptr)) {
    4937           0 :         return false;
    4938             :       }
    4939           0 :       if (!ParseSingleTokenVariant(second, VARIANT_COUNTER_SYMBOL, nullptr)) {
    4940           0 :         aValue = first;
    4941             :       } else {
    4942           0 :         aValue.SetPairValue(first, second);
    4943             :       }
    4944           0 :       return true;
    4945             :     }
    4946             : 
    4947             :     case eCSSCounterDesc_Prefix:
    4948             :     case eCSSCounterDesc_Suffix:
    4949          18 :       return ParseSingleTokenVariant(aValue, VARIANT_COUNTER_SYMBOL, nullptr);
    4950             : 
    4951             :     case eCSSCounterDesc_Range: {
    4952          14 :       if (ParseSingleTokenVariant(aValue, VARIANT_AUTO, nullptr)) {
    4953           0 :         return true;
    4954             :       }
    4955          14 :       nsCSSValuePairList* item = aValue.SetPairListValue();
    4956             :       for (;;) {
    4957          14 :         nsCSSValuePair pair;
    4958          14 :         if (!ParseCounterRange(pair)) {
    4959           0 :           return false;
    4960             :         }
    4961          14 :         item->mXValue = pair.mXValue;
    4962          14 :         item->mYValue = pair.mYValue;
    4963          14 :         if (!ExpectSymbol(',', true)) {
    4964          14 :           return true;
    4965             :         }
    4966           0 :         item->mNext = new nsCSSValuePairList;
    4967           0 :         item = item->mNext;
    4968           0 :       }
    4969             :       // should always return in the loop
    4970             :     }
    4971             : 
    4972             :     case eCSSCounterDesc_Pad: {
    4973           4 :       nsCSSValue width, symbol;
    4974           2 :       bool hasWidth = ParseNonNegativeInteger(width);
    4975           4 :       if (!ParseSingleTokenVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
    4976           2 :           (!hasWidth && !ParseNonNegativeInteger(width))) {
    4977           0 :         return false;
    4978             :       }
    4979           2 :       aValue.SetPairValue(width, symbol);
    4980           2 :       return true;
    4981             :     }
    4982             : 
    4983             :     case eCSSCounterDesc_Fallback:
    4984           4 :       return ParseCounterStyleNameValue(aValue);
    4985             : 
    4986             :     case eCSSCounterDesc_Symbols: {
    4987          66 :       nsCSSValueList* item = nullptr;
    4988             :       for (;;) {
    4989        2546 :         nsCSSValue value;
    4990        1306 :         if (!ParseSingleTokenVariant(value, VARIANT_COUNTER_SYMBOL, nullptr)) {
    4991          66 :           return !!item;
    4992             :         }
    4993        1240 :         if (!item) {
    4994          66 :           item = aValue.SetListValue();
    4995             :         } else {
    4996        1174 :           item->mNext = new nsCSSValueList;
    4997        1174 :           item = item->mNext;
    4998             :         }
    4999        1240 :         item->mValue = value;
    5000        1240 :       }
    5001             :       // should always return in the loop
    5002             :     }
    5003             : 
    5004             :     case eCSSCounterDesc_AdditiveSymbols: {
    5005          12 :       nsCSSValuePairList* item = nullptr;
    5006          12 :       int32_t lastWeight = -1;
    5007             :       for (;;) {
    5008         672 :         nsCSSValue weight, symbol;
    5009         342 :         bool hasWeight = ParseNonNegativeInteger(weight);
    5010         684 :         if (!ParseSingleTokenVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
    5011         342 :             (!hasWeight && !ParseNonNegativeInteger(weight))) {
    5012           0 :           return false;
    5013             :         }
    5014         342 :         if (lastWeight != -1 && weight.GetIntValue() >= lastWeight) {
    5015           0 :           REPORT_UNEXPECTED(PECounterASWeight);
    5016           0 :           return false;
    5017             :         }
    5018         342 :         lastWeight = weight.GetIntValue();
    5019         342 :         if (!item) {
    5020          12 :           item = aValue.SetPairListValue();
    5021             :         } else {
    5022         330 :           item->mNext = new nsCSSValuePairList;
    5023         330 :           item = item->mNext;
    5024             :         }
    5025         342 :         item->mXValue = weight;
    5026         342 :         item->mYValue = symbol;
    5027         342 :         if (!ExpectSymbol(',', true)) {
    5028          12 :           return true;
    5029             :         }
    5030         330 :       }
    5031             :       // should always return in the loop
    5032             :     }
    5033             : 
    5034             :     case eCSSCounterDesc_SpeakAs:
    5035           0 :       if (ParseSingleTokenVariant(aValue, VARIANT_AUTO | VARIANT_KEYWORD,
    5036             :                                   nsCSSProps::kCounterSpeakAsKTable)) {
    5037           0 :         if (aValue.GetUnit() == eCSSUnit_Enumerated &&
    5038           0 :             aValue.GetIntValue() == NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT) {
    5039             :           // Currently spell-out is not supported, so it is explicitly
    5040             :           // rejected here rather than parsed as a custom identifier.
    5041             :           // See bug 1024178.
    5042           0 :           return false;
    5043             :         }
    5044           0 :         return true;
    5045             :       }
    5046           0 :       return ParseCounterStyleNameValue(aValue);
    5047             : 
    5048             :     default:
    5049           0 :       NS_NOTREACHED("unknown descriptor");
    5050           0 :       return false;
    5051             :   }
    5052             : }
    5053             : 
    5054             : bool
    5055          14 : CSSParserImpl::ParseCounterRange(nsCSSValuePair& aPair)
    5056             : {
    5057             :   static const int32_t VARIANT_BOUND = VARIANT_INTEGER | VARIANT_KEYWORD;
    5058          28 :   nsCSSValue lower, upper;
    5059          28 :   if (!ParseSingleTokenVariant(lower, VARIANT_BOUND,
    5060          28 :                                nsCSSProps::kCounterRangeKTable) ||
    5061          14 :       !ParseSingleTokenVariant(upper, VARIANT_BOUND,
    5062             :                                nsCSSProps::kCounterRangeKTable)) {
    5063           0 :     return false;
    5064             :   }
    5065          42 :   if (lower.GetUnit() != eCSSUnit_Enumerated &&
    5066          26 :       upper.GetUnit() != eCSSUnit_Enumerated &&
    5067          12 :       lower.GetIntValue() > upper.GetIntValue()) {
    5068           0 :     return false;
    5069             :   }
    5070          14 :   aPair = nsCSSValuePair(lower, upper);
    5071          14 :   return true;
    5072             : }
    5073             : 
    5074             : bool
    5075          29 : CSSParserImpl::SkipUntil(char16_t aStopSymbol)
    5076             : {
    5077          29 :   nsCSSToken* tk = &mToken;
    5078          58 :   AutoTArray<char16_t, 16> stack;
    5079          29 :   stack.AppendElement(aStopSymbol);
    5080             :   for (;;) {
    5081         192 :     if (!GetToken(true)) {
    5082           0 :       return false;
    5083             :     }
    5084         192 :     if (eCSSToken_Symbol == tk->mType) {
    5085          90 :       char16_t symbol = tk->mSymbol;
    5086          90 :       uint32_t stackTopIndex = stack.Length() - 1;
    5087          90 :       if (symbol == stack.ElementAt(stackTopIndex)) {
    5088          62 :         stack.RemoveElementAt(stackTopIndex);
    5089          62 :         if (stackTopIndex == 0) {
    5090          29 :           return true;
    5091             :         }
    5092             : 
    5093             :       // Just handle out-of-memory by parsing incorrectly.  It's
    5094             :       // highly unlikely we're dealing with a legitimate style sheet
    5095             :       // anyway.
    5096          28 :       } else if ('{' == symbol) {
    5097           0 :         stack.AppendElement('}');
    5098          28 :       } else if ('[' == symbol) {
    5099           0 :         stack.AppendElement(']');
    5100          28 :       } else if ('(' == symbol) {
    5101           0 :         stack.AppendElement(')');
    5102             :       }
    5103         171 :     } else if (eCSSToken_Function == tk->mType ||
    5104          69 :                eCSSToken_Bad_URL == tk->mType) {
    5105          33 :       stack.AppendElement(')');
    5106             :     }
    5107         163 :   }
    5108             : }
    5109             : 
    5110             : bool
    5111           0 : CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol)
    5112             : {
    5113           0 :   nsCSSToken* tk = &mToken;
    5114           0 :   AutoTArray<char16_t, 16> stack;
    5115           0 :   stack.AppendElement(aStopSymbol);
    5116             :   for (;;) {
    5117           0 :     if (!GetToken(true)) {
    5118           0 :       return true;
    5119             :     }
    5120           0 :     if (eCSSToken_Symbol == tk->mType) {
    5121           0 :       char16_t symbol = tk->mSymbol;
    5122           0 :       uint32_t stackTopIndex = stack.Length() - 1;
    5123           0 :       if (symbol == stack.ElementAt(stackTopIndex)) {
    5124           0 :         stack.RemoveElementAt(stackTopIndex);
    5125           0 :         if (stackTopIndex == 0) {
    5126           0 :           return true;
    5127             :         }
    5128             : 
    5129             :       // Just handle out-of-memory by parsing incorrectly.  It's
    5130             :       // highly unlikely we're dealing with a legitimate style sheet
    5131             :       // anyway.
    5132           0 :       } else if ('{' == symbol) {
    5133           0 :         stack.AppendElement('}');
    5134           0 :       } else if ('[' == symbol) {
    5135           0 :         stack.AppendElement(']');
    5136           0 :       } else if ('(' == symbol) {
    5137           0 :         stack.AppendElement(')');
    5138           0 :       } else if (')' == symbol ||
    5139           0 :                  ']' == symbol ||
    5140             :                  '}' == symbol) {
    5141           0 :         UngetToken();
    5142           0 :         return false;
    5143             :       }
    5144           0 :     } else if (eCSSToken_Function == tk->mType ||
    5145           0 :                eCSSToken_Bad_URL == tk->mType) {
    5146           0 :       stack.AppendElement(')');
    5147             :     }
    5148           0 :   }
    5149             : }
    5150             : 
    5151             : void
    5152           0 : CSSParserImpl::SkipUntilOneOf(const char16_t* aStopSymbolChars)
    5153             : {
    5154           0 :   nsCSSToken* tk = &mToken;
    5155           0 :   nsDependentString stopSymbolChars(aStopSymbolChars);
    5156             :   for (;;) {
    5157           0 :     if (!GetToken(true)) {
    5158           0 :       break;
    5159             :     }
    5160           0 :     if (eCSSToken_Symbol == tk->mType) {
    5161           0 :       char16_t symbol = tk->mSymbol;
    5162           0 :       if (stopSymbolChars.FindChar(symbol) != -1) {
    5163           0 :         break;
    5164           0 :       } else if ('{' == symbol) {
    5165           0 :         SkipUntil('}');
    5166           0 :       } else if ('[' == symbol) {
    5167           0 :         SkipUntil(']');
    5168           0 :       } else if ('(' == symbol) {
    5169           0 :         SkipUntil(')');
    5170             :       }
    5171           0 :     } else if (eCSSToken_Function == tk->mType ||
    5172           0 :                eCSSToken_Bad_URL == tk->mType) {
    5173           0 :       SkipUntil(')');
    5174             :     }
    5175           0 :   }
    5176           0 : }
    5177             : 
    5178             : void
    5179           0 : CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars)
    5180             : {
    5181           0 :   uint32_t i = aStopSymbolChars.Length();
    5182           0 :   while (i--) {
    5183           0 :     SkipUntil(aStopSymbolChars[i]);
    5184             :   }
    5185           0 : }
    5186             : 
    5187             : bool
    5188        3002 : CSSParserImpl::SkipDeclaration(bool aCheckForBraces)
    5189             : {
    5190        3002 :   nsCSSToken* tk = &mToken;
    5191             :   for (;;) {
    5192        3002 :     if (!GetToken(true)) {
    5193           7 :       if (aCheckForBraces) {
    5194           0 :         REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
    5195             :       }
    5196           7 :       return false;
    5197             :     }
    5198        2995 :     if (eCSSToken_Symbol == tk->mType) {
    5199        2995 :       char16_t symbol = tk->mSymbol;
    5200        2995 :       if (';' == symbol) {
    5201           0 :         break;
    5202             :       }
    5203        2995 :       if (aCheckForBraces) {
    5204        2995 :         if ('}' == symbol) {
    5205        2995 :           UngetToken();
    5206        2995 :           break;
    5207             :         }
    5208             :       }
    5209           0 :       if ('{' == symbol) {
    5210           0 :         SkipUntil('}');
    5211           0 :       } else if ('(' == symbol) {
    5212           0 :         SkipUntil(')');
    5213           0 :       } else if ('[' == symbol) {
    5214           0 :         SkipUntil(']');
    5215             :       }
    5216           0 :     } else if (eCSSToken_Function == tk->mType ||
    5217           0 :                eCSSToken_Bad_URL == tk->mType) {
    5218           0 :       SkipUntil(')');
    5219             :     }
    5220           0 :   }
    5221        2995 :   return true;
    5222             : }
    5223             : 
    5224             : void
    5225           0 : CSSParserImpl::SkipRuleSet(bool aInsideBraces)
    5226             : {
    5227           0 :   nsCSSToken* tk = &mToken;
    5228             :   for (;;) {
    5229           0 :     if (!GetToken(true)) {
    5230           0 :       REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
    5231           0 :       break;
    5232             :     }
    5233           0 :     if (eCSSToken_Symbol == tk->mType) {
    5234           0 :       char16_t symbol = tk->mSymbol;
    5235           0 :       if ('}' == symbol && aInsideBraces) {
    5236             :         // leave block closer for higher-level grammar to consume
    5237           0 :         UngetToken();
    5238           0 :         break;
    5239           0 :       } else if ('{' == symbol) {
    5240           0 :         SkipUntil('}');
    5241           0 :         break;
    5242           0 :       } else if ('(' == symbol) {
    5243           0 :         SkipUntil(')');
    5244           0 :       } else if ('[' == symbol) {
    5245           0 :         SkipUntil(']');
    5246             :       }
    5247           0 :     } else if (eCSSToken_Function == tk->mType ||
    5248           0 :                eCSSToken_Bad_URL == tk->mType) {
    5249           0 :       SkipUntil(')');
    5250             :     }
    5251           0 :   }
    5252           0 : }
    5253             : 
    5254             : void
    5255          50 : CSSParserImpl::PushGroup(css::GroupRule* aRule)
    5256             : {
    5257          50 :   mGroupStack.AppendElement(aRule);
    5258          50 : }
    5259             : 
    5260             : void
    5261          50 : CSSParserImpl::PopGroup()
    5262             : {
    5263          50 :   uint32_t count = mGroupStack.Length();
    5264          50 :   if (0 < count) {
    5265          50 :     mGroupStack.RemoveElementAt(count - 1);
    5266             :   }
    5267          50 : }
    5268             : 
    5269             : void
    5270        3203 : CSSParserImpl::AppendRule(css::Rule* aRule)
    5271             : {
    5272        3203 :   uint32_t count = mGroupStack.Length();
    5273        3203 :   if (0 < count) {
    5274         129 :     mGroupStack[count - 1]->AppendStyleRule(aRule);
    5275             :   }
    5276             :   else {
    5277        3074 :     mSheet->AppendStyleRule(aRule);
    5278             :   }
    5279        3203 : }
    5280             : 
    5281             : bool
    5282        2917 : CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
    5283             :                             bool aInsideBraces)
    5284             : {
    5285             :   // First get the list of selectors for the rule
    5286        2917 :   nsCSSSelectorList* slist = nullptr;
    5287             :   uint32_t linenum, colnum;
    5288        5834 :   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
    5289        2917 :       !ParseSelectorList(slist, char16_t('{'))) {
    5290           0 :     REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
    5291           0 :     OUTPUT_ERROR();
    5292           0 :     SkipRuleSet(aInsideBraces);
    5293           0 :     return false;
    5294             :   }
    5295        2917 :   NS_ASSERTION(nullptr != slist, "null selector list");
    5296        2917 :   CLEAR_ERROR();
    5297             : 
    5298             :   // Next parse the declaration block
    5299             :   uint32_t parseFlags = eParseDeclaration_InBraces |
    5300        2917 :                         eParseDeclaration_AllowImportant;
    5301        5834 :   RefPtr<css::Declaration> declaration = ParseDeclarationBlock(parseFlags);
    5302        2917 :   if (nullptr == declaration) {
    5303           0 :     delete slist;
    5304           0 :     return false;
    5305             :   }
    5306             : 
    5307             : #if 0
    5308             :   slist->Dump();
    5309             :   fputs("{\n", stdout);
    5310             :   declaration->List();
    5311             :   fputs("}\n", stdout);
    5312             : #endif
    5313             : 
    5314             :   // Translate the selector list and declaration block into style data
    5315             : 
    5316             :   RefPtr<css::StyleRule> rule = new css::StyleRule(slist, declaration,
    5317        8751 :                                                      linenum, colnum);
    5318        2917 :   (*aAppendFunc)(rule, aData);
    5319             : 
    5320        2917 :   return true;
    5321             : }
    5322             : 
    5323             : bool
    5324        3086 : CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
    5325             :                                  char16_t aStopChar)
    5326             : {
    5327        3086 :   nsCSSSelectorList* list = nullptr;
    5328        3086 :   if (! ParseSelectorGroup(list)) {
    5329             :     // must have at least one selector group
    5330           0 :     aListHead = nullptr;
    5331           0 :     return false;
    5332             :   }
    5333        3086 :   NS_ASSERTION(nullptr != list, "no selector list");
    5334        3086 :   aListHead = list;
    5335             : 
    5336             :   // After that there must either be a "," or a "{" (the latter if
    5337             :   // StopChar is nonzero)
    5338        3086 :   nsCSSToken* tk = &mToken;
    5339             :   for (;;) {
    5340        5556 :     if (! GetToken(true)) {
    5341           7 :       if (aStopChar == char16_t(0)) {
    5342           7 :         return true;
    5343             :       }
    5344             : 
    5345           0 :       REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
    5346           0 :       break;
    5347             :     }
    5348             : 
    5349        5549 :     if (eCSSToken_Symbol == tk->mType) {
    5350        5549 :       if (',' == tk->mSymbol) {
    5351        2470 :         nsCSSSelectorList* newList = nullptr;
    5352             :         // Another selector group must follow
    5353        2470 :         if (! ParseSelectorGroup(newList)) {
    5354           0 :           break;
    5355             :         }
    5356             :         // add new list to the end of the selector list
    5357        2470 :         list->mNext = newList;
    5358        2470 :         list = newList;
    5359        2470 :         continue;
    5360        3079 :       } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) {
    5361        3079 :         UngetToken();
    5362        3079 :         return true;
    5363             :       }
    5364             :     }
    5365           0 :     REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
    5366           0 :     UngetToken();
    5367           0 :     break;
    5368        2470 :   }
    5369             : 
    5370           0 :   delete aListHead;
    5371           0 :   aListHead = nullptr;
    5372           0 :   return false;
    5373             : }
    5374             : 
    5375          92 : static bool IsUniversalSelector(const nsCSSSelector& aSelector)
    5376             : {
    5377         184 :   return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
    5378         184 :                 (aSelector.mLowercaseTag == nullptr) &&
    5379         184 :                 (aSelector.mIDList == nullptr) &&
    5380         184 :                 (aSelector.mClassList == nullptr) &&
    5381         184 :                 (aSelector.mAttrList == nullptr) &&
    5382         276 :                 (aSelector.mNegations == nullptr) &&
    5383         184 :                 (aSelector.mPseudoClassList == nullptr));
    5384             : }
    5385             : 
    5386             : bool
    5387        5556 : CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
    5388             : {
    5389        5556 :   char16_t combinator = 0;
    5390       11112 :   nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
    5391             : 
    5392             :   for (;;) {
    5393        8359 :     if (!ParseSelector(list, combinator)) {
    5394           0 :       return false;
    5395             :     }
    5396             : 
    5397             :     // Look for a combinator.
    5398        8359 :     if (!GetToken(false)) {
    5399           7 :       break; // EOF ok here
    5400             :     }
    5401             : 
    5402        8352 :     combinator = char16_t(0);
    5403        8352 :     if (mToken.mType == eCSSToken_Whitespace) {
    5404        5471 :       if (!GetToken(true)) {
    5405           0 :         break; // EOF ok here
    5406             :       }
    5407        5471 :       combinator = char16_t(' ');
    5408             :     }
    5409             : 
    5410        8352 :     if (mToken.mType != eCSSToken_Symbol) {
    5411         352 :       UngetToken(); // not a combinator
    5412             :     } else {
    5413        8000 :       char16_t symbol = mToken.mSymbol;
    5414        8000 :       if (symbol == '+' || symbol == '>' || symbol == '~') {
    5415        2178 :         combinator = mToken.mSymbol;
    5416             :       } else {
    5417        5822 :         UngetToken(); // not a combinator
    5418        5822 :         if (symbol == ',' || symbol == '{' || symbol == ')') {
    5419             :           break; // end of selector group
    5420             :         }
    5421             :       }
    5422             :     }
    5423             : 
    5424        2803 :     if (!combinator) {
    5425           0 :       REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
    5426           0 :       return false;
    5427             :     }
    5428        2803 :   }
    5429             : 
    5430        5556 :   aList = list.forget();
    5431        5556 :   return true;
    5432             : }
    5433             : 
    5434             : #define SEL_MASK_NSPACE   0x01
    5435             : #define SEL_MASK_ELEM     0x02
    5436             : #define SEL_MASK_ID       0x04
    5437             : #define SEL_MASK_CLASS    0x08
    5438             : #define SEL_MASK_ATTRIB   0x10
    5439             : #define SEL_MASK_PCLASS   0x20
    5440             : #define SEL_MASK_PELEM    0x40
    5441             : 
    5442             : //
    5443             : // Parses an ID selector #name
    5444             : //
    5445             : CSSParserImpl::nsSelectorParsingStatus
    5446        1963 : CSSParserImpl::ParseIDSelector(int32_t&       aDataMask,
    5447             :                                nsCSSSelector& aSelector)
    5448             : {
    5449        1963 :   NS_ASSERTION(!mToken.mIdent.IsEmpty(),
    5450             :                "Empty mIdent in eCSSToken_ID token?");
    5451        1963 :   aDataMask |= SEL_MASK_ID;
    5452        1963 :   aSelector.AddID(mToken.mIdent);
    5453        1963 :   return eSelectorParsingStatus_Continue;
    5454             : }
    5455             : 
    5456             : //
    5457             : // Parses a class selector .name
    5458             : //
    5459             : CSSParserImpl::nsSelectorParsingStatus
    5460        2497 : CSSParserImpl::ParseClassSelector(int32_t&       aDataMask,
    5461             :                                   nsCSSSelector& aSelector)
    5462             : {
    5463        2497 :   if (! GetToken(false)) { // get ident
    5464           0 :     REPORT_UNEXPECTED_EOF(PEClassSelEOF);
    5465           0 :     return eSelectorParsingStatus_Error;
    5466             :   }
    5467        2497 :   if (eCSSToken_Ident != mToken.mType) {  // malformed selector
    5468           0 :     REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
    5469           0 :     UngetToken();
    5470           0 :     return eSelectorParsingStatus_Error;
    5471             :   }
    5472        2497 :   aDataMask |= SEL_MASK_CLASS;
    5473             : 
    5474        2497 :   aSelector.AddClass(mToken.mIdent);
    5475             : 
    5476        2497 :   return eSelectorParsingStatus_Continue;
    5477             : }
    5478             : 
    5479             : //
    5480             : // Parse a type element selector or a universal selector
    5481             : // namespace|type or namespace|* or *|* or *
    5482             : //
    5483             : CSSParserImpl::nsSelectorParsingStatus
    5484        8389 : CSSParserImpl::ParseTypeOrUniversalSelector(int32_t&       aDataMask,
    5485             :                                             nsCSSSelector& aSelector,
    5486             :                                             bool           aIsNegated)
    5487             : {
    5488       16778 :   nsAutoString buffer;
    5489        8389 :   if (mToken.IsSymbol('*')) {  // universal element selector, or universal namespace
    5490         178 :     if (ExpectSymbol('|', false)) {  // was namespace
    5491         126 :       aDataMask |= SEL_MASK_NSPACE;
    5492         126 :       aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
    5493             : 
    5494         126 :       if (! GetToken(false)) {
    5495           0 :         REPORT_UNEXPECTED_EOF(PETypeSelEOF);
    5496           0 :         return eSelectorParsingStatus_Error;
    5497             :       }
    5498         126 :       if (eCSSToken_Ident == mToken.mType) {  // element name
    5499           0 :         aDataMask |= SEL_MASK_ELEM;
    5500             : 
    5501           0 :         aSelector.SetTag(mToken.mIdent);
    5502             :       }
    5503         126 :       else if (mToken.IsSymbol('*')) {  // universal selector
    5504         126 :         aDataMask |= SEL_MASK_ELEM;
    5505             :         // don't set tag
    5506             :       }
    5507             :       else {
    5508           0 :         REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
    5509           0 :         UngetToken();
    5510           0 :         return eSelectorParsingStatus_Error;
    5511             :       }
    5512             :     }
    5513             :     else {  // was universal element selector
    5514          52 :       SetDefaultNamespaceOnSelector(aSelector);
    5515          52 :       aDataMask |= SEL_MASK_ELEM;
    5516             :       // don't set any tag in the selector
    5517             :     }
    5518         178 :     if (! GetToken(false)) {   // premature eof is ok (here!)
    5519           0 :       return eSelectorParsingStatus_Done;
    5520             :     }
    5521             :   }
    5522        8211 :   else if (eCSSToken_Ident == mToken.mType) {    // element name or namespace name
    5523        3657 :     buffer = mToken.mIdent; // hang on to ident
    5524             : 
    5525        3657 :     if (ExpectSymbol('|', false)) {  // was namespace
    5526          76 :       aDataMask |= SEL_MASK_NSPACE;
    5527          76 :       int32_t nameSpaceID = GetNamespaceIdForPrefix(buffer);
    5528          76 :       if (nameSpaceID == kNameSpaceID_Unknown) {
    5529           0 :         return eSelectorParsingStatus_Error;
    5530             :       }
    5531          76 :       aSelector.SetNameSpace(nameSpaceID);
    5532             : 
    5533          76 :       if (! GetToken(false)) {
    5534           0 :         REPORT_UNEXPECTED_EOF(PETypeSelEOF);
    5535           0 :         return eSelectorParsingStatus_Error;
    5536             :       }
    5537          76 :       if (eCSSToken_Ident == mToken.mType) {  // element name
    5538          38 :         aDataMask |= SEL_MASK_ELEM;
    5539          38 :         aSelector.SetTag(mToken.mIdent);
    5540             :       }
    5541          38 :       else if (mToken.IsSymbol('*')) {  // universal selector
    5542          38 :         aDataMask |= SEL_MASK_ELEM;
    5543             :         // don't set tag
    5544             :       }
    5545             :       else {
    5546           0 :         REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
    5547           0 :         UngetToken();
    5548           0 :         return eSelectorParsingStatus_Error;
    5549             :       }
    5550             :     }
    5551             :     else {  // was element name
    5552        3581 :       SetDefaultNamespaceOnSelector(aSelector);
    5553        3581 :       aSelector.SetTag(buffer);
    5554             : 
    5555        3581 :       aDataMask |= SEL_MASK_ELEM;
    5556             :     }
    5557        3657 :     if (! GetToken(false)) {   // premature eof is ok (here!)
    5558           2 :       return eSelectorParsingStatus_Done;
    5559             :     }
    5560             :   }
    5561        4554 :   else if (mToken.IsSymbol('|')) {  // No namespace
    5562           0 :     aDataMask |= SEL_MASK_NSPACE;
    5563           0 :     aSelector.SetNameSpace(kNameSpaceID_None);  // explicit NO namespace
    5564             : 
    5565             :     // get mandatory tag
    5566           0 :     if (! GetToken(false)) {
    5567           0 :       REPORT_UNEXPECTED_EOF(PETypeSelEOF);
    5568           0 :       return eSelectorParsingStatus_Error;
    5569             :     }
    5570           0 :     if (eCSSToken_Ident == mToken.mType) {  // element name
    5571           0 :       aDataMask |= SEL_MASK_ELEM;
    5572           0 :       aSelector.SetTag(mToken.mIdent);
    5573             :     }
    5574           0 :     else if (mToken.IsSymbol('*')) {  // universal selector
    5575           0 :       aDataMask |= SEL_MASK_ELEM;
    5576             :       // don't set tag
    5577             :     }
    5578             :     else {
    5579           0 :       REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
    5580           0 :       UngetToken();
    5581           0 :       return eSelectorParsingStatus_Error;
    5582             :     }
    5583           0 :     if (! GetToken(false)) {   // premature eof is ok (here!)
    5584           0 :       return eSelectorParsingStatus_Done;
    5585             :     }
    5586             :   }
    5587             :   else {
    5588        4554 :     SetDefaultNamespaceOnSelector(aSelector);
    5589             :   }
    5590             : 
    5591        8387 :   if (aIsNegated) {
    5592             :     // restore last token read in case of a negated type selector
    5593          30 :     UngetToken();
    5594             :   }
    5595        8387 :   return eSelectorParsingStatus_Continue;
    5596             : }
    5597             : 
    5598             : //
    5599             : // Parse attribute selectors [attr], [attr=value], [attr|=value],
    5600             : // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
    5601             : //
    5602             : CSSParserImpl::nsSelectorParsingStatus
    5603        2382 : CSSParserImpl::ParseAttributeSelector(int32_t&       aDataMask,
    5604             :                                       nsCSSSelector& aSelector)
    5605             : {
    5606        2382 :   if (! GetToken(true)) { // premature EOF
    5607           0 :     REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
    5608           0 :     return eSelectorParsingStatus_Error;
    5609             :   }
    5610             : 
    5611        2382 :   int32_t nameSpaceID = kNameSpaceID_None;
    5612        4764 :   nsAutoString  attr;
    5613        2382 :   if (mToken.IsSymbol('*')) { // wildcard namespace
    5614           0 :     nameSpaceID = kNameSpaceID_Unknown;
    5615           0 :     if (ExpectSymbol('|', false)) {
    5616           0 :       if (! GetToken(false)) { // premature EOF
    5617           0 :         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
    5618           0 :         return eSelectorParsingStatus_Error;
    5619             :       }
    5620           0 :       if (eCSSToken_Ident == mToken.mType) { // attr name
    5621           0 :         attr = mToken.mIdent;
    5622             :       }
    5623             :       else {
    5624           0 :         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
    5625           0 :         UngetToken();
    5626           0 :         return eSelectorParsingStatus_Error;
    5627             :        }
    5628             :     }
    5629             :     else {
    5630           0 :       REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
    5631           0 :       return eSelectorParsingStatus_Error;
    5632             :     }
    5633             :   }
    5634        2382 :   else if (mToken.IsSymbol('|')) { // NO namespace
    5635           0 :     if (! GetToken(false)) { // premature EOF
    5636           0 :       REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
    5637           0 :       return eSelectorParsingStatus_Error;
    5638             :     }
    5639           0 :     if (eCSSToken_Ident == mToken.mType) { // attr name
    5640           0 :       attr = mToken.mIdent;
    5641             :     }
    5642             :     else {
    5643           0 :       REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
    5644           0 :       UngetToken();
    5645           0 :       return eSelectorParsingStatus_Error;
    5646             :     }
    5647             :   }
    5648        2382 :   else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
    5649        2382 :     attr = mToken.mIdent; // hang on to it
    5650        2382 :     if (ExpectSymbol('|', false)) {  // was a namespace
    5651           2 :       nameSpaceID = GetNamespaceIdForPrefix(attr);
    5652           2 :       if (nameSpaceID == kNameSpaceID_Unknown) {
    5653           0 :         return eSelectorParsingStatus_Error;
    5654             :       }
    5655           2 :       if (! GetToken(false)) { // premature EOF
    5656           0 :         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
    5657           0 :         return eSelectorParsingStatus_Error;
    5658             :       }
    5659           2 :       if (eCSSToken_Ident == mToken.mType) { // attr name
    5660           2 :         attr = mToken.mIdent;
    5661             :       }
    5662             :       else {
    5663           0 :         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
    5664           0 :         UngetToken();
    5665           0 :         return eSelectorParsingStatus_Error;
    5666             :       }
    5667             :     }
    5668             :   }
    5669             :   else {  // malformed
    5670           0 :     REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
    5671           0 :     UngetToken();
    5672           0 :     return eSelectorParsingStatus_Error;
    5673             :   }
    5674             : 
    5675        2382 :   bool gotEOF = false;
    5676        2382 :   if (! GetToken(true)) { // premature EOF
    5677             :     // Treat this just like we saw a ']', but do still output the
    5678             :     // warning, similar to what ExpectSymbol does.
    5679           0 :     REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
    5680           0 :     gotEOF = true;
    5681             :   }
    5682        4764 :   if (gotEOF ||
    5683        2499 :       (eCSSToken_Symbol == mToken.mType) ||
    5684         129 :       (eCSSToken_Includes == mToken.mType) ||
    5685          24 :       (eCSSToken_Dashmatch == mToken.mType) ||
    5686          14 :       (eCSSToken_Beginsmatch == mToken.mType) ||
    5687           4 :       (eCSSToken_Endsmatch == mToken.mType) ||
    5688           2 :       (eCSSToken_Containsmatch == mToken.mType)) {
    5689             :     uint8_t func;
    5690             :     // Important: Check the EOF/']' case first, since if gotEOF we
    5691             :     // don't want to be examining mToken.
    5692        2382 :     if (gotEOF || ']' == mToken.mSymbol) {
    5693         737 :       aDataMask |= SEL_MASK_ATTRIB;
    5694         737 :       aSelector.AddAttribute(nameSpaceID, attr);
    5695         737 :       func = NS_ATTR_FUNC_SET;
    5696             :     }
    5697        1645 :     else if (eCSSToken_Includes == mToken.mType) {
    5698         105 :       func = NS_ATTR_FUNC_INCLUDES;
    5699             :     }
    5700        1540 :     else if (eCSSToken_Dashmatch == mToken.mType) {
    5701           0 :       func = NS_ATTR_FUNC_DASHMATCH;
    5702             :     }
    5703        1540 :     else if (eCSSToken_Beginsmatch == mToken.mType) {
    5704          10 :       func = NS_ATTR_FUNC_BEGINSMATCH;
    5705             :     }
    5706        1530 :     else if (eCSSToken_Endsmatch == mToken.mType) {
    5707           0 :       func = NS_ATTR_FUNC_ENDSMATCH;
    5708             :     }
    5709        1530 :     else if (eCSSToken_Containsmatch == mToken.mType) {
    5710           2 :       func = NS_ATTR_FUNC_CONTAINSMATCH;
    5711             :     }
    5712        1528 :     else if ('=' == mToken.mSymbol) {
    5713        1528 :       func = NS_ATTR_FUNC_EQUALS;
    5714             :     }
    5715             :     else {
    5716           0 :       REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
    5717           0 :       UngetToken(); // bad function
    5718           0 :       return eSelectorParsingStatus_Error;
    5719             :     }
    5720        2382 :     if (NS_ATTR_FUNC_SET != func) { // get value
    5721        1645 :       if (! GetToken(true)) { // premature EOF
    5722           0 :         REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
    5723           0 :         return eSelectorParsingStatus_Error;
    5724             :       }
    5725        1645 :       if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
    5726        3290 :         nsAutoString  value(mToken.mIdent);
    5727             :         // Avoid duplicating the eof handling by just not checking for
    5728             :         // the 'i' annotation if we got eof.
    5729             :         typedef nsAttrSelector::ValueCaseSensitivity ValueCaseSensitivity;
    5730             :         ValueCaseSensitivity valueCaseSensitivity =
    5731        1645 :           ValueCaseSensitivity::CaseSensitive;
    5732        1645 :         bool eof = !GetToken(true);
    5733        1645 :         if (!eof) {
    5734        1645 :           if (eCSSToken_Ident == mToken.mType &&
    5735           0 :               mToken.mIdent.LowerCaseEqualsLiteral("i")) {
    5736           0 :             valueCaseSensitivity = ValueCaseSensitivity::CaseInsensitive;
    5737           0 :             eof = !GetToken(true);
    5738             :           }
    5739             :         }
    5740             :         bool gotClosingBracket;
    5741        1645 :         if (eof) { // premature EOF
    5742             :           // Report a warning, but then treat it as a closing bracket.
    5743           0 :           REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
    5744           0 :           gotClosingBracket = true;
    5745             :         } else {
    5746        1645 :           gotClosingBracket = mToken.IsSymbol(']');
    5747             :         }
    5748        1645 :         if (gotClosingBracket) {
    5749             :           // For cases when this style sheet is applied to an HTML
    5750             :           // element in an HTML document, and the attribute selector is
    5751             :           // for a non-namespaced attribute, then check to see if it's
    5752             :           // one of the known attributes whose VALUE is
    5753             :           // case-insensitive.
    5754        1645 :           if (valueCaseSensitivity != ValueCaseSensitivity::CaseInsensitive &&
    5755             :               nameSpaceID == kNameSpaceID_None) {
    5756             :             static const char* caseInsensitiveHTMLAttribute[] = {
    5757             :               // list based on http://www.w3.org/TR/html4/
    5758             :               "lang",
    5759             :               "dir",
    5760             :               "http-equiv",
    5761             :               "text",
    5762             :               "link",
    5763             :               "vlink",
    5764             :               "alink",
    5765             :               "compact",
    5766             :               "align",
    5767             :               "frame",
    5768             :               "rules",
    5769             :               "valign",
    5770             :               "scope",
    5771             :               "axis",
    5772             :               "nowrap",
    5773             :               "hreflang",
    5774             :               "rel",
    5775             :               "rev",
    5776             :               "charset",
    5777             :               "codetype",
    5778             :               "declare",
    5779             :               "valuetype",
    5780             :               "shape",
    5781             :               "nohref",
    5782             :               "media",
    5783             :               "bgcolor",
    5784             :               "clear",
    5785             :               "color",
    5786             :               "face",
    5787             :               "noshade",
    5788             :               "noresize",
    5789             :               "scrolling",
    5790             :               "target",
    5791             :               "method",
    5792             :               "enctype",
    5793             :               "accept-charset",
    5794             :               "accept",
    5795             :               "checked",
    5796             :               "multiple",
    5797             :               "selected",
    5798             :               "disabled",
    5799             :               "readonly",
    5800             :               "language",
    5801             :               "defer",
    5802             :               "type",
    5803             :               // additional attributes not in HTML4
    5804             :               "direction", // marquee
    5805             :               nullptr
    5806             :             };
    5807        1643 :             short i = 0;
    5808             :             const char* htmlAttr;
    5809      141417 :             while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
    5810       70426 :               if (attr.LowerCaseEqualsASCII(htmlAttr)) {
    5811         539 :                 valueCaseSensitivity = ValueCaseSensitivity::CaseInsensitiveInHTML;
    5812         539 :                 break;
    5813             :               }
    5814             :             }
    5815             :           }
    5816        1645 :           aDataMask |= SEL_MASK_ATTRIB;
    5817        1645 :           aSelector.AddAttribute(nameSpaceID, attr, func, value,
    5818        1645 :                                  valueCaseSensitivity);
    5819             :         }
    5820             :         else {
    5821           0 :           REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
    5822           0 :           UngetToken();
    5823           0 :           return eSelectorParsingStatus_Error;
    5824        1645 :         }
    5825             :       }
    5826             :       else {
    5827           0 :         REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
    5828           0 :         UngetToken();
    5829           0 :         return eSelectorParsingStatus_Error;
    5830             :       }
    5831        2382 :     }
    5832             :   }
    5833             :   else {
    5834           0 :     REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
    5835           0 :     UngetToken(); // bad dog, no biscut!
    5836           0 :     return eSelectorParsingStatus_Error;
    5837             :    }
    5838        2382 :    return eSelectorParsingStatus_Continue;
    5839             : }
    5840             : 
    5841             : //
    5842             : // Parse pseudo-classes and pseudo-elements
    5843             : //
    5844             : CSSParserImpl::nsSelectorParsingStatus
    5845        2857 : CSSParserImpl::ParsePseudoSelector(int32_t&       aDataMask,
    5846             :                                    nsCSSSelector& aSelector,
    5847             :                                    bool           aIsNegated,
    5848             :                                    nsIAtom**      aPseudoElement,
    5849             :                                    nsAtomList**   aPseudoElementArgs,
    5850             :                                    CSSPseudoElementType* aPseudoElementType)
    5851             : {
    5852        2857 :   NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs),
    5853             :                "expected location to store pseudo element");
    5854        2857 :   NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs),
    5855             :                "negated selectors shouldn't have a place to store "
    5856             :                "pseudo elements");
    5857        2857 :   if (! GetToken(false)) { // premature eof
    5858           0 :     REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
    5859           0 :     return eSelectorParsingStatus_Error;
    5860             :   }
    5861             : 
    5862             :   // First, find out whether we are parsing a CSS3 pseudo-element
    5863        2857 :   bool parsingPseudoElement = false;
    5864        2857 :   if (mToken.IsSymbol(':')) {
    5865         344 :     parsingPseudoElement = true;
    5866         344 :     if (! GetToken(false)) { // premature eof
    5867           0 :       REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
    5868           0 :       return eSelectorParsingStatus_Error;
    5869             :     }
    5870             :   }
    5871             : 
    5872             :   // Do some sanity-checking on the token
    5873        2857 :   if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
    5874             :     // malformed selector
    5875           0 :     REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
    5876           0 :     UngetToken();
    5877           0 :     return eSelectorParsingStatus_Error;
    5878             :   }
    5879             : 
    5880             :   // OK, now we know we have an mIdent.  Atomize it.  All the atoms, for
    5881             :   // pseudo-classes as well as pseudo-elements, start with a single ':'.
    5882        5714 :   nsAutoString buffer;
    5883        2857 :   buffer.Append(char16_t(':'));
    5884        2857 :   buffer.Append(mToken.mIdent);
    5885        2857 :   nsContentUtils::ASCIIToLower(buffer);
    5886        5714 :   nsCOMPtr<nsIAtom> pseudo = NS_Atomize(buffer);
    5887             : 
    5888             :   // stash away some info about this pseudo so we only have to get it once.
    5889        2857 :   bool isTreePseudo = false;
    5890        2857 :   CSSEnabledState enabledState = EnabledState();
    5891             :   CSSPseudoElementType pseudoElementType =
    5892        2857 :     nsCSSPseudoElements::GetPseudoType(pseudo, enabledState);
    5893             :   CSSPseudoClassType pseudoClassType =
    5894        2857 :     nsCSSPseudoClasses::GetPseudoType(pseudo, enabledState);
    5895             :   bool pseudoClassIsUserAction =
    5896        2857 :     nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
    5897             : 
    5898        2857 :   if (nsCSSAnonBoxes::IsNonElement(pseudo)) {
    5899             :     // Non-element anonymous boxes should not match any rule.
    5900           0 :     REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
    5901           0 :     UngetToken();
    5902           0 :     return eSelectorParsingStatus_Error;
    5903             :   }
    5904             : 
    5905             :   // We currently allow :-moz-placeholder and ::-moz-placeholder and
    5906             :   // ::placeholder. We have to be a bit stricter regarding the
    5907             :   // pseudo-element parsing rules.
    5908        2857 :   if (pseudoElementType == CSSPseudoElementType::placeholder &&
    5909             :       pseudoClassType == CSSPseudoClassType::mozPlaceholder) {
    5910           0 :     if (parsingPseudoElement) {
    5911           0 :       pseudoClassType = CSSPseudoClassType::NotPseudo;
    5912             :     } else {
    5913           0 :       pseudoElementType = CSSPseudoElementType::NotPseudo;
    5914             :     }
    5915             :   }
    5916             : 
    5917             : #ifdef MOZ_XUL
    5918        2857 :   isTreePseudo = (pseudoElementType == CSSPseudoElementType::XULTree);
    5919             :   // If a tree pseudo-element is using the function syntax, it will
    5920             :   // get isTree set here and will pass the check below that only
    5921             :   // allows functions if they are in our list of things allowed to be
    5922             :   // functions.  If it is _not_ using the function syntax, isTree will
    5923             :   // be false, and it will still pass that check.  So the tree
    5924             :   // pseudo-elements are allowed to be either functions or not, as
    5925             :   // desired.
    5926        2857 :   bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
    5927             : #endif
    5928        2857 :   bool isPseudoElement = (pseudoElementType < CSSPseudoElementType::Count);
    5929             :   // anonymous boxes are only allowed if they're the tree boxes or we have
    5930             :   // enabled agent rules
    5931        3019 :   bool isAnonBox = isTreePseudo ||
    5932        2697 :     ((pseudoElementType == CSSPseudoElementType::InheritingAnonBox ||
    5933          92 :       pseudoElementType == CSSPseudoElementType::NonInheritingAnonBox) &&
    5934        2949 :      AgentRulesEnabled());
    5935             :   bool isPseudoClass =
    5936        2857 :     (pseudoClassType != CSSPseudoClassType::NotPseudo);
    5937             : 
    5938        2857 :   NS_ASSERTION(!isPseudoClass ||
    5939             :                pseudoElementType == CSSPseudoElementType::NotPseudo,
    5940             :                "Why is this atom both a pseudo-class and a pseudo-element?");
    5941        2857 :   NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1,
    5942             :                "Shouldn't be more than one of these");
    5943             : 
    5944        2857 :   if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
    5945             :     // Not a pseudo-class, not a pseudo-element.... forget it
    5946           0 :     REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
    5947           0 :     UngetToken();
    5948           0 :     return eSelectorParsingStatus_Error;
    5949             :   }
    5950             : 
    5951             :   // If it's a function token, it better be on our "ok" list, and if the name
    5952             :   // is that of a function pseudo it better be a function token
    5953        5714 :   if ((eCSSToken_Function == mToken.mType) !=
    5954             :       (
    5955             : #ifdef MOZ_XUL
    5956        2812 :        isTree ||
    5957             : #endif
    5958        2238 :        CSSPseudoClassType::negation == pseudoClassType ||
    5959        4300 :        nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
    5960        6980 :        nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
    5961        2061 :        nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
    5962             :     // There are no other function pseudos
    5963           0 :     REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
    5964           0 :     UngetToken();
    5965           0 :     return eSelectorParsingStatus_Error;
    5966             :   }
    5967             : 
    5968             :   // If it starts with "::", it better be a pseudo-element
    5969        3201 :   if (parsingPseudoElement &&
    5970         506 :       !isPseudoElement &&
    5971         162 :       !isAnonBox) {
    5972           0 :     REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
    5973           0 :     UngetToken();
    5974           0 :     return eSelectorParsingStatus_Error;
    5975             :   }
    5976             : 
    5977        2857 :   if (aSelector.IsPseudoElement()) {
    5978           0 :     CSSPseudoElementType type = aSelector.PseudoType();
    5979           0 :     if (type >= CSSPseudoElementType::Count ||
    5980           0 :         !nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
    5981             :       // We only allow user action pseudo-classes on certain pseudo-elements.
    5982           0 :       REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
    5983           0 :       UngetToken();
    5984           0 :       return eSelectorParsingStatus_Error;
    5985             :     }
    5986           0 :     if (!isPseudoClass || !pseudoClassIsUserAction) {
    5987             :       // CSS 4 Selectors says that pseudo-elements can only be followed by
    5988             :       // a user action pseudo-class.
    5989           0 :       REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
    5990           0 :       UngetToken();
    5991           0 :       return eSelectorParsingStatus_Error;
    5992             :     }
    5993             :   }
    5994             : 
    5995        2857 :   if (!parsingPseudoElement &&
    5996             :       CSSPseudoClassType::negation == pseudoClassType) {
    5997         574 :     if (aIsNegated) { // :not() can't be itself negated
    5998           0 :       REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
    5999           0 :       UngetToken();
    6000           0 :       return eSelectorParsingStatus_Error;
    6001             :     }
    6002             :     // CSS 3 Negation pseudo-class takes one simple selector as argument
    6003             :     nsSelectorParsingStatus parsingStatus =
    6004         574 :       ParseNegatedSimpleSelector(aDataMask, aSelector);
    6005         574 :     if (eSelectorParsingStatus_Continue != parsingStatus) {
    6006           0 :       return parsingStatus;
    6007         574 :     }
    6008             :   }
    6009        2283 :   else if (!parsingPseudoElement && isPseudoClass) {
    6010        1927 :     aDataMask |= SEL_MASK_PCLASS;
    6011        1927 :     if (eCSSToken_Function == mToken.mType) {
    6012             :       nsSelectorParsingStatus parsingStatus;
    6013         339 :       if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
    6014             :         parsingStatus =
    6015         176 :           ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
    6016             :       }
    6017         163 :       else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) {
    6018             :         parsingStatus =
    6019           1 :           ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType);
    6020             :       }
    6021             :       else {
    6022         162 :         MOZ_ASSERT(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
    6023             :                    "unexpected pseudo with function token");
    6024             :         parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
    6025         162 :                                                             pseudoClassType);
    6026             :       }
    6027         339 :       if (eSelectorParsingStatus_Continue != parsingStatus) {
    6028           0 :         if (eSelectorParsingStatus_Error == parsingStatus) {
    6029           0 :           SkipUntil(')');
    6030             :         }
    6031           0 :         return parsingStatus;
    6032             :       }
    6033             :     }
    6034             :     else {
    6035        1588 :       aSelector.AddPseudoClass(pseudoClassType);
    6036        1927 :     }
    6037             :   }
    6038         356 :   else if (isPseudoElement || isAnonBox) {
    6039             :     // Pseudo-element.  Make some more sanity checks.
    6040             : 
    6041         356 :     if (aIsNegated) { // pseudo-elements can't be negated
    6042           0 :       REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
    6043           0 :       UngetToken();
    6044           0 :       return eSelectorParsingStatus_Error;
    6045             :     }
    6046             :     // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
    6047             :     // to have a single ':' on them.  Others (CSS3+ pseudo-elements and
    6048             :     // various -moz-* pseudo-elements) must have |parsingPseudoElement|
    6049             :     // set.
    6050         724 :     if (!parsingPseudoElement &&
    6051          12 :         !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
    6052             : #ifdef MOZ_XUL
    6053         356 :         && !isTreePseudo
    6054             : #endif
    6055             :         ) {
    6056           0 :       REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
    6057           0 :       UngetToken();
    6058           0 :       return eSelectorParsingStatus_Error;
    6059             :     }
    6060             : 
    6061         356 :     if (0 == (aDataMask & SEL_MASK_PELEM)) {
    6062         356 :       aDataMask |= SEL_MASK_PELEM;
    6063         356 :       NS_ADDREF(*aPseudoElement = pseudo);
    6064         356 :       *aPseudoElementType = pseudoElementType;
    6065             : 
    6066             : #ifdef MOZ_XUL
    6067         356 :       if (isTree) {
    6068             :         // We have encountered a pseudoelement of the form
    6069             :         // -moz-tree-xxxx(a,b,c).  We parse (a,b,c) and add each
    6070             :         // item in the list to the pseudoclass list.  They will be pulled
    6071             :         // from the list later along with the pseudo-element.
    6072          45 :         if (!ParseTreePseudoElement(aPseudoElementArgs)) {
    6073           0 :           return eSelectorParsingStatus_Error;
    6074             :         }
    6075             :       }
    6076             : #endif
    6077             : 
    6078             :       // Pseudo-elements can only be followed by user action pseudo-classes
    6079             :       // or be the end of the selector.  So the next non-whitespace token must
    6080             :       // be ':', '{' or ',' or EOF.
    6081         356 :       if (!GetToken(true)) { // premature eof is ok (here!)
    6082           0 :         return eSelectorParsingStatus_Done;
    6083             :       }
    6084         356 :       if (parsingPseudoElement && mToken.IsSymbol(':')) {
    6085           0 :         UngetToken();
    6086           0 :         return eSelectorParsingStatus_Continue;
    6087             :       }
    6088         356 :       if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
    6089         356 :         UngetToken();
    6090         356 :         return eSelectorParsingStatus_Done;
    6091             :       }
    6092           0 :       REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC);
    6093           0 :       UngetToken();
    6094           0 :       return eSelectorParsingStatus_Error;
    6095             :     }
    6096             :     else {  // multiple pseudo elements, not legal
    6097           0 :       REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
    6098           0 :       UngetToken();
    6099           0 :       return eSelectorParsingStatus_Error;
    6100             :     }
    6101             :   }
    6102             : #ifdef DEBUG
    6103             :   else {
    6104             :     // We should never end up here.  Indeed, if we ended up here, we know (from
    6105             :     // the current if/else cascade) that !isPseudoElement and !isAnonBox.  But
    6106             :     // then due to our earlier check we know that isPseudoClass.  Since we
    6107             :     // didn't fall into the isPseudoClass case in this cascade, we must have
    6108             :     // parsingPseudoElement.  But we've already checked the
    6109             :     // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
    6110             :     // it's happened.
    6111           0 :     NS_NOTREACHED("How did this happen?");
    6112             :   }
    6113             : #endif
    6114        2501 :   return eSelectorParsingStatus_Continue;
    6115             : }
    6116             : 
    6117             : //
    6118             : // Parse the argument of a negation pseudo-class :not()
    6119             : //
    6120             : CSSParserImpl::nsSelectorParsingStatus
    6121         574 : CSSParserImpl::ParseNegatedSimpleSelector(int32_t&       aDataMask,
    6122             :                                           nsCSSSelector& aSelector)
    6123             : {
    6124         574 :   if (! GetToken(true)) { // premature eof
    6125           0 :     REPORT_UNEXPECTED_EOF(PENegationEOF);
    6126           0 :     return eSelectorParsingStatus_Error;
    6127             :   }
    6128             : 
    6129         574 :   if (mToken.IsSymbol(')')) {
    6130           0 :     REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
    6131           0 :     return eSelectorParsingStatus_Error;
    6132             :   }
    6133             : 
    6134             :   // Create a new nsCSSSelector and add it to the end of
    6135             :   // aSelector.mNegations.
    6136             :   // Given the current parsing rules, every selector in mNegations
    6137             :   // contains only one simple selector (css3 definition) within it.
    6138             :   // This could easily change in future versions of CSS, and the only
    6139             :   // thing we need to change to support that is this parsing code and the
    6140             :   // serialization code for nsCSSSelector.
    6141         574 :   nsCSSSelector *newSel = new nsCSSSelector();
    6142         574 :   nsCSSSelector* negations = &aSelector;
    6143         944 :   while (negations->mNegations) {
    6144         185 :     negations = negations->mNegations;
    6145             :   }
    6146         574 :   negations->mNegations = newSel;
    6147             : 
    6148             :   nsSelectorParsingStatus parsingStatus;
    6149         574 :   if (eCSSToken_ID == mToken.mType) { // #id
    6150          26 :     parsingStatus = ParseIDSelector(aDataMask, *newSel);
    6151             :   }
    6152         548 :   else if (mToken.IsSymbol('.')) {    // .class
    6153          38 :     parsingStatus = ParseClassSelector(aDataMask, *newSel);
    6154             :   }
    6155         510 :   else if (mToken.IsSymbol(':')) {    // :pseudo
    6156             :     parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true,
    6157         101 :                                         nullptr, nullptr, nullptr);
    6158             :   }
    6159         409 :   else if (mToken.IsSymbol('[')) {    // [attribute
    6160         379 :     parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
    6161         379 :     if (eSelectorParsingStatus_Error == parsingStatus) {
    6162             :       // Skip forward to the matching ']'
    6163           0 :       SkipUntil(']');
    6164             :     }
    6165             :   }
    6166             :   else {
    6167             :     // then it should be a type element or universal selector
    6168          30 :     parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true);
    6169             :   }
    6170         574 :   if (eSelectorParsingStatus_Error == parsingStatus) {
    6171           0 :     REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
    6172           0 :     SkipUntil(')');
    6173           0 :     return parsingStatus;
    6174             :   }
    6175             :   // close the parenthesis
    6176         574 :   if (!ExpectSymbol(')', true)) {
    6177           0 :     REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
    6178           0 :     SkipUntil(')');
    6179           0 :     return eSelectorParsingStatus_Error;
    6180             :   }
    6181             : 
    6182         574 :   NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown ||
    6183             :                (!newSel->mIDList && !newSel->mClassList &&
    6184             :                 !newSel->mPseudoClassList && !newSel->mAttrList),
    6185             :                "Need to fix the serialization code to deal with this");
    6186             : 
    6187         574 :   return eSelectorParsingStatus_Continue;
    6188             : }
    6189             : 
    6190             : //
    6191             : // Parse the argument of a pseudo-class that has an ident arg
    6192             : //
    6193             : CSSParserImpl::nsSelectorParsingStatus
    6194         176 : CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
    6195             :                                             CSSPseudoClassType aType)
    6196             : {
    6197         176 :   if (! GetToken(true)) { // premature eof
    6198           0 :     REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
    6199           0 :     return eSelectorParsingStatus_Error;
    6200             :   }
    6201             :   // We expect an identifier with a language abbreviation
    6202         176 :   if (eCSSToken_Ident != mToken.mType) {
    6203           0 :     REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
    6204           0 :     UngetToken();
    6205           0 :     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6206             :   }
    6207             : 
    6208             :   // -moz-locale-dir and dir take an identifier argument.  While
    6209             :   // only 'ltr' and 'rtl' (case-insensitively) will match anything, any
    6210             :   // other identifier is still valid.
    6211         176 :   if (aType == CSSPseudoClassType::mozLocaleDir ||
    6212             :       aType == CSSPseudoClassType::dir) {
    6213         133 :     nsContentUtils::ASCIIToLower(mToken.mIdent); // case insensitive
    6214             :   }
    6215             : 
    6216             :   // Add the pseudo with the language parameter
    6217         176 :   aSelector.AddPseudoClass(aType, mToken.mIdent.get());
    6218             : 
    6219             :   // close the parenthesis
    6220         176 :   if (!ExpectSymbol(')', true)) {
    6221           0 :     REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
    6222           0 :     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6223             :   }
    6224             : 
    6225         176 :   return eSelectorParsingStatus_Continue;
    6226             : }
    6227             : 
    6228             : CSSParserImpl::nsSelectorParsingStatus
    6229           1 : CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
    6230             :                                               CSSPseudoClassType aType)
    6231             : {
    6232           1 :   int32_t numbers[2] = { 0, 0 };
    6233           1 :   bool lookForB = true;
    6234           1 :   bool onlyN = false;
    6235           1 :   bool hasSign = false;
    6236           1 :   int sign = 1;
    6237             : 
    6238             :   // Follow the whitespace rules as proposed in
    6239             :   // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
    6240             : 
    6241           1 :   if (! GetToken(true)) {
    6242           0 :     REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
    6243           0 :     return eSelectorParsingStatus_Error;
    6244             :   }
    6245             : 
    6246             :   // A helper function that checks if the token starts with literal string
    6247             :   // |aStr| using a case-insensitive match.
    6248           4 :   auto TokenBeginsWith = [this] (const nsLiteralString& aStr) {
    6249           4 :     return StringBeginsWith(mToken.mIdent, aStr,
    6250           4 :                             nsASCIICaseInsensitiveStringComparator());
    6251           5 :   };
    6252             : 
    6253           1 :   if (mToken.IsSymbol('+')) {
    6254             :     // This can only be +n, since +an, -an, +a, -a will all
    6255             :     // parse a number as the first token, and -n is an ident token.
    6256           0 :     numbers[0] = 1;
    6257           0 :     onlyN = true;
    6258             : 
    6259             :     // consume the `n`
    6260             :     // We do not allow whitespace here
    6261             :     // https://drafts.csswg.org/css-syntax-3/#the-anb-type
    6262           0 :     if (! GetToken(false)) {
    6263           0 :       REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
    6264           0 :       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6265             :     }
    6266             :   }
    6267             : 
    6268           1 :   if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
    6269             :     // The CSS tokenization doesn't handle :nth-child() containing - well:
    6270             :     //   2n-1 is a dimension
    6271             :     //   n-1 is an identifier
    6272             :     // The easiest way to deal with that is to push everything from the
    6273             :     // minus on back onto the scanner's pushback buffer.
    6274           1 :     uint32_t truncAt = 0;
    6275           1 :     if (TokenBeginsWith(NS_LITERAL_STRING("n-"))) {
    6276           0 :       truncAt = 1;
    6277           1 :     } else if (TokenBeginsWith(NS_LITERAL_STRING("-n-"))) {
    6278           0 :       truncAt = 2;
    6279             :     }
    6280           1 :     if (truncAt != 0) {
    6281           0 :       mScanner->Backup(mToken.mIdent.Length() - truncAt);
    6282           0 :       mToken.mIdent.Truncate(truncAt);
    6283             :     }
    6284             :   }
    6285             : 
    6286           1 :   if (onlyN) {
    6287             :     // If we parsed a + or -, check that the truncated
    6288             :     // token is an "n"
    6289           0 :     if (eCSSToken_Ident != mToken.mType || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
    6290           0 :       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
    6291           0 :       return eSelectorParsingStatus_Error;
    6292             :     }
    6293             :   } else {
    6294           1 :     if (eCSSToken_Ident == mToken.mType) {
    6295           1 :       if (mToken.mIdent.LowerCaseEqualsLiteral("odd")) {
    6296           1 :         numbers[0] = 2;
    6297           1 :         numbers[1] = 1;
    6298           1 :         lookForB = false;
    6299             :       }
    6300           0 :       else if (mToken.mIdent.LowerCaseEqualsLiteral("even")) {
    6301           0 :         numbers[0] = 2;
    6302           0 :         numbers[1] = 0;
    6303           0 :         lookForB = false;
    6304             :       }
    6305           0 :       else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
    6306           0 :           numbers[0] = 1;
    6307             :       }
    6308           0 :       else if (mToken.mIdent.LowerCaseEqualsLiteral("-n")) {
    6309           0 :         numbers[0] = -1;
    6310             :       }
    6311             :       else {
    6312           0 :         REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
    6313           0 :         return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6314             :       }
    6315             :     }
    6316           0 :     else if (eCSSToken_Number == mToken.mType) {
    6317           0 :       if (!mToken.mIntegerValid) {
    6318           0 :         REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
    6319           0 :         return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6320             :       }
    6321             : 
    6322           0 :       numbers[1] = mToken.mInteger;
    6323           0 :       lookForB = false;
    6324             :     }
    6325           0 :     else if (eCSSToken_Dimension == mToken.mType) {
    6326           0 :       if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
    6327           0 :         REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
    6328           0 :         return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6329             :       }
    6330           0 :       numbers[0] = mToken.mInteger;
    6331             :     }
    6332             :     // XXX If it's a ')', is that valid?  (as 0n+0)
    6333             :     else {
    6334           0 :       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
    6335           0 :       UngetToken();
    6336           0 :       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6337             :     }
    6338             :   }
    6339             : 
    6340           1 :   if (! GetToken(true)) {
    6341           0 :     REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
    6342           0 :     return eSelectorParsingStatus_Error;
    6343             :   }
    6344           1 :   if (lookForB && !mToken.IsSymbol(')')) {
    6345             :     // The '+' or '-' sign can optionally be separated by whitespace.
    6346             :     // If it is separated by whitespace from what follows it, it appears
    6347             :     // as a separate token rather than part of the number token.
    6348           0 :     if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
    6349           0 :       hasSign = true;
    6350           0 :       if (mToken.IsSymbol('-')) {
    6351           0 :         sign = -1;
    6352             :       }
    6353           0 :       if (! GetToken(true)) {
    6354           0 :         REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
    6355           0 :         return eSelectorParsingStatus_Error;
    6356             :       }
    6357             :     }
    6358           0 :     if (eCSSToken_Number != mToken.mType ||
    6359           0 :         !mToken.mIntegerValid || mToken.mHasSign == hasSign) {
    6360           0 :       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
    6361           0 :       UngetToken();
    6362           0 :       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6363             :     }
    6364           0 :     numbers[1] = mToken.mInteger * sign;
    6365           0 :     if (! GetToken(true)) {
    6366           0 :       REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
    6367           0 :       return eSelectorParsingStatus_Error;
    6368             :     }
    6369             :   }
    6370           1 :   if (!mToken.IsSymbol(')')) {
    6371           0 :     REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
    6372           0 :     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6373             :   }
    6374           1 :   aSelector.AddPseudoClass(aType, numbers);
    6375           1 :   return eSelectorParsingStatus_Continue;
    6376             : }
    6377             : 
    6378             : //
    6379             : // Parse the argument of a pseudo-class that has a selector list argument.
    6380             : // Such selector lists cannot contain combinators, but can contain
    6381             : // anything that goes between a pair of combinators.
    6382             : //
    6383             : CSSParserImpl::nsSelectorParsingStatus
    6384         162 : CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
    6385             :                                                    CSSPseudoClassType aType)
    6386             : {
    6387         324 :   nsAutoPtr<nsCSSSelectorList> slist;
    6388         162 :   if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) {
    6389           0 :     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6390             :   }
    6391             : 
    6392             :   // Check that none of the selectors in the list have combinators or
    6393             :   // pseudo-elements.
    6394         819 :   for (nsCSSSelectorList *l = slist; l; l = l->mNext) {
    6395         657 :     nsCSSSelector *s = l->mSelectors;
    6396         657 :     if (s->mNext || s->IsPseudoElement()) {
    6397           0 :       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6398             :     }
    6399             :   }
    6400             : 
    6401             :   // Add the pseudo with the selector list parameter
    6402         162 :   aSelector.AddPseudoClass(aType, slist.forget());
    6403             : 
    6404             :   // close the parenthesis
    6405         162 :   if (!ExpectSymbol(')', true)) {
    6406           0 :     REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
    6407           0 :     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
    6408             :   }
    6409             : 
    6410         162 :   return eSelectorParsingStatus_Continue;
    6411             : }
    6412             : 
    6413             : 
    6414             : /**
    6415             :  * This is the format for selectors:
    6416             :  * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
    6417             :  */
    6418             : bool
    6419        8359 : CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
    6420             :                              char16_t aPrevCombinator)
    6421             : {
    6422        8359 :   if (! GetToken(true)) {
    6423           0 :     REPORT_UNEXPECTED_EOF(PESelectorEOF);
    6424           0 :     return false;
    6425             :   }
    6426             : 
    6427        8359 :   nsCSSSelector* selector = aList->AddSelector(aPrevCombinator);
    6428       16718 :   nsCOMPtr<nsIAtom> pseudoElement;
    6429       16718 :   nsAutoPtr<nsAtomList> pseudoElementArgs;
    6430        8359 :   CSSPseudoElementType pseudoElementType = CSSPseudoElementType::NotPseudo;
    6431             : 
    6432        8359 :   int32_t dataMask = 0;
    6433             :   nsSelectorParsingStatus parsingStatus =
    6434        8359 :     ParseTypeOrUniversalSelector(dataMask, *selector, false);
    6435             : 
    6436       25947 :   while (parsingStatus == eSelectorParsingStatus_Continue) {
    6437       17151 :     if (mToken.IsSymbol(':')) {    // :pseudo
    6438        5512 :       parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
    6439        5512 :                                           getter_AddRefs(pseudoElement),
    6440        5512 :                                           getter_Transfers(pseudoElementArgs),
    6441        2756 :                                           &pseudoElementType);
    6442        3112 :       if (pseudoElement &&
    6443        3022 :           pseudoElementType != CSSPseudoElementType::InheritingAnonBox &&
    6444         266 :           pseudoElementType != CSSPseudoElementType::NonInheritingAnonBox) {
    6445             :         // Pseudo-elements other than anonymous boxes are represented with
    6446             :         // a special ':' combinator.
    6447             : 
    6448         264 :         aList->mWeight += selector->CalcWeight();
    6449             : 
    6450         264 :         selector = aList->AddSelector(':');
    6451             : 
    6452         264 :         selector->mLowercaseTag.swap(pseudoElement);
    6453         264 :         selector->mClassList = pseudoElementArgs.forget();
    6454         264 :         selector->SetPseudoType(pseudoElementType);
    6455             :       }
    6456       14395 :     } else if (selector->IsPseudoElement()) {
    6457             :       // Once we parsed a pseudo-element, we can only parse
    6458             :       // pseudo-classes (and only a limited set, which
    6459             :       // ParsePseudoSelector knows how to handle).
    6460           0 :       parsingStatus = eSelectorParsingStatus_Done;
    6461           0 :       UngetToken();
    6462           0 :       break;
    6463       14395 :     } else if (eCSSToken_ID == mToken.mType) { // #id
    6464        1937 :       parsingStatus = ParseIDSelector(dataMask, *selector);
    6465       12458 :     } else if (mToken.IsSymbol('.')) {    // .class
    6466        2459 :       parsingStatus = ParseClassSelector(dataMask, *selector);
    6467             :     }
    6468        9999 :     else if (mToken.IsSymbol('[')) {    // [attribute
    6469        2003 :       parsingStatus = ParseAttributeSelector(dataMask, *selector);
    6470        2003 :       if (eSelectorParsingStatus_Error == parsingStatus) {
    6471           0 :         SkipUntil(']');
    6472             :       }
    6473             :     }
    6474             :     else {  // not a selector token, we're done
    6475        7996 :       parsingStatus = eSelectorParsingStatus_Done;
    6476        7996 :       UngetToken();
    6477        7996 :       break;
    6478             :     }
    6479             : 
    6480        9155 :     if (parsingStatus != eSelectorParsingStatus_Continue) {
    6481         356 :       break;
    6482             :     }
    6483             : 
    6484        8799 :     if (! GetToken(false)) { // premature eof is ok (here!)
    6485           5 :       parsingStatus = eSelectorParsingStatus_Done;
    6486           5 :       break;
    6487             :     }
    6488             :   }
    6489             : 
    6490        8359 :   if (parsingStatus == eSelectorParsingStatus_Error) {
    6491           0 :     return false;
    6492             :   }
    6493             : 
    6494        8359 :   if (!dataMask) {
    6495           0 :     if (selector->mNext) {
    6496           0 :       REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
    6497             :     } else {
    6498           0 :       REPORT_UNEXPECTED(PESelectorGroupNoSelector);
    6499             :     }
    6500           0 :     return false;
    6501             :   }
    6502             : 
    6503       16628 :   if (pseudoElementType == CSSPseudoElementType::InheritingAnonBox ||
    6504        8269 :       pseudoElementType == CSSPseudoElementType::NonInheritingAnonBox) {
    6505             :     // We got an anonymous box pseudo-element; it must be the only
    6506             :     // thing in this selector group.
    6507          92 :     if (selector->mNext || !IsUniversalSelector(*selector)) {
    6508           0 :       REPORT_UNEXPECTED(PEAnonBoxNotAlone);
    6509           0 :       return false;
    6510             :     }
    6511             : 
    6512             :     // Rewrite the current selector as this pseudo-element.
    6513             :     // It does not contribute to selector weight.
    6514          92 :     selector->mLowercaseTag.swap(pseudoElement);
    6515          92 :     selector->mClassList = pseudoElementArgs.forget();
    6516          92 :     selector->SetPseudoType(pseudoElementType);
    6517          92 :     return true;
    6518             :   }
    6519             : 
    6520        8267 :   aList->mWeight += selector->CalcWeight();
    6521             : 
    6522        8267 :   return true;
    6523             : }
    6524             : 
    6525             : already_AddRefed<css::Declaration>
    6526        3002 : CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext)
    6527             : {
    6528        3002 :   bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
    6529             : 
    6530        3002 :   MOZ_ASSERT(mWebkitBoxUnprefixState == eNotParsingDecls,
    6531             :              "Someone forgot to clear mWebkitBoxUnprefixState!");
    6532        6004 :   AutoRestore<WebkitBoxUnprefixState> autoRestore(mWebkitBoxUnprefixState);
    6533        3002 :   mWebkitBoxUnprefixState = eHaveNotUnprefixed;
    6534             : 
    6535        3002 :   if (checkForBraces) {
    6536        2995 :     if (!ExpectSymbol('{', true)) {
    6537           0 :       REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
    6538           0 :       OUTPUT_ERROR();
    6539           0 :       return nullptr;
    6540             :     }
    6541             :   }
    6542        6004 :   RefPtr<css::Declaration> declaration = new css::Declaration();
    6543        3002 :   mData.AssertInitialState();
    6544             :   for (;;) {
    6545        9548 :     bool changed = false;
    6546        9548 :     if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) {
    6547        3002 :       if (!SkipDeclaration(checkForBraces)) {
    6548        3009 :         break;
    6549             :       }
    6550        2995 :       if (checkForBraces) {
    6551        2995 :         if (ExpectSymbol('}', true)) {
    6552        2995 :           break;
    6553             :         }
    6554             :       }
    6555             :       // Since the skipped declaration didn't end the block we parse
    6556             :       // the next declaration.
    6557             :     }
    6558        6546 :   }
    6559        3002 :   declaration->CompressFrom(&mData);
    6560        3002 :   return declaration.forget();
    6561             : }
    6562             : 
    6563             : static Maybe<int32_t>
    6564         335 : GetEnumColorValue(nsCSSKeyword aKeyword, bool aIsChrome)
    6565             : {
    6566             :   int32_t value;
    6567         335 :   if (!nsCSSProps::FindKeyword(aKeyword, nsCSSProps::kColorKTable, value)) {
    6568             :     // Unknown color keyword.
    6569          22 :     return Nothing();
    6570             :   }
    6571         313 :   if (value < 0) {
    6572             :     // Known special color keyword handled by style system,
    6573             :     // e.g. NS_COLOR_CURRENTCOLOR. See nsStyleConsts.h.
    6574          18 :     return Some(value);
    6575             :   }
    6576             :   nscolor color;
    6577         295 :   auto colorID = static_cast<LookAndFeel::ColorID>(value);
    6578         295 :   if (NS_FAILED(LookAndFeel::GetColor(colorID, !aIsChrome, &color))) {
    6579             :     // Known LookAndFeel::ColorID, but this platform's LookAndFeel impl
    6580             :     // doesn't map it to a color. (This might be a platform-specific
    6581             :     // ColorID, which only makes sense on another platform.)
    6582           0 :     return Nothing();
    6583             :   }
    6584             :   // Known color provided by LookAndFeel.
    6585         295 :   return Some(value);
    6586             : }
    6587             : 
    6588             : /// Returns the number of digits in a positive number
    6589             : /// assuming it has <= 6 digits
    6590             : static uint32_t
    6591           0 : CountNumbersForHashlessColor(uint32_t number) {
    6592             :   /// Just use a simple match instead of calculating a log
    6593             :   /// or dividing in a loop to be more efficient.
    6594           0 :   if (number < 10) {
    6595           0 :     return 1;
    6596           0 :   } else if (number < 100) {
    6597           0 :     return 2;
    6598           0 :   } else if (number < 1000) {
    6599           0 :     return 3;
    6600           0 :   } else if (number < 10000) {
    6601           0 :     return 4;
    6602           0 :   } else if (number < 100000) {
    6603           0 :     return 5;
    6604           0 :   } else if (number < 1000000) {
    6605           0 :     return 6;
    6606             :   } else {
    6607             :     // we don't care about numbers with more than 6 digits other
    6608             :     // than the fact that they have more than 6 digits, so just return something
    6609             :     // larger than 6 here. This is incorrect in the general case.
    6610           0 :     return 100;
    6611             :   }
    6612             : }
    6613             : 
    6614             : 
    6615             : CSSParseResult
    6616        1158 : CSSParserImpl::ParseColor(nsCSSValue& aValue)
    6617             : {
    6618        1158 :   if (!GetToken(true)) {
    6619           0 :     REPORT_UNEXPECTED_EOF(PEColorEOF);
    6620           0 :     return CSSParseResult::NotFound;
    6621             :   }
    6622             : 
    6623        1158 :   nsCSSToken* tk = &mToken;
    6624             :   nscolor rgba;
    6625        1158 :   switch (tk->mType) {
    6626             :     case eCSSToken_ID:
    6627             :     case eCSSToken_Hash:
    6628             :       // #rgb, #rrggbb, #rgba, #rrggbbaa
    6629         362 :       if (NS_HexToRGBA(tk->mIdent, nsHexColorType::AllowAlpha, &rgba)) {
    6630             :         nsCSSUnit unit;
    6631         362 :         switch (tk->mIdent.Length()) {
    6632             :           case 3:
    6633          59 :             unit = eCSSUnit_ShortHexColor;
    6634          59 :             break;
    6635             :           case 4:
    6636           0 :             unit = eCSSUnit_ShortHexColorAlpha;
    6637           0 :             break;
    6638             :           case 6:
    6639         303 :             unit = eCSSUnit_HexColor;
    6640         303 :             break;
    6641             :           default:
    6642           0 :             MOZ_FALLTHROUGH_ASSERT("unexpected hex color length");
    6643             :           case 8:
    6644           0 :             unit = eCSSUnit_HexColorAlpha;
    6645           0 :             break;
    6646             :         }
    6647         362 :         aValue.SetIntegerColorValue(rgba, unit);
    6648         362 :         return CSSParseResult::Ok;
    6649             :       }
    6650           0 :       break;
    6651             : 
    6652             :     case eCSSToken_Ident: {
    6653         557 :       if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
    6654             :         // Lowercase color name, since keyword values should be
    6655             :         // serialized in lowercase.
    6656         222 :         nsContentUtils::ASCIIToLower(tk->mIdent);
    6657         222 :         aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
    6658         222 :         return CSSParseResult::Ok;
    6659             :       }
    6660         335 :       nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
    6661         357 :       if (Maybe<int32_t> value = GetEnumColorValue(keyword, mIsChrome)) {
    6662         313 :         aValue.SetIntValue(value.value(), eCSSUnit_EnumColor);
    6663         313 :         return CSSParseResult::Ok;
    6664             :       }
    6665          22 :       break;
    6666             :     }
    6667             :     case eCSSToken_Function: {
    6668             :       bool isRGB;
    6669             :       bool isHSL;
    6670         464 :       if ((isRGB = mToken.mIdent.LowerCaseEqualsLiteral("rgb")) ||
    6671         225 :           mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
    6672             :         // rgb() = rgb( <percentage>{3} [ / <alpha-value> ]? ) |
    6673             :         //         rgb( <number>{3} [ / <alpha-value> ]? ) |
    6674             :         //         rgb( <percentage>#{3} , <alpha-value>? ) |
    6675             :         //         rgb( <number>#{3} , <alpha-value>? )
    6676             :         // <alpha-value> = <number> | <percentage>
    6677             :         // rgba is an alias of rgb.
    6678             : 
    6679          70 :         if (GetToken(true)) {
    6680          70 :           UngetToken();
    6681             :         }
    6682          70 :         if (mToken.mType == eCSSToken_Number) { // <number>
    6683             :           uint8_t r, g, b, a;
    6684             : 
    6685          68 :           if (ParseRGBColor(r, g, b, a)) {
    6686          68 :             aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a),
    6687          68 :                 isRGB ? eCSSUnit_RGBColor : eCSSUnit_RGBAColor);
    6688          68 :             return CSSParseResult::Ok;
    6689             :           }
    6690             :         } else {  // <percentage>
    6691             :           float r, g, b, a;
    6692             : 
    6693           2 :           if (ParseRGBColor(r, g, b, a)) {
    6694           2 :             aValue.SetFloatColorValue(r, g, b, a,
    6695           2 :                 isRGB ? eCSSUnit_PercentageRGBColor : eCSSUnit_PercentageRGBAColor);
    6696           2 :             return CSSParseResult::Ok;
    6697             :           }
    6698             :         }
    6699           0 :         SkipUntil(')');
    6700           0 :         return CSSParseResult::Error;
    6701             :       }
    6702         313 :       else if ((isHSL = mToken.mIdent.LowerCaseEqualsLiteral("hsl")) ||
    6703         144 :                mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
    6704             :         // hsl() = hsl( <hue> <percentage> <percentage> [ / <alpha-value> ]? ) ||
    6705             :         //         hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )
    6706             :         // <hue> = <number> | <angle>
    6707             :         // hsla is an alias of hsl.
    6708             : 
    6709             :         float h, s, l, a;
    6710             : 
    6711         169 :         if (ParseHSLColor(h, s, l, a)) {
    6712         169 :           aValue.SetFloatColorValue(h, s, l, a,
    6713         169 :               isHSL ? eCSSUnit_HSLColor : eCSSUnit_HSLAColor);
    6714         169 :           return CSSParseResult::Ok;
    6715             :         }
    6716           0 :         SkipUntil(')');
    6717           0 :         return CSSParseResult::Error;
    6718             :       }
    6719           0 :       break;
    6720             :     }
    6721             :     default:
    6722           0 :       break;
    6723             :   }
    6724             : 
    6725             :   // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
    6726          22 :   if (mHashlessColorQuirk) {
    6727             :     // https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
    6728             :     //
    6729             :     // - If the string starts with 'a-f', the nsCSSScanner builds the
    6730             :     //   token as a eCSSToken_Ident and we can parse the string as a
    6731             :     //   'xxyyzz' RGB color.
    6732             :     // - If it only contains up to six '0-9' digits, the token is a
    6733             :     //   eCSSToken_Number and it must be converted back to a 6
    6734             :     //   characters string to be parsed as a RGB color. The number cannot
    6735             :     //   be specified as more than six digits.
    6736             :     // - If it starts with '0-9' and contains any 'a-f', the token is a
    6737             :     //   eCSSToken_Dimension, the mNumber part must be converted back to
    6738             :     //   a string and the mIdent part must be appended to that string so
    6739             :     //   that the resulting string has 6 characters. The combined
    6740             :     //   dimension cannot be longer than 6 characters.
    6741             :     // Note: This is a hack for Nav compatibility.  Do not attempt to
    6742             :     // simplify it by hacking into the ncCSSScanner.  This would be very
    6743             :     // bad.
    6744           0 :     nsAutoString str;
    6745             :     char buffer[20];
    6746           0 :     switch (tk->mType) {
    6747             :       case eCSSToken_Ident:
    6748           0 :         str.Assign(tk->mIdent);
    6749           0 :         break;
    6750             : 
    6751             :       case eCSSToken_Number:
    6752           0 :         if (tk->mIntegerValid && tk->mInteger < 1000000 && tk->mInteger >= 0) {
    6753           0 :           SprintfLiteral(buffer, "%06d", tk->mInteger);
    6754           0 :           str.AssignWithConversion(buffer);
    6755             :         }
    6756           0 :         break;
    6757             : 
    6758             :       case eCSSToken_Dimension:
    6759           0 :         if (tk->mIntegerValid &&
    6760           0 :             tk->mIdent.Length() + CountNumbersForHashlessColor(tk->mInteger) <= 6 &&
    6761           0 :             tk->mInteger >= 0) {
    6762           0 :           SprintfLiteral(buffer, "%06d", tk->mInteger);
    6763           0 :           nsAutoString temp;
    6764           0 :           temp.AssignWithConversion(buffer);
    6765           0 :           temp.Right(str, 6 - tk->mIdent.Length());
    6766           0 :           str.Append(tk->mIdent);
    6767             :         }
    6768           0 :         break;
    6769             :       default:
    6770             :         // There is a whole bunch of cases that are
    6771             :         // not handled by this switch.  Ignore them.
    6772           0 :         break;
    6773             :     }
    6774             :     // The hashless color quirk does not support 4 & 8 digit colors with alpha.
    6775           0 :     if (NS_HexToRGBA(str, nsHexColorType::NoAlpha, &rgba)) {
    6776           0 :       aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor);
    6777           0 :       return CSSParseResult::Ok;
    6778             :     }
    6779             :   }
    6780             : 
    6781             :   // It's not a color
    6782          22 :   REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
    6783          22 :   UngetToken();
    6784          22 :   return CSSParseResult::NotFound;
    6785             : }
    6786             : 
    6787             : bool
    6788         204 : CSSParserImpl::ParseColorComponent(uint8_t& aComponent, const Maybe<char>& aSeparator)
    6789             : {
    6790         204 :   if (!GetToken(true)) {
    6791           0 :     REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
    6792           0 :     return false;
    6793             :   }
    6794             : 
    6795         204 :   if (mToken.mType != eCSSToken_Number) {
    6796           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
    6797           0 :     UngetToken();
    6798           0 :     return false;
    6799             :   }
    6800             : 
    6801         204 :   float value = mToken.mNumber;
    6802             : 
    6803         204 :   if (aSeparator && !ExpectSymbol(*aSeparator, true)) {
    6804           0 :     REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator);
    6805           0 :     return false;
    6806             :   }
    6807             : 
    6808         204 :   if (value < 0.0f) value = 0.0f;
    6809         204 :   if (value > 255.0f) value = 255.0f;
    6810             : 
    6811         204 :   aComponent = NSToIntRound(value);
    6812         204 :   return true;
    6813             : }
    6814             : 
    6815             : bool
    6816         344 : CSSParserImpl::ParseColorComponent(float& aComponent, const Maybe<char>& aSeparator)
    6817             : {
    6818         344 :   if (!GetToken(true)) {
    6819           0 :     REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
    6820           0 :     return false;
    6821             :   }
    6822             : 
    6823         344 :   if (mToken.mType != eCSSToken_Percentage) {
    6824           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
    6825           0 :     UngetToken();
    6826           0 :     return false;
    6827             :   }
    6828             : 
    6829         344 :   float value = mToken.mNumber;
    6830             : 
    6831         344 :   if (aSeparator && !ExpectSymbol(*aSeparator, true)) {
    6832           0 :     REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator);
    6833           0 :     return false;
    6834             :   }
    6835             : 
    6836         344 :   if (value < 0.0f) value = 0.0f;
    6837         344 :   if (value > 1.0f) value = 1.0f;
    6838             : 
    6839         344 :   aComponent = value;
    6840         344 :   return true;
    6841             : }
    6842             : 
    6843             : bool
    6844         169 : CSSParserImpl::ParseHue(float& aAngle)
    6845             : {
    6846         338 :   nsCSSValue value;
    6847             :   // <hue> = <number> | <angle>
    6848             :   // https://drafts.csswg.org/css-color/#typedef-hue
    6849         169 :   if (!ParseSingleTokenVariant(value,
    6850             :                                VARIANT_NUMBER | VARIANT_ANGLE,
    6851             :                                nullptr)) {
    6852           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedNumberOrAngle);
    6853           0 :     return false;
    6854             :   }
    6855             : 
    6856             :   float unclampedResult;
    6857         169 :   if (value.GetUnit() == eCSSUnit_Number) {
    6858         169 :     unclampedResult = value.GetFloatValue();
    6859             :   } else {
    6860             :     // Convert double value of GetAngleValueInDegrees() to float.
    6861           0 :     unclampedResult = value.GetAngleValueInDegrees();
    6862             :   }
    6863             : 
    6864             :   // Clamp it as finite values in float.
    6865         169 :   aAngle = mozilla::clamped(unclampedResult,
    6866         338 :                             -std::numeric_limits<float>::max(),
    6867         507 :                              std::numeric_limits<float>::max());
    6868         169 :   return true;
    6869             : }
    6870             : 
    6871             : bool
    6872         169 : CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
    6873             :                              float& aOpacity)
    6874             : {
    6875             :   // comma-less expression:
    6876             :   // hsl() = hsl( <hue> <saturation> <lightness> [ / <alpha-value> ]? )
    6877             :   // the expression with comma:
    6878             :   // hsl() = hsl( <hue>, <saturation>, <lightness>, <alpha-value>? )
    6879             : 
    6880         169 :   const char commaSeparator = ',';
    6881             : 
    6882             :   // Parse hue.
    6883             :   // <hue> = <number> | <angle>
    6884             :   float degreeAngle;
    6885         169 :   if (!ParseHue(degreeAngle)) {
    6886           0 :     return false;
    6887             :   }
    6888         169 :   aHue = degreeAngle / 360.0f;
    6889             :   // hue values are wraparound
    6890         169 :   aHue = aHue - floor(aHue);
    6891             :   // Look for a comma separator after "hue" component to determine if the
    6892             :   // expression is comma-less or not.
    6893         169 :   bool hasComma = ExpectSymbol(commaSeparator, true);
    6894             : 
    6895             :   // Parse saturation, lightness and opacity.
    6896             :   // The saturation and lightness are <percentage>, so reuse the float version
    6897             :   // of ParseColorComponent function for them. No need to check the separator
    6898             :   // after 'lightness'. It will be checked in opacity value parsing.
    6899         169 :   const char separatorBeforeAlpha = hasComma ? commaSeparator : '/';
    6900        1014 :   if (ParseColorComponent(aSaturation, hasComma ? Some(commaSeparator) : Nothing()) &&
    6901        1014 :       ParseColorComponent(aLightness, Nothing()) &&
    6902         169 :       ParseColorOpacityAndCloseParen(aOpacity, separatorBeforeAlpha)) {
    6903         169 :     return true;
    6904             :   }
    6905             : 
    6906           0 :   return false;
    6907             : }
    6908             : 
    6909             : 
    6910             : bool
    6911          68 : CSSParserImpl::ParseColorOpacityAndCloseParen(uint8_t& aOpacity,
    6912             :                                               char aSeparator)
    6913             : {
    6914             :   float floatOpacity;
    6915          68 :   if (!ParseColorOpacityAndCloseParen(floatOpacity, aSeparator)) {
    6916           0 :     return false;
    6917             :   }
    6918             : 
    6919          68 :   uint8_t value = nsStyleUtil::FloatToColorComponent(floatOpacity);
    6920             :   // Need to compare to something slightly larger
    6921             :   // than 0.5 due to floating point inaccuracies.
    6922          68 :   NS_ASSERTION(fabs(255.0f * floatOpacity - value) <= 0.51f,
    6923             :                "FloatToColorComponent did something weird");
    6924             : 
    6925          68 :   aOpacity = value;
    6926          68 :   return true;
    6927             : }
    6928             : 
    6929             : bool
    6930         239 : CSSParserImpl::ParseColorOpacityAndCloseParen(float& aOpacity,
    6931             :                                               char aSeparator)
    6932             : {
    6933         239 :   if (ExpectSymbol(')', true)) {
    6934             :     // The optional [separator <alpha-value>] was omitted, so set the opacity
    6935             :     // to a fully-opaque value '1.0f' and return success.
    6936          33 :     aOpacity = 1.0f;
    6937          33 :     return true;
    6938             :   }
    6939             : 
    6940         206 :   if (!ExpectSymbol(aSeparator, true)) {
    6941           0 :     REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aSeparator);
    6942           0 :     return false;
    6943             :   }
    6944             : 
    6945         206 :   if (!GetToken(true)) {
    6946           0 :     REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
    6947           0 :     return false;
    6948             :   }
    6949             : 
    6950             :   // eCSSToken_Number or eCSSToken_Percentage.
    6951         206 :   if (mToken.mType != eCSSToken_Number && mToken.mType != eCSSToken_Percentage) {
    6952           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedNumberOrPercent);
    6953           0 :     UngetToken();
    6954           0 :     return false;
    6955             :   }
    6956             : 
    6957         206 :   if (!ExpectSymbol(')', true)) {
    6958           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
    6959           0 :     return false;
    6960             :   }
    6961             : 
    6962         206 :   if (mToken.mNumber < 0.0f) {
    6963           0 :     mToken.mNumber = 0.0f;
    6964         206 :   } else if (mToken.mNumber > 1.0f) {
    6965           0 :     mToken.mNumber = 1.0f;
    6966             :   }
    6967             : 
    6968         206 :   aOpacity = mToken.mNumber;
    6969         206 :   return true;
    6970             : }
    6971             : 
    6972             : template<typename ComponentType>
    6973             : bool
    6974          70 : CSSParserImpl::ParseRGBColor(ComponentType& aR,
    6975             :                              ComponentType& aG,
    6976             :                              ComponentType& aB,
    6977             :                              ComponentType& aA)
    6978             : {
    6979             :   // comma-less expression:
    6980             :   // rgb() = rgb( component{3} [ / <alpha-value> ]? )
    6981             :   // the expression with comma:
    6982             :   // rgb() = rgb( component#{3} , <alpha-value>? )
    6983             : 
    6984          70 :   const char commaSeparator = ',';
    6985             : 
    6986             :   // Parse R.
    6987          70 :   if (!ParseColorComponent(aR, Nothing())) {
    6988           0 :     return false;
    6989             :   }
    6990             :   // Look for a comma separator after "r" component to determine if the
    6991             :   // expression is comma-less or not.
    6992          70 :   bool hasComma = ExpectSymbol(commaSeparator, true);
    6993             : 
    6994             :   // Parse G, B and A.
    6995             :   // No need to check the separator after 'B'. It will be checked in 'A' value
    6996             :   // parsing.
    6997          70 :   const char separatorBeforeAlpha = hasComma ? commaSeparator : '/';
    6998         280 :   if (ParseColorComponent(aG, hasComma ? Some(commaSeparator) : Nothing()) &&
    6999         280 :       ParseColorComponent(aB, Nothing()) &&
    7000             :       ParseColorOpacityAndCloseParen(aA, separatorBeforeAlpha)) {
    7001          70 :     return true;
    7002             :   }
    7003             : 
    7004           0 :   return false;
    7005             : }
    7006             : 
    7007             : #ifdef MOZ_XUL
    7008             : bool
    7009          45 : CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs)
    7010             : {
    7011             :   // The argument to a tree pseudo-element is a sequence of identifiers
    7012             :   // that are either space- or comma-separated.  (Was the intent to
    7013             :   // allow only comma-separated?  That's not what was done.)
    7014          90 :   nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass
    7015             : 
    7016         331 :   while (!ExpectSymbol(')', true)) {
    7017         143 :     if (!GetToken(true)) {
    7018           0 :       return false;
    7019             :     }
    7020         143 :     if (eCSSToken_Ident == mToken.mType) {
    7021          94 :       fakeSelector.AddClass(mToken.mIdent);
    7022             :     }
    7023          49 :     else if (!mToken.IsSymbol(',')) {
    7024           0 :       UngetToken();
    7025           0 :       SkipUntil(')');
    7026           0 :       return false;
    7027             :     }
    7028             :   }
    7029          45 :   *aPseudoElementArgs = fakeSelector.mClassList;
    7030          45 :   fakeSelector.mClassList = nullptr;
    7031          45 :   return true;
    7032             : }
    7033             : #endif
    7034             : 
    7035             : nsCSSKeyword
    7036        4471 : CSSParserImpl::LookupKeywordPrefixAware(nsAString& aKeywordStr,
    7037             :                                         const KTableEntry aKeywordTable[])
    7038             : {
    7039        4471 :   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aKeywordStr);
    7040             : 
    7041        4471 :   if (!sWebkitPrefixedAliasesEnabled) {
    7042             :     // Not accepting webkit-prefixed keywords -> don't do anything special.
    7043           0 :     return keyword;
    7044             :   }
    7045             : 
    7046        4471 :   if (aKeywordTable == nsCSSProps::kDisplayKTable) {
    7047         419 :     if ((keyword == eCSSKeyword__webkit_box ||
    7048             :          keyword == eCSSKeyword__webkit_inline_box)) {
    7049             :       // Make a note that we're accepting some "-webkit-{inline-}box" styling,
    7050             :       // so we can give special treatment to subsequent "-moz-{inline}-box".
    7051             :       // (See special treatment below.)
    7052           0 :       if (mWebkitBoxUnprefixState == eHaveNotUnprefixed) {
    7053           0 :         mWebkitBoxUnprefixState = eHaveUnprefixed;
    7054             :       }
    7055         419 :     } else if (mWebkitBoxUnprefixState == eHaveUnprefixed &&
    7056           0 :                (keyword == eCSSKeyword__moz_box ||
    7057             :                 keyword == eCSSKeyword__moz_inline_box)) {
    7058             :       // If we've seen "display: -webkit-box" (or "-webkit-inline-box") in an
    7059             :       // earlier declaration and we honored it, then we have to watch out for
    7060             :       // later "display: -moz-box" (and "-moz-inline-box") declarations; they're
    7061             :       // likely just a halfhearted attempt at compatibility, and they actually
    7062             :       // end up stomping on our emulation of the earlier -webkit-box
    7063             :       // display-value, via the CSS cascade. To prevent this problem, we treat
    7064             :       // "display: -moz-box" & "-moz-inline-box" as if they were simply a
    7065             :       // repetition of the webkit equivalent that we already parsed.
    7066           0 :       MOZ_ASSERT(sWebkitPrefixedAliasesEnabled,
    7067             :                  "The only way mWebkitBoxUnprefixState can be eHaveUnprefixed "
    7068             :                  "is if we're supporting webkit-prefixed aliases");
    7069           0 :       return (keyword == eCSSKeyword__moz_box) ?
    7070           0 :         eCSSKeyword__webkit_box : eCSSKeyword__webkit_inline_box;
    7071             :     }
    7072             :   }
    7073             : 
    7074        4471 :   return keyword;
    7075             : }
    7076             : 
    7077             : //----------------------------------------------------------------------
    7078             : 
    7079             : bool
    7080        9548 : CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
    7081             :                                 uint32_t aFlags,
    7082             :                                 bool aMustCallValueAppended,
    7083             :                                 bool* aChanged,
    7084             :                                 nsCSSContextType aContext)
    7085             : {
    7086        9548 :   NS_PRECONDITION(aContext == eCSSContext_General ||
    7087             :                   aContext == eCSSContext_Page,
    7088             :                   "Must be page or general context");
    7089             : 
    7090        9548 :   bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
    7091             : 
    7092        9548 :   mTempData.AssertInitialState();
    7093             : 
    7094             :   // Get property name
    7095        9548 :   nsCSSToken* tk = &mToken;
    7096       19096 :   nsAutoString propertyName;
    7097             :   for (;;) {
    7098        9548 :     if (!GetToken(true)) {
    7099           7 :       if (checkForBraces) {
    7100           0 :         REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
    7101             :       }
    7102           7 :       return false;
    7103             :     }
    7104        9541 :     if (eCSSToken_Ident == tk->mType) {
    7105        6546 :       propertyName = tk->mIdent;
    7106             :       // grab the ident before the ExpectSymbol trashes the token
    7107        6546 :       if (!ExpectSymbol(':', true)) {
    7108           0 :         REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
    7109           0 :         REPORT_UNEXPECTED(PEDeclDropped);
    7110           0 :         OUTPUT_ERROR();
    7111           0 :         return false;
    7112             :       }
    7113        6546 :       break;
    7114             :     }
    7115        2995 :     if (tk->IsSymbol(';')) {
    7116             :       // dangling semicolons are skipped
    7117           0 :       continue;
    7118             :     }
    7119             : 
    7120        2995 :     if (!tk->IsSymbol('}')) {
    7121           0 :       REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
    7122           0 :       REPORT_UNEXPECTED(PEDeclSkipped);
    7123           0 :       OUTPUT_ERROR();
    7124             : 
    7125           0 :       if (eCSSToken_AtKeyword == tk->mType) {
    7126           0 :         SkipAtRule(checkForBraces);
    7127           0 :         return true;  // Not a declaration, but don't skip until ';'
    7128             :       }
    7129             :     }
    7130             :     // Not a declaration...
    7131        2995 :     UngetToken();
    7132        2995 :     return false;
    7133             :   }
    7134             : 
    7135             :   // Don't report property parse errors if we're inside a failing @supports
    7136             :   // rule.
    7137       13092 :   nsAutoSuppressErrors suppressErrors(this, mInFailingSupportsRule);
    7138             : 
    7139             :   // Information about a parsed non-custom property.
    7140             :   nsCSSPropertyID propID;
    7141             : 
    7142             :   // Information about a parsed custom property.
    7143             :   CSSVariableDeclarations::Type variableType;
    7144       13092 :   nsString variableValue;
    7145             : 
    7146             :   // Check if the property name is a custom property.
    7147        6546 :   bool customProperty = nsCSSProps::IsCustomPropertyName(propertyName) &&
    7148        6546 :                         aContext == eCSSContext_General;
    7149             : 
    7150        6546 :   if (customProperty) {
    7151          74 :     if (!ParseVariableDeclaration(&variableType, variableValue)) {
    7152           0 :       REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
    7153           0 :       REPORT_UNEXPECTED(PEDeclDropped);
    7154           0 :       OUTPUT_ERROR();
    7155           0 :       return false;
    7156             :     }
    7157             :   } else {
    7158             :     // Map property name to its ID.
    7159        6472 :     propID = LookupEnabledProperty(propertyName);
    7160       12944 :     if (eCSSProperty_UNKNOWN == propID ||
    7161       12944 :         eCSSPropertyExtra_variable == propID ||
    7162           0 :         (aContext == eCSSContext_Page &&
    7163           0 :          !nsCSSProps::PropHasFlags(propID,
    7164             :                                    CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property
    7165           0 :       if (!NonMozillaVendorIdentifier(propertyName)) {
    7166           0 :         REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName);
    7167           0 :         REPORT_UNEXPECTED(PEDeclDropped);
    7168           0 :         OUTPUT_ERROR();
    7169             :       }
    7170           0 :       return false;
    7171             :     }
    7172             :     // Then parse the property.
    7173        6472 :     if (!ParseProperty(propID)) {
    7174             :       // XXX Much better to put stuff in the value parsers instead...
    7175           0 :       REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
    7176           0 :       REPORT_UNEXPECTED(PEDeclDropped);
    7177           0 :       OUTPUT_ERROR();
    7178           0 :       mTempData.ClearProperty(propID);
    7179           0 :       mTempData.AssertInitialState();
    7180           0 :       return false;
    7181             :     }
    7182             :   }
    7183             : 
    7184        6546 :   CLEAR_ERROR();
    7185             : 
    7186             :   // Look for "!important".
    7187             :   PriorityParsingStatus status;
    7188        6546 :   if ((aFlags & eParseDeclaration_AllowImportant) != 0) {
    7189        6448 :     status = ParsePriority();
    7190             :   } else {
    7191          98 :     status = ePriority_None;
    7192             :   }
    7193             : 
    7194             :   // Look for a semicolon or close brace.
    7195        6546 :   if (status != ePriority_Error) {
    7196        6546 :     if (!GetToken(true)) {
    7197             :       // EOF is always ok
    7198        6543 :     } else if (mToken.IsSymbol(';')) {
    7199             :       // semicolon is always ok
    7200           6 :     } else if (mToken.IsSymbol('}')) {
    7201             :       // brace is ok if checkForBraces, but don't eat it
    7202           6 :       UngetToken();
    7203           6 :       if (!checkForBraces) {
    7204           0 :         status = ePriority_Error;
    7205             :       }
    7206             :     } else {
    7207           0 :       UngetToken();
    7208           0 :       status = ePriority_Error;
    7209             :     }
    7210             :   }
    7211             : 
    7212        6546 :   if (status == ePriority_Error) {
    7213           0 :     if (checkForBraces) {
    7214           0 :       REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
    7215             :     } else {
    7216           0 :       REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
    7217             :     }
    7218           0 :     REPORT_UNEXPECTED(PEDeclDropped);
    7219           0 :     OUTPUT_ERROR();
    7220           0 :     if (!customProperty) {
    7221           0 :       mTempData.ClearProperty(propID);
    7222             :     }
    7223           0 :     mTempData.AssertInitialState();
    7224           0 :     return false;
    7225             :   }
    7226             : 
    7227        6546 :   if (customProperty) {
    7228          74 :     MOZ_ASSERT(Substring(propertyName, 0,
    7229             :                          CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
    7230             :     // remove '--'
    7231         148 :     nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
    7232          74 :     aDeclaration->AddVariable(varName, variableType, variableValue,
    7233          74 :                               status == ePriority_Important, false);
    7234             :   } else {
    7235       12944 :     *aChanged |= mData.TransferFromBlock(mTempData, propID, EnabledState(),
    7236             :                                          status == ePriority_Important,
    7237             :                                          false, aMustCallValueAppended,
    7238        6472 :                                          aDeclaration, GetDocument());
    7239             :   }
    7240             : 
    7241        6546 :   return true;
    7242             : }
    7243             : 
    7244             : static const nsCSSPropertyID kBorderTopIDs[] = {
    7245             :   eCSSProperty_border_top_width,
    7246             :   eCSSProperty_border_top_style,
    7247             :   eCSSProperty_border_top_color
    7248             : };
    7249             : static const nsCSSPropertyID kBorderRightIDs[] = {
    7250             :   eCSSProperty_border_right_width,
    7251             :   eCSSProperty_border_right_style,
    7252             :   eCSSProperty_border_right_color
    7253             : };
    7254             : static const nsCSSPropertyID kBorderBottomIDs[] = {
    7255             :   eCSSProperty_border_bottom_width,
    7256             :   eCSSProperty_border_bottom_style,
    7257             :   eCSSProperty_border_bottom_color
    7258             : };
    7259             : static const nsCSSPropertyID kBorderLeftIDs[] = {
    7260             :   eCSSProperty_border_left_width,
    7261             :   eCSSProperty_border_left_style,
    7262             :   eCSSProperty_border_left_color
    7263             : };
    7264             : static const nsCSSPropertyID kBorderInlineStartIDs[] = {
    7265             :   eCSSProperty_border_inline_start_width,
    7266             :   eCSSProperty_border_inline_start_style,
    7267             :   eCSSProperty_border_inline_start_color
    7268             : };
    7269             : static const nsCSSPropertyID kBorderInlineEndIDs[] = {
    7270             :   eCSSProperty_border_inline_end_width,
    7271             :   eCSSProperty_border_inline_end_style,
    7272             :   eCSSProperty_border_inline_end_color
    7273             : };
    7274             : static const nsCSSPropertyID kBorderBlockStartIDs[] = {
    7275             :   eCSSProperty_border_block_start_width,
    7276             :   eCSSProperty_border_block_start_style,
    7277             :   eCSSProperty_border_block_start_color
    7278             : };
    7279             : static const nsCSSPropertyID kBorderBlockEndIDs[] = {
    7280             :   eCSSProperty_border_block_end_width,
    7281             :   eCSSProperty_border_block_end_style,
    7282             :   eCSSProperty_border_block_end_color
    7283             : };
    7284             : static const nsCSSPropertyID kColumnRuleIDs[] = {
    7285             :   eCSSProperty_column_rule_width,
    7286             :   eCSSProperty_column_rule_style,
    7287             :   eCSSProperty_column_rule_color
    7288             : };
    7289             : 
    7290             : bool
    7291         316 : CSSParserImpl::ParseEnum(nsCSSValue& aValue,
    7292             :                          const KTableEntry aKeywordTable[])
    7293             : {
    7294         316 :   nsAString* ident = NextIdent();
    7295         316 :   if (nullptr == ident) {
    7296          88 :     return false;
    7297             :   }
    7298         228 :   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
    7299             :   int32_t value;
    7300         228 :   if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
    7301         217 :     aValue.SetIntValue(value, eCSSUnit_Enumerated);
    7302         217 :     return true;
    7303             :   }
    7304             : 
    7305             :   // Put the unknown identifier back and return
    7306          11 :   UngetToken();
    7307          11 :   return false;
    7308             : }
    7309             : 
    7310             : bool
    7311           9 : CSSParserImpl::ParseAlignEnum(nsCSSValue& aValue,
    7312             :                               const KTableEntry aKeywordTable[])
    7313             : {
    7314           9 :   MOZ_ASSERT(nsCSSProps::ValueToKeywordEnum(NS_STYLE_ALIGN_BASELINE,
    7315             :                                             aKeywordTable) !=
    7316             :                eCSSKeyword_UNKNOWN,
    7317             :              "Please use ParseEnum instead");
    7318           9 :   nsAString* ident = NextIdent();
    7319           9 :   if (!ident) {
    7320           0 :     return false;
    7321             :   }
    7322           9 :   nsCSSKeyword baselinePrefix = eCSSKeyword_first;
    7323           9 :   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
    7324           9 :   if (keyword == eCSSKeyword_first || keyword == eCSSKeyword_last) {
    7325           0 :     baselinePrefix = keyword;
    7326           0 :     ident = NextIdent();
    7327           0 :     if (!ident) {
    7328           0 :       return false;
    7329             :     }
    7330           0 :     keyword = nsCSSKeywords::LookupKeyword(*ident);
    7331             :   }
    7332             :   int32_t value;
    7333           9 :   if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
    7334           0 :     if (baselinePrefix == eCSSKeyword_last &&
    7335             :         keyword == eCSSKeyword_baseline) {
    7336           0 :       value = NS_STYLE_ALIGN_LAST_BASELINE;
    7337             :     }
    7338           0 :     aValue.SetIntValue(value, eCSSUnit_Enumerated);
    7339           0 :     return true;
    7340             :   }
    7341             : 
    7342             :   // Put the unknown identifier back and return
    7343           9 :   UngetToken();
    7344           9 :   return false;
    7345             : }
    7346             : 
    7347             : struct UnitInfo {
    7348             :   char name[6];  // needs to be long enough for the longest unit, with
    7349             :                  // terminating null.
    7350             :   uint32_t length;
    7351             :   nsCSSUnit unit;
    7352             :   int32_t type;
    7353             : };
    7354             : 
    7355             : #define STR_WITH_LEN(_str) \
    7356             :   _str, sizeof(_str) - 1
    7357             : 
    7358             : const UnitInfo UnitData[] = {
    7359             :   { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
    7360             :   { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
    7361             :   { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
    7362             :   { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
    7363             :   { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
    7364             :   { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
    7365             :   { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
    7366             :   { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH },
    7367             :   { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
    7368             :   { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH },
    7369             :   { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH },
    7370             :   { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH },
    7371             :   { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH },
    7372             :   { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH },
    7373             :   { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
    7374             :   { STR_WITH_LEN("q"), eCSSUnit_Quarter, VARIANT_LENGTH },
    7375             :   { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
    7376             :   { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
    7377             :   { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
    7378             :   { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE },
    7379             :   { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
    7380             :   { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
    7381             :   { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
    7382             :   { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
    7383             : };
    7384             : 
    7385             : #undef STR_WITH_LEN
    7386             : 
    7387             : bool
    7388        4311 : CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
    7389             :                                   uint32_t aVariantMask,
    7390             :                                   float aNumber,
    7391             :                                   const nsString& aUnit)
    7392             : {
    7393             :   nsCSSUnit units;
    7394        4311 :   int32_t   type = 0;
    7395        4311 :   if (!aUnit.IsEmpty()) {
    7396             :     uint32_t i;
    7397        9301 :     for (i = 0; i < ArrayLength(UnitData); ++i) {
    7398        9301 :       if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
    7399        9301 :                                      UnitData[i].length)) {
    7400        3332 :         units = UnitData[i].unit;
    7401        3332 :         type = UnitData[i].type;
    7402        3332 :         break;
    7403             :       }
    7404             :     }
    7405             : 
    7406        3332 :     if (i == ArrayLength(UnitData)) {
    7407             :       // Unknown unit
    7408           0 :       return false;
    7409             :     }
    7410             : 
    7411        3332 :     if (!mViewportUnitsEnabled &&
    7412           0 :         (eCSSUnit_ViewportWidth == units  ||
    7413           0 :          eCSSUnit_ViewportHeight == units ||
    7414           0 :          eCSSUnit_ViewportMin == units    ||
    7415             :          eCSSUnit_ViewportMax == units)) {
    7416             :       // Viewport units aren't allowed right now, probably because we're
    7417             :       // inside an @page declaration. Fail.
    7418           0 :       return false;
    7419             :     }
    7420             : 
    7421        3332 :     if ((VARIANT_ABSOLUTE_DIMENSION & aVariantMask) != 0 &&
    7422           0 :         !nsCSSValue::IsPixelLengthUnit(units)) {
    7423           0 :       return false;
    7424             :     }
    7425             :   } else {
    7426             :     // Must be a zero number...
    7427         979 :     NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
    7428         979 :     if ((VARIANT_LENGTH & aVariantMask) != 0) {
    7429         979 :       units = eCSSUnit_Pixel;
    7430         979 :       type = VARIANT_LENGTH;
    7431             :     }
    7432           0 :     else if ((VARIANT_ANGLE & aVariantMask) != 0) {
    7433           0 :       NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE,
    7434             :                    "must have allowed zero angle");
    7435           0 :       units = eCSSUnit_Degree;
    7436           0 :       type = VARIANT_ANGLE;
    7437             :     }
    7438             :     else {
    7439           0 :       NS_ERROR("Variant mask does not include dimension; why were we called?");
    7440           0 :       return false;
    7441             :     }
    7442             :   }
    7443        4311 :   if ((type & aVariantMask) != 0) {
    7444        4311 :     aValue.SetFloatValue(aNumber, units);
    7445        4311 :     return true;
    7446             :   }
    7447           0 :   return false;
    7448             : }
    7449             : 
    7450             : // Note that this does include VARIANT_CALC, which is numeric.  This is
    7451             : // because calc() parsing, as proposed, drops range restrictions inside
    7452             : // the calc() expression and clamps the result of the calculation to the
    7453             : // range.
    7454             : #define VARIANT_ALL_NONNUMERIC \
    7455             :   VARIANT_KEYWORD | \
    7456             :   VARIANT_COLOR | \
    7457             :   VARIANT_URL | \
    7458             :   VARIANT_STRING | \
    7459             :   VARIANT_COUNTER | \
    7460             :   VARIANT_ATTR | \
    7461             :   VARIANT_IDENTIFIER | \
    7462             :   VARIANT_IDENTIFIER_NO_INHERIT | \
    7463             :   VARIANT_AUTO | \
    7464             :   VARIANT_INHERIT | \
    7465             :   VARIANT_NONE | \
    7466             :   VARIANT_NORMAL | \
    7467             :   VARIANT_SYSFONT | \
    7468             :   VARIANT_GRADIENT | \
    7469             :   VARIANT_TIMING_FUNCTION | \
    7470             :   VARIANT_ALL | \
    7471             :   VARIANT_CALC | \
    7472             :   VARIANT_OPENTYPE_SVG_KEYWORD
    7473             : 
    7474             : CSSParseResult
    7475        8844 : CSSParserImpl::ParseVariantWithRestrictions(nsCSSValue& aValue,
    7476             :                                             int32_t aVariantMask,
    7477             :                                             const KTableEntry aKeywordTable[],
    7478             :                                             uint32_t aRestrictions)
    7479             : {
    7480        8844 :   switch (aRestrictions) {
    7481             :     default:
    7482           0 :       MOZ_FALLTHROUGH_ASSERT("should not be reached");
    7483             :     case 0:
    7484        6362 :       return ParseVariant(aValue, aVariantMask, aKeywordTable);
    7485             :     case CSS_PROPERTY_VALUE_NONNEGATIVE:
    7486        2476 :       return ParseNonNegativeVariant(aValue, aVariantMask, aKeywordTable);
    7487             :     case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
    7488           6 :       return ParseOneOrLargerVariant(aValue, aVariantMask, aKeywordTable);
    7489             :   }
    7490             : }
    7491             : 
    7492             : // Note that callers passing VARIANT_CALC in aVariantMask will get
    7493             : // full-range parsing inside the calc() expression, and the code that
    7494             : // computes the calc will be required to clamp the resulting value to an
    7495             : // appropriate range.
    7496             : CSSParseResult
    7497        3981 : CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue,
    7498             :                                        int32_t aVariantMask,
    7499             :                                        const KTableEntry aKeywordTable[])
    7500             : {
    7501             :   // The variant mask must only contain non-numeric variants or the ones
    7502             :   // that we specifically handle.
    7503        3981 :   MOZ_ASSERT((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
    7504             :                                VARIANT_NUMBER |
    7505             :                                VARIANT_LENGTH |
    7506             :                                VARIANT_PERCENT |
    7507             :                                VARIANT_INTEGER)) == 0,
    7508             :              "need to update code below to handle additional variants");
    7509             : 
    7510        3981 :   CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable);
    7511        3981 :   if (result == CSSParseResult::Ok) {
    7512        6126 :     if (eCSSUnit_Number == aValue.GetUnit() ||
    7513        2991 :         aValue.IsLengthUnit()){
    7514        2407 :       if (aValue.GetFloatValue() < 0) {
    7515           0 :         UngetToken();
    7516           0 :         return CSSParseResult::NotFound;
    7517             :       }
    7518             :     }
    7519         728 :     else if (aValue.GetUnit() == eCSSUnit_Percent) {
    7520         109 :       if (aValue.GetPercentValue() < 0) {
    7521           0 :         UngetToken();
    7522           0 :         return CSSParseResult::NotFound;
    7523             :       }
    7524         619 :     } else if (aValue.GetUnit() == eCSSUnit_Integer) {
    7525         350 :       if (aValue.GetIntValue() < 0) {
    7526           0 :         UngetToken();
    7527           0 :         return CSSParseResult::NotFound;
    7528             :       }
    7529             :     }
    7530             :   }
    7531        3981 :   return result;
    7532             : }
    7533             : 
    7534             : // Note that callers passing VARIANT_CALC in aVariantMask will get
    7535             : // full-range parsing inside the calc() expression, and the code that
    7536             : // computes the calc will be required to clamp the resulting value to an
    7537             : // appropriate range.
    7538             : CSSParseResult
    7539           9 : CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue,
    7540             :                                        int32_t aVariantMask,
    7541             :                                        const KTableEntry aKeywordTable[])
    7542             : {
    7543             :   // The variant mask must only contain non-numeric variants or the ones
    7544             :   // that we specifically handle.
    7545           9 :   MOZ_ASSERT((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
    7546             :                                VARIANT_NUMBER |
    7547             :                                VARIANT_INTEGER)) == 0,
    7548             :              "need to update code below to handle additional variants");
    7549             : 
    7550           9 :   CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable);
    7551           9 :   if (result == CSSParseResult::Ok) {
    7552           9 :     if (aValue.GetUnit() == eCSSUnit_Integer) {
    7553           3 :       if (aValue.GetIntValue() < 1) {
    7554           0 :         UngetToken();
    7555           0 :         return CSSParseResult::NotFound;
    7556             :       }
    7557           6 :     } else if (eCSSUnit_Number == aValue.GetUnit()) {
    7558           0 :       if (aValue.GetFloatValue() < 1.0f) {
    7559           0 :         UngetToken();
    7560           0 :         return CSSParseResult::NotFound;
    7561             :       }
    7562             :     }
    7563             :   }
    7564           9 :   return result;
    7565             : }
    7566             : 
    7567             : static bool
    7568        1563 : IsCSSTokenCalcFunction(const nsCSSToken& aToken)
    7569             : {
    7570        1877 :   return aToken.mType == eCSSToken_Function &&
    7571        1877 :          aToken.mIdent.LowerCaseEqualsLiteral("calc");
    7572             : }
    7573             : 
    7574             : // Assigns to aValue iff it returns CSSParseResult::Ok.
    7575             : CSSParseResult
    7576       15840 : CSSParserImpl::ParseVariant(nsCSSValue& aValue,
    7577             :                             uint32_t aVariantMask,
    7578             :                             const KTableEntry aKeywordTable[])
    7579             : {
    7580       15840 :   NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
    7581             :                !(aVariantMask & VARIANT_NUMBER),
    7582             :                "can't distinguish colors from numbers");
    7583       15840 :   NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
    7584             :                !(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)),
    7585             :                "can't distinguish colors from lengths");
    7586       15840 :   NS_ASSERTION(!(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)) ||
    7587             :                !(aVariantMask & VARIANT_NUMBER),
    7588             :                "can't distinguish lengths from numbers");
    7589       15840 :   MOZ_ASSERT(!(aVariantMask & VARIANT_IDENTIFIER) ||
    7590             :              !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT),
    7591             :              "must not set both VARIANT_IDENTIFIER and "
    7592             :              "VARIANT_IDENTIFIER_NO_INHERIT");
    7593             : 
    7594             :   uint32_t lineBefore, colBefore;
    7595       31226 :   if (!GetNextTokenLocation(true, &lineBefore, &colBefore) ||
    7596       15386 :       !GetToken(true)) {
    7597             :     // Must be at EOF.
    7598         454 :     return CSSParseResult::NotFound;
    7599             :   }
    7600             : 
    7601       15386 :   nsCSSToken* tk = &mToken;
    7602       26330 :   if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
    7603       10944 :       (eCSSToken_Ident == tk->mType)) {
    7604        4471 :     nsCSSKeyword keyword = LookupKeywordPrefixAware(tk->mIdent,
    7605        4471 :                                                     aKeywordTable);
    7606             : 
    7607        4471 :     if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
    7608        3927 :       if ((aVariantMask & VARIANT_AUTO) != 0) {
    7609         121 :         if (eCSSKeyword_auto == keyword) {
    7610          67 :           aValue.SetAutoValue();
    7611          67 :           return CSSParseResult::Ok;
    7612             :         }
    7613             :       }
    7614        3860 :       if ((aVariantMask & VARIANT_INHERIT) != 0) {
    7615             :         // XXX Should we check IsParsingCompoundProperty, or do all
    7616             :         // callers handle it?  (Not all callers set it, though, since
    7617             :         // they want the quirks that are disabled by setting it.)
    7618             : 
    7619             :         // IMPORTANT: If new keywords are added here,
    7620             :         // they probably need to be added in ParseCustomIdent as well.
    7621        3383 :         if (eCSSKeyword_inherit == keyword) {
    7622         410 :           aValue.SetInheritValue();
    7623         410 :           return CSSParseResult::Ok;
    7624             :         }
    7625        2973 :         else if (eCSSKeyword_initial == keyword) {
    7626          21 :           aValue.SetInitialValue();
    7627          21 :           return CSSParseResult::Ok;
    7628             :         }
    7629        2962 :         else if (eCSSKeyword_unset == keyword &&
    7630          10 :                  nsLayoutUtils::UnsetValueEnabled()) {
    7631          10 :           aValue.SetUnsetValue();
    7632          10 :           return CSSParseResult::Ok;
    7633             :         }
    7634             :       }
    7635        3419 :       if ((aVariantMask & VARIANT_NONE) != 0) {
    7636         257 :         if (eCSSKeyword_none == keyword) {
    7637         105 :           aValue.SetNoneValue();
    7638         105 :           return CSSParseResult::Ok;
    7639             :         }
    7640             :       }
    7641        3314 :       if ((aVariantMask & VARIANT_ALL) != 0) {
    7642           7 :         if (eCSSKeyword_all == keyword) {
    7643           0 :           aValue.SetAllValue();
    7644           0 :           return CSSParseResult::Ok;
    7645             :         }
    7646             :       }
    7647        3314 :       if ((aVariantMask & VARIANT_NORMAL) != 0) {
    7648          41 :         if (eCSSKeyword_normal == keyword) {
    7649          24 :           aValue.SetNormalValue();
    7650          24 :           return CSSParseResult::Ok;
    7651             :         }
    7652             :       }
    7653        3290 :       if ((aVariantMask & VARIANT_SYSFONT) != 0) {
    7654          78 :         if (eCSSKeyword__moz_use_system_font == keyword &&
    7655           0 :             !IsParsingCompoundProperty()) {
    7656           0 :           aValue.SetSystemFontValue();
    7657           0 :           return CSSParseResult::Ok;
    7658             :         }
    7659             :       }
    7660        3290 :       if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) {
    7661           6 :         if (sOpentypeSVGEnabled) {
    7662           6 :           aVariantMask |= VARIANT_KEYWORD;
    7663             :         }
    7664             :       }
    7665        3290 :       if ((aVariantMask & VARIANT_KEYWORD) != 0) {
    7666             :         int32_t value;
    7667        2552 :         if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
    7668        2403 :           aValue.SetIntValue(value, eCSSUnit_Enumerated);
    7669        2403 :           return CSSParseResult::Ok;
    7670             :         }
    7671             :       }
    7672             :     }
    7673             :   }
    7674             :   // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or
    7675             :   // VARIANT_ZERO_ANGLE.
    7676       13368 :   if (((aVariantMask & VARIANT_NUMBER) != 0) &&
    7677        1022 :       (eCSSToken_Number == tk->mType)) {
    7678         574 :     aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
    7679         574 :     return CSSParseResult::Ok;
    7680             :   }
    7681       12182 :   if (((aVariantMask & VARIANT_INTEGER) != 0) &&
    7682         815 :       (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
    7683         405 :     aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
    7684         405 :     return CSSParseResult::Ok;
    7685             :   }
    7686       11367 :   if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
    7687        5874 :                         VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
    7688       13909 :        eCSSToken_Dimension == tk->mType) ||
    7689       10312 :       ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
    7690        3290 :        eCSSToken_Number == tk->mType &&
    7691        1013 :        tk->mNumber == 0.0f)) {
    7692        4453 :     if ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 &&
    7693         142 :         tk->mNumber < 0.0) {
    7694           0 :         UngetToken();
    7695           0 :         AssertNextTokenAt(lineBefore, colBefore);
    7696           0 :         return CSSParseResult::NotFound;
    7697             :     }
    7698        4311 :     if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
    7699        4311 :       return CSSParseResult::Ok;
    7700             :     }
    7701             :     // Put the token back; we didn't parse it, so we shouldn't consume it
    7702           0 :     UngetToken();
    7703           0 :     AssertNextTokenAt(lineBefore, colBefore);
    7704           0 :     return CSSParseResult::NotFound;
    7705             :   }
    7706        8155 :   if (((aVariantMask & VARIANT_PERCENT) != 0) &&
    7707        1099 :       (eCSSToken_Percentage == tk->mType)) {
    7708         189 :     aValue.SetPercentValue(tk->mNumber);
    7709         189 :     return CSSParseResult::Ok;
    7710             :   }
    7711        6867 :   if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px
    7712           0 :     if (((aVariantMask & VARIANT_LENGTH) != 0) &&
    7713           0 :         (eCSSToken_Number == tk->mType)) {
    7714           0 :       aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
    7715           0 :       return CSSParseResult::Ok;
    7716             :     }
    7717             :   }
    7718             : 
    7719        6867 :   if (mIsSVGMode && !IsParsingCompoundProperty()) {
    7720             :     // STANDARD: SVG Spec states that lengths and coordinates can be unitless
    7721             :     // in which case they default to user-units (1 px = 1 user unit)
    7722          86 :     if (((aVariantMask & VARIANT_LENGTH) != 0) &&
    7723          34 :         (eCSSToken_Number == tk->mType)) {
    7724          34 :       aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
    7725          34 :       return CSSParseResult::Ok;
    7726             :     }
    7727             :   }
    7728             : 
    7729        7755 :   if (((aVariantMask & VARIANT_URL) != 0) &&
    7730         922 :       eCSSToken_URL == tk->mType) {
    7731         651 :     SetValueToURL(aValue, tk->mIdent);
    7732         651 :     return CSSParseResult::Ok;
    7733             :   }
    7734        6258 :   if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
    7735          76 :       eCSSToken_Function == tk->mType) {
    7736             :     // a generated gradient
    7737         101 :     nsDependentString tmp(tk->mIdent, 0);
    7738          76 :     uint8_t gradientFlags = 0;
    7739         304 :     if (sMozGradientsEnabled &&
    7740         304 :         StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) {
    7741          19 :       tmp.Rebind(tmp, 5);
    7742          19 :       gradientFlags |= eGradient_MozLegacy;
    7743         228 :     } else if (sWebkitPrefixedAliasesEnabled &&
    7744         228 :                StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) {
    7745           0 :       tmp.Rebind(tmp, 8);
    7746           0 :       gradientFlags |= eGradient_WebkitLegacy;
    7747             :     }
    7748          76 :     if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) {
    7749           1 :       tmp.Rebind(tmp, 10);
    7750           1 :       gradientFlags |= eGradient_Repeating;
    7751             :     }
    7752             : 
    7753          76 :     if (tmp.LowerCaseEqualsLiteral("linear-gradient")) {
    7754          49 :       if (!ParseLinearGradient(aValue, gradientFlags)) {
    7755           6 :         return CSSParseResult::Error;
    7756             :       }
    7757          43 :       return CSSParseResult::Ok;
    7758             :     }
    7759          27 :     if (tmp.LowerCaseEqualsLiteral("radial-gradient")) {
    7760           2 :       if (!ParseRadialGradient(aValue, gradientFlags)) {
    7761           0 :         return CSSParseResult::Error;
    7762             :       }
    7763           2 :       return CSSParseResult::Ok;
    7764             :     }
    7765          25 :     if ((gradientFlags == eGradient_WebkitLegacy) &&
    7766           0 :         tmp.LowerCaseEqualsLiteral("gradient")) {
    7767             :       // Note: we check gradientFlags using '==' to select *exactly*
    7768             :       // eGradient_WebkitLegacy -- and exclude eGradient_Repeating -- because
    7769             :       // we don't want to accept -webkit-repeating-gradient() expressions.
    7770             :       // (This is not a recognized syntax.)
    7771           0 :       if (!ParseWebkitGradient(aValue)) {
    7772           0 :         return CSSParseResult::Error;
    7773             :       }
    7774           0 :       return CSSParseResult::Ok;
    7775             :     }
    7776             :   }
    7777       12287 :   if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
    7778        6156 :       eCSSToken_Function == tk->mType &&
    7779          25 :       tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
    7780          16 :     if (!ParseImageRect(aValue)) {
    7781           0 :       return CSSParseResult::Error;
    7782             :     }
    7783          16 :     return CSSParseResult::Ok;
    7784             :   }
    7785       12239 :   if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
    7786        6124 :       eCSSToken_Function == tk->mType &&
    7787           9 :       tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) {
    7788           3 :     if (!ParseElement(aValue)) {
    7789           0 :       return CSSParseResult::Error;
    7790             :     }
    7791           3 :     return CSSParseResult::Ok;
    7792             :   }
    7793        6112 :   if ((aVariantMask & VARIANT_COLOR) != 0) {
    7794        4029 :     if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
    7795        2568 :         (eCSSToken_ID == tk->mType) ||
    7796        2358 :         (eCSSToken_Hash == tk->mType) ||
    7797        4058 :         (eCSSToken_Ident == tk->mType) ||
    7798         951 :         ((eCSSToken_Function == tk->mType) &&
    7799         736 :          (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
    7800         697 :           tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
    7801         616 :           tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
    7802         280 :           tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
    7803             :     {
    7804             :       // Put token back so that parse color can get it
    7805        1006 :       UngetToken();
    7806        1006 :       return ParseColor(aValue);
    7807             :     }
    7808             :   }
    7809        6843 :   if (((aVariantMask & VARIANT_STRING) != 0) &&
    7810        1737 :       (eCSSToken_String == tk->mType)) {
    7811          64 :     nsAutoString  buffer;
    7812          32 :     buffer.Append(tk->mIdent);
    7813          32 :     aValue.SetStringValue(buffer, eCSSUnit_String);
    7814          32 :     return CSSParseResult::Ok;
    7815             :   }
    7816       10148 :   if (((aVariantMask &
    7817        1850 :         (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
    7818        8642 :       (eCSSToken_Ident == tk->mType) &&
    7819        1821 :       ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
    7820         111 :        !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
    7821          37 :          tk->mIdent.LowerCaseEqualsLiteral("initial") ||
    7822          37 :          (tk->mIdent.LowerCaseEqualsLiteral("unset") &&
    7823           0 :           nsLayoutUtils::UnsetValueEnabled())))) {
    7824        1784 :     aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
    7825        1784 :     return CSSParseResult::Ok;
    7826             :   }
    7827        6585 :   if (((aVariantMask & VARIANT_COUNTER) != 0) &&
    7828        3295 :       (eCSSToken_Function == tk->mType) &&
    7829          10 :       (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
    7830           5 :        tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
    7831           0 :     if (!ParseCounter(aValue)) {
    7832           0 :       return CSSParseResult::Error;
    7833             :     }
    7834           0 :     return CSSParseResult::Ok;
    7835             :   }
    7836        6585 :   if (((aVariantMask & VARIANT_ATTR) != 0) &&
    7837        3295 :       (eCSSToken_Function == tk->mType) &&
    7838           5 :       tk->mIdent.LowerCaseEqualsLiteral("attr")) {
    7839           5 :     if (!ParseAttr(aValue)) {
    7840           0 :       SkipUntil(')');
    7841           0 :       return CSSParseResult::Error;
    7842             :     }
    7843           5 :     return CSSParseResult::Ok;
    7844             :   }
    7845        3419 :   if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
    7846         134 :       (eCSSToken_Function == tk->mType)) {
    7847           8 :     if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
    7848           0 :       if (!ParseTransitionTimingFunctionValues(aValue)) {
    7849           0 :         SkipUntil(')');
    7850           0 :         return CSSParseResult::Error;
    7851             :       }
    7852           0 :       return CSSParseResult::Ok;
    7853             :     }
    7854           8 :     if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
    7855           3 :       if (!ParseTransitionStepTimingFunctionValues(aValue)) {
    7856           0 :         SkipUntil(')');
    7857           0 :         return CSSParseResult::Error;
    7858             :       }
    7859           3 :       return CSSParseResult::Ok;
    7860             :     }
    7861          10 :     if (sFramesTimingFunctionEnabled &&
    7862           5 :         tk->mIdent.LowerCaseEqualsLiteral("frames")) {
    7863           0 :       if (!ParseTransitionFramesTimingFunctionValues(aValue)) {
    7864           0 :         SkipUntil(')');
    7865           0 :         return CSSParseResult::Error;
    7866             :       }
    7867           0 :       return CSSParseResult::Ok;
    7868             :     }
    7869             :   }
    7870        4307 :   if ((aVariantMask & VARIANT_CALC) &&
    7871        1025 :       IsCSSTokenCalcFunction(*tk)) {
    7872             :     // calc() currently allows only lengths, percents, numbers, and integers.
    7873             :     //
    7874             :     // Note that VARIANT_NUMBER can be mixed with VARIANT_LENGTH and
    7875             :     // VARIANT_PERCENTAGE in the list of allowed types (numbers can be used as
    7876             :     // coefficients).
    7877             :     // However, the the resulting type is not a mixed type with number.
    7878             :     // VARIANT_INTEGER can't be mixed with anything else.
    7879         226 :     if (!ParseCalc(aValue, aVariantMask & (VARIANT_LPN | VARIANT_INTEGER))) {
    7880          21 :       return CSSParseResult::Error;
    7881             :     }
    7882         205 :     return CSSParseResult::Ok;
    7883             :   }
    7884             : 
    7885        3056 :   UngetToken();
    7886        3056 :   AssertNextTokenAt(lineBefore, colBefore);
    7887        3056 :   return CSSParseResult::NotFound;
    7888             : }
    7889             : 
    7890             : bool
    7891         245 : CSSParserImpl::ParseCustomIdent(nsCSSValue& aValue,
    7892             :                                 const nsAutoString& aIdentValue,
    7893             :                                 const nsCSSKeyword aExcludedKeywords[],
    7894             :                                 const nsCSSProps::KTableEntry aPropertyKTable[])
    7895             : {
    7896         245 :   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aIdentValue);
    7897         245 :   if (keyword == eCSSKeyword_UNKNOWN) {
    7898             :     // Fast path for identifiers that are not known CSS keywords:
    7899         209 :     aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
    7900         209 :     return true;
    7901             :   }
    7902          72 :   if (keyword == eCSSKeyword_inherit ||
    7903          36 :       keyword == eCSSKeyword_initial ||
    7904          36 :       keyword == eCSSKeyword_unset ||
    7905          72 :       keyword == eCSSKeyword_default ||
    7906           0 :       (aPropertyKTable &&
    7907           0 :         nsCSSProps::FindIndexOfKeyword(keyword, aPropertyKTable) >= 0)) {
    7908           0 :     return false;
    7909             :   }
    7910          36 :   if (aExcludedKeywords) {
    7911           8 :     for (uint32_t i = 0;; i++) {
    7912           8 :       nsCSSKeyword excludedKeyword = aExcludedKeywords[i];
    7913           8 :       if (excludedKeyword == eCSSKeyword_UNKNOWN) {
    7914           2 :         break;
    7915             :       }
    7916           6 :       if (excludedKeyword == keyword) {
    7917           0 :         return false;
    7918             :       }
    7919           6 :     }
    7920             :   }
    7921          36 :   aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
    7922          36 :   return true;
    7923             : }
    7924             : 
    7925             : bool
    7926           0 : CSSParserImpl::ParseCounter(nsCSSValue& aValue)
    7927             : {
    7928           0 :   nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
    7929           0 :                     eCSSUnit_Counter : eCSSUnit_Counters);
    7930             : 
    7931             :   // A non-iterative for loop to break out when an error occurs.
    7932             :   for (;;) {
    7933           0 :     if (!GetToken(true)) {
    7934           0 :       break;
    7935             :     }
    7936           0 :     if (eCSSToken_Ident != mToken.mType) {
    7937           0 :       UngetToken();
    7938           0 :       break;
    7939             :     }
    7940             : 
    7941             :     RefPtr<nsCSSValue::Array> val =
    7942           0 :       nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
    7943             : 
    7944           0 :     val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
    7945             : 
    7946           0 :     if (eCSSUnit_Counters == unit) {
    7947             :       // must have a comma and then a separator string
    7948           0 :       if (!ExpectSymbol(',', true) || !GetToken(true)) {
    7949           0 :         break;
    7950             :       }
    7951           0 :       if (eCSSToken_String != mToken.mType) {
    7952           0 :         UngetToken();
    7953           0 :         break;
    7954             :       }
    7955           0 :       val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
    7956             :     }
    7957             : 
    7958             :     // get optional type
    7959           0 :     int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1;
    7960           0 :     nsCSSValue& type = val->Item(typeItem);
    7961           0 :     if (ExpectSymbol(',', true)) {
    7962           0 :       if (!ParseCounterStyleNameValue(type) && !ParseSymbols(type)) {
    7963           0 :         break;
    7964             :       }
    7965             :     } else {
    7966           0 :       type.SetAtomIdentValue(do_AddRef(nsGkAtoms::decimal));
    7967             :     }
    7968             : 
    7969           0 :     if (!ExpectSymbol(')', true)) {
    7970           0 :       break;
    7971             :     }
    7972             : 
    7973           0 :     aValue.SetArrayValue(val, unit);
    7974           0 :     return true;
    7975             :   }
    7976             : 
    7977           0 :   SkipUntil(')');
    7978           0 :   return false;
    7979             : }
    7980             : 
    7981             : bool
    7982          39 : CSSParserImpl::ParseContextProperties()
    7983             : {
    7984          78 :   nsCSSValue listValue;
    7985          39 :   nsCSSValueList* currentListValue = listValue.SetListValue();
    7986          39 :   bool first = true;
    7987             :   for (;;) {
    7988             :     const uint32_t variantMask = VARIANT_IDENTIFIER |
    7989             :                                  VARIANT_INHERIT |
    7990          40 :                                  VARIANT_NONE;
    7991          41 :     nsCSSValue value;
    7992          40 :     if (!ParseSingleTokenVariant(value, variantMask, nullptr)) {
    7993           0 :       return false;
    7994             :     }
    7995             : 
    7996          40 :     if (value.GetUnit() != eCSSUnit_Ident) {
    7997           0 :       if (first) {
    7998           0 :         AppendValue(eCSSProperty__moz_context_properties, value);
    7999           0 :         return true;
    8000             :       } else {
    8001           0 :         return false;
    8002             :       }
    8003             :     }
    8004             : 
    8005          40 :     value.AtomizeIdentValue();
    8006          40 :     nsIAtom* atom = value.GetAtomValue();
    8007          40 :     if (atom == nsGkAtoms::_default) {
    8008           0 :       return false;
    8009             :     }
    8010             : 
    8011          40 :     currentListValue->mValue = Move(value);
    8012             : 
    8013          40 :     if (!ExpectSymbol(',', true)) {
    8014          39 :       break;
    8015             :     }
    8016           1 :     currentListValue->mNext = new nsCSSValueList;
    8017           1 :     currentListValue = currentListValue->mNext;
    8018           1 :     first = false;
    8019           1 :   }
    8020             : 
    8021          39 :   AppendValue(eCSSProperty__moz_context_properties, listValue);
    8022          39 :   return true;
    8023             : }
    8024             : 
    8025             : bool
    8026           5 : CSSParserImpl::ParseAttr(nsCSSValue& aValue)
    8027             : {
    8028           5 :   if (!GetToken(true)) {
    8029           0 :     return false;
    8030             :   }
    8031             : 
    8032          10 :   nsAutoString attr;
    8033           5 :   if (eCSSToken_Ident == mToken.mType) {  // attr name or namespace
    8034          10 :     nsAutoString  holdIdent(mToken.mIdent);
    8035           5 :     if (ExpectSymbol('|', false)) {  // namespace
    8036           0 :       int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent);
    8037           0 :       if (nameSpaceID == kNameSpaceID_Unknown) {
    8038           0 :         return false;
    8039             :       }
    8040           0 :       attr.AppendInt(nameSpaceID, 10);
    8041           0 :       attr.Append(char16_t('|'));
    8042           0 :       if (! GetToken(false)) {
    8043           0 :         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
    8044           0 :         return false;
    8045             :       }
    8046           0 :       if (eCSSToken_Ident == mToken.mType) {
    8047           0 :         attr.Append(mToken.mIdent);
    8048             :       }
    8049             :       else {
    8050           0 :         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
    8051           0 :         UngetToken();
    8052           0 :         return false;
    8053             :       }
    8054             :     }
    8055             :     else {  // no namespace
    8056           5 :       attr = holdIdent;
    8057             :     }
    8058             :   }
    8059           0 :   else if (mToken.IsSymbol('*')) {  // namespace wildcard
    8060             :     // Wildcard namespace makes no sense here and is not allowed
    8061           0 :     REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
    8062           0 :     UngetToken();
    8063           0 :     return false;
    8064             :   }
    8065           0 :   else if (mToken.IsSymbol('|')) {  // explicit NO namespace
    8066           0 :     if (! GetToken(false)) {
    8067           0 :       REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
    8068           0 :       return false;
    8069             :     }
    8070           0 :     if (eCSSToken_Ident == mToken.mType) {
    8071           0 :       attr.Append(mToken.mIdent);
    8072             :     }
    8073             :     else {
    8074           0 :       REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
    8075           0 :       UngetToken();
    8076           0 :       return false;
    8077             :     }
    8078             :   }
    8079             :   else {
    8080           0 :     REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
    8081           0 :     UngetToken();
    8082           0 :     return false;
    8083             :   }
    8084           5 :   if (!ExpectSymbol(')', true)) {
    8085           0 :     return false;
    8086             :   }
    8087           5 :   aValue.SetStringValue(attr, eCSSUnit_Attr);
    8088           5 :   return true;
    8089             : }
    8090             : 
    8091             : bool
    8092           0 : CSSParserImpl::ParseSymbols(nsCSSValue& aValue)
    8093             : {
    8094           0 :   if (!GetToken(true)) {
    8095           0 :     return false;
    8096             :   }
    8097           0 :   if (mToken.mType != eCSSToken_Function &&
    8098           0 :       !mToken.mIdent.LowerCaseEqualsLiteral("symbols")) {
    8099           0 :     UngetToken();
    8100           0 :     return false;
    8101             :   }
    8102             : 
    8103           0 :   RefPtr<nsCSSValue::Array> params = nsCSSValue::Array::Create(2);
    8104           0 :   nsCSSValue& type = params->Item(0);
    8105           0 :   nsCSSValue& symbols = params->Item(1);
    8106             : 
    8107           0 :   if (!ParseEnum(type, nsCSSProps::kCounterSymbolsSystemKTable)) {
    8108           0 :     type.SetIntValue(NS_STYLE_COUNTER_SYSTEM_SYMBOLIC, eCSSUnit_Enumerated);
    8109             :   }
    8110             : 
    8111           0 :   bool first = true;
    8112           0 :   nsCSSValueList* item = symbols.SetListValue();
    8113             :   for (;;) {
    8114             :     // FIXME Should also include VARIANT_IMAGE. See bug 1071436.
    8115           0 :     if (!ParseSingleTokenVariant(item->mValue, VARIANT_STRING, nullptr)) {
    8116           0 :       break;
    8117             :     }
    8118           0 :     if (ExpectSymbol(')', true)) {
    8119           0 :       if (first) {
    8120           0 :         switch (type.GetIntValue()) {
    8121             :           case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
    8122             :           case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
    8123             :             // require at least two symbols
    8124           0 :             return false;
    8125             :         }
    8126             :       }
    8127           0 :       aValue.SetArrayValue(params, eCSSUnit_Symbols);
    8128           0 :       return true;
    8129             :     }
    8130           0 :     item->mNext = new nsCSSValueList;
    8131           0 :     item = item->mNext;
    8132           0 :     first = false;
    8133             :   }
    8134             : 
    8135           0 :   SkipUntil(')');
    8136           0 :   return false;
    8137             : }
    8138             : 
    8139             : bool
    8140         667 : CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL)
    8141             : {
    8142         667 :   if (!mSheetPrincipal) {
    8143           0 :     if (!mSheetPrincipalRequired) {
    8144             :       /* Pretend to succeed.  */
    8145           0 :       return true;
    8146             :     }
    8147             : 
    8148           0 :     NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
    8149             :                   "origin principal");
    8150           0 :     return false;
    8151             :   }
    8152             : 
    8153             :   mozilla::css::URLValue *urlVal =
    8154        1334 :     new mozilla::css::URLValue(aURL, mBaseURI, mSheetURI, mSheetPrincipal);
    8155         667 :   aValue.SetURLValue(urlVal);
    8156         667 :   return true;
    8157             : }
    8158             : 
    8159             : /**
    8160             :  * Parse the image-orientation property, which has the grammar:
    8161             :  * <angle> flip? | flip | from-image
    8162             :  */
    8163             : bool
    8164           0 : CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue)
    8165             : {
    8166           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
    8167             :     // 'inherit', 'initial' and 'unset' must be alone
    8168           0 :     return true;
    8169             :   }
    8170             : 
    8171             :   // Check for an angle with optional 'flip'.
    8172           0 :   nsCSSValue angle;
    8173           0 :   if (ParseSingleTokenVariant(angle, VARIANT_ANGLE, nullptr)) {
    8174           0 :     nsCSSValue flip;
    8175             : 
    8176           0 :     if (ParseSingleTokenVariant(flip, VARIANT_KEYWORD,
    8177             :                                 nsCSSProps::kImageOrientationFlipKTable)) {
    8178           0 :       RefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(2);
    8179           0 :       array->Item(0) = angle;
    8180           0 :       array->Item(1) = flip;
    8181           0 :       aValue.SetArrayValue(array, eCSSUnit_Array);
    8182             :     } else {
    8183           0 :       aValue = angle;
    8184             :     }
    8185             : 
    8186           0 :     return true;
    8187             :   }
    8188             : 
    8189             :   // The remaining possibilities (bare 'flip' and 'from-image') are both
    8190             :   // keywords, so we can handle them at the same time.
    8191           0 :   nsCSSValue keyword;
    8192           0 :   if (ParseSingleTokenVariant(keyword, VARIANT_KEYWORD,
    8193             :                               nsCSSProps::kImageOrientationKTable)) {
    8194           0 :     aValue = keyword;
    8195           0 :     return true;
    8196             :   }
    8197             : 
    8198             :   // All possibilities failed.
    8199           0 :   return false;
    8200             : }
    8201             : 
    8202             : /**
    8203             :  * Parse the arguments of -moz-image-rect() function.
    8204             :  * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
    8205             :  */
    8206             : bool
    8207          16 : CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
    8208             : {
    8209             :   // A non-iterative for loop to break out when an error occurs.
    8210             :   for (;;) {
    8211          16 :     nsCSSValue newFunction;
    8212             :     static const uint32_t kNumArgs = 5;
    8213             :     nsCSSValue::Array* func =
    8214          16 :       newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs);
    8215             : 
    8216             :     // func->Item(0) is reserved for the function name.
    8217          16 :     nsCSSValue& url    = func->Item(1);
    8218          16 :     nsCSSValue& top    = func->Item(2);
    8219          16 :     nsCSSValue& right  = func->Item(3);
    8220          16 :     nsCSSValue& bottom = func->Item(4);
    8221          16 :     nsCSSValue& left   = func->Item(5);
    8222             : 
    8223          16 :     nsAutoString urlString;
    8224          48 :     if (!ParseURLOrString(urlString) ||
    8225          32 :         !SetValueToURL(url, urlString) ||
    8226          16 :         !ExpectSymbol(',', true)) {
    8227           0 :       break;
    8228             :     }
    8229             : 
    8230             :     static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT;
    8231          48 :     if (!ParseSingleTokenNonNegativeVariant(top, VARIANT_SIDE, nullptr) ||
    8232          32 :         !ExpectSymbol(',', true) ||
    8233          32 :         !ParseSingleTokenNonNegativeVariant(right, VARIANT_SIDE, nullptr) ||
    8234          32 :         !ExpectSymbol(',', true) ||
    8235          32 :         !ParseSingleTokenNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) ||
    8236          32 :         !ExpectSymbol(',', true) ||
    8237          48 :         !ParseSingleTokenNonNegativeVariant(left, VARIANT_SIDE, nullptr) ||
    8238          16 :         !ExpectSymbol(')', true))
    8239           0 :       break;
    8240             : 
    8241          16 :     aImage = newFunction;
    8242          16 :     return true;
    8243             :   }
    8244             : 
    8245           0 :   SkipUntil(')');
    8246           0 :   return false;
    8247             : }
    8248             : 
    8249             : // <element>: -moz-element(# <element_id> )
    8250             : bool
    8251           3 : CSSParserImpl::ParseElement(nsCSSValue& aValue)
    8252             : {
    8253             :   // A non-iterative for loop to break out when an error occurs.
    8254             :   for (;;) {
    8255           3 :     if (!GetToken(true))
    8256           0 :       break;
    8257             : 
    8258           3 :     if (mToken.mType == eCSSToken_ID) {
    8259           3 :       aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element);
    8260             :     } else {
    8261           0 :       UngetToken();
    8262           0 :       break;
    8263             :     }
    8264             : 
    8265           3 :     if (!ExpectSymbol(')', true))
    8266           0 :       break;
    8267             : 
    8268           3 :     return true;
    8269             :   }
    8270             : 
    8271             :   // If we detect a syntax error, we must match the opening parenthesis of the
    8272             :   // function with the closing parenthesis and skip all the tokens in between.
    8273           0 :   SkipUntil(')');
    8274           0 :   return false;
    8275             : }
    8276             : 
    8277             : // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
    8278             : bool
    8279          22 : CSSParserImpl::ParseFlex()
    8280             : {
    8281             :   // First check for inherit / initial / unset
    8282          44 :   nsCSSValue tmpVal;
    8283          22 :   if (ParseSingleTokenVariant(tmpVal, VARIANT_INHERIT, nullptr)) {
    8284           0 :     AppendValue(eCSSProperty_flex_grow, tmpVal);
    8285           0 :     AppendValue(eCSSProperty_flex_shrink, tmpVal);
    8286           0 :     AppendValue(eCSSProperty_flex_basis, tmpVal);
    8287           0 :     return true;
    8288             :   }
    8289             : 
    8290             :   // Next, check for 'none' == '0 0 auto'
    8291          22 :   if (ParseSingleTokenVariant(tmpVal, VARIANT_NONE, nullptr)) {
    8292           6 :     AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number));
    8293           6 :     AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number));
    8294           6 :     AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto));
    8295           6 :     return true;
    8296             :   }
    8297             : 
    8298             :   // OK, try parsing our value as individual per-subproperty components:
    8299             :   //   [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
    8300             : 
    8301             :   // Each subproperty has a default value that it takes when it's omitted in a
    8302             :   // "flex" shorthand value. These default values are *only* for the shorthand
    8303             :   // syntax -- they're distinct from the subproperties' own initial values.  We
    8304             :   // start with each subproperty at its default, as if we had "flex: 1 1 0%".
    8305          32 :   nsCSSValue flexGrow(1.0f, eCSSUnit_Number);
    8306          32 :   nsCSSValue flexShrink(1.0f, eCSSUnit_Number);
    8307          32 :   nsCSSValue flexBasis(0.0f, eCSSUnit_Percent);
    8308             : 
    8309             :   // OVERVIEW OF PARSING STRATEGY:
    8310             :   // =============================
    8311             :   // a) Parse the first component as either flex-basis or flex-grow.
    8312             :   // b) If it wasn't flex-grow, parse the _next_ component as flex-grow.
    8313             :   // c) Now we've just parsed flex-grow -- so try parsing the next thing as
    8314             :   //    flex-shrink.
    8315             :   // d) Finally: If we didn't get flex-basis at the beginning, try to parse
    8316             :   //    it now, at the end.
    8317             :   //
    8318             :   // More details in each section below.
    8319             : 
    8320             :   uint32_t flexBasisVariantMask =
    8321          16 :     (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT));
    8322             : 
    8323             :   // (a) Parse first component. It can be either be a 'flex-basis' value or a
    8324             :   // 'flex-grow' value, so we use the flex-basis-specific variant mask, along
    8325             :   //  with VARIANT_NUMBER to accept 'flex-grow' values.
    8326             :   //
    8327             :   // NOTE: if we encounter unitless 0 here, we *must* interpret it as a
    8328             :   // 'flex-grow' value (a number), *not* as a 'flex-basis' value (a length).
    8329             :   // Conveniently, that's the behavior this combined variant-mask gives us --
    8330             :   // it'll treat unitless 0 as a number. The flexbox spec requires this:
    8331             :   // "a unitless zero that is not already preceded by two flex factors must be
    8332             :   //  interpreted as a flex factor.
    8333          16 :   if (ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER,
    8334             :                               nsCSSProps::kWidthKTable) != CSSParseResult::Ok) {
    8335             :     // First component was not a valid flex-basis or flex-grow value. Fail.
    8336           0 :     return false;
    8337             :   }
    8338             : 
    8339             :   // Record what we just parsed as either flex-basis or flex-grow:
    8340          16 :   bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number);
    8341          16 :   (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal;
    8342             : 
    8343             :   // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow.
    8344          16 :   bool doneParsing = false;
    8345          16 :   if (wasFirstComponentFlexBasis) {
    8346           2 :     if (ParseNonNegativeNumber(tmpVal)) {
    8347           0 :       flexGrow = tmpVal;
    8348             :     } else {
    8349             :       // Failed to parse anything after our flex-basis -- that's fine. We can
    8350             :       // skip the remaining parsing.
    8351           2 :       doneParsing = true;
    8352             :     }
    8353             :   }
    8354             : 
    8355          16 :   if (!doneParsing) {
    8356             :     // (c) OK -- the last thing we parsed was flex-grow, so look for a
    8357             :     //     flex-shrink in the next position.
    8358          14 :     if (ParseNonNegativeNumber(tmpVal)) {
    8359           7 :       flexShrink = tmpVal;
    8360             :     }
    8361             : 
    8362             :     // d) Finally: If we didn't get flex-basis at the beginning, try to parse
    8363             :     //    it now, at the end.
    8364             :     //
    8365             :     // NOTE: If we encounter unitless 0 in this final position, we'll parse it
    8366             :     // as a 'flex-basis' value.  That's OK, because we know it must have
    8367             :     // been "preceded by 2 flex factors" (justification below), which gets us
    8368             :     // out of the spec's requirement of otherwise having to treat unitless 0
    8369             :     // as a flex factor.
    8370             :     //
    8371             :     // JUSTIFICATION: How do we know that a unitless 0 here must have been
    8372             :     // preceded by 2 flex factors? Well, suppose we had a unitless 0 that
    8373             :     // was preceded by only 1 flex factor.  Then, we would have already
    8374             :     // accepted this unitless 0 as the 'flex-shrink' value, up above (since
    8375             :     // it's a valid flex-shrink value), and we'd have moved on to the next
    8376             :     // token (if any). And of course, if we instead had a unitless 0 preceded
    8377             :     // by *no* flex factors (if it were the first token), we would've already
    8378             :     // parsed it in our very first call to ParseNonNegativeVariant().  So, any
    8379             :     // unitless 0 encountered here *must* have been preceded by 2 flex factors.
    8380          14 :     if (!wasFirstComponentFlexBasis) {
    8381             :       CSSParseResult result =
    8382          14 :         ParseNonNegativeVariant(tmpVal, flexBasisVariantMask,
    8383          14 :                                 nsCSSProps::kWidthKTable);
    8384          14 :       if (result == CSSParseResult::Error) {
    8385           0 :         return false;
    8386          14 :       } else if (result == CSSParseResult::Ok) {
    8387           9 :         flexBasis = tmpVal;
    8388             :       }
    8389             :     }
    8390             :   }
    8391             : 
    8392          16 :   AppendValue(eCSSProperty_flex_grow,   flexGrow);
    8393          16 :   AppendValue(eCSSProperty_flex_shrink, flexShrink);
    8394          16 :   AppendValue(eCSSProperty_flex_basis,  flexBasis);
    8395             : 
    8396          16 :   return true;
    8397             : }
    8398             : 
    8399             : // flex-flow: <flex-direction> || <flex-wrap>
    8400             : bool
    8401           0 : CSSParserImpl::ParseFlexFlow()
    8402             : {
    8403             :   static const nsCSSPropertyID kFlexFlowSubprops[] = {
    8404             :     eCSSProperty_flex_direction,
    8405             :     eCSSProperty_flex_wrap
    8406             :   };
    8407           0 :   const size_t numProps = MOZ_ARRAY_LENGTH(kFlexFlowSubprops);
    8408           0 :   nsCSSValue values[numProps];
    8409             : 
    8410           0 :   int32_t found = ParseChoice(values, kFlexFlowSubprops, numProps);
    8411             : 
    8412             :   // Bail if we didn't successfully parse anything
    8413           0 :   if (found < 1) {
    8414           0 :     return false;
    8415             :   }
    8416             : 
    8417             :   // If either property didn't get an explicit value, use its initial value.
    8418           0 :   if ((found & 1) == 0) {
    8419           0 :     values[0].SetIntValue(NS_STYLE_FLEX_DIRECTION_ROW, eCSSUnit_Enumerated);
    8420             :   }
    8421           0 :   if ((found & 2) == 0) {
    8422           0 :     values[1].SetIntValue(NS_STYLE_FLEX_WRAP_NOWRAP, eCSSUnit_Enumerated);
    8423             :   }
    8424             : 
    8425             :   // Store these values and declare success!
    8426           0 :   for (size_t i = 0; i < numProps; i++) {
    8427           0 :     AppendValue(kFlexFlowSubprops[i], values[i]);
    8428             :   }
    8429           0 :   return true;
    8430             : }
    8431             : 
    8432             : bool
    8433           6 : CSSParserImpl::ParseGridAutoFlow()
    8434             : {
    8435          12 :   nsCSSValue value;
    8436           6 :   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
    8437           6 :     AppendValue(eCSSProperty_grid_auto_flow, value);
    8438           6 :     return true;
    8439             :   }
    8440             : 
    8441             :   static const int32_t mask[] = {
    8442             :     NS_STYLE_GRID_AUTO_FLOW_ROW | NS_STYLE_GRID_AUTO_FLOW_COLUMN,
    8443             :     MASK_END_VALUE
    8444             :   };
    8445           0 :   if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) {
    8446           0 :     return false;
    8447             :   }
    8448           0 :   int32_t bitField = value.GetIntValue();
    8449             : 
    8450             :   // If neither row nor column is provided, row is assumed.
    8451           0 :   if (!(bitField & (NS_STYLE_GRID_AUTO_FLOW_ROW |
    8452             :                     NS_STYLE_GRID_AUTO_FLOW_COLUMN))) {
    8453           0 :     value.SetIntValue(bitField | NS_STYLE_GRID_AUTO_FLOW_ROW,
    8454           0 :                       eCSSUnit_Enumerated);
    8455             :   }
    8456             : 
    8457           0 :   AppendValue(eCSSProperty_grid_auto_flow, value);
    8458           0 :   return true;
    8459             : }
    8460             : 
    8461             : static const nsCSSKeyword kGridLineKeywords[] = {
    8462             :   eCSSKeyword_span,
    8463             :   eCSSKeyword_UNKNOWN  // End-of-array marker
    8464             : };
    8465             : 
    8466             : CSSParseResult
    8467           0 : CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue)
    8468             : {
    8469           0 :   if (!ExpectSymbol('[', true)) {
    8470           0 :     return CSSParseResult::NotFound;
    8471             :   }
    8472           0 :   if (!GetToken(true) || mToken.IsSymbol(']')) {
    8473           0 :     return CSSParseResult::Ok;
    8474             :   }
    8475             :   // 'return' so far leaves aValue untouched, to represent an empty list.
    8476             : 
    8477             :   nsCSSValueList* item;
    8478           0 :   if (aValue.GetUnit() == eCSSUnit_List) {
    8479             :     // Find the end of an existing list.
    8480             :     // The grid-template shorthand uses this, at most once for a given list.
    8481             : 
    8482             :     // NOTE: we could avoid this traversal by somehow keeping around
    8483             :     // a pointer to the last item from the previous call.
    8484             :     // It's not yet clear if this is worth the additional code complexity.
    8485           0 :     item = aValue.GetListValue();
    8486           0 :     while (item->mNext) {
    8487           0 :       item = item->mNext;
    8488             :     }
    8489           0 :     item->mNext = new nsCSSValueList;
    8490           0 :     item = item->mNext;
    8491             :   } else {
    8492           0 :     MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "Unexpected unit");
    8493           0 :     item = aValue.SetListValue();
    8494             :   }
    8495             :   for (;;) {
    8496           0 :     if (!(eCSSToken_Ident == mToken.mType &&
    8497           0 :           ParseCustomIdent(item->mValue, mToken.mIdent, kGridLineKeywords))) {
    8498           0 :       UngetToken();
    8499           0 :       SkipUntil(']');
    8500           0 :       return CSSParseResult::Error;
    8501             :     }
    8502           0 :     if (!GetToken(true) || mToken.IsSymbol(']')) {
    8503           0 :       return CSSParseResult::Ok;
    8504             :     }
    8505           0 :     item->mNext = new nsCSSValueList;
    8506           0 :     item = item->mNext;
    8507             :   }
    8508             : }
    8509             : 
    8510             : // Assuming the 'repeat(' function token has already been consumed,
    8511             : // parse the rest of repeat(<positive-integer> | auto-fill, <line-names>+)
    8512             : // Append to the linked list whose end is given by |aTailPtr|,
    8513             : // and update |aTailPtr| to point to the new end of the list.
    8514             : bool
    8515           0 : CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr)
    8516             : {
    8517             :   int32_t repetitions;
    8518           0 :   Maybe<int32_t> repeatAutoEnum;
    8519           0 :   if (!ParseGridTrackRepeatIntro(true, &repetitions, &repeatAutoEnum)) {
    8520           0 :     return false;
    8521             :   }
    8522           0 :   if (repeatAutoEnum.isSome()) {
    8523             :     // Parse exactly one <line-names>.
    8524           0 :     nsCSSValue listValue;
    8525           0 :     nsCSSValueList* list = listValue.SetListValue();
    8526           0 :     if (ParseGridLineNames(list->mValue) != CSSParseResult::Ok) {
    8527           0 :       return false;
    8528             :     }
    8529           0 :     if (!ExpectSymbol(')', true)) {
    8530           0 :       return false;
    8531             :     }
    8532             :     // Instead of hooking up this list into the flat name list as usual,
    8533             :     // we create a pair(Int, List) where the first value is the auto-fill
    8534             :     // keyword and the second is the name list to repeat.
    8535           0 :     nsCSSValue kwd;
    8536           0 :     kwd.SetIntValue(repeatAutoEnum.value(), eCSSUnit_Enumerated);
    8537           0 :     *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
    8538           0 :     (*aTailPtr)->mValue.SetPairValue(kwd, listValue);
    8539           0 :     return true;
    8540             :   }
    8541             : 
    8542             :   // Parse at least one <line-names>
    8543           0 :   nsCSSValueList* tail = *aTailPtr;
    8544           0 :   do {
    8545           0 :     tail->mNext = new nsCSSValueList;
    8546           0 :     tail = tail->mNext;
    8547           0 :     if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) {
    8548           0 :       return false;
    8549             :     }
    8550           0 :   } while (!ExpectSymbol(')', true));
    8551           0 :   nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext;
    8552           0 :   nsCSSValueList* lastRepeatedItem = tail;
    8553             : 
    8554             :   // Our repeated items are already in the target list once,
    8555             :   // so they need to be repeated |repetitions - 1| more times.
    8556           0 :   MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
    8557           0 :   while (--repetitions) {
    8558           0 :     nsCSSValueList* repeatedItem = firstRepeatedItem;
    8559             :     for (;;) {
    8560           0 :       tail->mNext = new nsCSSValueList;
    8561           0 :       tail = tail->mNext;
    8562           0 :       tail->mValue = repeatedItem->mValue;
    8563           0 :       if (repeatedItem == lastRepeatedItem) {
    8564           0 :         break;
    8565             :       }
    8566           0 :       repeatedItem = repeatedItem->mNext;
    8567             :     }
    8568             :   }
    8569           0 :   *aTailPtr = tail;
    8570           0 :   return true;
    8571             : }
    8572             : 
    8573             : // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>?
    8574             : bool
    8575           0 : CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
    8576             : {
    8577           0 :   nsCSSValueList* item = aValue.SetListValue();
    8578             :   // This marker distinguishes the value from a <track-list>.
    8579           0 :   item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID,
    8580           0 :                            eCSSUnit_Enumerated);
    8581           0 :   bool haveRepeatAuto = false;
    8582             :   for (;;) {
    8583             :     // First try to parse <name-repeat>, i.e.
    8584             :     // repeat(<positive-integer> | auto-fill, <line-names>+)
    8585           0 :     if (!GetToken(true)) {
    8586           0 :       return true;
    8587             :     }
    8588           0 :     if (mToken.mType == eCSSToken_Function &&
    8589           0 :         mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
    8590           0 :       nsCSSValueList* startOfRepeat = item;
    8591           0 :       if (!ParseGridLineNameListRepeat(&item)) {
    8592           0 :         SkipUntil(')');
    8593           0 :         return false;
    8594             :       }
    8595           0 :       if (startOfRepeat->mNext->mValue.GetUnit() == eCSSUnit_Pair) {
    8596           0 :         if (haveRepeatAuto) {
    8597           0 :           REPORT_UNEXPECTED(PEMoreThanOneGridRepeatAutoFillInNameList);
    8598           0 :           return false;
    8599             :         }
    8600           0 :         haveRepeatAuto = true;
    8601             :       }
    8602             :     } else {
    8603           0 :       UngetToken();
    8604             : 
    8605             :       // This was not a repeat() function. Try to parse <line-names>.
    8606           0 :       nsCSSValue lineNames;
    8607           0 :       CSSParseResult result = ParseGridLineNames(lineNames);
    8608           0 :       if (result == CSSParseResult::NotFound) {
    8609           0 :         return true;
    8610             :       }
    8611           0 :       if (result == CSSParseResult::Error) {
    8612           0 :         return false;
    8613             :       }
    8614           0 :       item->mNext = new nsCSSValueList;
    8615           0 :       item = item->mNext;
    8616           0 :       item->mValue = lineNames;
    8617             :     }
    8618           0 :   }
    8619             : }
    8620             : 
    8621             : CSSParseResult
    8622           0 : CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
    8623             : {
    8624             :   CSSParseResult result = ParseNonNegativeVariant(aValue,
    8625             :                             VARIANT_AUTO | VARIANT_LPCALC | VARIANT_KEYWORD,
    8626           0 :                             nsCSSProps::kGridTrackBreadthKTable);
    8627             : 
    8628           0 :   if (result == CSSParseResult::Ok ||
    8629             :       result == CSSParseResult::Error) {
    8630           0 :     return result;
    8631             :   }
    8632             : 
    8633             :   // Attempt to parse <flex> (a dimension with the "fr" unit).
    8634           0 :   if (!GetToken(true)) {
    8635           0 :     return CSSParseResult::NotFound;
    8636             :   }
    8637           0 :   if (!(eCSSToken_Dimension == mToken.mType &&
    8638           0 :         mToken.mIdent.LowerCaseEqualsLiteral("fr") &&
    8639           0 :         mToken.mNumber >= 0)) {
    8640           0 :     UngetToken();
    8641           0 :     return CSSParseResult::NotFound;
    8642             :   }
    8643           0 :   aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction);
    8644           0 :   return CSSParseResult::Ok;
    8645             : }
    8646             : 
    8647             : // Parse a <track-size>, or <fixed-size> when aFlags has eFixedTrackSize.
    8648             : CSSParseResult
    8649           0 : CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue,
    8650             :                                   GridTrackSizeFlags aFlags)
    8651             : {
    8652             :   const bool requireFixedSize =
    8653           0 :     !!(aFlags & GridTrackSizeFlags::eFixedTrackSize);
    8654             :   // Attempt to parse a single <track-breadth>.
    8655           0 :   CSSParseResult result = ParseGridTrackBreadth(aValue);
    8656           0 :   if (requireFixedSize && result == CSSParseResult::Ok &&
    8657           0 :       !aValue.IsLengthPercentCalcUnit()) {
    8658           0 :     result = CSSParseResult::Error;
    8659             :   }
    8660           0 :   if (result == CSSParseResult::Error) {
    8661           0 :     return result;
    8662             :   }
    8663           0 :   if (result == CSSParseResult::Ok) {
    8664           0 :     if (aValue.GetUnit() == eCSSUnit_FlexFraction) {
    8665             :       // Single value <flex> is represented internally as minmax(auto, <flex>).
    8666           0 :       nsCSSValue minmax;
    8667           0 :       nsCSSValue::Array* func = minmax.InitFunction(eCSSKeyword_minmax, 2);
    8668           0 :       func->Item(1).SetAutoValue();
    8669           0 :       func->Item(2) = aValue;
    8670           0 :       aValue = minmax;
    8671             :     }
    8672           0 :     return result;
    8673             :   }
    8674             : 
    8675             :   // Attempt to parse a minmax() or fit-content() function.
    8676           0 :   if (!GetToken(true)) {
    8677           0 :     return CSSParseResult::NotFound;
    8678             :   }
    8679           0 :   if (eCSSToken_Function != mToken.mType) {
    8680           0 :     UngetToken();
    8681           0 :     return CSSParseResult::NotFound;
    8682             :   }
    8683           0 :   if (mToken.mIdent.LowerCaseEqualsLiteral("fit-content")) {
    8684           0 :     if (requireFixedSize) {
    8685           0 :       UngetToken();
    8686           0 :       return CSSParseResult::Error;
    8687             :     }
    8688           0 :     nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_fit_content, 1);
    8689           0 :     if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok &&
    8690           0 :         func->Item(1).IsLengthPercentCalcUnit() &&
    8691           0 :         ExpectSymbol(')', true)) {
    8692           0 :       return CSSParseResult::Ok;
    8693             :     }
    8694           0 :     SkipUntil(')');
    8695           0 :     return CSSParseResult::Error;
    8696             :   }
    8697           0 :   if (!mToken.mIdent.LowerCaseEqualsLiteral("minmax")) {
    8698           0 :     UngetToken();
    8699           0 :     return CSSParseResult::NotFound;
    8700             :   }
    8701           0 :   nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2);
    8702           0 :   if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok &&
    8703           0 :       ExpectSymbol(',', true) &&
    8704           0 :       ParseGridTrackBreadth(func->Item(2)) == CSSParseResult::Ok &&
    8705           0 :       ExpectSymbol(')', true)) {
    8706           0 :     if (requireFixedSize &&
    8707           0 :         !func->Item(1).IsLengthPercentCalcUnit() &&
    8708           0 :         !func->Item(2).IsLengthPercentCalcUnit()) {
    8709           0 :       return CSSParseResult::Error;
    8710             :     }
    8711             :     // Reject <flex> min-sizing.
    8712           0 :     if (func->Item(1).GetUnit() == eCSSUnit_FlexFraction) {
    8713           0 :       return CSSParseResult::Error;
    8714             :     }
    8715           0 :     return CSSParseResult::Ok;
    8716             :   }
    8717           0 :   SkipUntil(')');
    8718           0 :   return CSSParseResult::Error;
    8719             : }
    8720             : 
    8721             : bool
    8722          12 : CSSParserImpl::ParseGridAutoColumnsRows(nsCSSPropertyID aPropID)
    8723             : {
    8724          24 :   nsCSSValue value;
    8725          12 :   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) ||
    8726           0 :       ParseGridTrackSize(value) == CSSParseResult::Ok) {
    8727          12 :     AppendValue(aPropID, value);
    8728          12 :     return true;
    8729             :   }
    8730           0 :   return false;
    8731             : }
    8732             : 
    8733             : bool
    8734           0 : CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
    8735             :                                                     const nsCSSValue& aFirstLineNames,
    8736             :                                                     GridTrackListFlags aFlags)
    8737             : {
    8738           0 :   nsCSSValueList* firstLineNamesItem = aValue.SetListValue();
    8739           0 :   firstLineNamesItem->mValue = aFirstLineNames;
    8740             : 
    8741             :   // This function is trying to parse <track-list>, which is
    8742             :   //   [ <line-names>? [ <track-size> | <repeat()> ] ]+ <line-names>?
    8743             :   // and we're already past the first "<line-names>?".
    8744             :   // If aFlags contains eExplicitTrackList then <repeat()> is disallowed.
    8745             :   //
    8746             :   // Each iteration of the following loop attempts to parse either a
    8747             :   // repeat() or a <track-size> expression, and then an (optional)
    8748             :   // <line-names> expression.
    8749             :   //
    8750             :   // The only successful exit point from this loop is the ::NotFound
    8751             :   // case after ParseGridTrackSize(); i.e. we'll greedily parse
    8752             :   // repeat()/<track-size> until we can't find one.
    8753           0 :   nsCSSValueList* item = firstLineNamesItem;
    8754           0 :   bool haveRepeatAuto = false;
    8755             :   for (;;) {
    8756             :     // First try to parse repeat()
    8757           0 :     if (!GetToken(true)) {
    8758           0 :       break;
    8759             :     }
    8760           0 :     if (!(aFlags & GridTrackListFlags::eExplicitTrackList) &&
    8761           0 :         mToken.mType == eCSSToken_Function &&
    8762           0 :         mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
    8763           0 :       nsCSSValueList* startOfRepeat = item;
    8764           0 :       if (!ParseGridTrackListRepeat(&item)) {
    8765           0 :         SkipUntil(')');
    8766           0 :         return false;
    8767             :       }
    8768           0 :       auto firstRepeat = startOfRepeat->mNext;
    8769           0 :       if (firstRepeat->mValue.GetUnit() == eCSSUnit_Pair) {
    8770           0 :         if (haveRepeatAuto) {
    8771           0 :           REPORT_UNEXPECTED(PEMoreThanOneGridRepeatAutoFillFitInTrackList);
    8772           0 :           return false;
    8773             :         }
    8774           0 :         haveRepeatAuto = true;
    8775             :         // We're parsing an <auto-track-list>, which requires that all tracks
    8776             :         // are <fixed-size>, so we need to check the ones we've parsed already.
    8777           0 :         for (nsCSSValueList* list = firstLineNamesItem->mNext;
    8778           0 :              list != firstRepeat; list = list->mNext) {
    8779           0 :           if (list->mValue.GetUnit() == eCSSUnit_Function) {
    8780           0 :             nsCSSValue::Array* func = list->mValue.GetArrayValue();
    8781           0 :             auto funcName = func->Item(0).GetKeywordValue();
    8782           0 :             if (funcName == eCSSKeyword_minmax) {
    8783           0 :               if (!func->Item(1).IsLengthPercentCalcUnit() &&
    8784           0 :                   !func->Item(2).IsLengthPercentCalcUnit()) {
    8785           0 :                 return false;
    8786             :               }
    8787             :             } else {
    8788           0 :               MOZ_ASSERT(funcName == eCSSKeyword_fit_content,
    8789             :                          "Expected minmax() or fit-content() function");
    8790           0 :               return false; // fit-content() is not a <fixed-size>
    8791             :             }
    8792           0 :           } else if (!list->mValue.IsLengthPercentCalcUnit()) {
    8793           0 :             return false;
    8794             :           }
    8795           0 :           list = list->mNext; // skip line names
    8796             :         }
    8797             :       }
    8798             :     } else {
    8799           0 :       UngetToken();
    8800             : 
    8801             :       // Not a repeat() function; try to parse <track-size> | <fixed-size>.
    8802           0 :       nsCSSValue trackSize;
    8803             :       GridTrackSizeFlags flags = haveRepeatAuto
    8804           0 :         ? GridTrackSizeFlags::eFixedTrackSize
    8805           0 :         : GridTrackSizeFlags::eDefaultTrackSize;
    8806           0 :       CSSParseResult result = ParseGridTrackSize(trackSize, flags);
    8807           0 :       if (result == CSSParseResult::Error) {
    8808           0 :         return false;
    8809             :       }
    8810           0 :       if (result == CSSParseResult::NotFound) {
    8811             :         // What we've parsed so far is a valid <track-list>
    8812             :         // (modulo the "at least one <track-size>" check below.)
    8813             :         // Stop here.
    8814           0 :         break;
    8815             :       }
    8816           0 :       item->mNext = new nsCSSValueList;
    8817           0 :       item = item->mNext;
    8818           0 :       item->mValue = trackSize;
    8819             : 
    8820           0 :       item->mNext = new nsCSSValueList;
    8821           0 :       item = item->mNext;
    8822             :     }
    8823           0 :     if (ParseGridLineNames(item->mValue) == CSSParseResult::Error) {
    8824           0 :       return false;
    8825             :     }
    8826           0 :   }
    8827             : 
    8828             :   // Require at least one <track-size>.
    8829           0 :   if (item == firstLineNamesItem) {
    8830           0 :     return false;
    8831             :   }
    8832             : 
    8833           0 :   MOZ_ASSERT(aValue.GetListValue() &&
    8834             :              aValue.GetListValue()->mNext &&
    8835             :              aValue.GetListValue()->mNext->mNext,
    8836             :              "<track-list> should have a minimum length of 3");
    8837           0 :   return true;
    8838             : }
    8839             : 
    8840             : // Takes ownership of |aSecond|
    8841             : static void
    8842           0 : ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond)
    8843             : {
    8844           0 :   if (aSecond.GetUnit() == eCSSUnit_Null) {
    8845             :     // Nothing to do.
    8846           0 :     return;
    8847             :   }
    8848           0 :   if (aFirst.GetUnit() == eCSSUnit_Null) {
    8849             :     // Empty or omitted <line-names>. Replace it.
    8850           0 :     aFirst = aSecond;
    8851           0 :     return;
    8852             :   }
    8853             : 
    8854             :   // Join the two <line-names> lists.
    8855           0 :   nsCSSValueList* source = aSecond.GetListValue();
    8856           0 :   nsCSSValueList* target = aFirst.GetListValue();
    8857             :   // Find the end:
    8858           0 :   while (target->mNext) {
    8859           0 :     target = target->mNext;
    8860             :   }
    8861             :   // Copy the first name. We can't take ownership of it
    8862             :   // as it'll be destroyed when |aSecond| goes out of scope.
    8863           0 :   target->mNext = new nsCSSValueList;
    8864           0 :   target = target->mNext;
    8865           0 :   target->mValue = source->mValue;
    8866             :   // Move the rest of the linked list.
    8867           0 :   target->mNext = source->mNext;
    8868           0 :   source->mNext = nullptr;
    8869             : }
    8870             : 
    8871             : // Assuming the 'repeat(' function token has already been consumed,
    8872             : // parse "repeat( <positive-integer> | auto-fill | auto-fit ,"
    8873             : // (or "repeat( <positive-integer> | auto-fill ," when aForSubgrid is true)
    8874             : // and stop after the comma.  Return true when parsing succeeds,
    8875             : // with aRepetitions set to the number of repetitions and aRepeatAutoEnum set
    8876             : // to an enum value for auto-fill | auto-fit (it's not set at all when
    8877             : // <positive-integer> was parsed).
    8878             : bool
    8879           0 : CSSParserImpl::ParseGridTrackRepeatIntro(bool            aForSubgrid,
    8880             :                                          int32_t*        aRepetitions,
    8881             :                                          Maybe<int32_t>* aRepeatAutoEnum)
    8882             : {
    8883           0 :   if (!GetToken(true)) {
    8884           0 :     return false;
    8885             :   }
    8886           0 :   if (mToken.mType == eCSSToken_Ident) {
    8887           0 :     if (mToken.mIdent.LowerCaseEqualsLiteral("auto-fill")) {
    8888           0 :       aRepeatAutoEnum->emplace(NS_STYLE_GRID_REPEAT_AUTO_FILL);
    8889           0 :     } else if (!aForSubgrid &&
    8890           0 :                mToken.mIdent.LowerCaseEqualsLiteral("auto-fit")) {
    8891           0 :       aRepeatAutoEnum->emplace(NS_STYLE_GRID_REPEAT_AUTO_FIT);
    8892             :     } else {
    8893           0 :       return false;
    8894             :     }
    8895           0 :     *aRepetitions = 1;
    8896           0 :   } else if (mToken.mType == eCSSToken_Number) {
    8897           0 :     if (!(mToken.mIntegerValid &&
    8898           0 :           mToken.mInteger > 0)) {
    8899           0 :       return false;
    8900             :     }
    8901           0 :     *aRepetitions = std::min(mToken.mInteger, GRID_TEMPLATE_MAX_REPETITIONS);
    8902             :   } else {
    8903           0 :     return false;
    8904             :   }
    8905             : 
    8906           0 :   if (!ExpectSymbol(',', true)) {
    8907           0 :     return false;
    8908             :   }
    8909           0 :   return true;
    8910             : }
    8911             : 
    8912             : // Assuming the 'repeat(' function token has already been consumed,
    8913             : // parse the rest of
    8914             : // repeat( <positive-integer> | auto-fill | auto-fit ,
    8915             : //         [ <line-names>? <track-size> ]+ <line-names>? )
    8916             : // Append to the linked list whose end is given by |aTailPtr|,
    8917             : // and update |aTailPtr| to point to the new end of the list.
    8918             : // Note: only one <track-size> is allowed for auto-fill/fit
    8919             : bool
    8920           0 : CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
    8921             : {
    8922             :   int32_t repetitions;
    8923           0 :   Maybe<int32_t> repeatAutoEnum;
    8924           0 :   if (!ParseGridTrackRepeatIntro(false, &repetitions, &repeatAutoEnum)) {
    8925           0 :     return false;
    8926             :   }
    8927             : 
    8928             :   // Parse [ <line-names>? <track-size> ]+ <line-names>?
    8929             :   // but keep the first and last <line-names> separate
    8930             :   // because they'll need to be joined.
    8931             :   // http://dev.w3.org/csswg/css-grid/#repeat-notation
    8932           0 :   nsCSSValue firstLineNames;
    8933           0 :   nsCSSValue trackSize;
    8934           0 :   nsCSSValue lastLineNames;
    8935             :   // Optional
    8936           0 :   if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) {
    8937           0 :     return false;
    8938             :   }
    8939             :   // Required
    8940           0 :   GridTrackSizeFlags flags = repeatAutoEnum.isSome()
    8941           0 :     ? GridTrackSizeFlags::eFixedTrackSize
    8942           0 :     : GridTrackSizeFlags::eDefaultTrackSize;
    8943           0 :   if (ParseGridTrackSize(trackSize, flags) != CSSParseResult::Ok) {
    8944           0 :     return false;
    8945             :   }
    8946             :   // Use nsAutoPtr to free the list in case of early return.
    8947           0 :   nsAutoPtr<nsCSSValueList> firstTrackSizeItemAuto(new nsCSSValueList);
    8948           0 :   firstTrackSizeItemAuto->mValue = trackSize;
    8949             : 
    8950           0 :   nsCSSValueList* item = firstTrackSizeItemAuto;
    8951             :   for (;;) {
    8952             :     // Optional
    8953           0 :     if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) {
    8954           0 :       return false;
    8955             :     }
    8956             : 
    8957           0 :     if (ExpectSymbol(')', true)) {
    8958           0 :       break;
    8959             :     }
    8960             : 
    8961             :     // <auto-repeat> only accepts a single track size:
    8962             :     // <line-names>? <fixed-size> <line-names>?
    8963           0 :     if (repeatAutoEnum.isSome()) {
    8964           0 :       REPORT_UNEXPECTED(PEMoreThanOneGridRepeatTrackSize);
    8965           0 :       return false;
    8966             :     }
    8967             : 
    8968             :     // Required
    8969           0 :     if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
    8970           0 :       return false;
    8971             :     }
    8972             : 
    8973           0 :     item->mNext = new nsCSSValueList;
    8974           0 :     item = item->mNext;
    8975           0 :     item->mValue = lastLineNames;
    8976             :     // Do not append to this list at the next iteration.
    8977           0 :     lastLineNames.Reset();
    8978             : 
    8979           0 :     item->mNext = new nsCSSValueList;
    8980           0 :     item = item->mNext;
    8981           0 :     item->mValue = trackSize;
    8982             :   }
    8983           0 :   nsCSSValueList* lastTrackSizeItem = item;
    8984             : 
    8985             :   // [ <line-names>? <track-size> ]+ <line-names>?  is now parsed into:
    8986             :   // * firstLineNames: the first <line-names>
    8987             :   // * a linked list of odd length >= 1, from firstTrackSizeItem
    8988             :   //   (the first <track-size>) to lastTrackSizeItem (the last),
    8989             :   //   with the <line-names> sublists in between
    8990             :   // * lastLineNames: the last <line-names>
    8991             : 
    8992           0 :   if (repeatAutoEnum.isSome()) {
    8993             :     // Instead of hooking up this list into the flat track/name list as usual,
    8994             :     // we create a pair(Int, List) where the first value is the auto-fill/fit
    8995             :     // keyword and the second is the list to repeat.  There are three items
    8996             :     // in this list, the first is the list of line names before the track size,
    8997             :     // the second item is the track size, and the last item is the list of line
    8998             :     // names after the track size.  Note that the line names are NOT merged
    8999             :     // with any line names before/after the repeat() itself.
    9000           0 :     nsCSSValue listValue;
    9001           0 :     nsCSSValueList* list = listValue.SetListValue();
    9002           0 :     list->mValue = firstLineNames;
    9003           0 :     list = list->mNext = new nsCSSValueList;
    9004           0 :     list->mValue = trackSize;
    9005           0 :     list = list->mNext = new nsCSSValueList;
    9006           0 :     list->mValue = lastLineNames;
    9007           0 :     nsCSSValue kwd;
    9008           0 :     kwd.SetIntValue(repeatAutoEnum.value(), eCSSUnit_Enumerated);
    9009           0 :     *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
    9010           0 :     (*aTailPtr)->mValue.SetPairValue(kwd, listValue);
    9011             :     // Append an empty list since the caller expects that to represent the names
    9012             :     // that follows the repeat() function.
    9013           0 :     *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
    9014           0 :     return true;
    9015             :   }
    9016             : 
    9017             :   // Join the last and first <line-names> (in that order.)
    9018             :   // For example, repeat(3, (a) 100px (b) 200px (c)) results in
    9019             :   // (a) 100px (b) 200px (c a) 100px (b) 200px (c a) 100px (b) 200px (c)
    9020             :   // This is (c a).
    9021             :   // Make deep copies: the originals will be moved.
    9022           0 :   nsCSSValue joinerLineNames;
    9023             :   {
    9024           0 :     nsCSSValueList* target = nullptr;
    9025           0 :     if (lastLineNames.GetUnit() != eCSSUnit_Null) {
    9026           0 :       target = joinerLineNames.SetListValue();
    9027           0 :       nsCSSValueList* source = lastLineNames.GetListValue();
    9028             :       for (;;) {
    9029           0 :         target->mValue = source->mValue;
    9030           0 :         source = source->mNext;
    9031           0 :         if (!source) {
    9032           0 :           break;
    9033             :         }
    9034           0 :         target->mNext = new nsCSSValueList;
    9035           0 :         target = target->mNext;
    9036             :       }
    9037             :     }
    9038             : 
    9039           0 :     if (firstLineNames.GetUnit() != eCSSUnit_Null) {
    9040           0 :       if (target) {
    9041           0 :         target->mNext = new nsCSSValueList;
    9042           0 :         target = target->mNext;
    9043             :       } else {
    9044           0 :         target = joinerLineNames.SetListValue();
    9045             :       }
    9046           0 :       nsCSSValueList* source = firstLineNames.GetListValue();
    9047             :       for (;;) {
    9048           0 :         target->mValue = source->mValue;
    9049           0 :         source = source->mNext;
    9050           0 :         if (!source) {
    9051           0 :           break;
    9052             :         }
    9053           0 :         target->mNext = new nsCSSValueList;
    9054           0 :         target = target->mNext;
    9055             :       }
    9056             :     }
    9057             :   }
    9058             : 
    9059             :   // Join our first <line-names> with the one before repeat().
    9060             :   // (a) repeat(1, (b) 20px) expands to (a b) 20px
    9061           0 :   nsCSSValueList* previousItemBeforeRepeat = *aTailPtr;
    9062           0 :   ConcatLineNames(previousItemBeforeRepeat->mValue, firstLineNames);
    9063             : 
    9064             :   // Move our linked list
    9065             :   // (first to last <track-size>, with the <line-names> sublists in between).
    9066             :   // This is the first repetition.
    9067           0 :   NS_ASSERTION(previousItemBeforeRepeat->mNext == nullptr,
    9068             :                "Expected the end of a linked list");
    9069           0 :   previousItemBeforeRepeat->mNext = firstTrackSizeItemAuto.forget();
    9070           0 :   nsCSSValueList* firstTrackSizeItem = previousItemBeforeRepeat->mNext;
    9071           0 :   nsCSSValueList* tail = lastTrackSizeItem;
    9072             : 
    9073             :   // Repeat |repetitions - 1| more times:
    9074             :   // * the joiner <line-names>
    9075             :   // * the linked list
    9076             :   //   (first to last <track-size>, with the <line-names> sublists in between)
    9077           0 :   MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
    9078           0 :   while (--repetitions) {
    9079           0 :     tail->mNext = new nsCSSValueList;
    9080           0 :     tail = tail->mNext;
    9081           0 :     tail->mValue = joinerLineNames;
    9082             : 
    9083           0 :     nsCSSValueList* repeatedItem = firstTrackSizeItem;
    9084             :     for (;;) {
    9085           0 :       tail->mNext = new nsCSSValueList;
    9086           0 :       tail = tail->mNext;
    9087           0 :       tail->mValue = repeatedItem->mValue;
    9088           0 :       if (repeatedItem == lastTrackSizeItem) {
    9089           0 :         break;
    9090             :       }
    9091           0 :       repeatedItem = repeatedItem->mNext;
    9092             :     }
    9093             :   }
    9094             : 
    9095             :   // Finally, move our last <line-names>.
    9096             :   // Any <line-names> immediately after repeat() will append to it.
    9097           0 :   tail->mNext = new nsCSSValueList;
    9098           0 :   tail = tail->mNext;
    9099           0 :   tail->mValue = lastLineNames;
    9100             : 
    9101           0 :   *aTailPtr = tail;
    9102           0 :   return true;
    9103             : }
    9104             : 
    9105             : bool
    9106           0 : CSSParserImpl::ParseGridTrackList(nsCSSPropertyID aPropID,
    9107             :                                   GridTrackListFlags aFlags)
    9108             : {
    9109           0 :   nsCSSValue value;
    9110           0 :   nsCSSValue firstLineNames;
    9111           0 :   if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
    9112           0 :       !ParseGridTrackListWithFirstLineNames(value, firstLineNames, aFlags)) {
    9113           0 :     return false;
    9114             :   }
    9115           0 :   AppendValue(aPropID, value);
    9116           0 :   return true;
    9117             : }
    9118             : 
    9119             : bool
    9120          12 : CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSPropertyID aPropID)
    9121             : {
    9122          24 :   nsCSSValue value;
    9123          12 :   if (ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
    9124          12 :     AppendValue(aPropID, value);
    9125          12 :     return true;
    9126             :   }
    9127             : 
    9128           0 :   nsAString* ident = NextIdent();
    9129           0 :   if (ident) {
    9130           0 :     if (ident->LowerCaseEqualsLiteral("subgrid")) {
    9131           0 :       if (!nsLayoutUtils::IsGridTemplateSubgridValueEnabled()) {
    9132           0 :         REPORT_UNEXPECTED(PESubgridNotSupported);
    9133           0 :         return false;
    9134             :       }
    9135           0 :       if (!ParseOptionalLineNameListAfterSubgrid(value)) {
    9136           0 :         return false;
    9137             :       }
    9138           0 :       AppendValue(aPropID, value);
    9139           0 :       return true;
    9140             :     }
    9141           0 :     UngetToken();
    9142             :   }
    9143             : 
    9144           0 :   return ParseGridTrackList(aPropID);
    9145             : }
    9146             : 
    9147             : bool
    9148           0 : CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput,
    9149             :                                           css::GridTemplateAreasValue* aAreas,
    9150             :                                           nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices)
    9151             : {
    9152           0 :   aAreas->mTemplates.AppendElement(mToken.mIdent);
    9153             : 
    9154           0 :   nsCSSGridTemplateAreaScanner scanner(aInput);
    9155           0 :   nsCSSGridTemplateAreaToken token;
    9156           0 :   css::GridNamedArea* currentArea = nullptr;
    9157           0 :   uint32_t row = aAreas->NRows();
    9158             :   // Column numbers starts at 1, but we might not have any, eg
    9159             :   // grid-template-areas:""; which will result in mNColumns == 0.
    9160           0 :   uint32_t column = 0;
    9161           0 :   while (scanner.Next(token)) {
    9162           0 :     ++column;
    9163           0 :     if (token.isTrash) {
    9164           0 :       return false;
    9165             :     }
    9166           0 :     if (currentArea) {
    9167           0 :       if (token.mName == currentArea->mName) {
    9168           0 :         if (currentArea->mRowStart == row) {
    9169             :           // Next column in the first row of this named area.
    9170           0 :           currentArea->mColumnEnd++;
    9171             :         }
    9172           0 :         continue;
    9173             :       }
    9174             :       // We're exiting |currentArea|, so currentArea is ending at |column|.
    9175             :       // Make sure that this is consistent with currentArea on previous rows:
    9176           0 :       if (currentArea->mColumnEnd != column) {
    9177           0 :         NS_ASSERTION(currentArea->mRowStart != row,
    9178             :                      "Inconsistent column end for the first row of a named area.");
    9179             :         // Not a rectangle
    9180           0 :         return false;
    9181             :       }
    9182           0 :       currentArea = nullptr;
    9183             :     }
    9184           0 :     if (!token.mName.IsEmpty()) {
    9185             :       // Named cell that doesn't have a cell with the same name on its left.
    9186             : 
    9187             :       // Check if this is the continuation of an existing named area:
    9188             :       uint32_t index;
    9189           0 :       if (aAreaIndices.Get(token.mName, &index)) {
    9190           0 :         MOZ_ASSERT(index < aAreas->mNamedAreas.Length(),
    9191             :                    "Invalid aAreaIndices hash table");
    9192           0 :         currentArea = &aAreas->mNamedAreas[index];
    9193           0 :         if (currentArea->mColumnStart != column ||
    9194           0 :             currentArea->mRowEnd != row) {
    9195             :           // Existing named area, but not forming a rectangle
    9196           0 :           return false;
    9197             :         }
    9198             :         // Next row of an existing named area
    9199           0 :         currentArea->mRowEnd++;
    9200             :       } else {
    9201             :         // New named area
    9202           0 :         aAreaIndices.Put(token.mName, aAreas->mNamedAreas.Length());
    9203           0 :         currentArea = aAreas->mNamedAreas.AppendElement();
    9204           0 :         currentArea->mName = token.mName;
    9205             :         // For column or row N (starting at 1),
    9206             :         // the start line is N, the end line is N + 1
    9207           0 :         currentArea->mColumnStart = column;
    9208           0 :         currentArea->mColumnEnd = column + 1;
    9209           0 :         currentArea->mRowStart = row;
    9210           0 :         currentArea->mRowEnd = row + 1;
    9211             :       }
    9212             :     }
    9213             :   }
    9214           0 :   if (currentArea && currentArea->mColumnEnd != column + 1) {
    9215           0 :     NS_ASSERTION(currentArea->mRowStart != row,
    9216             :                  "Inconsistent column end for the first row of a named area.");
    9217             :     // Not a rectangle
    9218           0 :     return false;
    9219             :   }
    9220             : 
    9221             :   // On the first row, set the number of columns
    9222             :   // that grid-template-areas contributes to the explicit grid.
    9223             :   // On other rows, check that the number of columns is consistent
    9224             :   // between rows.
    9225           0 :   if (row == 1) {
    9226           0 :     aAreas->mNColumns = column;
    9227           0 :   } else if (aAreas->mNColumns != column) {
    9228           0 :     return false;
    9229             :   }
    9230           0 :   return true;
    9231             : }
    9232             : 
    9233             : bool
    9234           6 : CSSParserImpl::ParseGridTemplateAreas()
    9235             : {
    9236          12 :   nsCSSValue value;
    9237           6 :   if (ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
    9238           6 :     AppendValue(eCSSProperty_grid_template_areas, value);
    9239           6 :     return true;
    9240             :   }
    9241             : 
    9242             :   RefPtr<css::GridTemplateAreasValue> areas =
    9243           0 :     new css::GridTemplateAreasValue();
    9244           0 :   nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
    9245             :   for (;;) {
    9246           0 :     if (!GetToken(true)) {
    9247           0 :       break;
    9248             :     }
    9249           0 :     if (eCSSToken_String != mToken.mType) {
    9250           0 :       UngetToken();
    9251           0 :       break;
    9252             :     }
    9253           0 :     if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
    9254           0 :       return false;
    9255             :     }
    9256             :   }
    9257             : 
    9258           0 :   if (areas->NRows() == 0) {
    9259           0 :     return false;
    9260             :   }
    9261             : 
    9262           0 :   AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
    9263           0 :   return true;
    9264             : }
    9265             : 
    9266             : // [ auto-flow && dense? ] <'grid-auto-columns'>? |
    9267             : // <'grid-template-columns'>
    9268             : bool
    9269           0 : CSSParserImpl::ParseGridTemplateColumnsOrAutoFlow(bool aForGridShorthand)
    9270             : {
    9271           0 :   if (aForGridShorthand) {
    9272           0 :     auto res = ParseGridShorthandAutoProps(NS_STYLE_GRID_AUTO_FLOW_COLUMN);
    9273           0 :     if (res == CSSParseResult::Error) {
    9274           0 :       return false;
    9275             :     }
    9276           0 :     if (res == CSSParseResult::Ok) {
    9277           0 :       nsCSSValue value(eCSSUnit_None);
    9278           0 :       AppendValue(eCSSProperty_grid_template_columns, value);
    9279           0 :       return true;
    9280             :     }
    9281             :   }
    9282           0 :   return ParseGridTemplateColumnsRows(eCSSProperty_grid_template_columns);
    9283             : }
    9284             : 
    9285             : bool
    9286           0 : CSSParserImpl::ParseGridTemplate(bool aForGridShorthand)
    9287             : {
    9288             :   // none |
    9289             :   // subgrid |
    9290             :   // <'grid-template-rows'> / <'grid-template-columns'> |
    9291             :   // [ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list>]?
    9292             :   // or additionally when aForGridShorthand is true:
    9293             :   // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>?
    9294           0 :   nsCSSValue value;
    9295           0 :   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
    9296           0 :     AppendValue(eCSSProperty_grid_template_areas, value);
    9297           0 :     AppendValue(eCSSProperty_grid_template_rows, value);
    9298           0 :     AppendValue(eCSSProperty_grid_template_columns, value);
    9299           0 :     return true;
    9300             :   }
    9301             : 
    9302             :   // 'none' can appear either by itself,
    9303             :   // or as the beginning of <'grid-template-rows'> / <'grid-template-columns'>
    9304           0 :   if (ParseSingleTokenVariant(value, VARIANT_NONE, nullptr)) {
    9305           0 :     AppendValue(eCSSProperty_grid_template_rows, value);
    9306           0 :     AppendValue(eCSSProperty_grid_template_areas, value);
    9307           0 :     if (ExpectSymbol('/', true)) {
    9308           0 :       return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand);
    9309             :     }
    9310           0 :     AppendValue(eCSSProperty_grid_template_columns, value);
    9311           0 :     return true;
    9312             :   }
    9313             : 
    9314             :   // 'subgrid' can appear either by itself,
    9315             :   // or as the beginning of <'grid-template-rows'> / <'grid-template-columns'>
    9316           0 :   nsAString* ident = NextIdent();
    9317           0 :   if (ident) {
    9318           0 :     if (ident->LowerCaseEqualsLiteral("subgrid")) {
    9319           0 :       if (!nsLayoutUtils::IsGridTemplateSubgridValueEnabled()) {
    9320           0 :         REPORT_UNEXPECTED(PESubgridNotSupported);
    9321           0 :         return false;
    9322             :       }
    9323           0 :       if (!ParseOptionalLineNameListAfterSubgrid(value)) {
    9324           0 :         return false;
    9325             :       }
    9326           0 :       AppendValue(eCSSProperty_grid_template_rows, value);
    9327           0 :       AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(eCSSUnit_None));
    9328           0 :       if (ExpectSymbol('/', true)) {
    9329           0 :         return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand);
    9330             :       }
    9331           0 :       if (value.GetListValue()->mNext) {
    9332             :         // Non-empty <line-name-list> after 'subgrid'.
    9333             :         // This is only valid as part of <'grid-template-rows'>,
    9334             :         // which must be followed by a slash.
    9335           0 :         return false;
    9336             :       }
    9337             :       // 'subgrid' by itself sets both grid-template-rows/columns.
    9338           0 :       AppendValue(eCSSProperty_grid_template_columns, value);
    9339           0 :       return true;
    9340             :     }
    9341           0 :     UngetToken();
    9342             :   }
    9343             : 
    9344             :   // [ <line-names>? ] here is ambiguous:
    9345             :   // it can be either the start of a <track-list> (in a <'grid-template-rows'>),
    9346             :   // or the start of [ <line-names>? <string> <track-size>? <line-names>? ]+
    9347           0 :   nsCSSValue firstLineNames;
    9348           0 :   if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
    9349           0 :       !GetToken(true)) {
    9350           0 :     return false;
    9351             :   }
    9352           0 :   if (mToken.mType == eCSSToken_String) {
    9353             :     // It's the [ <line-names>? <string> <track-size>? <line-names>? ]+ case.
    9354           0 :     if (!ParseGridTemplateAfterString(firstLineNames)) {
    9355           0 :       return false;
    9356             :     }
    9357             :     // Parse an optional [ / <explicit-track-list> ] as the columns value.
    9358           0 :     if (ExpectSymbol('/', true)) {
    9359             :       return ParseGridTrackList(eCSSProperty_grid_template_columns,
    9360           0 :                                 GridTrackListFlags::eExplicitTrackList);
    9361             :     }
    9362           0 :     value.SetNoneValue(); // absent means 'none'
    9363           0 :     AppendValue(eCSSProperty_grid_template_columns, value);
    9364           0 :     return true;
    9365             :   }
    9366           0 :   UngetToken();
    9367             : 
    9368             :   // Finish parsing <'grid-template-rows'> with the |firstLineNames| we have,
    9369             :   // and then parse a mandatory [ / <'grid-template-columns'> ].
    9370           0 :   if (!ParseGridTrackListWithFirstLineNames(value, firstLineNames) ||
    9371           0 :       !ExpectSymbol('/', true)) {
    9372           0 :     return false;
    9373             :   }
    9374           0 :   AppendValue(eCSSProperty_grid_template_rows, value);
    9375           0 :   value.SetNoneValue();
    9376           0 :   AppendValue(eCSSProperty_grid_template_areas, value);
    9377           0 :   return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand);
    9378             : }
    9379             : 
    9380             : // Helper for parsing the 'grid-template' shorthand:
    9381             : // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
    9382             : // with a <line-names>? already consumed, stored in |aFirstLineNames|,
    9383             : // and the current token a <string>
    9384             : bool
    9385           0 : CSSParserImpl::ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames)
    9386             : {
    9387           0 :   MOZ_ASSERT(mToken.mType == eCSSToken_String,
    9388             :              "ParseGridTemplateAfterString called with a non-string token");
    9389             : 
    9390           0 :   nsCSSValue rowsValue;
    9391             :   RefPtr<css::GridTemplateAreasValue> areas =
    9392           0 :     new css::GridTemplateAreasValue();
    9393           0 :   nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
    9394           0 :   nsCSSValueList* rowsItem = rowsValue.SetListValue();
    9395           0 :   rowsItem->mValue = aFirstLineNames;
    9396             : 
    9397             :   for (;;) {
    9398           0 :     if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
    9399           0 :       return false;
    9400             :     }
    9401             : 
    9402           0 :     rowsItem->mNext = new nsCSSValueList;
    9403           0 :     rowsItem = rowsItem->mNext;
    9404           0 :     CSSParseResult result = ParseGridTrackSize(rowsItem->mValue);
    9405           0 :     if (result == CSSParseResult::Error) {
    9406           0 :       return false;
    9407             :     }
    9408           0 :     if (result == CSSParseResult::NotFound) {
    9409           0 :       rowsItem->mValue.SetAutoValue();
    9410             :     }
    9411             : 
    9412           0 :     rowsItem->mNext = new nsCSSValueList;
    9413           0 :     rowsItem = rowsItem->mNext;
    9414           0 :     result = ParseGridLineNames(rowsItem->mValue);
    9415           0 :     if (result == CSSParseResult::Error) {
    9416           0 :       return false;
    9417             :     }
    9418           0 :     if (result == CSSParseResult::Ok) {
    9419             :       // Append to the same list as the previous call to ParseGridLineNames.
    9420           0 :       result = ParseGridLineNames(rowsItem->mValue);
    9421           0 :       if (result == CSSParseResult::Error) {
    9422           0 :         return false;
    9423             :       }
    9424           0 :       if (result == CSSParseResult::Ok) {
    9425             :         // Parsed <line-name> twice.
    9426             :         // The property value can not end here, we expect a string next.
    9427           0 :         if (!GetToken(true)) {
    9428           0 :           return false;
    9429             :         }
    9430           0 :         if (eCSSToken_String != mToken.mType) {
    9431           0 :           UngetToken();
    9432           0 :           return false;
    9433             :         }
    9434           0 :         continue;
    9435             :       }
    9436             :     }
    9437             : 
    9438             :     // Did not find a <line-names>.
    9439             :     // Next, we expect either a string or the end of the property value.
    9440           0 :     if (!GetToken(true)) {
    9441           0 :       break;
    9442             :     }
    9443           0 :     if (eCSSToken_String != mToken.mType) {
    9444           0 :       UngetToken();
    9445           0 :       break;
    9446             :     }
    9447           0 :   }
    9448             : 
    9449           0 :   AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
    9450           0 :   AppendValue(eCSSProperty_grid_template_rows, rowsValue);
    9451           0 :   return true;
    9452             : }
    9453             : 
    9454             : // <'grid-template'> |
    9455             : // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? |
    9456             : // [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'>
    9457             : bool
    9458           0 : CSSParserImpl::ParseGrid()
    9459             : {
    9460           0 :   nsCSSValue value;
    9461           0 :   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
    9462           0 :     for (const nsCSSPropertyID* subprops =
    9463           0 :            nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid);
    9464           0 :          *subprops != eCSSProperty_UNKNOWN; ++subprops) {
    9465           0 :       AppendValue(*subprops, value);
    9466             :     }
    9467           0 :     return true;
    9468             :   }
    9469             : 
    9470             :   // https://drafts.csswg.org/css-grid/#grid-shorthand
    9471             :   // "Also, the gutter properties are reset by this shorthand,
    9472             :   //  even though they can't be set by it."
    9473           0 :   value.SetFloatValue(0.0f, eCSSUnit_Pixel);
    9474           0 :   AppendValue(eCSSProperty_grid_row_gap, value);
    9475           0 :   AppendValue(eCSSProperty_grid_column_gap, value);
    9476             : 
    9477             :   // [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'>
    9478           0 :   auto res = ParseGridShorthandAutoProps(NS_STYLE_GRID_AUTO_FLOW_ROW);
    9479           0 :   if (res == CSSParseResult::Error) {
    9480           0 :     return false;
    9481             :   }
    9482           0 :   if (res == CSSParseResult::Ok) {
    9483           0 :     value.SetAutoValue();
    9484           0 :     AppendValue(eCSSProperty_grid_auto_columns, value);
    9485           0 :     nsCSSValue none(eCSSUnit_None);
    9486           0 :     AppendValue(eCSSProperty_grid_template_areas, none);
    9487           0 :     AppendValue(eCSSProperty_grid_template_rows, none);
    9488           0 :     if (!ExpectSymbol('/', true)) {
    9489           0 :       return false;
    9490             :     }
    9491           0 :     return ParseGridTemplateColumnsRows(eCSSProperty_grid_template_columns);
    9492             :   }
    9493             : 
    9494             :   // Set remaining subproperties that might not be set by ParseGridTemplate to
    9495             :   // their initial values and then parse <'grid-template'> |
    9496             :   // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? .
    9497           0 :   value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_ROW, eCSSUnit_Enumerated);
    9498           0 :   AppendValue(eCSSProperty_grid_auto_flow, value);
    9499           0 :   value.SetAutoValue();
    9500           0 :   AppendValue(eCSSProperty_grid_auto_rows, value);
    9501           0 :   AppendValue(eCSSProperty_grid_auto_columns, value);
    9502           0 :   return ParseGridTemplate(true);
    9503             : }
    9504             : 
    9505             : // Parse [ auto-flow && dense? ] <'grid-auto-[rows|columns]'>? for the 'grid'
    9506             : // shorthand.  If aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW then we're
    9507             : // parsing row values, otherwise column values.
    9508             : CSSParseResult
    9509           0 : CSSParserImpl::ParseGridShorthandAutoProps(int32_t aAutoFlowAxis)
    9510             : {
    9511           0 :   MOZ_ASSERT(aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW ||
    9512             :              aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_COLUMN);
    9513           0 :   if (!GetToken(true)) {
    9514           0 :     return CSSParseResult::NotFound;
    9515             :   }
    9516             :   // [ auto-flow && dense? ]
    9517           0 :   int32_t autoFlowValue = 0;
    9518           0 :   if (mToken.mType == eCSSToken_Ident) {
    9519           0 :     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
    9520           0 :     if (keyword == eCSSKeyword_auto_flow) {
    9521           0 :       autoFlowValue = aAutoFlowAxis;
    9522           0 :       if (GetToken(true)) {
    9523           0 :         if (mToken.mType == eCSSToken_Ident &&
    9524           0 :             nsCSSKeywords::LookupKeyword(mToken.mIdent) == eCSSKeyword_dense) {
    9525           0 :           autoFlowValue |= NS_STYLE_GRID_AUTO_FLOW_DENSE;
    9526             :         } else {
    9527           0 :           UngetToken();
    9528             :         }
    9529             :       }
    9530           0 :     } else if (keyword == eCSSKeyword_dense) {
    9531           0 :       if (!GetToken(true)) {
    9532           0 :         return CSSParseResult::Error;
    9533             :       }
    9534           0 :       if (mToken.mType != eCSSToken_Ident ||
    9535           0 :           nsCSSKeywords::LookupKeyword(mToken.mIdent) != eCSSKeyword_auto_flow) {
    9536           0 :         UngetToken();
    9537           0 :         return CSSParseResult::Error;
    9538             :       }
    9539           0 :       autoFlowValue = aAutoFlowAxis | NS_STYLE_GRID_AUTO_FLOW_DENSE;
    9540             :     }
    9541             :   }
    9542           0 :   if (autoFlowValue) {
    9543           0 :     nsCSSValue value;
    9544           0 :     value.SetIntValue(autoFlowValue, eCSSUnit_Enumerated);
    9545           0 :     AppendValue(eCSSProperty_grid_auto_flow, value);
    9546             :   } else {
    9547           0 :     UngetToken();
    9548           0 :     return CSSParseResult::NotFound;
    9549             :   }
    9550             : 
    9551             :   // <'grid-auto-[rows|columns]'>?
    9552           0 :   nsCSSValue autoTrackValue;
    9553           0 :   CSSParseResult result = ParseGridTrackSize(autoTrackValue);
    9554           0 :   if (result == CSSParseResult::Error) {
    9555           0 :     return result;
    9556             :   }
    9557           0 :   if (result == CSSParseResult::NotFound) {
    9558           0 :     autoTrackValue.SetAutoValue();
    9559             :   }
    9560           0 :   AppendValue(aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW ?
    9561             :                 eCSSProperty_grid_auto_rows : eCSSProperty_grid_auto_columns,
    9562           0 :               autoTrackValue);
    9563           0 :   return CSSParseResult::Ok;
    9564             : }
    9565             : 
    9566             : // Parse a <grid-line>.
    9567             : // If successful, set aValue to eCSSUnit_Auto,
    9568             : // or a eCSSUnit_List containing, in that order:
    9569             : //
    9570             : // * An optional eCSSUnit_Enumerated marking a "span" keyword.
    9571             : // * An optional eCSSUnit_Integer
    9572             : // * An optional eCSSUnit_Ident
    9573             : //
    9574             : // At least one of eCSSUnit_Integer or eCSSUnit_Ident is present.
    9575             : bool
    9576           0 : CSSParserImpl::ParseGridLine(nsCSSValue& aValue)
    9577             : {
    9578             :   //  <grid-line> =
    9579             :   //    auto |
    9580             :   //    <custom-ident> |
    9581             :   //    [ <integer> && <custom-ident>? ] |
    9582             :   //    [ span && [ <integer> || <custom-ident> ] ]
    9583             :   //
    9584             :   // Syntactically, this simplifies to:
    9585             :   //
    9586             :   //  <grid-line> =
    9587             :   //    auto |
    9588             :   //    [ span? && [ <integer> || <custom-ident> ] ]
    9589             : 
    9590           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_AUTO, nullptr)) {
    9591           0 :     return true;
    9592             :   }
    9593             : 
    9594           0 :   bool hasSpan = false;
    9595           0 :   bool hasIdent = false;
    9596           0 :   Maybe<int32_t> integer;
    9597           0 :   nsCSSValue ident;
    9598             : 
    9599             : #ifdef MOZ_VALGRIND
    9600             :   // Make the contained value be defined even though we really want a
    9601             :   // Nothing here.  This works around an otherwise difficult to avoid
    9602             :   // Memcheck false positive when this is compiled by gcc-5.3 -O2.
    9603             :   // See bug 1301856.
    9604             :   integer.emplace(0);
    9605             :   integer.reset();
    9606             : #endif
    9607             : 
    9608           0 :   if (!GetToken(true)) {
    9609           0 :     return false;
    9610             :   }
    9611           0 :   if (mToken.mType == eCSSToken_Ident &&
    9612           0 :       mToken.mIdent.LowerCaseEqualsLiteral("span")) {
    9613           0 :     hasSpan = true;
    9614           0 :     if (!GetToken(true)) {
    9615           0 :       return false;
    9616             :     }
    9617             :   }
    9618             : 
    9619           0 :   do {
    9620           0 :     if (!hasIdent &&
    9621           0 :         mToken.mType == eCSSToken_Ident &&
    9622           0 :         ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) {
    9623           0 :       hasIdent = true;
    9624           0 :     } else if (integer.isNothing() &&
    9625           0 :                mToken.mType == eCSSToken_Number &&
    9626           0 :                mToken.mIntegerValid &&
    9627           0 :                mToken.mInteger != 0) {
    9628           0 :       integer.emplace(mToken.mInteger);
    9629             :     } else {
    9630           0 :       UngetToken();
    9631           0 :       break;
    9632             :     }
    9633           0 :   } while (!(integer.isSome() && hasIdent) && GetToken(true));
    9634             : 
    9635             :   // Require at least one of <integer> or <custom-ident>
    9636           0 :   if (!(integer.isSome() || hasIdent)) {
    9637           0 :     return false;
    9638             :   }
    9639             : 
    9640           0 :   if (!hasSpan && GetToken(true)) {
    9641           0 :     if (mToken.mType == eCSSToken_Ident &&
    9642           0 :         mToken.mIdent.LowerCaseEqualsLiteral("span")) {
    9643           0 :       hasSpan = true;
    9644             :     } else {
    9645           0 :       UngetToken();
    9646             :     }
    9647             :   }
    9648             : 
    9649           0 :   nsCSSValueList* item = aValue.SetListValue();
    9650           0 :   if (hasSpan) {
    9651             :     // Given "span", a negative <integer> is invalid.
    9652           0 :     if (integer.isSome() && integer.ref() < 0) {
    9653           0 :       return false;
    9654             :     }
    9655             :     // '1' here is a dummy value.
    9656             :     // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword.
    9657           0 :     item->mValue.SetIntValue(1, eCSSUnit_Enumerated);
    9658           0 :     item->mNext = new nsCSSValueList;
    9659           0 :     item = item->mNext;
    9660             :   }
    9661           0 :   if (integer.isSome()) {
    9662           0 :     item->mValue.SetIntValue(integer.ref(), eCSSUnit_Integer);
    9663           0 :     if (hasIdent) {
    9664           0 :       item->mNext = new nsCSSValueList;
    9665           0 :       item = item->mNext;
    9666             :     }
    9667             :   }
    9668           0 :   if (hasIdent) {
    9669           0 :     item->mValue = ident;
    9670             :   }
    9671           0 :   return true;
    9672             : }
    9673             : 
    9674             : bool
    9675           8 : CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSPropertyID aPropID)
    9676             : {
    9677          16 :   nsCSSValue value;
    9678           8 :   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) ||
    9679           0 :       ParseGridLine(value)) {
    9680           8 :     AppendValue(aPropID, value);
    9681           8 :     return true;
    9682             :   }
    9683           0 :   return false;
    9684             : }
    9685             : 
    9686             : // If |aFallback| is a List containing a single Ident, set |aValue| to that.
    9687             : // Otherwise, set |aValue| to Auto.
    9688             : // Used with |aFallback| from ParseGridLine()
    9689             : static void
    9690           0 : HandleGridLineFallback(const nsCSSValue& aFallback, nsCSSValue& aValue)
    9691             : {
    9692           0 :   if (aFallback.GetUnit() == eCSSUnit_List &&
    9693           0 :       aFallback.GetListValue()->mValue.GetUnit() == eCSSUnit_Ident &&
    9694           0 :       !aFallback.GetListValue()->mNext) {
    9695           0 :     aValue = aFallback;
    9696             :   } else {
    9697           0 :     aValue.SetAutoValue();
    9698             :   }
    9699           0 : }
    9700             : 
    9701             : bool
    9702           0 : CSSParserImpl::ParseGridColumnRow(nsCSSPropertyID aStartPropID,
    9703             :                                   nsCSSPropertyID aEndPropID)
    9704             : {
    9705           0 :   nsCSSValue value;
    9706           0 :   nsCSSValue secondValue;
    9707           0 :   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
    9708           0 :     AppendValue(aStartPropID, value);
    9709           0 :     AppendValue(aEndPropID, value);
    9710           0 :     return true;
    9711             :   }
    9712             : 
    9713           0 :   if (!ParseGridLine(value)) {
    9714           0 :     return false;
    9715             :   }
    9716           0 :   if (GetToken(true)) {
    9717           0 :     if (mToken.IsSymbol('/')) {
    9718           0 :       if (ParseGridLine(secondValue)) {
    9719           0 :         AppendValue(aStartPropID, value);
    9720           0 :         AppendValue(aEndPropID, secondValue);
    9721           0 :         return true;
    9722             :       } else {
    9723           0 :         return false;
    9724             :       }
    9725             :     }
    9726           0 :     UngetToken();
    9727             :   }
    9728             : 
    9729             :   // A single <custom-ident> is repeated to both properties,
    9730             :   // anything else sets the grid-{column,row}-end property to 'auto'.
    9731           0 :   HandleGridLineFallback(value, secondValue);
    9732             : 
    9733           0 :   AppendValue(aStartPropID, value);
    9734           0 :   AppendValue(aEndPropID, secondValue);
    9735           0 :   return true;
    9736             : }
    9737             : 
    9738             : bool
    9739           0 : CSSParserImpl::ParseGridArea()
    9740             : {
    9741           0 :   nsCSSValue values[4];
    9742           0 :   if (ParseSingleTokenVariant(values[0], VARIANT_INHERIT, nullptr)) {
    9743           0 :     AppendValue(eCSSProperty_grid_row_start, values[0]);
    9744           0 :     AppendValue(eCSSProperty_grid_column_start, values[0]);
    9745           0 :     AppendValue(eCSSProperty_grid_row_end, values[0]);
    9746           0 :     AppendValue(eCSSProperty_grid_column_end, values[0]);
    9747           0 :     return true;
    9748             :   }
    9749             : 
    9750           0 :   int32_t i = 0;
    9751             :   for (;;) {
    9752           0 :     if (!ParseGridLine(values[i])) {
    9753           0 :       return false;
    9754             :     }
    9755           0 :     if (++i == 4 || !GetToken(true)) {
    9756           0 :       break;
    9757             :     }
    9758           0 :     if (!mToken.IsSymbol('/')) {
    9759           0 :       UngetToken();
    9760           0 :       break;
    9761             :     }
    9762             :   }
    9763             : 
    9764           0 :   MOZ_ASSERT(i >= 1, "should have parsed at least one grid-line (or returned)");
    9765           0 :   if (i < 2) {
    9766           0 :     HandleGridLineFallback(values[0], values[1]);
    9767             :   }
    9768           0 :   if (i < 3) {
    9769           0 :     HandleGridLineFallback(values[0], values[2]);
    9770             :   }
    9771           0 :   if (i < 4) {
    9772           0 :     HandleGridLineFallback(values[1], values[3]);
    9773             :   }
    9774             : 
    9775           0 :   AppendValue(eCSSProperty_grid_row_start, values[0]);
    9776           0 :   AppendValue(eCSSProperty_grid_column_start, values[1]);
    9777           0 :   AppendValue(eCSSProperty_grid_row_end, values[2]);
    9778           0 :   AppendValue(eCSSProperty_grid_column_end, values[3]);
    9779           0 :   return true;
    9780             : }
    9781             : 
    9782             : bool
    9783           0 : CSSParserImpl::ParseGridGap()
    9784             : {
    9785           0 :   nsCSSValue first;
    9786           0 :   if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
    9787           0 :     AppendValue(eCSSProperty_grid_row_gap, first);
    9788           0 :     AppendValue(eCSSProperty_grid_column_gap, first);
    9789           0 :     return true;
    9790             :   }
    9791           0 :   if (ParseNonNegativeVariant(first, VARIANT_LPCALC, nullptr) !=
    9792             :         CSSParseResult::Ok) {
    9793           0 :     return false;
    9794             :   }
    9795           0 :   nsCSSValue second;
    9796           0 :   auto result = ParseNonNegativeVariant(second, VARIANT_LPCALC, nullptr);
    9797           0 :   if (result == CSSParseResult::Error) {
    9798           0 :     return false;
    9799             :   }
    9800           0 :   AppendValue(eCSSProperty_grid_row_gap, first);
    9801           0 :   AppendValue(eCSSProperty_grid_column_gap,
    9802           0 :               result == CSSParseResult::NotFound ? first : second);
    9803           0 :   return true;
    9804             : }
    9805             : 
    9806             : // normal | [<number> <integer>?]
    9807             : bool
    9808           0 : CSSParserImpl::ParseInitialLetter()
    9809             : {
    9810           0 :   nsCSSValue value;
    9811             :   // 'inherit', 'initial', 'unset', 'none', and 'normal' must be alone
    9812           0 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NORMAL,
    9813             :                                nullptr)) {
    9814           0 :     nsCSSValue first, second;
    9815           0 :     if (!ParseOneOrLargerNumber(first)) {
    9816           0 :       return false;
    9817             :     }
    9818             : 
    9819           0 :     if (!ParseOneOrLargerInteger(second)) {
    9820           0 :       AppendValue(eCSSProperty_initial_letter, first);
    9821           0 :       return true;
    9822             :     } else {
    9823           0 :       RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
    9824           0 :       val->Item(0) = first;
    9825           0 :       val->Item(1) = second;
    9826           0 :       value.SetArrayValue(val, eCSSUnit_Array);
    9827             :     }
    9828             :   }
    9829           0 :   AppendValue(eCSSProperty_initial_letter, value);
    9830           0 :   return true;
    9831             : }
    9832             : 
    9833             : // [ $aTable && <overflow-position>? ] ?
    9834             : // $aTable is for <content-position> or <self-position>
    9835             : bool
    9836           9 : CSSParserImpl::ParseAlignJustifyPosition(nsCSSValue& aResult,
    9837             :                                          const KTableEntry aTable[])
    9838             : {
    9839          18 :   nsCSSValue pos, overflowPos;
    9840           9 :   int32_t value = 0;
    9841           9 :   if (ParseEnum(pos, aTable)) {
    9842           9 :     value = pos.GetIntValue();
    9843           9 :     if (ParseEnum(overflowPos, nsCSSProps::kAlignOverflowPosition)) {
    9844           0 :       value |= overflowPos.GetIntValue();
    9845             :     }
    9846           9 :     aResult.SetIntValue(value, eCSSUnit_Enumerated);
    9847           9 :     return true;
    9848             :   }
    9849           0 :   if (ParseEnum(overflowPos, nsCSSProps::kAlignOverflowPosition)) {
    9850           0 :     if (ParseEnum(pos, aTable)) {
    9851           0 :       aResult.SetIntValue(pos.GetIntValue() | overflowPos.GetIntValue(),
    9852           0 :                           eCSSUnit_Enumerated);
    9853           0 :       return true;
    9854             :     }
    9855           0 :     return false; // <overflow-position> must be followed by a value in $table
    9856             :   }
    9857           0 :   return true;
    9858             : }
    9859             : 
    9860             : // auto | normal | stretch | <baseline-position> |
    9861             : // [ <self-position> && <overflow-position>? ] |
    9862             : // [ legacy && [ left | right | center ] ]
    9863             : bool
    9864           6 : CSSParserImpl::ParseJustifyItems()
    9865             : {
    9866          12 :   nsCSSValue value;
    9867           6 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
    9868           0 :     if (MOZ_UNLIKELY(ParseEnum(value, nsCSSProps::kAlignLegacy))) {
    9869           0 :       nsCSSValue legacy;
    9870           0 :       if (!ParseEnum(legacy, nsCSSProps::kAlignLegacyPosition)) {
    9871           0 :         return false; // leading 'legacy' not followed by 'left' etc is an error
    9872             :       }
    9873           0 :       value.SetIntValue(value.GetIntValue() | legacy.GetIntValue(),
    9874           0 :                         eCSSUnit_Enumerated);
    9875             :     } else {
    9876           0 :       if (!ParseAlignEnum(value, nsCSSProps::kAlignAutoNormalStretchBaseline)) {
    9877           0 :         if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) ||
    9878           0 :             value.GetUnit() == eCSSUnit_Null) {
    9879           0 :           return false;
    9880             :         }
    9881             :         // check for a trailing 'legacy' after 'left' etc
    9882           0 :         auto val = value.GetIntValue();
    9883           0 :         if (val == NS_STYLE_JUSTIFY_CENTER ||
    9884           0 :             val == NS_STYLE_JUSTIFY_LEFT   ||
    9885             :             val == NS_STYLE_JUSTIFY_RIGHT) {
    9886           0 :           nsCSSValue legacy;
    9887           0 :           if (ParseEnum(legacy, nsCSSProps::kAlignLegacy)) {
    9888           0 :             value.SetIntValue(val | legacy.GetIntValue(), eCSSUnit_Enumerated);
    9889             :           }
    9890             :         }
    9891             :       }
    9892             :     }
    9893             :   }
    9894           6 :   AppendValue(eCSSProperty_justify_items, value);
    9895           6 :   return true;
    9896             : }
    9897             : 
    9898             : // normal | stretch | <baseline-position> |
    9899             : // [ <overflow-position>? && <self-position> ]
    9900             : bool
    9901          10 : CSSParserImpl::ParseAlignItems()
    9902             : {
    9903          20 :   nsCSSValue value;
    9904          10 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
    9905           4 :     if (!ParseAlignEnum(value, nsCSSProps::kAlignNormalStretchBaseline)) {
    9906           8 :       if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) ||
    9907           4 :           value.GetUnit() == eCSSUnit_Null) {
    9908           0 :         return false;
    9909             :       }
    9910             :     }
    9911             :   }
    9912          10 :   AppendValue(eCSSProperty_align_items, value);
    9913          10 :   return true;
    9914             : }
    9915             : 
    9916             : // auto | normal | stretch | <baseline-position> |
    9917             : // [ <overflow-position>? && <self-position> ]
    9918             : bool
    9919           7 : CSSParserImpl::ParseAlignJustifySelf(nsCSSPropertyID aPropID)
    9920             : {
    9921          14 :   nsCSSValue value;
    9922           7 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
    9923           3 :     if (!ParseAlignEnum(value, nsCSSProps::kAlignAutoNormalStretchBaseline)) {
    9924           6 :       if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) ||
    9925           3 :           value.GetUnit() == eCSSUnit_Null) {
    9926           0 :         return false;
    9927             :       }
    9928             :     }
    9929             :   }
    9930           7 :   AppendValue(aPropID, value);
    9931           7 :   return true;
    9932             : }
    9933             : 
    9934             : // normal | <baseline-position> | [ <content-distribution> ||
    9935             : //   [ <overflow-position>? && <content-position> ] ]
    9936             : // (the part after the || is called <*-position> below)
    9937             : bool
    9938          14 : CSSParserImpl::ParseAlignJustifyContent(nsCSSPropertyID aPropID)
    9939             : {
    9940          28 :   nsCSSValue value;
    9941          14 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
    9942           2 :     if (!ParseAlignEnum(value, nsCSSProps::kAlignNormalBaseline)) {
    9943           4 :       nsCSSValue fallbackValue;
    9944           2 :       if (!ParseEnum(value, nsCSSProps::kAlignContentDistribution)) {
    9945           4 :         if (!ParseAlignJustifyPosition(fallbackValue,
    9946           4 :                                        nsCSSProps::kAlignContentPosition) ||
    9947           2 :             fallbackValue.GetUnit() == eCSSUnit_Null) {
    9948           0 :           return false;
    9949             :         }
    9950             :         // optional <content-distribution> after <*-position> ...
    9951           2 :         if (!ParseEnum(value, nsCSSProps::kAlignContentDistribution)) {
    9952             :           // ... is missing so the <*-position> is the value, not the fallback
    9953           2 :           value = fallbackValue;
    9954           2 :           fallbackValue.Reset();
    9955             :         }
    9956             :       } else {
    9957             :         // any optional <*-position> is a fallback value
    9958           0 :         if (!ParseAlignJustifyPosition(fallbackValue,
    9959             :                                        nsCSSProps::kAlignContentPosition)) {
    9960           0 :           return false;
    9961             :         }
    9962             :       }
    9963           2 :       if (fallbackValue.GetUnit() != eCSSUnit_Null) {
    9964           0 :         auto fallback = fallbackValue.GetIntValue();
    9965           0 :         value.SetIntValue(value.GetIntValue() |
    9966           0 :                             (fallback << NS_STYLE_ALIGN_ALL_SHIFT),
    9967           0 :                           eCSSUnit_Enumerated);
    9968             :       }
    9969             :     }
    9970             :   }
    9971          14 :   AppendValue(aPropID, value);
    9972          14 :   return true;
    9973             : }
    9974             : 
    9975             : // place-content: [ normal | <baseline-position> | <content-distribution> |
    9976             : //                  <content-position> ]{1,2}
    9977             : bool
    9978           0 : CSSParserImpl::ParsePlaceContent()
    9979             : {
    9980           0 :   nsCSSValue first;
    9981           0 :   if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
    9982           0 :     AppendValue(eCSSProperty_align_content, first);
    9983           0 :     AppendValue(eCSSProperty_justify_content, first);
    9984           0 :     return true;
    9985             :   }
    9986           0 :   if (!ParseAlignEnum(first, nsCSSProps::kAlignNormalBaseline) &&
    9987           0 :       !ParseEnum(first, nsCSSProps::kAlignContentDistribution) &&
    9988           0 :       !ParseEnum(first, nsCSSProps::kAlignContentPosition)) {
    9989           0 :     return false;
    9990             :   }
    9991           0 :   AppendValue(eCSSProperty_align_content, first);
    9992           0 :   nsCSSValue second;
    9993           0 :   if (!ParseAlignEnum(second, nsCSSProps::kAlignNormalBaseline) &&
    9994           0 :       !ParseEnum(second, nsCSSProps::kAlignContentDistribution) &&
    9995           0 :       !ParseEnum(second, nsCSSProps::kAlignContentPosition)) {
    9996           0 :     AppendValue(eCSSProperty_justify_content, first);
    9997             :   } else {
    9998           0 :     AppendValue(eCSSProperty_justify_content, second);
    9999             :   }
   10000           0 :   return true;
   10001             : }
   10002             : 
   10003             : // place-items:  <x> [ auto | <x> ]?
   10004             : // <x> = [ normal | stretch | <baseline-position> | <self-position> ]
   10005             : bool
   10006           0 : CSSParserImpl::ParsePlaceItems()
   10007             : {
   10008           0 :   nsCSSValue first;
   10009           0 :   if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
   10010           0 :     AppendValue(eCSSProperty_align_items, first);
   10011           0 :     AppendValue(eCSSProperty_justify_items, first);
   10012           0 :     return true;
   10013             :   }
   10014           0 :   if (!ParseAlignEnum(first, nsCSSProps::kAlignNormalStretchBaseline) &&
   10015           0 :       !ParseEnum(first, nsCSSProps::kAlignSelfPosition)) {
   10016           0 :     return false;
   10017             :   }
   10018           0 :   AppendValue(eCSSProperty_align_items, first);
   10019           0 :   nsCSSValue second;
   10020             :   // Note: 'auto' is valid for justify-items, but not align-items.
   10021           0 :   if (!ParseAlignEnum(second, nsCSSProps::kAlignAutoNormalStretchBaseline) &&
   10022           0 :       !ParseEnum(second, nsCSSProps::kAlignSelfPosition)) {
   10023           0 :     AppendValue(eCSSProperty_justify_items, first);
   10024             :   } else {
   10025           0 :     AppendValue(eCSSProperty_justify_items, second);
   10026             :   }
   10027           0 :   return true;
   10028             : }
   10029             : 
   10030             : // place-self: [ auto | normal | stretch | <baseline-position> |
   10031             : //               <self-position> ]{1,2}
   10032             : bool
   10033           0 : CSSParserImpl::ParsePlaceSelf()
   10034             : {
   10035           0 :   nsCSSValue first;
   10036           0 :   if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
   10037           0 :     AppendValue(eCSSProperty_align_self, first);
   10038           0 :     AppendValue(eCSSProperty_justify_self, first);
   10039           0 :     return true;
   10040             :   }
   10041           0 :   if (!ParseAlignEnum(first, nsCSSProps::kAlignAutoNormalStretchBaseline) &&
   10042           0 :       !ParseEnum(first, nsCSSProps::kAlignSelfPosition)) {
   10043           0 :     return false;
   10044             :   }
   10045           0 :   AppendValue(eCSSProperty_align_self, first);
   10046           0 :   nsCSSValue second;
   10047           0 :   if (!ParseAlignEnum(second, nsCSSProps::kAlignAutoNormalStretchBaseline) &&
   10048           0 :       !ParseEnum(second, nsCSSProps::kAlignSelfPosition)) {
   10049           0 :     AppendValue(eCSSProperty_justify_self, first);
   10050             :   } else {
   10051           0 :     AppendValue(eCSSProperty_justify_self, second);
   10052             :   }
   10053           0 :   return true;
   10054             : }
   10055             : 
   10056             : // <color-stop> : <color> [ <percentage> | <length> ]?
   10057             : bool
   10058         141 : CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
   10059             : {
   10060         141 :   nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
   10061         141 :   CSSParseResult result = ParseVariant(stop->mColor, VARIANT_COLOR, nullptr);
   10062         141 :   if (result == CSSParseResult::Error) {
   10063           0 :     return false;
   10064         141 :   } else if (result == CSSParseResult::NotFound) {
   10065           6 :     stop->mIsInterpolationHint = true;
   10066             :   }
   10067             : 
   10068             :   // Stop positions do not have to fall between the starting-point and
   10069             :   // ending-point, so we don't use ParseNonNegativeVariant.
   10070         141 :   result = ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr);
   10071         141 :   if (result == CSSParseResult::Error) {
   10072           0 :     return false;
   10073         141 :   } else if (result == CSSParseResult::NotFound) {
   10074          73 :     if (stop->mIsInterpolationHint) {
   10075           6 :       return false;
   10076             :     }
   10077          67 :     stop->mLocation.SetNoneValue();
   10078             :   }
   10079         135 :   return true;
   10080             : }
   10081             : 
   10082             : // Helper for ParseLinearGradient -- returns true iff aPosition represents a
   10083             : // box-position value which was parsed with only edge keywords.
   10084             : // e.g. "left top", or "bottom", but not "left 10px"
   10085             : //
   10086             : // (NOTE: Even though callers may want to exclude explicit "center", we still
   10087             : // need to allow for _CENTER here, because omitted position-values (e.g. the
   10088             : // x-component of a value like "top") will have been parsed as being *implicit*
   10089             : // center. The correct way to disallow *explicit* center is to pass "false" for
   10090             : // ParseBoxPositionValues()'s "aAllowExplicitCenter" parameter, before you
   10091             : // call this function.)
   10092             : static bool
   10093           4 : IsBoxPositionStrictlyEdgeKeywords(nsCSSValuePair& aPosition)
   10094             : {
   10095           4 :   const nsCSSValue& xValue = aPosition.mXValue;
   10096           4 :   const nsCSSValue& yValue = aPosition.mYValue;
   10097           8 :   return (xValue.GetUnit() == eCSSUnit_Enumerated &&
   10098           4 :           (xValue.GetIntValue() & (NS_STYLE_IMAGELAYER_POSITION_LEFT |
   10099             :                                    NS_STYLE_IMAGELAYER_POSITION_CENTER |
   10100           4 :                                    NS_STYLE_IMAGELAYER_POSITION_RIGHT)) &&
   10101          12 :           yValue.GetUnit() == eCSSUnit_Enumerated &&
   10102           4 :           (yValue.GetIntValue() & (NS_STYLE_IMAGELAYER_POSITION_TOP |
   10103             :                                    NS_STYLE_IMAGELAYER_POSITION_CENTER |
   10104           4 :                                    NS_STYLE_IMAGELAYER_POSITION_BOTTOM)));
   10105             : }
   10106             : 
   10107             : // <gradient>
   10108             : //    : linear-gradient( <linear-gradient-line>? <color-stops> ')'
   10109             : //    | radial-gradient( <radial-gradient-line>? <color-stops> ')'
   10110             : //
   10111             : // <linear-gradient-line> : [ to [left | right] || [top | bottom] ] ,
   10112             : //                        | <legacy-gradient-line>
   10113             : // <radial-gradient-line> : [ <shape> || <size> ] [ at <position> ]? ,
   10114             : //                        | [ at <position> ] ,
   10115             : //                        | <legacy-gradient-line>? <legacy-shape-size>?
   10116             : // <shape> : circle | ellipse
   10117             : // <size> : closest-side | closest-corner | farthest-side | farthest-corner
   10118             : //        | <length> | [<length> | <percentage>]{2}
   10119             : //
   10120             : // <legacy-gradient-line> : [ <position> || <angle>] ,
   10121             : //
   10122             : // <legacy-shape-size> : [ <shape> || <legacy-size> ] ,
   10123             : // <legacy-size> : closest-side | closest-corner | farthest-side
   10124             : //               | farthest-corner | contain | cover
   10125             : //
   10126             : // <color-stops> : <color-stop> , <color-stop> [, <color-stop>]*
   10127             : bool
   10128          49 : CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue,
   10129             :                                    uint8_t aFlags)
   10130             : {
   10131             :   RefPtr<nsCSSValueGradient> cssGradient
   10132          98 :     = new nsCSSValueGradient(false, aFlags & eGradient_Repeating);
   10133             : 
   10134          49 :   if (!GetToken(true)) {
   10135           0 :     return false;
   10136             :   }
   10137             : 
   10138             :   // Check for "to" syntax (but not if parsing a -webkit-linear-gradient)
   10139         147 :   if (!(aFlags & eGradient_WebkitLegacy) &&
   10140          69 :       mToken.mType == eCSSToken_Ident &&
   10141          20 :       mToken.mIdent.LowerCaseEqualsLiteral("to")) {
   10142             : 
   10143             :     // "to" syntax doesn't allow explicit "center"
   10144           4 :     if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) {
   10145           0 :       SkipUntil(')');
   10146           0 :       return false;
   10147             :     }
   10148             : 
   10149             :     // [ to [left | right] || [top | bottom] ] ,
   10150           4 :     if (!IsBoxPositionStrictlyEdgeKeywords(cssGradient->mBgPos)) {
   10151           0 :       SkipUntil(')');
   10152           0 :       return false;
   10153             :     }
   10154             : 
   10155           4 :     if (!ExpectSymbol(',', true)) {
   10156           0 :       SkipUntil(')');
   10157           0 :       return false;
   10158             :     }
   10159             : 
   10160           4 :     return ParseGradientColorStops(cssGradient, aValue);
   10161             :   }
   10162             : 
   10163          45 :   if (!(aFlags & eGradient_AnyLegacy)) {
   10164             :     // We're parsing an unprefixed linear-gradient, and we tried & failed to
   10165             :     // parse a 'to' token above. Put the token back & try to re-parse our
   10166             :     // expression as <angle>? <color-stop-list>
   10167          45 :     UngetToken();
   10168             : 
   10169             :     // <angle> ,
   10170          90 :     if (ParseSingleTokenVariant(cssGradient->mAngle,
   10171          46 :                                 VARIANT_ANGLE_OR_ZERO, nullptr) &&
   10172           1 :         !ExpectSymbol(',', true)) {
   10173           0 :       SkipUntil(')');
   10174           0 :       return false;
   10175             :     }
   10176             : 
   10177          45 :     return ParseGradientColorStops(cssGradient, aValue);
   10178             :   }
   10179             : 
   10180             :   // If we get here, we're parsing a prefixed linear-gradient expression.  Put
   10181             :   // back the first token (which we may have checked for "to" above) and try to
   10182             :   // parse expression as <legacy-gradient-line>? <color-stop-list>
   10183           0 :   bool haveGradientLine = IsLegacyGradientLine(mToken.mType, mToken.mIdent);
   10184           0 :   UngetToken();
   10185             : 
   10186           0 :   if (haveGradientLine) {
   10187             :     // Parse a <legacy-gradient-line>
   10188           0 :     cssGradient->mIsLegacySyntax = true;
   10189           0 :     cssGradient->mIsMozLegacySyntax = (aFlags & eGradient_MozLegacy);
   10190             :     // In -webkit-linear-gradient expressions (handled below), we need to accept
   10191             :     // unitless 0 for angles, to match WebKit/Blink.
   10192           0 :     int32_t angleFlags = (aFlags & eGradient_WebkitLegacy) ?
   10193             :       VARIANT_ANGLE | VARIANT_ZERO_ANGLE :
   10194           0 :       VARIANT_ANGLE;
   10195             : 
   10196             :     bool haveAngle =
   10197           0 :       ParseSingleTokenVariant(cssGradient->mAngle, angleFlags, nullptr);
   10198             : 
   10199             :     // If we got an angle, we might now have a comma, ending the gradient-line.
   10200           0 :     bool haveAngleComma = haveAngle && ExpectSymbol(',', true);
   10201             : 
   10202             :     // And in fact, if we're -webkit-linear-gradient and got an angle, there
   10203             :     // *must* be a comma after the angle. (Though -moz-linear-gradient is more
   10204             :     // permissive because it will allow box-position before the comma.)
   10205           0 :     if (aFlags & eGradient_WebkitLegacy && haveAngle && !haveAngleComma) {
   10206           0 :       SkipUntil(')');
   10207           0 :       return false;
   10208             :     }
   10209             : 
   10210             :     // If we're prefixed & didn't get an angle + comma, then proceed to parse a
   10211             :     // box-position. (Note that due to the above webkit-specific early-return,
   10212             :     // this will only parse an *initial* box-position inside of
   10213             :     // -webkit-linear-gradient, vs. an initial OR post-angle box-position
   10214             :     // inside of -moz-linear-gradient.)
   10215           0 :     if (((aFlags & eGradient_AnyLegacy) && !haveAngleComma)) {
   10216             :       // (Note: 3rd arg controls whether the "center" keyword is allowed.
   10217             :       // -moz-linear-gradient allows it; -webkit-linear-gradient does not.)
   10218           0 :       if (!ParseBoxPositionValues(cssGradient->mBgPos, false,
   10219           0 :                                   (aFlags & eGradient_MozLegacy))) {
   10220           0 :         SkipUntil(')');
   10221           0 :         return false;
   10222             :       }
   10223             : 
   10224             :       // -webkit-linear-gradient only supports edge keywords here.
   10225           0 :       if ((aFlags & eGradient_WebkitLegacy) &&
   10226           0 :           !IsBoxPositionStrictlyEdgeKeywords(cssGradient->mBgPos)) {
   10227           0 :         SkipUntil(')');
   10228           0 :         return false;
   10229             :       }
   10230             : 
   10231           0 :       if (!ExpectSymbol(',', true) &&
   10232             :           // If we didn't already get an angle, and we're not -webkit prefixed,
   10233             :           // we can parse an angle+comma now.  Otherwise it's an error.
   10234           0 :           (haveAngle ||
   10235           0 :            (aFlags & eGradient_WebkitLegacy) ||
   10236           0 :            !ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE,
   10237           0 :                                     nullptr) ||
   10238             :            // now we better have a comma
   10239           0 :            !ExpectSymbol(',', true))) {
   10240           0 :         SkipUntil(')');
   10241           0 :         return false;
   10242             :       }
   10243             :     }
   10244             :   }
   10245             : 
   10246           0 :   return ParseGradientColorStops(cssGradient, aValue);
   10247             : }
   10248             : 
   10249             : bool
   10250           2 : CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue,
   10251             :                                    uint8_t aFlags)
   10252             : {
   10253             :   RefPtr<nsCSSValueGradient> cssGradient
   10254           4 :     = new nsCSSValueGradient(true, aFlags & eGradient_Repeating);
   10255             : 
   10256             :   // [ <shape> || <size> ]
   10257             :   bool haveShape =
   10258           2 :     ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
   10259           2 :                             nsCSSProps::kRadialGradientShapeKTable);
   10260             : 
   10261             :   bool haveSize =
   10262           2 :     ParseSingleTokenVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
   10263           2 :                             (aFlags & eGradient_AnyLegacy) ?
   10264             :                             nsCSSProps::kRadialGradientLegacySizeKTable :
   10265           2 :                             nsCSSProps::kRadialGradientSizeKTable);
   10266           2 :   if (haveSize) {
   10267           0 :     if (!haveShape) {
   10268             :       // <size> <shape>
   10269             :       haveShape =
   10270           0 :         ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
   10271           0 :                                 nsCSSProps::kRadialGradientShapeKTable);
   10272             :     }
   10273           2 :   } else if (!(aFlags & eGradient_AnyLegacy)) {
   10274             :     // Save RadialShape before parsing RadiusX because RadialShape and
   10275             :     // RadiusX share the storage.
   10276             :     int32_t shape =
   10277           2 :       cssGradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated ?
   10278           2 :       cssGradient->GetRadialShape().GetIntValue() : -1;
   10279             :     // <length> | [<length> | <percentage>]{2}
   10280           2 :     cssGradient->mIsExplicitSize = true;
   10281             :     haveSize =
   10282           2 :       ParseSingleTokenNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP,
   10283           2 :                                          nullptr);
   10284           2 :     if (!haveSize) {
   10285             :       // It was not an explicit size after all.
   10286             :       // Note that ParseNonNegativeVariant may have put something
   10287             :       // invalid into our storage, but only in the case where it was
   10288             :       // rejected only for being negative.  Since this means the token
   10289             :       // was a length or a percentage, we know it's not valid syntax
   10290             :       // (which must be a comma, the 'at' keyword, or a color), so we
   10291             :       // know this value will be dropped.  This means it doesn't matter
   10292             :       // that we have something invalid in our storage.
   10293           0 :       cssGradient->mIsExplicitSize = false;
   10294             :     } else {
   10295             :       // vertical extent is optional
   10296             :       bool haveYSize =
   10297           2 :         ParseSingleTokenNonNegativeVariant(cssGradient->GetRadiusY(),
   10298           2 :                                            VARIANT_LP, nullptr);
   10299           2 :       if (!haveShape) {
   10300           4 :         nsCSSValue shapeValue;
   10301             :         haveShape =
   10302             :           ParseSingleTokenVariant(shapeValue, VARIANT_KEYWORD,
   10303           2 :                                   nsCSSProps::kRadialGradientShapeKTable);
   10304           2 :         if (haveShape) {
   10305           0 :           shape = shapeValue.GetIntValue();
   10306             :         }
   10307             :       }
   10308           2 :       if (haveYSize
   10309           2 :             ? shape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR
   10310           0 :             : cssGradient->GetRadiusX().GetUnit() == eCSSUnit_Percent ||
   10311             :               shape == NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL) {
   10312           0 :         SkipUntil(')');
   10313           0 :         return false;
   10314             :       }
   10315             :     }
   10316             :   }
   10317             : 
   10318           2 :   if ((haveShape || haveSize) && ExpectSymbol(',', true)) {
   10319             :     // [ <shape> || <size> ] ,
   10320           2 :     return ParseGradientColorStops(cssGradient, aValue);
   10321             :   }
   10322             : 
   10323           0 :   if (!GetToken(true)) {
   10324           0 :     return false;
   10325             :   }
   10326             : 
   10327           0 :   if (!(aFlags & eGradient_AnyLegacy)) {
   10328           0 :     if (mToken.mType == eCSSToken_Ident &&
   10329           0 :         mToken.mIdent.LowerCaseEqualsLiteral("at")) {
   10330             :       // [ <shape> || <size> ]? at <position> ,
   10331           0 :       if (!ParseBoxPositionValues(cssGradient->mBgPos, false) ||
   10332           0 :           !ExpectSymbol(',', true)) {
   10333           0 :         SkipUntil(')');
   10334           0 :         return false;
   10335             :       }
   10336             : 
   10337           0 :       return ParseGradientColorStops(cssGradient, aValue);
   10338             :     }
   10339             : 
   10340             :     // <color-stops> only
   10341           0 :     UngetToken();
   10342           0 :     return ParseGradientColorStops(cssGradient, aValue);
   10343             :   }
   10344           0 :   MOZ_ASSERT(!cssGradient->mIsExplicitSize);
   10345             : 
   10346           0 :   nsCSSTokenType ty = mToken.mType;
   10347           0 :   nsString id = mToken.mIdent;
   10348           0 :   UngetToken();
   10349             : 
   10350             :   // <legacy-gradient-line>
   10351           0 :   bool haveGradientLine = false;
   10352             :   // if we already encountered a shape or size,
   10353             :   // we can not have a gradient-line in legacy syntax
   10354           0 :   if (!haveShape && !haveSize) {
   10355           0 :       haveGradientLine = IsLegacyGradientLine(ty, id);
   10356             :   }
   10357           0 :   if (haveGradientLine) {
   10358             :     // Note: -webkit-radial-gradient() doesn't accept angles.
   10359           0 :     bool haveAngle = (aFlags & eGradient_WebkitLegacy)
   10360           0 :       ? false
   10361           0 :       : ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
   10362             : 
   10363             :     // If we got an angle, we might now have a comma, ending the gradient-line
   10364           0 :     if (!haveAngle || !ExpectSymbol(',', true)) {
   10365           0 :       if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
   10366           0 :         SkipUntil(')');
   10367           0 :         return false;
   10368             :       }
   10369             : 
   10370           0 :       if (!ExpectSymbol(',', true) &&
   10371             :           // If we didn't already get an angle, and we're not -webkit prefixed,
   10372             :           // can parse an angle+comma now.  Otherwise it's an error.
   10373           0 :           (haveAngle ||
   10374           0 :            (aFlags & eGradient_WebkitLegacy) ||
   10375           0 :            !ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE,
   10376           0 :                                     nullptr) ||
   10377             :            // now we better have a comma
   10378           0 :            !ExpectSymbol(',', true))) {
   10379           0 :         SkipUntil(')');
   10380           0 :         return false;
   10381             :       }
   10382             :     }
   10383             : 
   10384           0 :     if (cssGradient->mAngle.GetUnit() != eCSSUnit_None) {
   10385           0 :       cssGradient->mIsLegacySyntax = true;
   10386           0 :       cssGradient->mIsMozLegacySyntax = (aFlags & eGradient_MozLegacy);
   10387             :     }
   10388             :   }
   10389             : 
   10390             :   // radial gradients might have a shape and size here for legacy syntax
   10391           0 :   if (!haveShape && !haveSize) {
   10392             :     haveShape =
   10393           0 :       ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
   10394           0 :                               nsCSSProps::kRadialGradientShapeKTable);
   10395             :     haveSize =
   10396           0 :       ParseSingleTokenVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
   10397           0 :                               nsCSSProps::kRadialGradientLegacySizeKTable);
   10398             : 
   10399             :     // could be in either order
   10400           0 :     if (!haveShape) {
   10401             :       haveShape =
   10402           0 :         ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
   10403           0 :                                 nsCSSProps::kRadialGradientShapeKTable);
   10404             :     }
   10405             :   }
   10406             : 
   10407           0 :   if ((haveShape || haveSize) && !ExpectSymbol(',', true)) {
   10408           0 :     SkipUntil(')');
   10409           0 :     return false;
   10410             :   }
   10411             : 
   10412           0 :   return ParseGradientColorStops(cssGradient, aValue);
   10413             : }
   10414             : 
   10415             : bool
   10416           0 : CSSParserImpl::IsLegacyGradientLine(const nsCSSTokenType& aType,
   10417             :                                     const nsString& aId)
   10418             : {
   10419             :   // N.B. ParseBoxPositionValues is not guaranteed to put back
   10420             :   // everything it scanned if it fails, so we must only call it
   10421             :   // if there is no alternative to consuming a <box-position>.
   10422             :   // ParseVariant, as used here, will either succeed and consume
   10423             :   // a single token, or fail and consume none, so we can be more
   10424             :   // cavalier about calling it.
   10425             : 
   10426           0 :   bool haveGradientLine = false;
   10427           0 :   switch (aType) {
   10428             :   case eCSSToken_Percentage:
   10429             :   case eCSSToken_Number:
   10430             :   case eCSSToken_Dimension:
   10431           0 :     haveGradientLine = true;
   10432           0 :     break;
   10433             : 
   10434             :   case eCSSToken_Function:
   10435           0 :     if (aId.LowerCaseEqualsLiteral("calc")) {
   10436           0 :       haveGradientLine = true;
   10437           0 :       break;
   10438             :     }
   10439             :     MOZ_FALLTHROUGH;
   10440             :   case eCSSToken_ID:
   10441             :   case eCSSToken_Hash:
   10442             :     // this is a color
   10443           0 :     break;
   10444             : 
   10445             :   case eCSSToken_Ident: {
   10446             :     // This is only a gradient line if it's a box position keyword.
   10447           0 :     nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(aId);
   10448             :     int32_t junk;
   10449           0 :     if (nsCSSProps::FindKeyword(kw, nsCSSProps::kImageLayerPositionKTable,
   10450             :                                 junk)) {
   10451           0 :       haveGradientLine = true;
   10452             :     }
   10453           0 :     break;
   10454             :   }
   10455             : 
   10456             :   default:
   10457             :     // error
   10458           0 :     break;
   10459             :   }
   10460             : 
   10461           0 :   return haveGradientLine;
   10462             : }
   10463             : 
   10464             : bool
   10465          51 : CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient,
   10466             :                                        nsCSSValue& aValue)
   10467             : {
   10468             :   // At least two color stops are required
   10469         151 :   if (!ParseColorStop(aGradient) ||
   10470         100 :       !ExpectSymbol(',', true) ||
   10471          49 :       !ParseColorStop(aGradient)) {
   10472           6 :     SkipUntil(')');
   10473           6 :     return false;
   10474             :   }
   10475             : 
   10476             :   // Additional color stops
   10477         127 :   while (ExpectSymbol(',', true)) {
   10478          41 :     if (!ParseColorStop(aGradient)) {
   10479           0 :       SkipUntil(')');
   10480           0 :       return false;
   10481             :     }
   10482             :   }
   10483             : 
   10484          45 :   if (!ExpectSymbol(')', true)) {
   10485           0 :     SkipUntil(')');
   10486           0 :     return false;
   10487             :   }
   10488             : 
   10489             :   // Check if interpolation hints are in the correct location
   10490          45 :   bool previousPointWasInterpolationHint = true;
   10491         176 :   for (size_t x = 0; x < aGradient->mStops.Length(); x++) {
   10492         131 :     bool isInterpolationHint = aGradient->mStops[x].mIsInterpolationHint;
   10493         131 :     if (isInterpolationHint && previousPointWasInterpolationHint) {
   10494           0 :       return false;
   10495             :     }
   10496         131 :     previousPointWasInterpolationHint = isInterpolationHint;
   10497             :   }
   10498             : 
   10499          45 :   if (previousPointWasInterpolationHint) {
   10500           0 :     return false;
   10501             :   }
   10502             : 
   10503          45 :   aValue.SetGradientValue(aGradient);
   10504          45 :   return true;
   10505             : }
   10506             : 
   10507             : // Parses the x or y component of a -webkit-gradient() <point> expression.
   10508             : // See ParseWebkitGradientPoint() documentation for more.
   10509             : bool
   10510           0 : CSSParserImpl::ParseWebkitGradientPointComponent(nsCSSValue& aComponent,
   10511             :                                                  bool aIsHorizontal)
   10512             : {
   10513           0 :   if (!GetToken(true)) {
   10514           0 :     return false;
   10515             :   }
   10516             : 
   10517             :   // Keyword tables to use for keyword-matching
   10518             :   // (Keyword order is important; we assume the index can be multiplied by 50%
   10519             :   // to convert to a percent-valued component.)
   10520             :   static const nsCSSKeyword kHorizKeywords[] = {
   10521             :     eCSSKeyword_left,   //   0%
   10522             :     eCSSKeyword_center, //  50%
   10523             :     eCSSKeyword_right   // 100%
   10524             :   };
   10525             :   static const nsCSSKeyword kVertKeywords[] = {
   10526             :     eCSSKeyword_top,     //   0%
   10527             :     eCSSKeyword_center,  //  50%
   10528             :     eCSSKeyword_bottom   // 100%
   10529             :   };
   10530             :   static const size_t kNumKeywords = MOZ_ARRAY_LENGTH(kHorizKeywords);
   10531             :   static_assert(kNumKeywords == MOZ_ARRAY_LENGTH(kVertKeywords),
   10532             :                 "Horizontal & vertical keyword tables must have same count");
   10533             : 
   10534             :   // Try to parse the component as a number, or a percent, or a
   10535             :   // keyword-converted-to-percent.
   10536           0 :   if (mToken.mType == eCSSToken_Number) {
   10537           0 :     aComponent.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
   10538           0 :   } else if (mToken.mType == eCSSToken_Percentage) {
   10539           0 :     aComponent.SetPercentValue(mToken.mNumber);
   10540           0 :   } else if (mToken.mType == eCSSToken_Ident) {
   10541           0 :     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
   10542           0 :     if (keyword == eCSSKeyword_UNKNOWN) {
   10543           0 :       return false;
   10544             :     }
   10545             :     // Choose our keyword table:
   10546           0 :     const nsCSSKeyword* kwTable = aIsHorizontal ? kHorizKeywords : kVertKeywords;
   10547             :     // Convert keyword to percent value (0%, 50%, or 100%)
   10548           0 :     bool didAcceptKeyword = false;
   10549           0 :     for (size_t i = 0; i < kNumKeywords; i++) {
   10550           0 :       if (keyword == kwTable[i]) {
   10551             :         // 0%, 50%, or 100%:
   10552           0 :         aComponent.SetPercentValue(i * 0.5);
   10553           0 :         didAcceptKeyword = true;
   10554           0 :         break;
   10555             :       }
   10556             :     }
   10557           0 :     if (!didAcceptKeyword) {
   10558           0 :       return false;
   10559             :     }
   10560             :   } else {
   10561             :     // Unrecognized token type. Put it back. (It might be a closing-paren of an
   10562             :     // invalid -webkit-gradient(...) expression, and we need to be sure caller
   10563             :     // can see it & stops parsing at that point.)
   10564           0 :     UngetToken();
   10565           0 :     return false;
   10566             :   }
   10567             : 
   10568           0 :   MOZ_ASSERT(aComponent.GetUnit() == eCSSUnit_Pixel ||
   10569             :              aComponent.GetUnit() == eCSSUnit_Percent,
   10570             :              "If we get here, we should've successfully parsed a number (as a "
   10571             :              "pixel length), a percent, or a keyword (converted to percent)");
   10572           0 :   return true;
   10573             : }
   10574             : 
   10575             : // This function parses a "<point>" expression for -webkit-gradient(...)
   10576             : // Quoting https://www.webkit.org/blog/175/introducing-css-gradients/ :
   10577             : //   "A point is a pair of space-separated values.
   10578             : //    The syntax supports numbers, percentages or
   10579             : //    the keywords top, bottom, left and right
   10580             : //    for point values."
   10581             : //
   10582             : // Two additional notes:
   10583             : //  - WebKit also accepts the "center" keyword (not listed in the text above).
   10584             : //  - WebKit only accepts horizontal-flavored keywords (left/center/right) in
   10585             : //    the first ("x") component, and vertical-flavored keywords
   10586             : //    (top/center/bottom) in the second ("y") component. (This is different
   10587             : //    from the standard gradient syntax, which accepts both orderings, e.g.
   10588             : //    "top left" as well as "left top".)
   10589             : bool
   10590           0 : CSSParserImpl::ParseWebkitGradientPoint(nsCSSValuePair& aPoint)
   10591             : {
   10592           0 :   return ParseWebkitGradientPointComponent(aPoint.mXValue, true) &&
   10593           0 :     ParseWebkitGradientPointComponent(aPoint.mYValue, false);
   10594             : }
   10595             : 
   10596             : // Parse the next token as a <number> (for a <radius> in a -webkit-gradient
   10597             : // expresison).  Returns true on success; returns false & puts back
   10598             : // whatever it parsed on failure.
   10599             : bool
   10600           0 : CSSParserImpl::ParseWebkitGradientRadius(float& aRadius)
   10601             : {
   10602           0 :   if (!GetToken(true)) {
   10603           0 :     return false;
   10604             :   }
   10605             : 
   10606           0 :   if (mToken.mType != eCSSToken_Number) {
   10607           0 :     UngetToken();
   10608           0 :     return false;
   10609             :   }
   10610             : 
   10611           0 :   aRadius = mToken.mNumber;
   10612           0 :   return true;
   10613             : }
   10614             : 
   10615             : // Parse one of:
   10616             : //  color-stop(number|percent, color)
   10617             : //  from(color)
   10618             : //  to(color)
   10619             : //
   10620             : // Quoting https://www.webkit.org/blog/175/introducing-css-gradients/ :
   10621             : //   A stop is a function, color-stop, that takes two arguments, the stop value
   10622             : //   (either a percentage or a number between 0 and 1.0), and a color (any
   10623             : //   valid CSS color). In addition the shorthand functions from and to are
   10624             : //   supported. These functions only require a color argument and are
   10625             : //   equivalent to color-stop(0, ...) and color-stop(1.0, …) respectively.
   10626             : bool
   10627           0 : CSSParserImpl::ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient)
   10628             : {
   10629           0 :   MOZ_ASSERT(aGradient, "null gradient");
   10630             : 
   10631           0 :   if (!GetToken(true)) {
   10632           0 :     return false;
   10633             :   }
   10634             : 
   10635             :   // We're expecting color-stop(...), from(...), or to(...) which are all
   10636             :   // functions. Bail if we got anything else.
   10637           0 :   if (mToken.mType != eCSSToken_Function) {
   10638           0 :     UngetToken();
   10639           0 :     return false;
   10640             :   }
   10641             : 
   10642           0 :   nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
   10643             : 
   10644             :   // Parse color-stop location (or infer it, for shorthands "from"/"to"):
   10645           0 :   if (mToken.mIdent.LowerCaseEqualsLiteral("color-stop")) {
   10646             :     // Parse stop location, followed by comma.
   10647           0 :     if (!ParseSingleTokenVariant(stop->mLocation,
   10648             :                                  VARIANT_NUMBER | VARIANT_PERCENT,
   10649           0 :                                  nullptr) ||
   10650           0 :         !ExpectSymbol(',', true)) {
   10651           0 :       SkipUntil(')'); // Skip to end of color-stop(...) expression.
   10652           0 :       return false;
   10653             :     }
   10654             : 
   10655             :     // If we got a <number>, convert it to percentage for consistency:
   10656           0 :     if (stop->mLocation.GetUnit() == eCSSUnit_Number) {
   10657           0 :       stop->mLocation.SetPercentValue(stop->mLocation.GetFloatValue());
   10658             :     }
   10659           0 :   } else if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
   10660             :     // Shorthand for color-stop(0%, ...)
   10661           0 :     stop->mLocation.SetPercentValue(0.0f);
   10662           0 :   } else if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
   10663             :     // Shorthand for color-stop(100%, ...)
   10664           0 :     stop->mLocation.SetPercentValue(1.0f);
   10665             :   } else {
   10666             :     // Unrecognized function name (invalid for a -webkit-gradient color stop).
   10667           0 :     UngetToken();
   10668           0 :     return false;
   10669             :   }
   10670             : 
   10671           0 :   CSSParseResult result = ParseVariant(stop->mColor, VARIANT_COLOR, nullptr);
   10672           0 :   if (result != CSSParseResult::Ok ||
   10673           0 :       (stop->mColor.GetUnit() == eCSSUnit_EnumColor &&
   10674           0 :        stop->mColor.GetIntValue() == NS_COLOR_CURRENTCOLOR)) {
   10675             :     // Parse failure, or parsed "currentColor" which is forbidden in
   10676             :     // -webkit-gradient for some reason.
   10677           0 :     SkipUntil(')');
   10678           0 :     return false;
   10679             :   }
   10680             : 
   10681             :   // Parse color-stop function close-paren
   10682           0 :   if (!ExpectSymbol(')', true)) {
   10683           0 :     SkipUntil(')');
   10684           0 :     return false;
   10685             :   }
   10686             : 
   10687           0 :   MOZ_ASSERT(stop->mLocation.GetUnit() == eCSSUnit_Percent,
   10688             :              "Should produce only percent-valued stop-locations. "
   10689             :              "(Caller depends on this when sorting color stops.)");
   10690             : 
   10691           0 :   return true;
   10692             : }
   10693             : 
   10694             : // Comparatison function to use for sorting -webkit-gradient() stops by
   10695             : // location. This function assumes stops have percent-valued locations (and
   10696             : // CSSParserImpl::ParseWebkitGradientColorStop should enforce this).
   10697             : static bool
   10698           0 : IsColorStopPctLocationLessThan(const nsCSSValueGradientStop& aStop1,
   10699             :                                const nsCSSValueGradientStop& aStop2) {
   10700           0 :   return (aStop1.mLocation.GetPercentValue() <
   10701           0 :           aStop2.mLocation.GetPercentValue());
   10702             : }
   10703             : 
   10704             : // This function parses a list of comma-separated color-stops for a
   10705             : // -webkit-gradient(...) expression, and then pads & sorts the list as-needed.
   10706             : bool
   10707           0 : CSSParserImpl::ParseWebkitGradientColorStops(nsCSSValueGradient* aGradient)
   10708             : {
   10709           0 :   MOZ_ASSERT(aGradient, "null gradient");
   10710             : 
   10711             :   // Parse any number of ", <color-stop>" expressions. (0 or more)
   10712             :   // Note: This is different from unprefixed gradient syntax, which
   10713             :   // requires at least 2 stops.
   10714           0 :   while (ExpectSymbol(',', true)) {
   10715           0 :     if (!ParseWebkitGradientColorStop(aGradient)) {
   10716           0 :       return false;
   10717             :     }
   10718             :   }
   10719             : 
   10720             :   // Pad up to 2 stops as-needed:
   10721             :   // (Modern gradient expressions are required to have at least 2 stops, so we
   10722             :   // depend on this internally -- e.g. we have an assertion about this in
   10723             :   // nsCSSRendering.cpp. -webkit-gradient syntax allows 0 stops or 1 stop,
   10724             :   // though, so we just pad up to 2 stops in this case).
   10725             : 
   10726             :   // If we have no stops, pad with transparent-black:
   10727           0 :   if (aGradient->mStops.IsEmpty()) {
   10728           0 :     nsCSSValueGradientStop* stop1 = aGradient->mStops.AppendElement();
   10729           0 :     stop1->mColor.SetIntegerColorValue(NS_RGBA(0, 0, 0, 0),
   10730           0 :                                        eCSSUnit_RGBAColor);
   10731           0 :     stop1->mLocation.SetPercentValue(0.0f);
   10732             : 
   10733           0 :     nsCSSValueGradientStop* stop2 = aGradient->mStops.AppendElement();
   10734           0 :     stop2->mColor.SetIntegerColorValue(NS_RGBA(0, 0, 0, 0),
   10735           0 :                                        eCSSUnit_RGBAColor);
   10736           0 :     stop2->mLocation.SetPercentValue(1.0f);
   10737           0 :   } else if (aGradient->mStops.Length() == 1) {
   10738             :     // Copy whatever the author provided in the first stop:
   10739           0 :     nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
   10740           0 :     *stop = aGradient->mStops[0];
   10741             :   } else {
   10742             :     // We have >2 stops. Sort them in order of increasing location.
   10743           0 :     std::stable_sort(aGradient->mStops.begin(),
   10744           0 :                      aGradient->mStops.end(),
   10745           0 :                      IsColorStopPctLocationLessThan);
   10746             :   }
   10747           0 :   return true;
   10748             : }
   10749             : 
   10750             : // Compares aStartCoord to aEndCoord, and returns true iff they share the same
   10751             : // unit (both pixel, or both percent) and aStartCoord is larger.
   10752             : static bool
   10753           0 : IsWebkitGradientCoordLarger(const nsCSSValue& aStartCoord,
   10754             :                             const nsCSSValue& aEndCoord)
   10755             : {
   10756           0 :   if (aStartCoord.GetUnit() == eCSSUnit_Percent &&
   10757           0 :       aEndCoord.GetUnit() == eCSSUnit_Percent) {
   10758           0 :     return aStartCoord.GetPercentValue() > aEndCoord.GetPercentValue();
   10759             :   }
   10760             : 
   10761           0 :   if (aStartCoord.GetUnit() == eCSSUnit_Pixel &&
   10762           0 :       aEndCoord.GetUnit() == eCSSUnit_Pixel) {
   10763           0 :     return aStartCoord.GetFloatValue() > aEndCoord.GetFloatValue();
   10764             :   }
   10765             : 
   10766             :   // We can't compare them, since their units differ. Returning false suggests
   10767             :   // that aEndCoord is larger, which is probably a decent guess anyway.
   10768           0 :   return false;
   10769             : }
   10770             : 
   10771             : // Finalize our internal representation of a -webkit-gradient(linear, ...)
   10772             : // expression, given the parsed points.  (The parsed color stops
   10773             : // should already be hanging off of the passed-in nsCSSValueGradient.)
   10774             : //
   10775             : // Note: linear gradients progress along a line between two points.  The
   10776             : // -webkit-gradient(linear, ...) syntax lets the author precisely specify the
   10777             : // starting and ending point. However, our internal gradient structures only
   10778             : // store ONE point (which for modern linear-gradient() expressions is a side or
   10779             : // corner & represents the ending point of the gradient -- and the starting
   10780             : // point is implicitly the opposite side/corner).
   10781             : //
   10782             : // In this function, we analyze the start & end points from a
   10783             : // -webkit-gradient(linear, ...) expression, and we choose an appropriate
   10784             : // target point to produce a modern linear-gradient() representation that has
   10785             : // the same rough trajectory.  If we can't determine a target point for some
   10786             : // reason, we just fall back to the default top-to-bottom linear-gradient()
   10787             : // directionality.
   10788             : void
   10789           0 : CSSParserImpl::FinalizeLinearWebkitGradient(nsCSSValueGradient* aGradient,
   10790             :                                             const nsCSSValuePair& aStartPoint,
   10791             :                                             const nsCSSValuePair& aEndPoint)
   10792             : {
   10793           0 :   MOZ_ASSERT(!aGradient->mIsRadial, "passed-in gradient must be linear");
   10794             : 
   10795           0 :   if (aStartPoint == aEndPoint ||
   10796           0 :       aStartPoint.mXValue.GetUnit() != aEndPoint.mXValue.GetUnit() ||
   10797           0 :       aStartPoint.mYValue.GetUnit() != aEndPoint.mYValue.GetUnit()) {
   10798             :     // Start point & end point are the same, OR they use different units for at
   10799             :     // least one coordinate.  Either way, this isn't something we can represent
   10800             :     // using an unprefixed linear-gradient expression, so instead we'll just
   10801             :     // arbitrarily pretend this is a top-to-bottom gradient (which is the
   10802             :     // default for modern linear-gradient if a direction is unspecified).
   10803           0 :     aGradient->mBgPos.mYValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_BOTTOM,
   10804           0 :                                           eCSSUnit_Enumerated);
   10805           0 :     aGradient->mBgPos.mXValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_CENTER,
   10806           0 :                                           eCSSUnit_Enumerated);
   10807           0 :     return;
   10808             :   }
   10809             : 
   10810             :   // Set the target point to one of the box's corners (or the center of an
   10811             :   // edge), by comparing aStartPoint and aEndPoint to extract a general
   10812             :   // direction.
   10813             : 
   10814             :   int32_t targetX;
   10815           0 :   if (aStartPoint.mXValue == aEndPoint.mXValue) {
   10816           0 :     targetX = NS_STYLE_IMAGELAYER_POSITION_CENTER;
   10817           0 :   } else if (IsWebkitGradientCoordLarger(aStartPoint.mXValue,
   10818             :                                          aEndPoint.mXValue)) {
   10819           0 :     targetX = NS_STYLE_IMAGELAYER_POSITION_LEFT;
   10820             :   } else {
   10821           0 :     MOZ_ASSERT(IsWebkitGradientCoordLarger(aEndPoint.mXValue,
   10822             :                                            aStartPoint.mXValue),
   10823             :                "IsWebkitGradientCoordLarger returning inconsistent results?");
   10824           0 :     targetX = NS_STYLE_IMAGELAYER_POSITION_RIGHT;
   10825             :   }
   10826             : 
   10827             :   int32_t targetY;
   10828           0 :   if (aStartPoint.mYValue == aEndPoint.mYValue) {
   10829           0 :     targetY = NS_STYLE_IMAGELAYER_POSITION_CENTER;
   10830           0 :   } else if (IsWebkitGradientCoordLarger(aStartPoint.mYValue,
   10831             :                                          aEndPoint.mYValue)) {
   10832           0 :     targetY = NS_STYLE_IMAGELAYER_POSITION_TOP;
   10833             :   } else {
   10834           0 :     MOZ_ASSERT(IsWebkitGradientCoordLarger(aEndPoint.mYValue,
   10835             :                                            aStartPoint.mYValue),
   10836             :                "IsWebkitGradientCoordLarger returning inconsistent results?");
   10837           0 :     targetY = NS_STYLE_IMAGELAYER_POSITION_BOTTOM;
   10838             :   }
   10839             : 
   10840           0 :   aGradient->mBgPos.mXValue.SetIntValue(targetX, eCSSUnit_Enumerated);
   10841           0 :   aGradient->mBgPos.mYValue.SetIntValue(targetY, eCSSUnit_Enumerated);
   10842             : }
   10843             : 
   10844             : // Finalize our internal representation of a -webkit-gradient(radial, ...)
   10845             : // expression, given the parsed points & radii.  (The parsed color-stops
   10846             : // should already be hanging off of the passed-in nsCSSValueGradient).
   10847             : void
   10848           0 : CSSParserImpl::FinalizeRadialWebkitGradient(nsCSSValueGradient* aGradient,
   10849             :                                             const nsCSSValuePair& aFirstCenter,
   10850             :                                             const nsCSSValuePair& aSecondCenter,
   10851             :                                             const float aFirstRadius,
   10852             :                                             const float aSecondRadius)
   10853             : {
   10854           0 :   MOZ_ASSERT(aGradient->mIsRadial, "passed-in gradient must be radial");
   10855             : 
   10856             :   // NOTE: -webkit-gradient(radial, ...) has *two arbitrary circles*, with the
   10857             :   // gradient stretching between the circles' edges.  In contrast, the standard
   10858             :   // syntax (and hence our data structures) can only represent *one* circle,
   10859             :   // with the gradient going from its center to its edge.  To bridge this gap
   10860             :   // in expressiveness, we'll just see which of our two circles is smaller, and
   10861             :   // we'll treat that circle as if it were zero-sized and located at the center
   10862             :   // of the larger circle. Then, we'll be able to use the same data structures
   10863             :   // that we use for the standard radial-gradient syntax.
   10864           0 :   if (aSecondRadius >= aFirstRadius) {
   10865             :     // Second circle is larger.
   10866           0 :     aGradient->mBgPos = aSecondCenter;
   10867           0 :     aGradient->mIsExplicitSize = true;
   10868           0 :     aGradient->GetRadiusX().SetFloatValue(aSecondRadius, eCSSUnit_Pixel);
   10869           0 :     return;
   10870             :   }
   10871             : 
   10872             :   // First circle is larger, so we'll have it be the outer circle.
   10873           0 :   aGradient->mBgPos = aFirstCenter;
   10874           0 :   aGradient->mIsExplicitSize = true;
   10875           0 :   aGradient->GetRadiusX().SetFloatValue(aFirstRadius, eCSSUnit_Pixel);
   10876             : 
   10877             :   // For this to work properly (with the earlier color stops attached to the
   10878             :   // first circle), we need to also reverse the color-stop list, so that
   10879             :   // e.g. the author's "from" color is attached to the outer edge (the first
   10880             :   // circle), rather than attached to the center (the collapsed second circle).
   10881           0 :   std::reverse(aGradient->mStops.begin(), aGradient->mStops.end());
   10882             : 
   10883             :   // And now invert the stop locations:
   10884           0 :   for (nsCSSValueGradientStop& colorStop : aGradient->mStops) {
   10885           0 :     float origLocation = colorStop.mLocation.GetPercentValue();
   10886           0 :     colorStop.mLocation.SetPercentValue(1.0f - origLocation);
   10887             :   }
   10888             : }
   10889             : 
   10890             : bool
   10891           0 : CSSParserImpl::ParseWebkitGradient(nsCSSValue& aValue)
   10892             : {
   10893             :   // Parse type of gradient
   10894           0 :   if (!GetToken(true)) {
   10895           0 :     return false;
   10896             :   }
   10897             : 
   10898           0 :   if (mToken.mType != eCSSToken_Ident) {
   10899           0 :     UngetToken(); // Important; the token might be ")", which we're about to
   10900             :                   // seek to.
   10901           0 :     SkipUntil(')');
   10902           0 :     return false;
   10903             :   }
   10904             : 
   10905             :   bool isRadial;
   10906           0 :   if (mToken.mIdent.LowerCaseEqualsLiteral("radial")) {
   10907           0 :     isRadial = true;
   10908           0 :   } else if (mToken.mIdent.LowerCaseEqualsLiteral("linear")) {
   10909           0 :     isRadial = false;
   10910             :   } else {
   10911             :     // Unrecognized gradient type.
   10912           0 :     SkipUntil(')');
   10913           0 :     return false;
   10914             :   }
   10915             : 
   10916             :   // Parse a comma + first point:
   10917           0 :   nsCSSValuePair firstPoint;
   10918           0 :   if (!ExpectSymbol(',', true) ||
   10919           0 :       !ParseWebkitGradientPoint(firstPoint)) {
   10920           0 :     SkipUntil(')');
   10921           0 :     return false;
   10922             :   }
   10923             : 
   10924             :   // If radial, parse comma + first radius:
   10925             :   float firstRadius;
   10926           0 :   if (isRadial) {
   10927           0 :     if (!ExpectSymbol(',', true) ||
   10928           0 :         !ParseWebkitGradientRadius(firstRadius)) {
   10929           0 :       SkipUntil(')');
   10930           0 :       return false;
   10931             :     }
   10932             :   }
   10933             : 
   10934             :   // Parse a comma + second point:
   10935           0 :   nsCSSValuePair secondPoint;
   10936           0 :   if (!ExpectSymbol(',', true) ||
   10937           0 :       !ParseWebkitGradientPoint(secondPoint)) {
   10938           0 :     SkipUntil(')');
   10939           0 :     return false;
   10940             :   }
   10941             : 
   10942             :   // If radial, parse comma + second radius:
   10943             :   float secondRadius;
   10944           0 :   if (isRadial) {
   10945           0 :     if (!ExpectSymbol(',', true) ||
   10946           0 :         !ParseWebkitGradientRadius(secondRadius)) {
   10947           0 :       SkipUntil(')');
   10948           0 :       return false;
   10949             :     }
   10950             :   }
   10951             : 
   10952             :   // Construct a nsCSSValueGradient object, and parse color stops into it:
   10953             :   RefPtr<nsCSSValueGradient> cssGradient =
   10954           0 :     new nsCSSValueGradient(isRadial, false /* aIsRepeating */);
   10955             : 
   10956           0 :   if (!ParseWebkitGradientColorStops(cssGradient) ||
   10957           0 :       !ExpectSymbol(')', true)) {
   10958             :     // Failed to parse color-stops, or found trailing junk between them & ')'.
   10959           0 :     SkipUntil(')');
   10960           0 :     return false;
   10961             :   }
   10962             : 
   10963             :   // Finish building cssGradient, based on our parsed positioning/sizing info:
   10964           0 :   if (isRadial) {
   10965           0 :     FinalizeRadialWebkitGradient(cssGradient, firstPoint, secondPoint,
   10966           0 :                            firstRadius, secondRadius);
   10967             :   } else {
   10968           0 :     FinalizeLinearWebkitGradient(cssGradient, firstPoint, secondPoint);
   10969             :   }
   10970             : 
   10971           0 :   aValue.SetGradientValue(cssGradient);
   10972           0 :   return true;
   10973             : }
   10974             : 
   10975             : bool
   10976           0 : CSSParserImpl::ParseWebkitTextStroke()
   10977             : {
   10978             :   static const nsCSSPropertyID kWebkitTextStrokeIDs[] = {
   10979             :     eCSSProperty__webkit_text_stroke_width,
   10980             :     eCSSProperty__webkit_text_stroke_color
   10981             :   };
   10982             : 
   10983           0 :   const size_t numProps = MOZ_ARRAY_LENGTH(kWebkitTextStrokeIDs);
   10984           0 :   nsCSSValue values[numProps];
   10985             : 
   10986           0 :   int32_t found = ParseChoice(values, kWebkitTextStrokeIDs, numProps);
   10987           0 :   if (found < 1) {
   10988           0 :     return false;
   10989             :   }
   10990             : 
   10991           0 :   if (!(found & 1)) { // Provide default -webkit-text-stroke-width
   10992           0 :     values[0].SetFloatValue(0, eCSSUnit_Pixel);
   10993             :   }
   10994             : 
   10995           0 :   if (!(found & 2)) { // Provide default -webkit-text-stroke-color
   10996           0 :     values[1].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
   10997             :   }
   10998             : 
   10999           0 :   for (size_t index = 0; index < numProps; ++index) {
   11000           0 :     AppendValue(kWebkitTextStrokeIDs[index], values[index]);
   11001             :   }
   11002             : 
   11003           0 :   return true;
   11004             : }
   11005             : 
   11006             :   int32_t
   11007         412 : CSSParserImpl::ParseChoice(nsCSSValue aValues[],
   11008             :                            const nsCSSPropertyID aPropIDs[], int32_t aNumIDs)
   11009             : {
   11010         412 :   int32_t found = 0;
   11011         824 :   nsAutoParseCompoundProperty compound(this);
   11012             : 
   11013             :   int32_t loop;
   11014        1342 :   for (loop = 0; loop < aNumIDs; loop++) {
   11015             :     // Try each property parser in order
   11016        1121 :     int32_t hadFound = found;
   11017             :     int32_t index;
   11018        2544 :     for (index = 0; index < aNumIDs; index++) {
   11019        2353 :       int32_t bit = 1 << index;
   11020        2353 :       if ((found & bit) == 0) {
   11021             :         CSSParseResult result =
   11022        1375 :           ParseSingleValueProperty(aValues[index], aPropIDs[index]);
   11023        1375 :         if (result == CSSParseResult::Error) {
   11024           0 :           return -1;
   11025             :         }
   11026        1375 :         if (result == CSSParseResult::Ok) {
   11027         930 :           found |= bit;
   11028             :           // It's more efficient to break since it will reset |hadFound|
   11029             :           // to |found|.  Furthermore, ParseListStyle depends on our going
   11030             :           // through the properties in order for each value..
   11031         930 :           break;
   11032             :         }
   11033             :       }
   11034             :     }
   11035        1121 :     if (found == hadFound) {  // found nothing new
   11036         191 :       break;
   11037             :     }
   11038             :   }
   11039         412 :   if (0 < found) {
   11040         405 :     if (1 == found) { // only first property
   11041          43 :       if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
   11042          36 :         for (loop = 1; loop < aNumIDs; loop++) {
   11043          24 :           aValues[loop].SetInheritValue();
   11044             :         }
   11045          12 :         found = ((1 << aNumIDs) - 1);
   11046             :       }
   11047          31 :       else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
   11048           0 :         for (loop = 1; loop < aNumIDs; loop++) {
   11049           0 :           aValues[loop].SetInitialValue();
   11050             :         }
   11051           0 :         found = ((1 << aNumIDs) - 1);
   11052             :       }
   11053          31 :       else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset
   11054           6 :         for (loop = 1; loop < aNumIDs; loop++) {
   11055           4 :           aValues[loop].SetUnsetValue();
   11056             :         }
   11057           2 :         found = ((1 << aNumIDs) - 1);
   11058             :       }
   11059             :     }
   11060             :     else {  // more than one value, verify no inherits, initials or unsets
   11061        1450 :       for (loop = 0; loop < aNumIDs; loop++) {
   11062        1088 :         if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
   11063           0 :           found = -1;
   11064           0 :           break;
   11065             :         }
   11066        1088 :         else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
   11067           0 :           found = -1;
   11068           0 :           break;
   11069             :         }
   11070        1088 :         else if (eCSSUnit_Unset == aValues[loop].GetUnit()) {
   11071           0 :           found = -1;
   11072           0 :           break;
   11073             :         }
   11074             :       }
   11075             :     }
   11076             :   }
   11077         412 :   return found;
   11078             : }
   11079             : 
   11080             : void
   11081       17610 : CSSParserImpl::AppendValue(nsCSSPropertyID aPropID, const nsCSSValue& aValue)
   11082             : {
   11083       17610 :   mTempData.AddLonghandProperty(aPropID, aValue);
   11084       17610 : }
   11085             : 
   11086             : /**
   11087             :  * Parse a "box" property. Box properties have 1 to 4 values. When less
   11088             :  * than 4 values are provided a standard mapping is used to replicate
   11089             :  * existing values.
   11090             :  */
   11091             : bool
   11092         702 : CSSParserImpl::ParseBoxProperties(const nsCSSPropertyID aPropIDs[])
   11093             : {
   11094             :   // Get up to four values for the property
   11095         702 :   int32_t count = 0;
   11096        1404 :   nsCSSRect result;
   11097        1738 :   NS_FOR_CSS_SIDES (index) {
   11098             :     CSSParseResult parseResult =
   11099        1722 :       ParseBoxProperty(result.*(nsCSSRect::sides[index]), aPropIDs[index]);
   11100        1722 :     if (parseResult == CSSParseResult::NotFound) {
   11101         683 :       break;
   11102             :     }
   11103        1039 :     if (parseResult == CSSParseResult::Error) {
   11104           3 :       return false;
   11105             :     }
   11106        1036 :     count++;
   11107             :   }
   11108         699 :   if (count == 0) {
   11109           9 :     return false;
   11110             :   }
   11111             : 
   11112         690 :   if (1 < count) { // verify no more than single inherit, initial or unset
   11113        1070 :     NS_FOR_CSS_SIDES (index) {
   11114         856 :       nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
   11115         856 :       if (eCSSUnit_Inherit == unit ||
   11116         856 :           eCSSUnit_Initial == unit ||
   11117             :           eCSSUnit_Unset == unit) {
   11118           0 :         return false;
   11119             :       }
   11120             :     }
   11121             :   }
   11122             : 
   11123             :   // Provide missing values by replicating some of the values found
   11124         690 :   switch (count) {
   11125             :     case 1: // Make right == top
   11126         476 :       result.mRight = result.mTop;
   11127             :       MOZ_FALLTHROUGH;
   11128             :     case 2: // Make bottom == top
   11129         577 :       result.mBottom = result.mTop;
   11130             :       MOZ_FALLTHROUGH;
   11131             :     case 3: // Make left == right
   11132         674 :       result.mLeft = result.mRight;
   11133             :   }
   11134             : 
   11135        3450 :   NS_FOR_CSS_SIDES (index) {
   11136        2760 :     AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index]));
   11137             :   }
   11138         690 :   return true;
   11139             : }
   11140             : 
   11141             : // Similar to ParseBoxProperties, except there is only one property
   11142             : // with the result as its value, not four.
   11143             : bool
   11144           4 : CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask,
   11145             :                                        /** outparam */ nsCSSValue& aValue,
   11146             :                                        uint32_t aRestrictions)
   11147             : {
   11148           4 :   nsCSSRect& result = aValue.SetRectValue();
   11149             : 
   11150           4 :   int32_t count = 0;
   11151           8 :   NS_FOR_CSS_SIDES (index) {
   11152             :     CSSParseResult parseResult =
   11153           8 :       ParseVariantWithRestrictions(result.*(nsCSSRect::sides[index]),
   11154             :                                    aVariantMask, nullptr,
   11155           8 :                                    aRestrictions);
   11156           8 :     if (parseResult == CSSParseResult::NotFound) {
   11157           4 :       break;
   11158             :     }
   11159           4 :     if (parseResult == CSSParseResult::Error) {
   11160           0 :       return false;
   11161             :     }
   11162           4 :     count++;
   11163             :   }
   11164             : 
   11165           4 :   if (count == 0) {
   11166           0 :     return false;
   11167             :   }
   11168             : 
   11169             :   // Provide missing values by replicating some of the values found
   11170           4 :   switch (count) {
   11171             :     case 1: // Make right == top
   11172           4 :       result.mRight = result.mTop;
   11173             :       MOZ_FALLTHROUGH;
   11174             :     case 2: // Make bottom == top
   11175           4 :       result.mBottom = result.mTop;
   11176             :       MOZ_FALLTHROUGH;
   11177             :     case 3: // Make left == right
   11178           4 :       result.mLeft = result.mRight;
   11179             :   }
   11180             : 
   11181           4 :   return true;
   11182             : }
   11183             : 
   11184             : bool
   11185          22 : CSSParserImpl::ParseBoxCornerRadius(nsCSSPropertyID aPropID)
   11186             : {
   11187          44 :   nsCSSValue dimenX, dimenY;
   11188             :   // required first value
   11189          22 :   if (ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr) !=
   11190             :       CSSParseResult::Ok) {
   11191           0 :     return false;
   11192             :   }
   11193             : 
   11194             :   // optional second value (forbidden if first value is inherit/initial/unset)
   11195          66 :   if (dimenX.GetUnit() != eCSSUnit_Inherit &&
   11196          44 :       dimenX.GetUnit() != eCSSUnit_Initial &&
   11197          22 :       dimenX.GetUnit() != eCSSUnit_Unset) {
   11198          22 :     if (ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr) ==
   11199             :         CSSParseResult::Error) {
   11200           0 :       return false;
   11201             :     }
   11202             :   }
   11203             : 
   11204          22 :   if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) {
   11205          22 :     AppendValue(aPropID, dimenX);
   11206             :   } else {
   11207           0 :     nsCSSValue value;
   11208           0 :     value.SetPairValue(dimenX, dimenY);
   11209           0 :     AppendValue(aPropID, value);
   11210             :   }
   11211          22 :   return true;
   11212             : }
   11213             : 
   11214             : bool
   11215         355 : CSSParserImpl::ParseBoxCornerRadiiInternals(nsCSSValue array[])
   11216             : {
   11217             :   // Rectangles are used as scratch storage.
   11218             :   // top => top-left, right => top-right,
   11219             :   // bottom => bottom-right, left => bottom-left.
   11220         710 :   nsCSSRect dimenX, dimenY;
   11221         355 :   int32_t countX = 0, countY = 0;
   11222             : 
   11223        1007 :   NS_FOR_CSS_SIDES (side) {
   11224             :     CSSParseResult result =
   11225         907 :       ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side],
   11226         907 :                               (side > 0 ? 0 : VARIANT_INHERIT) |
   11227             :                                 VARIANT_LP | VARIANT_CALC,
   11228         907 :                               nullptr);
   11229         907 :     if (result == CSSParseResult::Error) {
   11230           0 :       return false;
   11231         907 :     } else if (result == CSSParseResult::NotFound) {
   11232         255 :       break;
   11233             :     }
   11234         652 :     countX++;
   11235             :   }
   11236         355 :   if (countX == 0)
   11237           3 :     return false;
   11238             : 
   11239         352 :   if (ExpectSymbol('/', true)) {
   11240           0 :     NS_FOR_CSS_SIDES (side) {
   11241             :       CSSParseResult result =
   11242           0 :         ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side],
   11243           0 :                                 VARIANT_LP | VARIANT_CALC, nullptr);
   11244           0 :       if (result == CSSParseResult::Error) {
   11245           0 :         return false;
   11246           0 :       } else if (result == CSSParseResult::NotFound) {
   11247           0 :         break;
   11248             :       }
   11249           0 :       countY++;
   11250             :     }
   11251           0 :     if (countY == 0)
   11252           0 :       return false;
   11253             :   }
   11254             : 
   11255             :   // if 'initial', 'inherit' or 'unset' was used, it must be the only value
   11256         352 :   if (countX > 1 || countY > 0) {
   11257         100 :     nsCSSUnit unit = dimenX.mTop.GetUnit();
   11258         100 :     if (eCSSUnit_Inherit == unit ||
   11259         100 :         eCSSUnit_Initial == unit ||
   11260             :         eCSSUnit_Unset == unit)
   11261           0 :       return false;
   11262             :   }
   11263             : 
   11264             :   // if we have no Y-values, use the X-values
   11265         352 :   if (countY == 0) {
   11266         352 :     dimenY = dimenX;
   11267         352 :     countY = countX;
   11268             :   }
   11269             : 
   11270             :   // Provide missing values by replicating some of the values found
   11271         352 :   switch (countX) {
   11272             :     case 1: // Make top-right same as top-left
   11273         252 :       dimenX.mRight = dimenX.mTop;
   11274             :       MOZ_FALLTHROUGH;
   11275             :     case 2: // Make bottom-right same as top-left
   11276         252 :       dimenX.mBottom = dimenX.mTop;
   11277             :       MOZ_FALLTHROUGH;
   11278             :     case 3: // Make bottom-left same as top-right
   11279         252 :       dimenX.mLeft = dimenX.mRight;
   11280             :   }
   11281             : 
   11282         352 :   switch (countY) {
   11283             :     case 1: // Make top-right same as top-left
   11284         252 :       dimenY.mRight = dimenY.mTop;
   11285             :       MOZ_FALLTHROUGH;
   11286             :     case 2: // Make bottom-right same as top-left
   11287         252 :       dimenY.mBottom = dimenY.mTop;
   11288             :       MOZ_FALLTHROUGH;
   11289             :     case 3: // Make bottom-left same as top-right
   11290         252 :       dimenY.mLeft = dimenY.mRight;
   11291             :   }
   11292             : 
   11293        1760 :   NS_FOR_CSS_SIDES(side) {
   11294        1408 :     nsCSSValue& x = dimenX.*nsCSSRect::sides[side];
   11295        1408 :     nsCSSValue& y = dimenY.*nsCSSRect::sides[side];
   11296             : 
   11297        1408 :     if (x == y) {
   11298        1408 :       array[side] = x;
   11299             :     } else {
   11300           0 :       nsCSSValue pair;
   11301           0 :       pair.SetPairValue(x, y);
   11302           0 :       array[side] = pair;
   11303             :     }
   11304             :   }
   11305         352 :   return true;
   11306             : }
   11307             : 
   11308             : bool
   11309         355 : CSSParserImpl::ParseBoxCornerRadii(const nsCSSPropertyID aPropIDs[])
   11310             : {
   11311         710 :   nsCSSValue value[4];
   11312         355 :   if (!ParseBoxCornerRadiiInternals(value)) {
   11313           3 :     return false;
   11314             :   }
   11315             : 
   11316        1760 :   NS_FOR_CSS_SIDES(side) {
   11317        1408 :     AppendValue(aPropIDs[side], value[side]);
   11318             :   }
   11319         352 :   return true;
   11320             : }
   11321             : 
   11322             : // These must be in CSS order (top,right,bottom,left) for indexing to work
   11323             : static const nsCSSPropertyID kBorderStyleIDs[] = {
   11324             :   eCSSProperty_border_top_style,
   11325             :   eCSSProperty_border_right_style,
   11326             :   eCSSProperty_border_bottom_style,
   11327             :   eCSSProperty_border_left_style
   11328             : };
   11329             : static const nsCSSPropertyID kBorderWidthIDs[] = {
   11330             :   eCSSProperty_border_top_width,
   11331             :   eCSSProperty_border_right_width,
   11332             :   eCSSProperty_border_bottom_width,
   11333             :   eCSSProperty_border_left_width
   11334             : };
   11335             : static const nsCSSPropertyID kBorderColorIDs[] = {
   11336             :   eCSSProperty_border_top_color,
   11337             :   eCSSProperty_border_right_color,
   11338             :   eCSSProperty_border_bottom_color,
   11339             :   eCSSProperty_border_left_color
   11340             : };
   11341             : static const nsCSSPropertyID kBorderRadiusIDs[] = {
   11342             :   eCSSProperty_border_top_left_radius,
   11343             :   eCSSProperty_border_top_right_radius,
   11344             :   eCSSProperty_border_bottom_right_radius,
   11345             :   eCSSProperty_border_bottom_left_radius
   11346             : };
   11347             : static const nsCSSPropertyID kOutlineRadiusIDs[] = {
   11348             :   eCSSProperty__moz_outline_radius_topleft,
   11349             :   eCSSProperty__moz_outline_radius_topright,
   11350             :   eCSSProperty__moz_outline_radius_bottomright,
   11351             :   eCSSProperty__moz_outline_radius_bottomleft
   11352             : };
   11353             : 
   11354             : void
   11355        7458 : CSSParserImpl::SaveInputState(CSSParserInputState& aState)
   11356             : {
   11357        7458 :   aState.mToken = mToken;
   11358        7458 :   aState.mHavePushBack = mHavePushBack;
   11359        7458 :   mScanner->SavePosition(aState.mPosition);
   11360        7458 : }
   11361             : 
   11362             : void
   11363         258 : CSSParserImpl::RestoreSavedInputState(const CSSParserInputState& aState)
   11364             : {
   11365         258 :   mToken = aState.mToken;
   11366         258 :   mHavePushBack = aState.mHavePushBack;
   11367         258 :   mScanner->RestoreSavedPosition(aState.mPosition);
   11368         258 : }
   11369             : 
   11370             : bool
   11371        7458 : CSSParserImpl::ParseProperty(nsCSSPropertyID aPropID)
   11372             : {
   11373             :   // Can't use AutoRestore<bool> because it's a bitfield.
   11374        7458 :   MOZ_ASSERT(!mHashlessColorQuirk,
   11375             :              "hashless color quirk should not be set");
   11376        7458 :   MOZ_ASSERT(!mUnitlessLengthQuirk,
   11377             :              "unitless length quirk should not be set");
   11378        7458 :   MOZ_ASSERT(aPropID != eCSSPropertyExtra_variable);
   11379             : 
   11380        7458 :   if (mNavQuirkMode) {
   11381           0 :     mHashlessColorQuirk =
   11382           0 :       nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_HASHLESS_COLOR_QUIRK);
   11383           0 :     mUnitlessLengthQuirk =
   11384           0 :       nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_UNITLESS_LENGTH_QUIRK);
   11385             :   }
   11386             : 
   11387             :   // Save the current input state so that we can restore it later if we
   11388             :   // have to re-parse the property value as a variable-reference-containing
   11389             :   // token stream.
   11390       14916 :   CSSParserInputState stateBeforeProperty;
   11391        7458 :   SaveInputState(stateBeforeProperty);
   11392        7458 :   mScanner->ClearSeenVariableReference();
   11393             : 
   11394        7458 :   NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
   11395        7458 :   bool allowVariables = true;
   11396             :   bool result;
   11397        7458 :   switch (nsCSSProps::PropertyParseType(aPropID)) {
   11398             :     case CSS_PROPERTY_PARSE_INACCESSIBLE: {
   11399             :       // The user can't use these
   11400           0 :       REPORT_UNEXPECTED(PEInaccessibleProperty2);
   11401           0 :       allowVariables = false;
   11402           0 :       result = false;
   11403           0 :       break;
   11404             :     }
   11405             :     case CSS_PROPERTY_PARSE_FUNCTION: {
   11406        2594 :       result = ParsePropertyByFunction(aPropID);
   11407        2594 :       break;
   11408             :     }
   11409             :     case CSS_PROPERTY_PARSE_VALUE: {
   11410        4622 :       result = false;
   11411        9244 :       nsCSSValue value;
   11412        4622 :       if (ParseSingleValueProperty(value, aPropID) == CSSParseResult::Ok) {
   11413        4475 :         AppendValue(aPropID, value);
   11414        4475 :         result = true;
   11415             :       }
   11416             :       // XXX Report errors?
   11417        4622 :       break;
   11418             :     }
   11419             :     case CSS_PROPERTY_PARSE_VALUE_LIST: {
   11420         242 :       result = ParseValueList(aPropID);
   11421         242 :       break;
   11422             :     }
   11423             :     default: {
   11424           0 :       result = false;
   11425           0 :       allowVariables = false;
   11426           0 :       MOZ_ASSERT(false,
   11427             :                  "Property's flags field in nsCSSPropList.h is missing "
   11428             :                  "one of the CSS_PROPERTY_PARSE_* constants");
   11429             :       break;
   11430             :     }
   11431             :   }
   11432             : 
   11433        7458 :   if (result) {
   11434             :     // We need to call ExpectEndProperty() to decide whether to reparse
   11435             :     // with variables.  This is needed because the property parsing may
   11436             :     // have stopped upon finding a variable (e.g., 'margin: 1px var(a)')
   11437             :     // in a way that future variable substitutions will be valid, or
   11438             :     // because it parsed everything that's possible but we still want to
   11439             :     // act as though the property contains variables even though we know
   11440             :     // the substitution will never work (e.g., for 'margin: 1px 2px 3px
   11441             :     // 4px 5px var(a)').
   11442             :     //
   11443             :     // It would be nice to find a better solution here
   11444             :     // (and for the SkipUntilOneOf below), though, that doesn't depend
   11445             :     // on using what we don't accept for doing parsing correctly.
   11446        7244 :     if (!ExpectEndProperty()) {
   11447          44 :       result = false;
   11448             :     }
   11449             :   }
   11450             : 
   11451       14916 :   bool seenVariable = mScanner->SeenVariableReference() ||
   11452        7200 :     (stateBeforeProperty.mHavePushBack &&
   11453           0 :      stateBeforeProperty.mToken.mType == eCSSToken_Function &&
   11454        7458 :      stateBeforeProperty.mToken.mIdent.LowerCaseEqualsLiteral("var"));
   11455             :   bool parseAsTokenStream;
   11456             : 
   11457        7458 :   if (!result && allowVariables) {
   11458         258 :     parseAsTokenStream = true;
   11459         258 :     if (!seenVariable) {
   11460             :       // We might have stopped parsing the property before its end and before
   11461             :       // finding a variable reference.  Keep checking until the end of the
   11462             :       // property.
   11463           0 :       CSSParserInputState stateAtError;
   11464           0 :       SaveInputState(stateAtError);
   11465             : 
   11466           0 :       const char16_t stopChars[] = { ';', '!', '}', ')', 0 };
   11467           0 :       SkipUntilOneOf(stopChars);
   11468           0 :       UngetToken();
   11469           0 :       parseAsTokenStream = mScanner->SeenVariableReference();
   11470             : 
   11471           0 :       if (!parseAsTokenStream) {
   11472             :         // If we parsed to the end of the propery and didn't find any variable
   11473             :         // references, then the real position we want to report the error at
   11474             :         // is |stateAtError|.
   11475           0 :         RestoreSavedInputState(stateAtError);
   11476             :       }
   11477         258 :     }
   11478             :   } else {
   11479        7200 :     parseAsTokenStream = false;
   11480             :   }
   11481             : 
   11482        7458 :   if (parseAsTokenStream) {
   11483             :     // Go back to the start of the property value and parse it to make sure
   11484             :     // its variable references are syntactically valid and is otherwise
   11485             :     // balanced.
   11486         258 :     RestoreSavedInputState(stateBeforeProperty);
   11487             : 
   11488         258 :     if (!mInSupportsCondition) {
   11489         258 :       mScanner->StartRecording();
   11490             :     }
   11491             : 
   11492             :     CSSVariableDeclarations::Type type;
   11493             :     bool dropBackslash;
   11494         516 :     nsString impliedCharacters;
   11495         516 :     nsCSSValue value;
   11496         258 :     if (ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
   11497             :                                 nullptr, nullptr)) {
   11498         258 :       MOZ_ASSERT(type == CSSVariableDeclarations::eTokenStream,
   11499             :                  "a non-custom property reparsed since it contained variable "
   11500             :                  "references should not have been 'initial' or 'inherit'");
   11501             : 
   11502         516 :       nsString propertyValue;
   11503             : 
   11504         258 :       if (!mInSupportsCondition) {
   11505             :         // If we are in an @supports condition, we don't need to store the
   11506             :         // actual token stream on the nsCSSValue.
   11507         258 :         mScanner->StopRecording(propertyValue);
   11508         258 :         if (dropBackslash) {
   11509           0 :           MOZ_ASSERT(!propertyValue.IsEmpty() &&
   11510             :                      propertyValue[propertyValue.Length() - 1] == '\\');
   11511           0 :           propertyValue.Truncate(propertyValue.Length() - 1);
   11512             :         }
   11513         258 :         propertyValue.Append(impliedCharacters);
   11514             :       }
   11515             : 
   11516         258 :       if (mHavePushBack) {
   11517             :         // If we came to the end of a property value that had a variable
   11518             :         // reference and a token was pushed back, then it would have been
   11519             :         // ended by '!', ')', ';', ']' or '}'.  We should remove it from the
   11520             :         // recorded property value.
   11521         258 :         MOZ_ASSERT(mToken.IsSymbol('!') ||
   11522             :                    mToken.IsSymbol(')') ||
   11523             :                    mToken.IsSymbol(';') ||
   11524             :                    mToken.IsSymbol(']') ||
   11525             :                    mToken.IsSymbol('}'));
   11526         258 :         if (!mInSupportsCondition) {
   11527         258 :           MOZ_ASSERT(!propertyValue.IsEmpty());
   11528         258 :           MOZ_ASSERT(propertyValue[propertyValue.Length() - 1] ==
   11529             :                      mToken.mSymbol);
   11530         258 :           propertyValue.Truncate(propertyValue.Length() - 1);
   11531             :         }
   11532             :       }
   11533             : 
   11534         258 :       if (!mInSupportsCondition) {
   11535         258 :         if (nsCSSProps::IsShorthand(aPropID)) {
   11536             :           // If this is a shorthand property, we store the token stream on each
   11537             :           // of its corresponding longhand properties.
   11538         520 :           CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID, EnabledState()) {
   11539         434 :             nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
   11540         434 :             tokenStream->mPropertyID = *p;
   11541         434 :             tokenStream->mShorthandPropertyID = aPropID;
   11542         434 :             tokenStream->mTokenStream = propertyValue;
   11543         434 :             tokenStream->mBaseURI = mBaseURI;
   11544         434 :             tokenStream->mSheetURI = mSheetURI;
   11545         434 :             tokenStream->mSheetPrincipal = mSheetPrincipal;
   11546             :             // XXX Should store sheet here (see bug 952338).
   11547             :             // tokenStream->mSheet = mSheet;
   11548         434 :             tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
   11549         434 :             tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
   11550         434 :             value.SetTokenStreamValue(tokenStream);
   11551         434 :             AppendValue(*p, value);
   11552             :           }
   11553             :         } else {
   11554         172 :           nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
   11555         172 :           tokenStream->mPropertyID = aPropID;
   11556         172 :           tokenStream->mTokenStream = propertyValue;
   11557         172 :           tokenStream->mBaseURI = mBaseURI;
   11558         172 :           tokenStream->mSheetURI = mSheetURI;
   11559         172 :           tokenStream->mSheetPrincipal = mSheetPrincipal;
   11560             :           // XXX Should store sheet here (see bug 952338).
   11561             :           // tokenStream->mSheet = mSheet;
   11562         172 :           tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
   11563         172 :           tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
   11564         172 :           value.SetTokenStreamValue(tokenStream);
   11565         172 :           AppendValue(aPropID, value);
   11566             :         }
   11567             :       }
   11568         258 :       result = true;
   11569             :     } else {
   11570           0 :       if (!mInSupportsCondition) {
   11571           0 :         mScanner->StopRecording();
   11572             :       }
   11573             :     }
   11574             :   }
   11575             : 
   11576        7458 :   if (mNavQuirkMode) {
   11577           0 :     mHashlessColorQuirk = false;
   11578           0 :     mUnitlessLengthQuirk = false;
   11579             :   }
   11580             : 
   11581       14916 :   return result;
   11582             : }
   11583             : 
   11584             : bool
   11585        2594 : CSSParserImpl::ParsePropertyByFunction(nsCSSPropertyID aPropID)
   11586             : {
   11587        2594 :   switch (aPropID) {  // handle shorthand or multiple properties
   11588             :   case eCSSProperty_place_content:
   11589           0 :     return ParsePlaceContent();
   11590             :   case eCSSProperty_place_items:
   11591           0 :     return ParsePlaceItems();
   11592             :   case eCSSProperty_place_self:
   11593           0 :     return ParsePlaceSelf();
   11594             :   case eCSSProperty_background:
   11595          65 :     return ParseImageLayers(nsStyleImageLayers::kBackgroundLayerTable);
   11596             :   case eCSSProperty_background_repeat:
   11597          38 :     return ParseImageLayerRepeat(eCSSProperty_background_repeat);
   11598             :   case eCSSProperty_background_position:
   11599          47 :     return ParseImageLayerPosition(nsStyleImageLayers::kBackgroundLayerTable);
   11600             :   case eCSSProperty_background_position_x:
   11601             :   case eCSSProperty_background_position_y:
   11602           0 :     return ParseImageLayerPositionCoord(aPropID,
   11603           0 :                aPropID == eCSSProperty_background_position_x);
   11604             :   case eCSSProperty_background_size:
   11605          36 :     return ParseImageLayerSize(eCSSProperty_background_size);
   11606             :   case eCSSProperty_border:
   11607         239 :     return ParseBorderSide(kBorderTopIDs, true);
   11608             :   case eCSSProperty_border_color:
   11609          42 :     return ParseBorderColor();
   11610             :   case eCSSProperty_border_spacing:
   11611           2 :     return ParseBorderSpacing();
   11612             :   case eCSSProperty_border_style:
   11613          44 :     return ParseBorderStyle();
   11614             :   case eCSSProperty_border_block_end:
   11615           0 :     return ParseBorderSide(kBorderBlockEndIDs, false);
   11616             :   case eCSSProperty_border_block_start:
   11617           0 :     return ParseBorderSide(kBorderBlockStartIDs, false);
   11618             :   case eCSSProperty_border_bottom:
   11619          26 :     return ParseBorderSide(kBorderBottomIDs, false);
   11620             :   case eCSSProperty_border_inline_end:
   11621           3 :     return ParseBorderSide(kBorderInlineEndIDs, false);
   11622             :   case eCSSProperty_border_inline_start:
   11623           6 :     return ParseBorderSide(kBorderInlineStartIDs, false);
   11624             :   case eCSSProperty_border_left:
   11625          24 :     return ParseBorderSide(kBorderLeftIDs, false);
   11626             :   case eCSSProperty_border_right:
   11627          20 :     return ParseBorderSide(kBorderRightIDs, false);
   11628             :   case eCSSProperty_border_top:
   11629          36 :     return ParseBorderSide(kBorderTopIDs, false);
   11630             :   case eCSSProperty__moz_border_bottom_colors:
   11631             :   case eCSSProperty__moz_border_left_colors:
   11632             :   case eCSSProperty__moz_border_right_colors:
   11633             :   case eCSSProperty__moz_border_top_colors:
   11634          34 :     return ParseBorderColors(aPropID);
   11635             :   case eCSSProperty_border_image_slice:
   11636           4 :     return ParseBorderImageSlice(true, nullptr);
   11637             :   case eCSSProperty_border_image_width:
   11638           0 :     return ParseBorderImageWidth(true);
   11639             :   case eCSSProperty_border_image_outset:
   11640           0 :     return ParseBorderImageOutset(true);
   11641             :   case eCSSProperty_border_image_repeat:
   11642           0 :     return ParseBorderImageRepeat(true);
   11643             :   case eCSSProperty_border_image:
   11644           6 :     return ParseBorderImage();
   11645             :   case eCSSProperty_border_width:
   11646          27 :     return ParseBorderWidth();
   11647             :   case eCSSProperty_border_radius:
   11648         354 :     return ParseBoxCornerRadii(kBorderRadiusIDs);
   11649             :   case eCSSProperty__moz_outline_radius:
   11650           1 :     return ParseBoxCornerRadii(kOutlineRadiusIDs);
   11651             : 
   11652             :   case eCSSProperty_border_top_left_radius:
   11653             :   case eCSSProperty_border_top_right_radius:
   11654             :   case eCSSProperty_border_bottom_right_radius:
   11655             :   case eCSSProperty_border_bottom_left_radius:
   11656             :   case eCSSProperty__moz_outline_radius_topleft:
   11657             :   case eCSSProperty__moz_outline_radius_topright:
   11658             :   case eCSSProperty__moz_outline_radius_bottomright:
   11659             :   case eCSSProperty__moz_outline_radius_bottomleft:
   11660          22 :     return ParseBoxCornerRadius(aPropID);
   11661             : 
   11662             :   case eCSSProperty_box_shadow:
   11663             :   case eCSSProperty_text_shadow:
   11664          69 :     return ParseShadowList(aPropID);
   11665             : 
   11666             :   case eCSSProperty_clip:
   11667           3 :     return ParseRect(eCSSProperty_clip);
   11668             :   case eCSSProperty_columns:
   11669           0 :     return ParseColumns();
   11670             :   case eCSSProperty_column_rule:
   11671           6 :     return ParseBorderSide(kColumnRuleIDs, false);
   11672             :   case eCSSProperty_content:
   11673          27 :     return ParseContent();
   11674             :   case eCSSProperty__moz_context_properties:
   11675          39 :     return ParseContextProperties();
   11676             :   case eCSSProperty_counter_increment:
   11677             :   case eCSSProperty_counter_reset:
   11678           0 :     return ParseCounterData(aPropID);
   11679             :   case eCSSProperty_cursor:
   11680          67 :     return ParseCursor();
   11681             :   case eCSSProperty_filter:
   11682          14 :     return ParseFilter();
   11683             :   case eCSSProperty_flex:
   11684          22 :     return ParseFlex();
   11685             :   case eCSSProperty_flex_flow:
   11686           0 :     return ParseFlexFlow();
   11687             :   case eCSSProperty_font:
   11688          31 :     return ParseFont();
   11689             :   case eCSSProperty_font_variant:
   11690           3 :     return ParseFontVariant();
   11691             :   case eCSSProperty_grid_auto_flow:
   11692           6 :     return ParseGridAutoFlow();
   11693             :   case eCSSProperty_grid_auto_columns:
   11694             :   case eCSSProperty_grid_auto_rows:
   11695          12 :     return ParseGridAutoColumnsRows(aPropID);
   11696             :   case eCSSProperty_grid_template_areas:
   11697           6 :     return ParseGridTemplateAreas();
   11698             :   case eCSSProperty_grid_template_columns:
   11699             :   case eCSSProperty_grid_template_rows:
   11700          12 :     return ParseGridTemplateColumnsRows(aPropID);
   11701             :   case eCSSProperty_grid_template:
   11702           0 :     return ParseGridTemplate();
   11703             :   case eCSSProperty_grid:
   11704           0 :     return ParseGrid();
   11705             :   case eCSSProperty_grid_column_start:
   11706             :   case eCSSProperty_grid_column_end:
   11707             :   case eCSSProperty_grid_row_start:
   11708             :   case eCSSProperty_grid_row_end:
   11709           8 :     return ParseGridColumnRowStartEnd(aPropID);
   11710             :   case eCSSProperty_grid_column:
   11711             :     return ParseGridColumnRow(eCSSProperty_grid_column_start,
   11712           0 :                               eCSSProperty_grid_column_end);
   11713             :   case eCSSProperty_grid_row:
   11714             :     return ParseGridColumnRow(eCSSProperty_grid_row_start,
   11715           0 :                               eCSSProperty_grid_row_end);
   11716             :   case eCSSProperty_grid_area:
   11717           0 :     return ParseGridArea();
   11718             :   case eCSSProperty_grid_gap:
   11719           0 :     return ParseGridGap();
   11720             :   case eCSSProperty__moz_image_region:
   11721          86 :     return ParseRect(eCSSProperty__moz_image_region);
   11722             :   case eCSSProperty_align_content:
   11723             :   case eCSSProperty_justify_content:
   11724          14 :     return ParseAlignJustifyContent(aPropID);
   11725             :   case eCSSProperty_align_items:
   11726          10 :     return ParseAlignItems();
   11727             :   case eCSSProperty_align_self:
   11728             :   case eCSSProperty_justify_self:
   11729           7 :     return ParseAlignJustifySelf(aPropID);
   11730             :   case eCSSProperty_initial_letter:
   11731           0 :     return ParseInitialLetter();
   11732             :   case eCSSProperty_justify_items:
   11733           6 :     return ParseJustifyItems();
   11734             :   case eCSSProperty_list_style:
   11735           2 :     return ParseListStyle();
   11736             :   case eCSSProperty_margin:
   11737         246 :     return ParseMargin();
   11738             :   case eCSSProperty_object_position:
   11739           2 :     return ParseObjectPosition();
   11740             :   case eCSSProperty_outline:
   11741          27 :     return ParseOutline();
   11742             :   case eCSSProperty_overflow:
   11743          71 :     return ParseOverflow();
   11744             :   case eCSSProperty_padding:
   11745         343 :     return ParsePadding();
   11746             :   case eCSSProperty_quotes:
   11747           0 :     return ParseQuotes();
   11748             :   case eCSSProperty_text_decoration:
   11749          16 :     return ParseTextDecoration();
   11750             :   case eCSSProperty_text_emphasis:
   11751           2 :     return ParseTextEmphasis();
   11752             :   case eCSSProperty_will_change:
   11753           1 :     return ParseWillChange();
   11754             :   case eCSSProperty_transform:
   11755             :   case eCSSProperty__moz_window_transform:
   11756          84 :     return ParseTransform(false, aPropID);
   11757             :   case eCSSProperty__moz_transform:
   11758           0 :     return ParseTransform(true, eCSSProperty_transform);
   11759             :   case eCSSProperty_transform_origin:
   11760             :   case eCSSProperty_perspective_origin:
   11761             :   case eCSSProperty__moz_window_transform_origin:
   11762           4 :     return ParseTransformOrigin(aPropID);
   11763             :   case eCSSProperty_transition:
   11764         106 :     return ParseTransition();
   11765             :   case eCSSProperty_animation:
   11766          11 :     return ParseAnimation();
   11767             :   case eCSSProperty_transition_property:
   11768           7 :     return ParseTransitionProperty();
   11769             :   case eCSSProperty_fill:
   11770             :   case eCSSProperty_stroke:
   11771         141 :     return ParsePaint(aPropID);
   11772             :   case eCSSProperty_stroke_dasharray:
   11773           2 :     return ParseDasharray();
   11774             :   case eCSSProperty_marker:
   11775           0 :     return ParseMarker();
   11776             :   case eCSSProperty_paint_order:
   11777           0 :     return ParsePaintOrder();
   11778             :   case eCSSProperty_scroll_snap_type:
   11779           0 :     return ParseScrollSnapType();
   11780             : #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
   11781             :   case eCSSProperty_mask:
   11782           5 :     return ParseImageLayers(nsStyleImageLayers::kMaskLayerTable);
   11783             :   case eCSSProperty_mask_repeat:
   11784           0 :     return ParseImageLayerRepeat(eCSSProperty_mask_repeat);
   11785             :   case eCSSProperty_mask_position:
   11786           0 :     return ParseImageLayerPosition(nsStyleImageLayers::kMaskLayerTable);
   11787             :   case eCSSProperty_mask_position_x:
   11788             :   case eCSSProperty_mask_position_y:
   11789           0 :     return ParseImageLayerPositionCoord(aPropID,
   11790           0 :                aPropID == eCSSProperty_mask_position_x);
   11791             :   case eCSSProperty_mask_size:
   11792           0 :     return ParseImageLayerSize(eCSSProperty_mask_size);
   11793             : #endif
   11794             :   case eCSSProperty__webkit_text_stroke:
   11795           0 :     return ParseWebkitTextStroke();
   11796             :   case eCSSProperty_all:
   11797           0 :     return ParseAll();
   11798             :   default:
   11799           0 :     MOZ_ASSERT(false, "should not be called");
   11800             :     return false;
   11801             :   }
   11802             : }
   11803             : 
   11804             : // Bits used in determining which background position info we have
   11805             : #define BG_CENTER  NS_STYLE_IMAGELAYER_POSITION_CENTER
   11806             : #define BG_TOP     NS_STYLE_IMAGELAYER_POSITION_TOP
   11807             : #define BG_BOTTOM  NS_STYLE_IMAGELAYER_POSITION_BOTTOM
   11808             : #define BG_LEFT    NS_STYLE_IMAGELAYER_POSITION_LEFT
   11809             : #define BG_RIGHT   NS_STYLE_IMAGELAYER_POSITION_RIGHT
   11810             : #define BG_CTB    (BG_CENTER | BG_TOP | BG_BOTTOM)
   11811             : #define BG_TB     (BG_TOP | BG_BOTTOM)
   11812             : #define BG_CLR    (BG_CENTER | BG_LEFT | BG_RIGHT)
   11813             : #define BG_LR     (BG_LEFT | BG_RIGHT)
   11814             : 
   11815             : CSSParseResult
   11816        1722 : CSSParserImpl::ParseBoxProperty(nsCSSValue& aValue,
   11817             :                                 nsCSSPropertyID aPropID)
   11818             : {
   11819        1722 :   if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
   11820           0 :     MOZ_ASSERT(false, "must only be called for longhand properties");
   11821             :     return CSSParseResult::NotFound;
   11822             :   }
   11823             : 
   11824        1722 :   MOZ_ASSERT(!nsCSSProps::PropHasFlags(aPropID,
   11825             :                                        CSS_PROPERTY_VALUE_PARSER_FUNCTION),
   11826             :              "must only be called for non-function-parsed properties");
   11827             : 
   11828        1722 :   uint32_t variant = nsCSSProps::ParserVariant(aPropID);
   11829        1722 :   if (variant == 0) {
   11830           0 :     MOZ_ASSERT(false, "must only be called for variant-parsed properties");
   11831             :     return CSSParseResult::NotFound;
   11832             :   }
   11833             : 
   11834        1722 :   if (variant & ~(VARIANT_AHKLP | VARIANT_COLOR | VARIANT_CALC)) {
   11835           0 :     MOZ_ASSERT(false, "must only be called for properties that take certain "
   11836             :                       "variants");
   11837             :     return CSSParseResult::NotFound;
   11838             :   }
   11839             : 
   11840        1722 :   const KTableEntry* kwtable = nsCSSProps::kKeywordTableTable[aPropID];
   11841        1722 :   uint32_t restrictions = nsCSSProps::ValueRestrictions(aPropID);
   11842             : 
   11843        1722 :   return ParseVariantWithRestrictions(aValue, variant, kwtable, restrictions);
   11844             : }
   11845             : 
   11846             : bool
   11847         186 : CSSParserImpl::ParseSingleValuePropertyByFunction(nsCSSValue& aValue,
   11848             :                                                   nsCSSPropertyID aPropID)
   11849             : {
   11850         186 :   switch (aPropID) {
   11851             :     case eCSSProperty_clip_path:
   11852           8 :       return ParseClipPath(aValue);
   11853             :     case eCSSProperty_contain:
   11854           0 :       return ParseContain(aValue);
   11855             :     case eCSSProperty_font_family:
   11856          13 :       return ParseFamily(aValue);
   11857             :     case eCSSProperty_font_synthesis:
   11858           0 :       return ParseFontSynthesis(aValue);
   11859             :     case eCSSProperty_font_variant_alternates:
   11860           0 :       return ParseFontVariantAlternates(aValue);
   11861             :     case eCSSProperty_font_variant_east_asian:
   11862           2 :       return ParseFontVariantEastAsian(aValue);
   11863             :     case eCSSProperty_font_variant_ligatures:
   11864           0 :       return ParseFontVariantLigatures(aValue);
   11865             :     case eCSSProperty_font_variant_numeric:
   11866           2 :       return ParseFontVariantNumeric(aValue);
   11867             :     case eCSSProperty_font_feature_settings:
   11868           0 :       return ParseFontFeatureSettings(aValue);
   11869             :     case eCSSProperty_font_variation_settings:
   11870           0 :       return ParseFontVariationSettings(aValue);
   11871             :     case eCSSProperty_font_weight:
   11872          54 :       return ParseFontWeight(aValue);
   11873             :     case eCSSProperty_image_orientation:
   11874           0 :       return ParseImageOrientation(aValue);
   11875             :     case eCSSProperty_list_style_type:
   11876          12 :       return ParseListStyleType(aValue);
   11877             :     case eCSSProperty_scroll_snap_points_x:
   11878           0 :       return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_x);
   11879             :     case eCSSProperty_scroll_snap_points_y:
   11880           0 :       return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_y);
   11881             :     case eCSSProperty_scroll_snap_destination:
   11882           0 :       return ParseScrollSnapDestination(aValue);
   11883             :     case eCSSProperty_scroll_snap_coordinate:
   11884           0 :       return ParseScrollSnapCoordinate(aValue);
   11885             :     case eCSSProperty_shape_outside:
   11886           0 :       return ParseShapeOutside(aValue);
   11887             :     case eCSSProperty_text_align:
   11888          57 :       return ParseTextAlign(aValue);
   11889             :     case eCSSProperty_text_align_last:
   11890           0 :       return ParseTextAlignLast(aValue);
   11891             :     case eCSSProperty_text_decoration_line:
   11892          18 :       return ParseTextDecorationLine(aValue);
   11893             :     case eCSSProperty_text_combine_upright:
   11894           0 :       return ParseTextCombineUpright(aValue);
   11895             :     case eCSSProperty_text_emphasis_position:
   11896           0 :       return ParseTextEmphasisPosition(aValue);
   11897             :     case eCSSProperty_text_emphasis_style:
   11898           2 :       return ParseTextEmphasisStyle(aValue);
   11899             :     case eCSSProperty_text_overflow:
   11900          18 :       return ParseTextOverflow(aValue);
   11901             :     case eCSSProperty_touch_action:
   11902           0 :       return ParseTouchAction(aValue);
   11903             :     default:
   11904           0 :       MOZ_ASSERT(false, "should not reach here");
   11905             :       return false;
   11906             :   }
   11907             : }
   11908             : 
   11909             : CSSParseResult
   11910        7306 : CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
   11911             :                                         nsCSSPropertyID aPropID)
   11912             : {
   11913        7306 :   if (aPropID == eCSSPropertyExtra_x_none_value) {
   11914           6 :     return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nullptr);
   11915             :   }
   11916             : 
   11917        7300 :   if (aPropID == eCSSPropertyExtra_x_auto_value) {
   11918           0 :     return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nullptr);
   11919             :   }
   11920             : 
   11921        7300 :   if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
   11922           0 :     MOZ_ASSERT(false, "not a single value property");
   11923             :     return CSSParseResult::NotFound;
   11924             :   }
   11925             : 
   11926        7300 :   if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) {
   11927             :     uint32_t lineBefore, colBefore;
   11928         186 :     if (!GetNextTokenLocation(true, &lineBefore, &colBefore)) {
   11929             :       // We're at EOF before parsing.
   11930           0 :       return CSSParseResult::NotFound;
   11931             :     }
   11932             : 
   11933         186 :     if (ParseSingleValuePropertyByFunction(aValue, aPropID)) {
   11934         179 :       return CSSParseResult::Ok;
   11935             :     }
   11936             : 
   11937             :     uint32_t lineAfter, colAfter;
   11938          21 :     if (!GetNextTokenLocation(true, &lineAfter, &colAfter) ||
   11939          14 :         lineAfter != lineBefore ||
   11940           7 :         colAfter != colBefore) {
   11941             :       // Any single token value that was invalid will have been pushed back,
   11942             :       // so GetNextTokenLocation encountering EOF means we failed while
   11943             :       // parsing a multi-token value.
   11944           0 :       return CSSParseResult::Error;
   11945             :     }
   11946             : 
   11947           7 :     return CSSParseResult::NotFound;
   11948             :   }
   11949             : 
   11950        7114 :   uint32_t variant = nsCSSProps::ParserVariant(aPropID);
   11951        7114 :   if (variant == 0) {
   11952           0 :     MOZ_ASSERT(false, "not a single value property");
   11953             :     return CSSParseResult::NotFound;
   11954             :   }
   11955             : 
   11956        7114 :   const KTableEntry* kwtable = nsCSSProps::kKeywordTableTable[aPropID];
   11957        7114 :   uint32_t restrictions = nsCSSProps::ValueRestrictions(aPropID);
   11958        7114 :   return ParseVariantWithRestrictions(aValue, variant, kwtable, restrictions);
   11959             : }
   11960             : 
   11961             : // font-descriptor: descriptor ':' value ';'
   11962             : // caller has advanced mToken to point at the descriptor
   11963             : bool
   11964           0 : CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
   11965             :                                         nsCSSValue& aValue)
   11966             : {
   11967           0 :   switch (aDescID) {
   11968             :     // These four are similar to the properties of the same name,
   11969             :     // possibly with more restrictions on the values they can take.
   11970             :   case eCSSFontDesc_Family: {
   11971           0 :     nsCSSValue value;
   11972           0 :     if (!ParseFamily(value) ||
   11973           0 :         value.GetUnit() != eCSSUnit_FontFamilyList)
   11974           0 :       return false;
   11975             : 
   11976             :     // name can only be a single, non-generic name
   11977           0 :     const FontFamilyList* f = value.GetFontFamilyListValue();
   11978           0 :     const nsTArray<FontFamilyName>& fontlist = f->GetFontlist();
   11979             : 
   11980           0 :     if (fontlist.Length() != 1 || !fontlist[0].IsNamed()) {
   11981           0 :       return false;
   11982             :     }
   11983             : 
   11984           0 :     aValue.SetStringValue(fontlist[0].mName, eCSSUnit_String);
   11985           0 :     return true;
   11986             :   }
   11987             : 
   11988             :   case eCSSFontDesc_Style:
   11989             :     // property is VARIANT_HMK|VARIANT_SYSFONT
   11990             :     return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
   11991           0 :                                    nsCSSProps::kFontStyleKTable);
   11992             : 
   11993             :   case eCSSFontDesc_Display:
   11994             :     return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD,
   11995           0 :                                    nsCSSProps::kFontDisplayKTable);
   11996             : 
   11997             :   case eCSSFontDesc_Weight:
   11998           0 :     return (ParseFontWeight(aValue) &&
   11999           0 :             aValue.GetUnit() != eCSSUnit_Inherit &&
   12000           0 :             aValue.GetUnit() != eCSSUnit_Initial &&
   12001           0 :             aValue.GetUnit() != eCSSUnit_Unset &&
   12002           0 :             (aValue.GetUnit() != eCSSUnit_Enumerated ||
   12003           0 :              (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
   12004           0 :               aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
   12005             : 
   12006             :   case eCSSFontDesc_Stretch:
   12007             :     // property is VARIANT_HK|VARIANT_SYSFONT
   12008             :     return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD,
   12009           0 :                                    nsCSSProps::kFontStretchKTable);
   12010             : 
   12011             :     // These two are unique to @font-face and have their own special grammar.
   12012             :   case eCSSFontDesc_Src:
   12013           0 :     return ParseFontSrc(aValue);
   12014             : 
   12015             :   case eCSSFontDesc_UnicodeRange:
   12016           0 :     return ParseFontRanges(aValue);
   12017             : 
   12018             :   case eCSSFontDesc_FontFeatureSettings:
   12019           0 :     return ParseFontFeatureSettings(aValue);
   12020             : 
   12021             :   case eCSSFontDesc_FontLanguageOverride:
   12022             :     return ParseSingleTokenVariant(aValue, VARIANT_NORMAL | VARIANT_STRING,
   12023           0 :                                    nullptr);
   12024             : 
   12025             :   case eCSSFontDesc_UNKNOWN:
   12026             :   case eCSSFontDesc_COUNT:
   12027           0 :     NS_NOTREACHED("bad nsCSSFontDesc code");
   12028             :   }
   12029             :   // explicitly do NOT have a default case to let the compiler
   12030             :   // help find missing descriptors
   12031           0 :   return false;
   12032             : }
   12033             : 
   12034             : static nsCSSValue
   12035           8 : BoxPositionMaskToCSSValue(int32_t aMask, bool isX)
   12036             : {
   12037           8 :   int32_t val = NS_STYLE_IMAGELAYER_POSITION_CENTER;
   12038           8 :   if (isX) {
   12039           4 :     if (aMask & BG_LEFT) {
   12040           2 :       val = NS_STYLE_IMAGELAYER_POSITION_LEFT;
   12041             :     }
   12042           2 :     else if (aMask & BG_RIGHT) {
   12043           2 :       val = NS_STYLE_IMAGELAYER_POSITION_RIGHT;
   12044             :     }
   12045             :   }
   12046             :   else {
   12047           4 :     if (aMask & BG_TOP) {
   12048           0 :       val = NS_STYLE_IMAGELAYER_POSITION_TOP;
   12049             :     }
   12050           4 :     else if (aMask & BG_BOTTOM) {
   12051           0 :       val = NS_STYLE_IMAGELAYER_POSITION_BOTTOM;
   12052             :     }
   12053             :   }
   12054             : 
   12055           8 :   return nsCSSValue(val, eCSSUnit_Enumerated);
   12056             : }
   12057             : 
   12058             : bool
   12059          70 : CSSParserImpl::ParseImageLayers(const nsCSSPropertyID aTable[])
   12060             : {
   12061         140 :   nsAutoParseCompoundProperty compound(this);
   12062             : 
   12063             :   // background-color can only be set once, so it's not a list.
   12064         140 :   nsCSSValue color;
   12065             : 
   12066             :   // Check first for inherit/initial/unset.
   12067          70 :   if (ParseSingleTokenVariant(color, VARIANT_INHERIT, nullptr)) {
   12068             :     // must be alone
   12069          36 :     for (const nsCSSPropertyID* subprops =
   12070           4 :            nsCSSProps::SubpropertyEntryFor(aTable[nsStyleImageLayers::shorthand]);
   12071          40 :          *subprops != eCSSProperty_UNKNOWN; ++subprops) {
   12072          36 :       AppendValue(*subprops, color);
   12073             :     }
   12074           4 :     return true;
   12075             :   }
   12076             : 
   12077         132 :   nsCSSValue image, repeat, attachment, clip, origin, positionX, positionY, size,
   12078         132 :              composite, maskMode;
   12079             :   ImageLayersShorthandParseState state(color, image.SetListValue(),
   12080             :                                        repeat.SetPairListValue(),
   12081             :                                        attachment.SetListValue(), clip.SetListValue(),
   12082             :                                        origin.SetListValue(),
   12083             :                                        positionX.SetListValue(), positionY.SetListValue(),
   12084             :                                        size.SetPairListValue(), composite.SetListValue(),
   12085          66 :                                        maskMode.SetListValue());
   12086             : 
   12087             :   for (;;) {
   12088          66 :     if (!ParseImageLayersItem(state, aTable)) {
   12089           9 :       return false;
   12090             :     }
   12091             : 
   12092             :     // If we saw a color, this must be the last item.
   12093          57 :     if (color.GetUnit() != eCSSUnit_Null) {
   12094          34 :       MOZ_ASSERT(aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN);
   12095          34 :       break;
   12096             :     }
   12097             : 
   12098             :     // If there's a comma, expect another item.
   12099          23 :     if (!ExpectSymbol(',', true)) {
   12100          23 :       break;
   12101             :     }
   12102             : 
   12103             : #define APPENDNEXT(propID_, propMember_, propType_) \
   12104             :   if (aTable[propID_] != eCSSProperty_UNKNOWN) { \
   12105             :     propMember_->mNext = new propType_; \
   12106             :     propMember_ = propMember_->mNext; \
   12107             :   }
   12108             :     // Chain another entry on all the lists.
   12109           0 :     APPENDNEXT(nsStyleImageLayers::image, state.mImage,
   12110             :                nsCSSValueList);
   12111           0 :     APPENDNEXT(nsStyleImageLayers::repeat, state.mRepeat,
   12112             :                nsCSSValuePairList);
   12113           0 :     APPENDNEXT(nsStyleImageLayers::clip, state.mClip,
   12114             :                nsCSSValueList);
   12115           0 :     APPENDNEXT(nsStyleImageLayers::origin, state.mOrigin,
   12116             :                nsCSSValueList);
   12117           0 :     APPENDNEXT(nsStyleImageLayers::positionX, state.mPositionX,
   12118             :                nsCSSValueList);
   12119           0 :     APPENDNEXT(nsStyleImageLayers::positionY, state.mPositionY,
   12120             :                nsCSSValueList);
   12121           0 :     APPENDNEXT(nsStyleImageLayers::size, state.mSize,
   12122             :                nsCSSValuePairList);
   12123           0 :     APPENDNEXT(nsStyleImageLayers::attachment, state.mAttachment,
   12124             :                nsCSSValueList);
   12125           0 :     APPENDNEXT(nsStyleImageLayers::maskMode, state.mMode,
   12126             :                nsCSSValueList);
   12127           0 :     APPENDNEXT(nsStyleImageLayers::composite, state.mComposite,
   12128             :                nsCSSValueList);
   12129             : #undef APPENDNEXT
   12130             :   }
   12131             : 
   12132             :   // If we get to this point without seeing a color, provide a default.
   12133          57 :  if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
   12134          56 :     if (color.GetUnit() == eCSSUnit_Null) {
   12135          22 :       color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor);
   12136             :     }
   12137             :   }
   12138             : 
   12139             : #define APPENDVALUE(propID_, propValue_) \
   12140             :   if (propID_ != eCSSProperty_UNKNOWN) { \
   12141             :     AppendValue(propID_,  propValue_); \
   12142             :   }
   12143             : 
   12144          57 :   APPENDVALUE(aTable[nsStyleImageLayers::image],      image);
   12145          57 :   APPENDVALUE(aTable[nsStyleImageLayers::repeat],     repeat);
   12146          57 :   APPENDVALUE(aTable[nsStyleImageLayers::clip],       clip);
   12147          57 :   APPENDVALUE(aTable[nsStyleImageLayers::origin],     origin);
   12148          57 :   APPENDVALUE(aTable[nsStyleImageLayers::positionX],  positionX);
   12149          57 :   APPENDVALUE(aTable[nsStyleImageLayers::positionY],  positionY);
   12150          57 :   APPENDVALUE(aTable[nsStyleImageLayers::size],       size);
   12151          57 :   APPENDVALUE(aTable[nsStyleImageLayers::color],      color);
   12152          57 :   APPENDVALUE(aTable[nsStyleImageLayers::attachment], attachment);
   12153          57 :   APPENDVALUE(aTable[nsStyleImageLayers::maskMode],   maskMode);
   12154          57 :   APPENDVALUE(aTable[nsStyleImageLayers::composite],  composite);
   12155             : 
   12156             : #undef APPENDVALUE
   12157             : 
   12158          57 :   return true;
   12159             : }
   12160             : 
   12161             : // Helper for ParseImageLayersItem. Returns true if the passed-in nsCSSToken is
   12162             : // a function which is accepted for background-image.
   12163             : bool
   12164          25 : CSSParserImpl::IsFunctionTokenValidForImageLayerImage(
   12165             :   const nsCSSToken& aToken) const
   12166             : {
   12167          25 :   MOZ_ASSERT(aToken.mType == eCSSToken_Function,
   12168             :              "Should only be called for function-typed tokens");
   12169             : 
   12170          25 :   const nsAString& funcName = aToken.mIdent;
   12171             : 
   12172          37 :   return funcName.LowerCaseEqualsLiteral("linear-gradient") ||
   12173          24 :     funcName.LowerCaseEqualsLiteral("radial-gradient") ||
   12174          23 :     funcName.LowerCaseEqualsLiteral("repeating-linear-gradient") ||
   12175          22 :     funcName.LowerCaseEqualsLiteral("repeating-radial-gradient") ||
   12176          22 :     funcName.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
   12177          22 :     funcName.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
   12178          22 :     funcName.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
   12179          22 :     funcName.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
   12180          22 :     funcName.LowerCaseEqualsLiteral("-moz-image-rect") ||
   12181          61 :     funcName.LowerCaseEqualsLiteral("-moz-element") ||
   12182          11 :     (sWebkitPrefixedAliasesEnabled &&
   12183          22 :      (funcName.LowerCaseEqualsLiteral("-webkit-gradient") ||
   12184          22 :       funcName.LowerCaseEqualsLiteral("-webkit-linear-gradient") ||
   12185          22 :       funcName.LowerCaseEqualsLiteral("-webkit-radial-gradient") ||
   12186          22 :       funcName.LowerCaseEqualsLiteral("-webkit-repeating-linear-gradient") ||
   12187          36 :       funcName.LowerCaseEqualsLiteral("-webkit-repeating-radial-gradient")));
   12188             : }
   12189             : 
   12190             : // Parse one item of the background shorthand property.
   12191             : bool
   12192          66 : CSSParserImpl::ParseImageLayersItem(
   12193             :   CSSParserImpl::ImageLayersShorthandParseState& aState,
   12194             :   const nsCSSPropertyID aTable[])
   12195             : {
   12196             :   // Fill in the values that the shorthand will set if we don't find
   12197             :   // other values.
   12198          66 :   aState.mImage->mValue.SetNoneValue();
   12199          66 :   aState.mAttachment->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL,
   12200          66 :                                          eCSSUnit_Enumerated);
   12201          66 :   aState.mClip->mValue.SetEnumValue(StyleGeometryBox::BorderBox);
   12202             : 
   12203          66 :   aState.mRepeat->mXValue.SetIntValue(uint8_t(StyleImageLayerRepeat::Repeat),
   12204          66 :                                       eCSSUnit_Enumerated);
   12205          66 :   aState.mRepeat->mYValue.Reset();
   12206             : 
   12207         132 :   RefPtr<nsCSSValue::Array> positionXArr = nsCSSValue::Array::Create(2);
   12208         132 :   RefPtr<nsCSSValue::Array> positionYArr = nsCSSValue::Array::Create(2);
   12209          66 :   aState.mPositionX->mValue.SetArrayValue(positionXArr, eCSSUnit_Array);
   12210          66 :   aState.mPositionY->mValue.SetArrayValue(positionYArr, eCSSUnit_Array);
   12211             : 
   12212          66 :   if (eCSSProperty_mask == aTable[nsStyleImageLayers::shorthand]) {
   12213           1 :     aState.mOrigin->mValue.SetEnumValue(StyleGeometryBox::BorderBox);
   12214             :   } else {
   12215          65 :     aState.mOrigin->mValue.SetEnumValue(StyleGeometryBox::PaddingBox);
   12216             :   }
   12217          66 :   positionXArr->Item(1).SetPercentValue(0.0f);
   12218          66 :   positionYArr->Item(1).SetPercentValue(0.0f);
   12219             : 
   12220          66 :   aState.mSize->mXValue.SetAutoValue();
   12221          66 :   aState.mSize->mYValue.SetAutoValue();
   12222          66 :   aState.mComposite->mValue.SetIntValue(NS_STYLE_MASK_COMPOSITE_ADD,
   12223          66 :                                         eCSSUnit_Enumerated);
   12224          66 :   aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_MATCH_SOURCE,
   12225          66 :                                    eCSSUnit_Enumerated);
   12226          66 :   bool haveColor = false,
   12227          66 :        haveImage = false,
   12228          66 :        haveRepeat = false,
   12229          66 :        haveAttach = false,
   12230          66 :        havePositionAndSize = false,
   12231          66 :        haveOrigin = false,
   12232          66 :        haveClip = false,
   12233          66 :        haveComposite = false,
   12234          66 :        haveMode = false,
   12235          66 :        haveSomething = false;
   12236             : 
   12237             :   const KTableEntry* originTable =
   12238          66 :     nsCSSProps::kKeywordTableTable[aTable[nsStyleImageLayers::origin]];
   12239             :   const KTableEntry* clipTable =
   12240          66 :     nsCSSProps::kKeywordTableTable[aTable[nsStyleImageLayers::clip]];
   12241             : 
   12242         270 :   while (GetToken(true)) {
   12243         167 :     nsCSSTokenType tt = mToken.mType;
   12244         167 :     UngetToken(); // ...but we'll still cheat and use mToken
   12245         167 :     if (tt == eCSSToken_Symbol) {
   12246             :       // ExpectEndProperty only looks for symbols, and nothing else will
   12247             :       // show up as one.
   12248          56 :       break;
   12249             :     }
   12250             : 
   12251         111 :     if (tt == eCSSToken_Ident) {
   12252          57 :       nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
   12253             :       int32_t dummy;
   12254          57 :       if (keyword == eCSSKeyword_inherit ||
   12255          57 :           keyword == eCSSKeyword_initial ||
   12256             :           keyword == eCSSKeyword_unset) {
   12257           0 :         return false;
   12258          57 :       } else if (!haveImage && keyword == eCSSKeyword_none) {
   12259           4 :         haveImage = true;
   12260           8 :         if (ParseSingleValueProperty(aState.mImage->mValue,
   12261           4 :                                      aTable[nsStyleImageLayers::image]) !=
   12262             :             CSSParseResult::Ok) {
   12263           0 :           NS_NOTREACHED("should be able to parse");
   12264           0 :           return false;
   12265             :         }
   12266         159 :       } else if (!haveAttach &&
   12267          53 :                  aTable[nsStyleImageLayers::attachment] !=
   12268         106 :                    eCSSProperty_UNKNOWN &&
   12269          53 :                  nsCSSProps::FindKeyword(
   12270             :                    keyword, nsCSSProps::kImageLayerAttachmentKTable, dummy)) {
   12271           2 :         haveAttach = true;
   12272           2 :         if (ParseSingleValueProperty(aState.mAttachment->mValue,
   12273           2 :                                      aTable[nsStyleImageLayers::attachment]) !=
   12274             :             CSSParseResult::Ok) {
   12275           0 :           NS_NOTREACHED("should be able to parse");
   12276           0 :           return false;
   12277             :         }
   12278          93 :       } else if (!haveRepeat &&
   12279          42 :                  nsCSSProps::FindKeyword(
   12280             :                    keyword, nsCSSProps::kImageLayerRepeatKTable, dummy)) {
   12281          17 :         haveRepeat = true;
   12282          34 :         nsCSSValuePair scratch;
   12283          17 :         if (!ParseImageLayerRepeatValues(scratch)) {
   12284           0 :           NS_NOTREACHED("should be able to parse");
   12285           0 :           return false;
   12286             :         }
   12287          17 :         aState.mRepeat->mXValue = scratch.mXValue;
   12288          17 :         aState.mRepeat->mYValue = scratch.mYValue;
   12289          66 :       } else if (!havePositionAndSize &&
   12290          32 :                  nsCSSProps::FindKeyword(keyword,
   12291             :                    nsCSSProps::kImageLayerPositionKTable, dummy)) {
   12292          14 :         havePositionAndSize = true;
   12293             : 
   12294          14 :         if (!ParsePositionValueSeparateCoords(aState.mPositionX->mValue,
   12295          14 :                                               aState.mPositionY->mValue)) {
   12296           0 :           return false;
   12297             :         }
   12298          14 :         if (ExpectSymbol('/', true)) {
   12299           2 :           nsCSSValuePair scratch;
   12300           1 :           if (!ParseImageLayerSizeValues(scratch)) {
   12301           0 :             return false;
   12302             :           }
   12303           1 :           aState.mSize->mXValue = scratch.mXValue;
   12304           1 :           aState.mSize->mYValue = scratch.mYValue;
   12305             :         }
   12306          40 :       } else if (!haveOrigin &&
   12307          20 :                  nsCSSProps::FindKeyword(keyword, originTable, dummy)) {
   12308           0 :         haveOrigin = true;
   12309           0 :         if (ParseSingleValueProperty(aState.mOrigin->mValue,
   12310           0 :                                      aTable[nsStyleImageLayers::origin]) !=
   12311             :             CSSParseResult::Ok) {
   12312           0 :           NS_NOTREACHED("should be able to parse");
   12313           0 :           return false;
   12314             :         }
   12315             :         // Set clip value to origin if clip is not set yet.
   12316             :         // Note that we don't set haveClip here so that it can be
   12317             :         // overridden if we see it later.
   12318           0 :         if (!haveClip) {
   12319             : #ifdef DEBUG
   12320           0 :           for (size_t i = 0; originTable[i].mValue != -1; i++) {
   12321             :             // For each keyword & value in kOriginKTable, ensure that
   12322             :             // kBackgroundKTable has a matching entry at the same position.
   12323           0 :             MOZ_ASSERT(originTable[i].mKeyword == clipTable[i].mKeyword);
   12324           0 :             MOZ_ASSERT(originTable[i].mValue == clipTable[i].mValue);
   12325             :           }
   12326             : #endif
   12327           0 :           aState.mClip->mValue = aState.mOrigin->mValue;
   12328             :         }
   12329          40 :       } else if (!haveClip &&
   12330          20 :                  nsCSSProps::FindKeyword(keyword, clipTable, dummy)) {
   12331             :         // It is important that we try parsing clip later than origin
   12332             :         // because if there are two <box> / <geometry-box> values, the
   12333             :         // first should be origin, and the second should be clip.
   12334           0 :         haveClip = true;
   12335           0 :         if (ParseSingleValueProperty(aState.mClip->mValue,
   12336           0 :                                      aTable[nsStyleImageLayers::clip]) !=
   12337             :             CSSParseResult::Ok) {
   12338           0 :           NS_NOTREACHED("should be able to parse");
   12339           0 :           return false;
   12340             :         }
   12341          60 :       } else if (!haveComposite &&
   12342          20 :                  aTable[nsStyleImageLayers::composite] !=
   12343          20 :                    eCSSProperty_UNKNOWN &&
   12344           0 :                  nsCSSProps::FindKeyword(
   12345             :                    keyword, nsCSSProps::kImageLayerCompositeKTable, dummy)) {
   12346           0 :         haveComposite = true;
   12347           0 :         if (ParseSingleValueProperty(aState.mComposite->mValue,
   12348           0 :                                      aTable[nsStyleImageLayers::composite]) !=
   12349             :             CSSParseResult::Ok) {
   12350           0 :           NS_NOTREACHED("should be able to parse");
   12351           0 :           return false;
   12352             :         }
   12353          60 :       } else if (!haveMode &&
   12354          20 :                  aTable[nsStyleImageLayers::maskMode] != eCSSProperty_UNKNOWN &&
   12355           0 :                  nsCSSProps::FindKeyword(
   12356             :                    keyword, nsCSSProps::kImageLayerModeKTable, dummy)) {
   12357           0 :         haveMode = true;
   12358           0 :         if (ParseSingleValueProperty(aState.mMode->mValue,
   12359           0 :                                      aTable[nsStyleImageLayers::maskMode]) !=
   12360             :             CSSParseResult::Ok) {
   12361           0 :           NS_NOTREACHED("should be able to parse");
   12362           0 :           return false;
   12363             :         }
   12364          40 :       } else if (!haveColor &&
   12365          20 :                  aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
   12366          20 :         haveColor = true;
   12367          40 :         if (ParseSingleValueProperty(aState.mColor,
   12368          20 :                                      aTable[nsStyleImageLayers::color]) !=
   12369             :                                        CSSParseResult::Ok) {
   12370           0 :           return false;
   12371             :         }
   12372             :       } else {
   12373           0 :         return false;
   12374             :       }
   12375          85 :     } else if (tt == eCSSToken_URL ||
   12376          25 :                (tt == eCSSToken_Function &&
   12377          25 :                 IsFunctionTokenValidForImageLayerImage(mToken))) {
   12378          31 :       if (haveImage)
   12379           0 :         return false;
   12380          31 :       haveImage = true;
   12381          31 :       if (ParseSingleValueProperty(aState.mImage->mValue,
   12382          31 :                                    aTable[nsStyleImageLayers::image]) !=
   12383             :           CSSParseResult::Ok) {
   12384           3 :         return false;
   12385             :       }
   12386          45 :     } else if (tt == eCSSToken_Dimension ||
   12387          20 :                tt == eCSSToken_Number ||
   12388          46 :                tt == eCSSToken_Percentage ||
   12389          11 :                (tt == eCSSToken_Function &&
   12390          11 :                 mToken.mIdent.LowerCaseEqualsLiteral("calc"))) {
   12391           3 :       if (havePositionAndSize)
   12392           0 :         return false;
   12393           3 :       havePositionAndSize = true;
   12394           3 :       if (!ParsePositionValueSeparateCoords(aState.mPositionX->mValue,
   12395           3 :                                             aState.mPositionY->mValue)) {
   12396           0 :         return false;
   12397             :       }
   12398           3 :       if (ExpectSymbol('/', true)) {
   12399           0 :         nsCSSValuePair scratch;
   12400           0 :         if (!ParseImageLayerSizeValues(scratch)) {
   12401           0 :           return false;
   12402             :         }
   12403           0 :         aState.mSize->mXValue = scratch.mXValue;
   12404           0 :         aState.mSize->mYValue = scratch.mYValue;
   12405             :       }
   12406          20 :     } else if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
   12407          20 :       if (haveColor)
   12408           0 :         return false;
   12409          20 :       haveColor = true;
   12410             :       // Note: This parses 'inherit', 'initial' and 'unset', but
   12411             :       // we've already checked for them, so it's ok.
   12412          20 :       if (ParseSingleValueProperty(aState.mColor,
   12413          20 :                                    aTable[nsStyleImageLayers::color]) !=
   12414             :                                      CSSParseResult::Ok) {
   12415           6 :         return false;
   12416             :       }
   12417             :     } else {
   12418           0 :       return false;
   12419             :     }
   12420             : 
   12421         102 :     haveSomething = true;
   12422             :   }
   12423             : 
   12424          57 :   return haveSomething;
   12425             : }
   12426             : 
   12427             : // This function is very similar to ParseScrollSnapCoordinate,
   12428             : // ParseImageLayerPosition, and ParseImageLayersSize.
   12429             : bool
   12430         242 : CSSParserImpl::ParseValueList(nsCSSPropertyID aPropID)
   12431             : {
   12432             :   // aPropID is a single value prop-id
   12433         484 :   nsCSSValue value;
   12434             :   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
   12435         242 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   12436         242 :     nsCSSValueList* item = value.SetListValue();
   12437             :     for (;;) {
   12438         308 :       if (ParseSingleValueProperty(item->mValue, aPropID) !=
   12439             :           CSSParseResult::Ok) {
   12440           7 :         return false;
   12441             :       }
   12442         268 :       if (!ExpectSymbol(',', true)) {
   12443         235 :         break;
   12444             :       }
   12445          33 :       item->mNext = new nsCSSValueList;
   12446          33 :       item = item->mNext;
   12447             :     }
   12448             :   }
   12449         235 :   AppendValue(aPropID, value);
   12450         235 :   return true;
   12451             : }
   12452             : 
   12453             : bool
   12454          38 : CSSParserImpl::ParseImageLayerRepeat(nsCSSPropertyID aPropID)
   12455             : {
   12456          76 :   nsCSSValue value;
   12457             :   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
   12458          38 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   12459          75 :     nsCSSValuePair valuePair;
   12460          38 :     if (!ParseImageLayerRepeatValues(valuePair)) {
   12461           1 :       return false;
   12462             :     }
   12463          37 :     nsCSSValuePairList* item = value.SetPairListValue();
   12464             :     for (;;) {
   12465          45 :       item->mXValue = valuePair.mXValue;
   12466          41 :       item->mYValue = valuePair.mYValue;
   12467          41 :       if (!ExpectSymbol(',', true)) {
   12468          37 :         break;
   12469             :       }
   12470           4 :       if (!ParseImageLayerRepeatValues(valuePair)) {
   12471           0 :         return false;
   12472             :       }
   12473           4 :       item->mNext = new nsCSSValuePairList;
   12474           4 :       item = item->mNext;
   12475             :     }
   12476             :   }
   12477             : 
   12478          37 :   AppendValue(aPropID, value);
   12479          37 :   return true;
   12480             : }
   12481             : 
   12482             : bool
   12483          59 : CSSParserImpl::ParseImageLayerRepeatValues(nsCSSValuePair& aValue)
   12484             : {
   12485          59 :   nsCSSValue& xValue = aValue.mXValue;
   12486          59 :   nsCSSValue& yValue = aValue.mYValue;
   12487             : 
   12488          59 :   if (ParseEnum(xValue, nsCSSProps::kImageLayerRepeatKTable)) {
   12489          58 :     int32_t value = xValue.GetIntValue();
   12490             :     // For single values set yValue as eCSSUnit_Null.
   12491         112 :     if (value == uint8_t(StyleImageLayerRepeat::RepeatX) ||
   12492         112 :         value == uint8_t(StyleImageLayerRepeat::RepeatY) ||
   12493          54 :         !ParseEnum(yValue, nsCSSProps::kImageLayerRepeatPartKTable)) {
   12494             :       // the caller will fail cases like "repeat-x no-repeat"
   12495             :       // by expecting a list separator or an end property.
   12496          58 :       yValue.Reset();
   12497             :     }
   12498          58 :     return true;
   12499             :   }
   12500             : 
   12501           1 :   return false;
   12502             : }
   12503             : 
   12504             : bool
   12505          47 : CSSParserImpl::ParseImageLayerPosition(const nsCSSPropertyID aTable[])
   12506             : {
   12507             :   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
   12508          94 :   nsCSSValue position;
   12509          47 :   if (ParseSingleTokenVariant(position, VARIANT_INHERIT, nullptr)) {
   12510           0 :     AppendValue(aTable[nsStyleImageLayers::positionX], position);
   12511           0 :     AppendValue(aTable[nsStyleImageLayers::positionY], position);
   12512           0 :     return true;
   12513             :   }
   12514             : 
   12515          94 :   nsCSSValue itemValueX;
   12516          94 :   nsCSSValue itemValueY;
   12517          47 :   if (!ParsePositionValueSeparateCoords(itemValueX, itemValueY)) {
   12518           3 :     return false;
   12519             :   }
   12520             : 
   12521          88 :   nsCSSValue valueX;
   12522          88 :   nsCSSValue valueY;
   12523          44 :   nsCSSValueList* itemX = valueX.SetListValue();
   12524          44 :   nsCSSValueList* itemY = valueY.SetListValue();
   12525             :   for (;;) {
   12526          54 :     itemX->mValue = itemValueX;
   12527          49 :     itemY->mValue = itemValueY;
   12528          49 :     if (!ExpectSymbol(',', true)) {
   12529          43 :       break;
   12530             :     }
   12531           6 :     if (!ParsePositionValueSeparateCoords(itemValueX, itemValueY)) {
   12532           1 :       return false;
   12533             :     }
   12534           5 :     itemX->mNext = new nsCSSValueList;
   12535           5 :     itemY->mNext = new nsCSSValueList;
   12536           5 :     itemX = itemX->mNext;
   12537           5 :     itemY = itemY->mNext;
   12538             :   }
   12539          43 :   AppendValue(aTable[nsStyleImageLayers::positionX], valueX);
   12540          43 :   AppendValue(aTable[nsStyleImageLayers::positionY], valueY);
   12541          43 :   return true;
   12542             : }
   12543             : 
   12544             : bool
   12545           0 : CSSParserImpl::ParseImageLayerPositionCoord(nsCSSPropertyID aPropID, bool aIsHorizontal)
   12546             : {
   12547           0 :   nsCSSValue value;
   12548             :   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
   12549           0 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   12550           0 :     nsCSSValue itemValue;
   12551           0 :     if (!ParseImageLayerPositionCoordItem(itemValue, aIsHorizontal)) {
   12552           0 :       return false;
   12553             :     }
   12554           0 :     nsCSSValueList* item = value.SetListValue();
   12555             :     for (;;) {
   12556           0 :       item->mValue = itemValue;
   12557           0 :       if (!ExpectSymbol(',', true)) {
   12558           0 :         break;
   12559             :       }
   12560           0 :       if (!ParseImageLayerPositionCoordItem(itemValue, aIsHorizontal)) {
   12561           0 :         return false;
   12562             :       }
   12563           0 :       item->mNext = new nsCSSValueList;
   12564           0 :       item = item->mNext;
   12565             :     }
   12566             :   }
   12567           0 :   AppendValue(aPropID, value);
   12568           0 :   return true;
   12569             : }
   12570             : 
   12571             : /**
   12572             :  * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used
   12573             :  * for parsing the CSS 2.1 background-position syntax (which has at
   12574             :  * most two values).  (Compare to the css3-background syntax which
   12575             :  * takes up to four values.)  Some current CSS specifications that
   12576             :  * use background-position-like syntax still use this old syntax.
   12577             :  **
   12578             :  * Parses two values that correspond to positions in a box.  These can be
   12579             :  * values corresponding to percentages of the box, raw offsets, or keywords
   12580             :  * like "top," "left center," etc.
   12581             :  *
   12582             :  * @param aOut The nsCSSValuePair in which to place the result.
   12583             :  * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are
   12584             :  *   legal values
   12585             :  * @param aAllowExplicitCenter If true, 'center' is a legal value
   12586             :  * @return Whether or not the operation succeeded.
   12587             :  */
   12588           8 : bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
   12589             :                                            bool aAcceptsInherit,
   12590             :                                            bool aAllowExplicitCenter)
   12591             : {
   12592             :   // First try a percentage or a length value
   12593           8 :   nsCSSValue &xValue = aOut.mXValue,
   12594           8 :              &yValue = aOut.mYValue;
   12595             :   int32_t variantMask =
   12596           8 :     (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC;
   12597           8 :   CSSParseResult result = ParseVariant(xValue, variantMask, nullptr);
   12598           8 :   if (result == CSSParseResult::Error) {
   12599           0 :     return false;
   12600           8 :   } else if (result == CSSParseResult::Ok) {
   12601          10 :     if (eCSSUnit_Inherit == xValue.GetUnit() ||
   12602           6 :         eCSSUnit_Initial == xValue.GetUnit() ||
   12603           2 :         eCSSUnit_Unset == xValue.GetUnit()) {  // both are inherit, initial or unset
   12604           2 :       yValue = xValue;
   12605           2 :       return true;
   12606             :     }
   12607             :     // We have one percentage/length/calc. Get the optional second
   12608             :     // percentage/length/calc/keyword.
   12609           2 :     result = ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr);
   12610           2 :     if (result == CSSParseResult::Error) {
   12611           0 :       return false;
   12612           2 :     } else if (result == CSSParseResult::Ok) {
   12613             :       // We have two numbers
   12614           2 :       return true;
   12615             :     }
   12616             : 
   12617           0 :     if (ParseEnum(yValue, nsCSSProps::kImageLayerPositionKTable)) {
   12618           0 :       int32_t yVal = yValue.GetIntValue();
   12619           0 :       if (!(yVal & BG_CTB)) {
   12620             :         // The second keyword can only be 'center', 'top', or 'bottom'
   12621           0 :         return false;
   12622             :       }
   12623           0 :       yValue = BoxPositionMaskToCSSValue(yVal, false);
   12624           0 :       return true;
   12625             :     }
   12626             : 
   12627             :     // If only one percentage or length value is given, it sets the
   12628             :     // horizontal position only, and the vertical position will be 50%.
   12629           0 :     yValue.SetPercentValue(0.5f);
   12630           0 :     return true;
   12631             :   }
   12632             : 
   12633             :   // Now try keywords. We do this manually to allow for the first
   12634             :   // appearance of "center" to apply to the either the x or y
   12635             :   // position (it's ambiguous so we have to disambiguate). Each
   12636             :   // allowed keyword value is assigned it's own bit. We don't allow
   12637             :   // any duplicate keywords other than center. We try to get two
   12638             :   // keywords but it's okay if there is only one.
   12639           4 :   int32_t mask = 0;
   12640           4 :   if (ParseEnum(xValue, nsCSSProps::kImageLayerPositionKTable)) {
   12641           4 :     int32_t bit = xValue.GetIntValue();
   12642           4 :     mask |= bit;
   12643           4 :     if (ParseEnum(xValue, nsCSSProps::kImageLayerPositionKTable)) {
   12644           0 :       bit = xValue.GetIntValue();
   12645           0 :       if (mask & (bit & ~BG_CENTER)) {
   12646             :         // Only the 'center' keyword can be duplicated.
   12647           0 :         return false;
   12648             :       }
   12649           0 :       mask |= bit;
   12650             :     }
   12651             :     else {
   12652             :       // Only one keyword.  See if we have a length, percentage, or calc.
   12653           4 :       result = ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr);
   12654           4 :       if (result == CSSParseResult::Error) {
   12655           0 :         return false;
   12656           4 :       } else if (result == CSSParseResult::Ok) {
   12657           0 :         if (!(mask & BG_CLR)) {
   12658             :           // The first keyword can only be 'center', 'left', or 'right'
   12659           0 :           return false;
   12660             :         }
   12661             : 
   12662           0 :         xValue = BoxPositionMaskToCSSValue(mask, true);
   12663           0 :         return true;
   12664             :       }
   12665             :     }
   12666             :   }
   12667             : 
   12668             :   // Check for bad input. Bad input consists of no matching keywords,
   12669             :   // or pairs of x keywords or pairs of y keywords.
   12670           4 :   if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
   12671           4 :       (mask == (BG_LEFT | BG_RIGHT)) ||
   12672           8 :       (!aAllowExplicitCenter && (mask & BG_CENTER))) {
   12673           0 :     return false;
   12674             :   }
   12675             : 
   12676             :   // Create style values
   12677           4 :   xValue = BoxPositionMaskToCSSValue(mask, true);
   12678           4 :   yValue = BoxPositionMaskToCSSValue(mask, false);
   12679           4 :   return true;
   12680             : }
   12681             : 
   12682             : // Parses a CSS <position> value, for e.g. the 'background-position' property.
   12683             : // Spec reference: http://www.w3.org/TR/css3-background/#ltpositiongt
   12684             : // Invariants:
   12685             : //  - Always produces a four-value array on a successful parse.
   12686             : //  - The values are: X edge, X offset, Y edge, Y offset.
   12687             : //  - Edges are always keywords or null.
   12688             : //  - A |center| edge will not have an offset.
   12689             : bool
   12690          70 : CSSParserImpl::ParsePositionValue(nsCSSValue& aOut)
   12691             : {
   12692         140 :   RefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(4);
   12693          70 :   aOut.SetArrayValue(value, eCSSUnit_Array);
   12694             : 
   12695             :   // The following clarifies organisation of the array.
   12696          70 :   nsCSSValue &xEdge   = value->Item(0),
   12697          70 :              &xOffset = value->Item(1),
   12698          70 :              &yEdge   = value->Item(2),
   12699          70 :              &yOffset = value->Item(3);
   12700             : 
   12701             :   // Parse all the values into the array.
   12702          70 :   uint32_t valueCount = 0;
   12703         194 :   for (int32_t i = 0; i < 4; i++) {
   12704             :     CSSParseResult result =
   12705         192 :       ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD,
   12706         192 :                    nsCSSProps::kImageLayerPositionKTable);
   12707         192 :     if (result == CSSParseResult::Error) {
   12708           2 :       return false;
   12709         190 :     } else if (result == CSSParseResult::NotFound) {
   12710          66 :       break;
   12711             :     }
   12712         124 :     ++valueCount;
   12713             :   }
   12714             : 
   12715          68 :   switch (valueCount) {
   12716             :     case 4:
   12717             :       // "If three or four values are given, then each <percentage> or <length>
   12718             :       // represents an offset and must be preceded by a keyword, which specifies
   12719             :       // from which edge the offset is given."
   12720           6 :       if (eCSSUnit_Enumerated != xEdge.GetUnit() ||
   12721           4 :           BG_CENTER == xEdge.GetIntValue() ||
   12722           4 :           eCSSUnit_Enumerated == xOffset.GetUnit() ||
   12723           4 :           eCSSUnit_Enumerated != yEdge.GetUnit() ||
   12724           6 :           BG_CENTER == yEdge.GetIntValue() ||
   12725           2 :           eCSSUnit_Enumerated == yOffset.GetUnit()) {
   12726           0 :         return false;
   12727             :       }
   12728           2 :       break;
   12729             :     case 3:
   12730             :       // "If three or four values are given, then each <percentage> or<length>
   12731             :       // represents an offset and must be preceded by a keyword, which specifies
   12732             :       // from which edge the offset is given." ... "If three values are given,
   12733             :       // the missing offset is assumed to be zero."
   12734           7 :       if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) {
   12735             :         // keyword offset keyword
   12736             :         // Second value is non-keyword, thus first value must be a non-center
   12737             :         // keyword.
   12738           6 :         if (eCSSUnit_Enumerated != value->Item(0).GetUnit() ||
   12739           3 :             BG_CENTER == value->Item(0).GetIntValue()) {
   12740           0 :           return false;
   12741             :         }
   12742             : 
   12743             :         // Remaining value must be a keyword.
   12744           3 :         if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
   12745           0 :           return false;
   12746             :         }
   12747             : 
   12748           3 :         yOffset.Reset(); // Everything else is in the correct position.
   12749           4 :       } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
   12750             :         // keyword keyword offset
   12751             :         // Third value is non-keyword, thus second value must be non-center
   12752             :         // keyword.
   12753           4 :         if (BG_CENTER == value->Item(1).GetIntValue()) {
   12754           0 :           return false;
   12755             :         }
   12756             : 
   12757             :         // Remaining value must be a keyword.
   12758           4 :         if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) {
   12759           0 :           return false;
   12760             :         }
   12761             : 
   12762             :         // Move the values to the correct position in the array.
   12763           4 :         value->Item(3) = value->Item(2); // yOffset
   12764           4 :         value->Item(2) = value->Item(1); // yEdge
   12765           4 :         value->Item(1).Reset(); // xOffset
   12766             :       } else {
   12767           0 :         return false;
   12768             :       }
   12769           7 :       break;
   12770             :     case 2:
   12771             :       // "If two values are given and at least one value is not a keyword, then
   12772             :       // the first value represents the horizontal position (or offset) and the
   12773             :       // second represents the vertical position (or offset)"
   12774          36 :       if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
   12775          24 :         if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
   12776             :           // keyword keyword
   12777          24 :           value->Item(2) = value->Item(1); // move yEdge to correct position
   12778          24 :           xOffset.Reset();
   12779          24 :           yOffset.Reset();
   12780             :         } else {
   12781             :           // keyword offset
   12782             :           // First value must represent horizontal position.
   12783           0 :           if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) {
   12784           0 :             return false;
   12785             :           }
   12786           0 :           value->Item(3) = value->Item(1); // move yOffset to correct position
   12787           0 :           xOffset.Reset();
   12788           0 :           yEdge.Reset();
   12789             :         }
   12790             :       } else {
   12791          12 :         if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
   12792             :           // offset keyword
   12793             :           // Second value must represent vertical position.
   12794           2 :           if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) {
   12795           0 :             return false;
   12796             :           }
   12797           2 :           value->Item(2) = value->Item(1); // move yEdge to correct position
   12798           2 :           value->Item(1) = value->Item(0); // move xOffset to correct position
   12799           2 :           xEdge.Reset();
   12800           2 :           yOffset.Reset();
   12801             :         } else {
   12802             :           // offset offset
   12803          10 :           value->Item(3) = value->Item(1); // move yOffset to correct position
   12804          10 :           value->Item(1) = value->Item(0); // move xOffset to correct position
   12805          10 :           xEdge.Reset();
   12806          10 :           yEdge.Reset();
   12807             :         }
   12808             :       }
   12809          36 :       break;
   12810             :     case 1:
   12811             :       // "If only one value is specified, the second value is assumed to be
   12812             :       // center."
   12813          21 :       if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
   12814          18 :         xOffset.Reset();
   12815             :       } else {
   12816           3 :         value->Item(1) = value->Item(0); // move xOffset to correct position
   12817           3 :         xEdge.Reset();
   12818             :       }
   12819          21 :       yEdge.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_CENTER, eCSSUnit_Enumerated);
   12820          21 :       yOffset.Reset();
   12821          21 :       break;
   12822             :     default:
   12823           2 :       return false;
   12824             :   }
   12825             : 
   12826             :   // For compatibility with CSS2.1 code the edges can be unspecified.
   12827             :   // Unspecified edges are recorded as nullptr.
   12828          66 :   NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit()  ||
   12829             :                 eCSSUnit_Null       == xEdge.GetUnit()) &&
   12830             :                (eCSSUnit_Enumerated == yEdge.GetUnit()  ||
   12831             :                 eCSSUnit_Null       == yEdge.GetUnit()) &&
   12832             :                 eCSSUnit_Enumerated != xOffset.GetUnit()  &&
   12833             :                 eCSSUnit_Enumerated != yOffset.GetUnit(),
   12834             :                 "Unexpected units");
   12835             : 
   12836             :   // Keywords in first and second pairs can not both be vertical or
   12837             :   // horizontal keywords. (eg. left right, bottom top). Additionally,
   12838             :   // non-center keyword can not be duplicated (eg. left left).
   12839             :   int32_t xEdgeEnum =
   12840          66 :           xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0;
   12841             :   int32_t yEdgeEnum =
   12842          66 :           yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0;
   12843         132 :   if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) ||
   12844         132 :       (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) ||
   12845          66 :       (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) {
   12846           0 :     return false;
   12847             :   }
   12848             : 
   12849             :   // The values could be in an order that is different than expected.
   12850             :   // eg. x contains vertical information, y contains horizontal information.
   12851             :   // Swap if incorrect order.
   12852         129 :   if (xEdgeEnum & (BG_TOP | BG_BOTTOM) ||
   12853          63 :       yEdgeEnum & (BG_LEFT | BG_RIGHT)) {
   12854          14 :     nsCSSValue swapEdge = xEdge;
   12855          14 :     nsCSSValue swapOffset = xOffset;
   12856           7 :     xEdge = yEdge;
   12857           7 :     xOffset = yOffset;
   12858           7 :     yEdge = swapEdge;
   12859           7 :     yOffset = swapOffset;
   12860             :   }
   12861             : 
   12862          66 :   return true;
   12863             : }
   12864             : 
   12865             : static void
   12866           0 : AdjustEdgeOffsetPairForBasicShape(nsCSSValue& aEdge,
   12867             :                                   nsCSSValue& aOffset,
   12868             :                                   uint8_t aDefaultEdge)
   12869             : {
   12870             :   // 0 length offsets are 0%
   12871           0 :   if (aOffset.IsLengthUnit() && aOffset.GetFloatValue() == 0.0) {
   12872           0 :     aOffset.SetPercentValue(0);
   12873             :   }
   12874             : 
   12875             :   // Default edge is top/left in the 4-value case
   12876             :   // In case of 1 or 0 values, the default is center,
   12877             :   // but ParsePositionValue already handles this case
   12878           0 :   if (eCSSUnit_Null == aEdge.GetUnit()) {
   12879           0 :     aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
   12880             :   }
   12881             :   // Default offset is 0%
   12882           0 :   if (eCSSUnit_Null == aOffset.GetUnit()) {
   12883           0 :     aOffset.SetPercentValue(0.0);
   12884             :   }
   12885           0 :   if (eCSSUnit_Enumerated == aEdge.GetUnit() &&
   12886           0 :       eCSSUnit_Percent == aOffset.GetUnit()) {
   12887           0 :     switch (aEdge.GetIntValue()) {
   12888             :       case NS_STYLE_IMAGELAYER_POSITION_CENTER:
   12889           0 :         aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
   12890           0 :         MOZ_ASSERT(aOffset.GetPercentValue() == 0.0,
   12891             :                    "center cannot be used with an offset");
   12892           0 :         aOffset.SetPercentValue(0.5);
   12893           0 :         break;
   12894             :       case NS_STYLE_IMAGELAYER_POSITION_BOTTOM:
   12895           0 :         MOZ_ASSERT(aDefaultEdge == NS_STYLE_IMAGELAYER_POSITION_TOP);
   12896           0 :         aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
   12897           0 :         aOffset.SetPercentValue(1 - aOffset.GetPercentValue());
   12898           0 :         break;
   12899             :       case NS_STYLE_IMAGELAYER_POSITION_RIGHT:
   12900           0 :         MOZ_ASSERT(aDefaultEdge == NS_STYLE_IMAGELAYER_POSITION_LEFT);
   12901           0 :         aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
   12902           0 :         aOffset.SetPercentValue(1 - aOffset.GetPercentValue());
   12903             :     }
   12904             :   }
   12905           0 : }
   12906             : 
   12907             : // https://drafts.csswg.org/css-shapes/#basic-shape-serialization
   12908             : // We set values to defaults while parsing for basic shapes
   12909             : // Invariants:
   12910             : //  - Always produces a four-value array on a successful parse.
   12911             : //  - The values are: X edge, X offset, Y edge, Y offset
   12912             : //  - Edges are always keywords (not including center)
   12913             : //  - Offsets are nonnull
   12914             : //  - Percentage offsets have keywords folded into them,
   12915             : //    so "bottom 40%" or "right 20%" will not exist.
   12916             : bool
   12917           0 : CSSParserImpl::ParsePositionValueForBasicShape(nsCSSValue& aOut)
   12918             : {
   12919           0 :   if (!ParsePositionValue(aOut)) {
   12920           0 :     return false;
   12921             :   }
   12922           0 :   nsCSSValue::Array* value = aOut.GetArrayValue();
   12923           0 :   nsCSSValue& xEdge   = value->Item(0);
   12924           0 :   nsCSSValue& xOffset = value->Item(1);
   12925           0 :   nsCSSValue& yEdge   = value->Item(2);
   12926           0 :   nsCSSValue& yOffset = value->Item(3);
   12927             :   // A keyword edge + percent offset pair can be contracted
   12928             :   // into the percentage with the default value in the edge.
   12929             :   // Offset lengths which are 0 can also be rewritten as 0%
   12930             :   AdjustEdgeOffsetPairForBasicShape(xEdge, xOffset,
   12931           0 :                                     NS_STYLE_IMAGELAYER_POSITION_LEFT);
   12932             :   AdjustEdgeOffsetPairForBasicShape(yEdge, yOffset,
   12933           0 :                                     NS_STYLE_IMAGELAYER_POSITION_TOP);
   12934           0 :   return true;
   12935             : }
   12936             : 
   12937             : bool
   12938          70 : CSSParserImpl::ParsePositionValueSeparateCoords(nsCSSValue& aOutX, nsCSSValue& aOutY)
   12939             : {
   12940         140 :   nsCSSValue scratch;
   12941          70 :   if (!ParsePositionValue(scratch)) {
   12942           4 :     return false;
   12943             :   }
   12944             : 
   12945             :   // Separate the four values into two pairs of two values for X and Y.
   12946         132 :   RefPtr<nsCSSValue::Array> valueX = nsCSSValue::Array::Create(2);
   12947         132 :   RefPtr<nsCSSValue::Array> valueY = nsCSSValue::Array::Create(2);
   12948          66 :   aOutX.SetArrayValue(valueX, eCSSUnit_Array);
   12949          66 :   aOutY.SetArrayValue(valueY, eCSSUnit_Array);
   12950             : 
   12951         132 :   RefPtr<nsCSSValue::Array> value = scratch.GetArrayValue();
   12952          66 :   valueX->Item(0) = value->Item(0);
   12953          66 :   valueX->Item(1) = value->Item(1);
   12954          66 :   valueY->Item(0) = value->Item(2);
   12955          66 :   valueY->Item(1) = value->Item(3);
   12956          66 :   return true;
   12957             : }
   12958             : 
   12959             : // Parses one item in a list of values for the 'background-position-x' or
   12960             : // 'background-position-y' property. Does not support the start/end keywords.
   12961             : // Spec reference: https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-x
   12962             : bool
   12963           0 : CSSParserImpl::ParseImageLayerPositionCoordItem(nsCSSValue& aOut, bool aIsHorizontal)
   12964             : {
   12965           0 :   RefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(2);
   12966           0 :   aOut.SetArrayValue(value, eCSSUnit_Array);
   12967             : 
   12968           0 :   nsCSSValue &edge   = value->Item(0),
   12969           0 :              &offset = value->Item(1);
   12970             : 
   12971           0 :   nsCSSValue edgeOrOffset;
   12972             :   CSSParseResult result =
   12973             :     ParseVariant(edgeOrOffset, VARIANT_LPCALC | VARIANT_KEYWORD,
   12974           0 :                  nsCSSProps::kImageLayerPositionKTable);
   12975           0 :   if (result != CSSParseResult::Ok) {
   12976           0 :     return false;
   12977             :   }
   12978             : 
   12979           0 :   if (edgeOrOffset.GetUnit() == eCSSUnit_Enumerated) {
   12980           0 :     edge = edgeOrOffset;
   12981             : 
   12982             :     // The edge can be followed by an optional offset.
   12983           0 :     result = ParseVariant(offset, VARIANT_LPCALC, nullptr);
   12984           0 :     if (result == CSSParseResult::Error) {
   12985           0 :       return false;
   12986             :     }
   12987             :   } else {
   12988           0 :     offset = edgeOrOffset;
   12989             :   }
   12990             : 
   12991             :   // Keywords for horizontal properties cannot be vertical keywords, and
   12992             :   // keywords for vertical properties cannot be horizontal keywords.
   12993             :   // Also, if an offset is specified, the edge cannot be center.
   12994             :   int32_t edgeEnum =
   12995           0 :           edge.GetUnit() == eCSSUnit_Enumerated ? edge.GetIntValue() : 0;
   12996             :   int32_t allowedKeywords =
   12997           0 :     (aIsHorizontal ? (BG_LEFT | BG_RIGHT) : (BG_TOP | BG_BOTTOM)) |
   12998           0 :     (offset.GetUnit() == eCSSUnit_Null ? BG_CENTER : 0);
   12999           0 :   if (edgeEnum & ~allowedKeywords) {
   13000           0 :     return false;
   13001             :   }
   13002             : 
   13003           0 :   NS_ASSERTION((eCSSUnit_Enumerated == edge.GetUnit() ||
   13004             :                 eCSSUnit_Null       == edge.GetUnit()) &&
   13005             :                eCSSUnit_Enumerated != offset.GetUnit(),
   13006             :                "Unexpected units");
   13007             : 
   13008           0 :   return true;
   13009             : }
   13010             : 
   13011             : // This function is very similar to ParseScrollSnapCoordinate,
   13012             : // ParseImageLayers, and ParseImageLayerPosition.
   13013             : bool
   13014          36 : CSSParserImpl::ParseImageLayerSize(nsCSSPropertyID aPropID)
   13015             : {
   13016          72 :   nsCSSValue value;
   13017             :   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
   13018          36 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   13019          69 :     nsCSSValuePair valuePair;
   13020          36 :     if (!ParseImageLayerSizeValues(valuePair)) {
   13021           3 :       return false;
   13022             :     }
   13023          33 :     nsCSSValuePairList* item = value.SetPairListValue();
   13024             :     for (;;) {
   13025          47 :       item->mXValue = valuePair.mXValue;
   13026          40 :       item->mYValue = valuePair.mYValue;
   13027          40 :       if (!ExpectSymbol(',', true)) {
   13028          33 :         break;
   13029             :       }
   13030           7 :       if (!ParseImageLayerSizeValues(valuePair)) {
   13031           0 :         return false;
   13032             :       }
   13033           7 :       item->mNext = new nsCSSValuePairList;
   13034           7 :       item = item->mNext;
   13035             :     }
   13036             :   }
   13037          33 :   AppendValue(aPropID, value);
   13038          33 :   return true;
   13039             : }
   13040             : 
   13041             : /**
   13042             :  * Parses two values that correspond to lengths for the background-size
   13043             :  * property.  These can be one or two lengths (or the 'auto' keyword) or
   13044             :  * percentages corresponding to the element's dimensions or the single keywords
   13045             :  * 'contain' or 'cover'.  'initial', 'inherit' and 'unset' must be handled by
   13046             :  * the caller if desired.
   13047             :  *
   13048             :  * @param aOut The nsCSSValuePair in which to place the result.
   13049             :  * @return Whether or not the operation succeeded.
   13050             :  */
   13051             : #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC)
   13052          44 : bool CSSParserImpl::ParseImageLayerSizeValues(nsCSSValuePair &aOut)
   13053             : {
   13054             :   // First try a percentage or a length value
   13055          44 :   nsCSSValue &xValue = aOut.mXValue,
   13056          44 :              &yValue = aOut.mYValue;
   13057             :   CSSParseResult result =
   13058          44 :     ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr);
   13059          44 :   if (result == CSSParseResult::Error) {
   13060           0 :     return false;
   13061          44 :   } else if (result == CSSParseResult::Ok) {
   13062             :     // We have one percentage/length/calc/auto. Get the optional second
   13063             :     // percentage/length/calc/keyword.
   13064          37 :     result = ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr);
   13065          37 :     if (result == CSSParseResult::Error) {
   13066           0 :       return false;
   13067          37 :     } else if (result == CSSParseResult::Ok) {
   13068             :       // We have a second percentage/length/calc/auto.
   13069          25 :       return true;
   13070             :     }
   13071             : 
   13072             :     // If only one percentage or length value is given, it sets the
   13073             :     // horizontal size only, and the vertical size will be as if by 'auto'.
   13074          12 :     yValue.SetAutoValue();
   13075          12 :     return true;
   13076             :   }
   13077             : 
   13078             :   // Now address 'contain' and 'cover'.
   13079           7 :   if (!ParseEnum(xValue, nsCSSProps::kImageLayerSizeKTable))
   13080           3 :     return false;
   13081           4 :   yValue.Reset();
   13082           4 :   return true;
   13083             : }
   13084             : 
   13085             : #undef BG_SIZE_VARIANT
   13086             : 
   13087             : bool
   13088          42 : CSSParserImpl::ParseBorderColor()
   13089             : {
   13090          42 :   return ParseBoxProperties(kBorderColorIDs);
   13091             : }
   13092             : 
   13093             : void
   13094         241 : CSSParserImpl::SetBorderImageInitialValues()
   13095             : {
   13096             :   // border-image-source: none
   13097         482 :   nsCSSValue source;
   13098         241 :   source.SetNoneValue();
   13099         241 :   AppendValue(eCSSProperty_border_image_source, source);
   13100             : 
   13101             :   // border-image-slice: 100%
   13102         482 :   nsCSSValue sliceBoxValue;
   13103         241 :   nsCSSRect& sliceBox = sliceBoxValue.SetRectValue();
   13104         241 :   sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent));
   13105         482 :   nsCSSValue slice;
   13106         241 :   nsCSSValueList* sliceList = slice.SetListValue();
   13107         241 :   sliceList->mValue = sliceBoxValue;
   13108         241 :   AppendValue(eCSSProperty_border_image_slice, slice);
   13109             : 
   13110             :   // border-image-width: 1
   13111         482 :   nsCSSValue width;
   13112         241 :   nsCSSRect& widthBox = width.SetRectValue();
   13113         241 :   widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number));
   13114         241 :   AppendValue(eCSSProperty_border_image_width, width);
   13115             : 
   13116             :   // border-image-outset: 0
   13117         482 :   nsCSSValue outset;
   13118         241 :   nsCSSRect& outsetBox = outset.SetRectValue();
   13119         241 :   outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number));
   13120         241 :   AppendValue(eCSSProperty_border_image_outset, outset);
   13121             : 
   13122             :   // border-image-repeat: repeat
   13123         482 :   nsCSSValue repeat;
   13124         482 :   nsCSSValuePair repeatPair;
   13125         482 :   repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
   13126         241 :                                         eCSSUnit_Enumerated));
   13127         241 :   repeat.SetPairValue(&repeatPair);
   13128         241 :   AppendValue(eCSSProperty_border_image_repeat, repeat);
   13129         241 : }
   13130             : 
   13131             : bool
   13132           4 : CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
   13133             :                                      bool* aConsumedTokens)
   13134             : {
   13135             :   // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
   13136           8 :   nsCSSValue value;
   13137             : 
   13138           4 :   if (aConsumedTokens) {
   13139           0 :     *aConsumedTokens = true;
   13140             :   }
   13141             : 
   13142           8 :   if (aAcceptsInherit &&
   13143           4 :       ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   13144             :     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
   13145             :     // are done.
   13146           0 :     AppendValue(eCSSProperty_border_image_slice, value);
   13147           0 :     return true;
   13148             :   }
   13149             : 
   13150             :   // Try parsing "fill" value.
   13151           8 :   nsCSSValue imageSliceFillValue;
   13152             :   bool hasFill = ParseEnum(imageSliceFillValue,
   13153           4 :                            nsCSSProps::kBorderImageSliceKTable);
   13154             : 
   13155             :   // Parse the box dimensions.
   13156           8 :   nsCSSValue imageSliceBoxValue;
   13157           4 :   if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue,
   13158             :                                CSS_PROPERTY_VALUE_NONNEGATIVE)) {
   13159           0 :     if (!hasFill && aConsumedTokens) {
   13160           0 :       *aConsumedTokens = false;
   13161             :     }
   13162             : 
   13163           0 :     return false;
   13164             :   }
   13165             : 
   13166             :   // Try parsing "fill" keyword again if the first time failed because keyword
   13167             :   // and slice dimensions can be in any order.
   13168           4 :   if (!hasFill) {
   13169             :     hasFill = ParseEnum(imageSliceFillValue,
   13170           4 :                         nsCSSProps::kBorderImageSliceKTable);
   13171             :   }
   13172             : 
   13173           4 :   nsCSSValueList* borderImageSlice = value.SetListValue();
   13174             :   // Put the box value into the list.
   13175           4 :   borderImageSlice->mValue = imageSliceBoxValue;
   13176             : 
   13177           4 :   if (hasFill) {
   13178             :     // Put the "fill" value into the list.
   13179           0 :     borderImageSlice->mNext = new nsCSSValueList;
   13180           0 :     borderImageSlice->mNext->mValue = imageSliceFillValue;
   13181             :   }
   13182             : 
   13183           4 :   AppendValue(eCSSProperty_border_image_slice, value);
   13184           4 :   return true;
   13185             : }
   13186             : 
   13187             : bool
   13188           0 : CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
   13189             : {
   13190             :   // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
   13191           0 :   nsCSSValue value;
   13192             : 
   13193           0 :   if (aAcceptsInherit &&
   13194           0 :       ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   13195             :     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
   13196             :     // are done.
   13197           0 :     AppendValue(eCSSProperty_border_image_width, value);
   13198           0 :     return true;
   13199             :   }
   13200             : 
   13201             :   // Parse the box dimensions.
   13202           0 :   if (!ParseGroupedBoxProperty(VARIANT_ALPN, value, CSS_PROPERTY_VALUE_NONNEGATIVE)) {
   13203           0 :     return false;
   13204             :   }
   13205             : 
   13206           0 :   AppendValue(eCSSProperty_border_image_width, value);
   13207           0 :   return true;
   13208             : }
   13209             : 
   13210             : bool
   13211           0 : CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
   13212             : {
   13213             :   // border-image-outset: initial | [<length>|<number>]{1,4}
   13214           0 :   nsCSSValue value;
   13215             : 
   13216           0 :   if (aAcceptsInherit &&
   13217           0 :       ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   13218             :     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
   13219             :     // are done.
   13220           0 :     AppendValue(eCSSProperty_border_image_outset, value);
   13221           0 :     return true;
   13222             :   }
   13223             : 
   13224             :   // Parse the box dimensions.
   13225           0 :   if (!ParseGroupedBoxProperty(VARIANT_LN, value, CSS_PROPERTY_VALUE_NONNEGATIVE)) {
   13226           0 :     return false;
   13227             :   }
   13228             : 
   13229           0 :   AppendValue(eCSSProperty_border_image_outset, value);
   13230           0 :   return true;
   13231             : }
   13232             : 
   13233             : bool
   13234           0 : CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit)
   13235             : {
   13236           0 :   nsCSSValue value;
   13237           0 :   if (aAcceptsInherit &&
   13238           0 :       ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   13239             :     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
   13240             :     // are done.
   13241           0 :     AppendValue(eCSSProperty_border_image_repeat, value);
   13242           0 :     return true;
   13243             :   }
   13244             : 
   13245           0 :   nsCSSValuePair result;
   13246           0 :   if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) {
   13247           0 :     return false;
   13248             :   }
   13249             : 
   13250             :   // optional second keyword, defaults to first
   13251           0 :   if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) {
   13252           0 :     result.mYValue = result.mXValue;
   13253             :   }
   13254             : 
   13255           0 :   value.SetPairValue(&result);
   13256           0 :   AppendValue(eCSSProperty_border_image_repeat, value);
   13257           0 :   return true;
   13258             : }
   13259             : 
   13260             : bool
   13261           6 : CSSParserImpl::ParseBorderImage()
   13262             : {
   13263          12 :   nsAutoParseCompoundProperty compound(this);
   13264             : 
   13265             :   // border-image: inherit | initial |
   13266             :   // <border-image-source> ||
   13267             :   // <border-image-slice>
   13268             :   //   [ / <border-image-width> |
   13269             :   //     / <border-image-width>? / <border-image-outset> ]? ||
   13270             :   // <border-image-repeat>
   13271             : 
   13272          12 :   nsCSSValue value;
   13273           6 :   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   13274           2 :     AppendValue(eCSSProperty_border_image_source, value);
   13275           2 :     AppendValue(eCSSProperty_border_image_slice, value);
   13276           2 :     AppendValue(eCSSProperty_border_image_width, value);
   13277           2 :     AppendValue(eCSSProperty_border_image_outset, value);
   13278           2 :     AppendValue(eCSSProperty_border_image_repeat, value);
   13279             :     // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done.
   13280           2 :     return true;
   13281             :   }
   13282             : 
   13283             :   // No empty property.
   13284           4 :   if (CheckEndProperty()) {
   13285           0 :     return false;
   13286             :   }
   13287             : 
   13288             :   // Shorthand properties are required to set everything they can.
   13289           4 :   SetBorderImageInitialValues();
   13290             : 
   13291           4 :   bool foundSource = false;
   13292           4 :   bool foundSliceWidthOutset = false;
   13293           4 :   bool foundRepeat = false;
   13294             : 
   13295             :   // This loop is used to handle the parsing of border-image properties which
   13296             :   // can appear in any order.
   13297           8 :   nsCSSValue imageSourceValue;
   13298           8 :   while (!CheckEndProperty()) {
   13299             :     // <border-image-source>
   13300           4 :     if (!foundSource) {
   13301             :       CSSParseResult result =
   13302           4 :         ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr);
   13303           4 :       if (result == CSSParseResult::Error) {
   13304           2 :         return false;
   13305           2 :       } else if (result == CSSParseResult::Ok) {
   13306           2 :         AppendValue(eCSSProperty_border_image_source, imageSourceValue);
   13307           2 :         foundSource = true;
   13308           2 :         continue;
   13309             :       }
   13310             :     }
   13311             : 
   13312             :     // <border-image-slice>
   13313             :     // ParseBorderImageSlice is weird.  It may consume tokens and then return
   13314             :     // false, because it parses a property with two required components that
   13315             :     // can appear in either order.  Since the tokens that were consumed cannot
   13316             :     // parse as anything else we care about, this isn't a problem.
   13317           0 :     if (!foundSliceWidthOutset) {
   13318           0 :       bool sliceConsumedTokens = false;
   13319           0 :       if (ParseBorderImageSlice(false, &sliceConsumedTokens)) {
   13320           0 :         foundSliceWidthOutset = true;
   13321             : 
   13322             :         // [ / <border-image-width>?
   13323           0 :         if (ExpectSymbol('/', true)) {
   13324           0 :           bool foundBorderImageWidth = ParseBorderImageWidth(false);
   13325             : 
   13326             :           // [ / <border-image-outset>
   13327           0 :           if (ExpectSymbol('/', true)) {
   13328           0 :             if (!ParseBorderImageOutset(false)) {
   13329           0 :               return false;
   13330             :             }
   13331           0 :           } else if (!foundBorderImageWidth) {
   13332             :             // If this part has an trailing slash, the whole declaration is
   13333             :             // invalid.
   13334           0 :             return false;
   13335             :           }
   13336             :         }
   13337             : 
   13338           0 :         continue;
   13339             :       } else {
   13340             :         // If we consumed some tokens for <border-image-slice> but did not
   13341             :         // successfully parse it, we have an error.
   13342           0 :         if (sliceConsumedTokens) {
   13343           0 :           return false;
   13344             :         }
   13345             :       }
   13346             :     }
   13347             : 
   13348             :     // <border-image-repeat>
   13349           0 :     if (!foundRepeat && ParseBorderImageRepeat(false)) {
   13350           0 :       foundRepeat = true;
   13351           0 :       continue;
   13352             :     }
   13353             : 
   13354           0 :     return false;
   13355             :   }
   13356             : 
   13357           2 :   return true;
   13358             : }
   13359             : 
   13360             : bool
   13361           2 : CSSParserImpl::ParseBorderSpacing()
   13362             : {
   13363           4 :   nsCSSValue xValue, yValue;
   13364           2 :   if (ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr) !=
   13365             :       CSSParseResult::Ok) {
   13366           0 :     return false;
   13367             :   }
   13368             : 
   13369             :   // If we have one length, get the optional second length.
   13370             :   // set the second value equal to the first.
   13371           2 :   if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) {
   13372           2 :     if (ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC,
   13373             :                                 nullptr) == CSSParseResult::Error) {
   13374           0 :       return false;
   13375             :     }
   13376             :   }
   13377             : 
   13378           2 :   if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) {
   13379           2 :     AppendValue(eCSSProperty_border_spacing, xValue);
   13380             :   } else {
   13381           0 :     nsCSSValue pair;
   13382           0 :     pair.SetPairValue(xValue, yValue);
   13383           0 :     AppendValue(eCSSProperty_border_spacing, pair);
   13384             :   }
   13385           2 :   return true;
   13386             : }
   13387             : 
   13388             : bool
   13389         360 : CSSParserImpl::ParseBorderSide(const nsCSSPropertyID aPropIDs[],
   13390             :                                bool aSetAllSides)
   13391             : {
   13392         360 :   const int32_t numProps = 3;
   13393         720 :   nsCSSValue  values[numProps];
   13394             : 
   13395         360 :   int32_t found = ParseChoice(values, aPropIDs, numProps);
   13396         360 :   if (found < 1) {
   13397           2 :     return false;
   13398             :   }
   13399             : 
   13400         358 :   if ((found & 1) == 0) { // Provide default border-width
   13401          56 :     values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
   13402             :   }
   13403         358 :   if ((found & 2) == 0) { // Provide default border-style
   13404          17 :     values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
   13405             :   }
   13406         358 :   if ((found & 4) == 0) { // text color will be used
   13407         134 :     values[2].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
   13408             :   }
   13409             : 
   13410         358 :   if (aSetAllSides) {
   13411             :     // Parsing "border" shorthand; set all four sides to the same thing
   13412        1195 :     for (int32_t index = 0; index < 4; index++) {
   13413             :       NS_ASSERTION(numProps == 3, "This code needs updating");
   13414         956 :       AppendValue(kBorderWidthIDs[index], values[0]);
   13415         956 :       AppendValue(kBorderStyleIDs[index], values[1]);
   13416         956 :       AppendValue(kBorderColorIDs[index], values[2]);
   13417             :     }
   13418             : 
   13419             :     static const nsCSSPropertyID kBorderColorsProps[] = {
   13420             :       eCSSProperty__moz_border_top_colors,
   13421             :       eCSSProperty__moz_border_right_colors,
   13422             :       eCSSProperty__moz_border_bottom_colors,
   13423             :       eCSSProperty__moz_border_left_colors
   13424             :     };
   13425             : 
   13426             :     // Set the other properties that the border shorthand sets to their
   13427             :     // initial values.
   13428         478 :     nsCSSValue extraValue;
   13429         239 :     switch (values[0].GetUnit()) {
   13430             :     case eCSSUnit_Inherit:
   13431             :     case eCSSUnit_Initial:
   13432             :     case eCSSUnit_Unset:
   13433           2 :       extraValue = values[0];
   13434             :       // Set value of border-image properties to initial/inherit/unset
   13435           2 :       AppendValue(eCSSProperty_border_image_source, extraValue);
   13436           2 :       AppendValue(eCSSProperty_border_image_slice, extraValue);
   13437           2 :       AppendValue(eCSSProperty_border_image_width, extraValue);
   13438           2 :       AppendValue(eCSSProperty_border_image_outset, extraValue);
   13439           2 :       AppendValue(eCSSProperty_border_image_repeat, extraValue);
   13440           2 :       break;
   13441             :     default:
   13442         237 :       extraValue.SetNoneValue();
   13443         237 :       SetBorderImageInitialValues();
   13444         237 :       break;
   13445             :     }
   13446        1195 :     NS_FOR_CSS_SIDES(side) {
   13447         956 :       AppendValue(kBorderColorsProps[side], extraValue);
   13448             :     }
   13449             :   }
   13450             :   else {
   13451             :     // Just set our one side
   13452         476 :     for (int32_t index = 0; index < numProps; index++) {
   13453         357 :       AppendValue(aPropIDs[index], values[index]);
   13454             :     }
   13455             :   }
   13456         358 :   return true;
   13457             : }
   13458             : 
   13459             : bool
   13460          44 : CSSParserImpl::ParseBorderStyle()
   13461             : {
   13462          44 :   return ParseBoxProperties(kBorderStyleIDs);
   13463             : }
   13464             : 
   13465             : bool
   13466          27 : CSSParserImpl::ParseBorderWidth()
   13467             : {
   13468          27 :   return ParseBoxProperties(kBorderWidthIDs);
   13469             : }
   13470             : 
   13471             : bool
   13472          34 : CSSParserImpl::ParseBorderColors(nsCSSPropertyID aProperty)
   13473             : {
   13474          68 :   nsCSSValue value;
   13475             :   // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
   13476          34 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
   13477             :                                nullptr)) {
   13478          30 :     nsCSSValueList *cur = value.SetListValue();
   13479             :     for (;;) {
   13480          82 :       if (ParseVariant(cur->mValue, VARIANT_COLOR, nullptr) !=
   13481             :           CSSParseResult::Ok) {
   13482           0 :         return false;
   13483             :       }
   13484          56 :       if (CheckEndProperty()) {
   13485          30 :         break;
   13486             :       }
   13487          26 :       cur->mNext = new nsCSSValueList;
   13488          26 :       cur = cur->mNext;
   13489             :     }
   13490             :   }
   13491          34 :   AppendValue(aProperty, value);
   13492          34 :   return true;
   13493             : }
   13494             : 
   13495             : // Parse the top level of a calc() expression.
   13496             : bool
   13497         226 : CSSParserImpl::ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask)
   13498             : {
   13499             :   // Parsing calc expressions requires, in a number of cases, looking
   13500             :   // for a token that is *either* a value of the property or a number.
   13501             :   // This can be done without lookahead when we assume that the property
   13502             :   // values cannot themselves be numbers.
   13503         226 :   MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
   13504         226 :   MOZ_ASSERT(!(aVariantMask & VARIANT_LPN) != !(aVariantMask & VARIANT_INTEGER),
   13505             :              "variant mask must intersect with exactly one of VARIANT_LPN "
   13506             :              "or VARIANT_INTEGER");
   13507             : 
   13508         226 :   bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk;
   13509         226 :   mUnitlessLengthQuirk = false;
   13510             : 
   13511             :   // One-iteration loop so we can break to the error-handling case.
   13512             :   do {
   13513             :     // The toplevel of a calc() is always an nsCSSValue::Array of length 1.
   13514         226 :     RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
   13515             : 
   13516         226 :     if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask))
   13517          21 :       break;
   13518             : 
   13519         205 :     if (!ExpectSymbol(')', true))
   13520           0 :       break;
   13521             : 
   13522         205 :     aValue.SetArrayValue(arr, eCSSUnit_Calc);
   13523         205 :     mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
   13524         205 :     return true;
   13525             :   } while (false);
   13526             : 
   13527          21 :   SkipUntil(')');
   13528          21 :   mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
   13529          21 :   return false;
   13530             : }
   13531             : 
   13532             : // We optimize away the <value-expression> production given that
   13533             : // ParseVariant consumes initial whitespace and we call
   13534             : // ExpectSymbol(')') with true for aSkipWS.
   13535             : //  * If aVariantMask is VARIANT_NUMBER, this function parses the
   13536             : //    <number-additive-expression> production.
   13537             : //  * If aVariantMask does not contain VARIANT_NUMBER, this function
   13538             : //    parses the <value-additive-expression> production.
   13539             : //  * Otherwise (VARIANT_NUMBER and other bits) this function parses
   13540             : //    whichever one of the productions matches ***and modifies
   13541             : //    aVariantMask*** to reflect which one it has parsed by either
   13542             : //    removing VARIANT_NUMBER or removing all other bits.
   13543             : // It does so iteratively, but builds the correct recursive
   13544             : // data structure.
   13545             : bool
   13546         251 : CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue,
   13547             :                                            uint32_t& aVariantMask)
   13548             : {
   13549         251 :   MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
   13550         251 :   nsCSSValue *storage = &aValue;
   13551             :   for (;;) {
   13552             :     bool haveWS;
   13553         428 :     if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS))
   13554         274 :       return false;
   13555             : 
   13556         405 :     if (!haveWS || !GetToken(false))
   13557         228 :       return true;
   13558             :     nsCSSUnit unit;
   13559         177 :     if (mToken.IsSymbol('+')) {
   13560         125 :       unit = eCSSUnit_Calc_Plus;
   13561          52 :     } else if (mToken.IsSymbol('-')) {
   13562          52 :       unit = eCSSUnit_Calc_Minus;
   13563             :     } else {
   13564           0 :       UngetToken();
   13565           0 :       return true;
   13566             :     }
   13567         177 :     if (!RequireWhitespace())
   13568           0 :       return false;
   13569             : 
   13570         354 :     RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
   13571         177 :     arr->Item(0) = aValue;
   13572         177 :     storage = &arr->Item(1);
   13573         177 :     aValue.SetArrayValue(arr, unit);
   13574         177 :   }
   13575             : }
   13576             : 
   13577             : //  * If aVariantMask is VARIANT_NUMBER, this function parses the
   13578             : //    <number-multiplicative-expression> production.
   13579             : //  * If aVariantMask does not contain VARIANT_NUMBER, this function
   13580             : //    parses the <value-multiplicative-expression> production.
   13581             : //  * Otherwise (VARIANT_NUMBER and other bits) this function parses
   13582             : //    whichever one of the productions matches ***and modifies
   13583             : //    aVariantMask*** to reflect which one it has parsed by either
   13584             : //    removing VARIANT_NUMBER or removing all other bits.
   13585             : // It does so iteratively, but builds the correct recursive data
   13586             : // structure.
   13587             : // This function always consumes *trailing* whitespace when it returns
   13588             : // true; whether there was any such whitespace is returned in the
   13589             : // aHadFinalWS parameter.
   13590             : bool
   13591         428 : CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
   13592             :                                                  uint32_t& aVariantMask,
   13593             :                                                  bool *aHadFinalWS)
   13594             : {
   13595         428 :   MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
   13596         428 :   bool gotValue = false; // already got the part with the unit
   13597         428 :   bool afterDivision = false;
   13598             : 
   13599         428 :   nsCSSValue *storage = &aValue;
   13600             :   for (;;) {
   13601             :     uint32_t variantMask;
   13602         563 :     if (aVariantMask & VARIANT_INTEGER) {
   13603           0 :       MOZ_ASSERT(aVariantMask == VARIANT_INTEGER,
   13604             :                  "integers in calc expressions can't be mixed with anything "
   13605             :                  "else.");
   13606           0 :       variantMask = aVariantMask;
   13607         563 :     } else if (afterDivision || gotValue) {
   13608             :       // At this point in the calc expression, we expect a coefficient or a
   13609             :       // divisor, which must be a number. (Not a length/%/etc.)
   13610          26 :       variantMask = VARIANT_NUMBER;
   13611             :     } else {
   13612             :       // At this point in the calc expression, we'll accept a coefficient
   13613             :       // (a number) or a value of whatever type |aVariantMask| specifies.
   13614         537 :       variantMask = aVariantMask | VARIANT_NUMBER;
   13615             :     }
   13616         563 :     if (!ParseCalcTerm(*storage, variantMask))
   13617          46 :       return false;
   13618         540 :     MOZ_ASSERT(variantMask != 0,
   13619             :                "ParseCalcTerm did not set variantMask appropriately");
   13620         540 :     MOZ_ASSERT(!(variantMask & VARIANT_NUMBER) ||
   13621             :                !(variantMask & ~int32_t(VARIANT_NUMBER)),
   13622             :                "ParseCalcTerm did not set variantMask appropriately");
   13623             : 
   13624         540 :     if (variantMask & VARIANT_NUMBER) {
   13625             :       // Simplify the value immediately so we can check for division by
   13626             :       // zero.
   13627             :       mozilla::css::ReduceNumberCalcOps ops;
   13628         135 :       float number = mozilla::css::ComputeCalc(*storage, ops);
   13629         135 :       if (number == 0.0 && afterDivision)
   13630           0 :         return false;
   13631         135 :       storage->SetFloatValue(number, eCSSUnit_Number);
   13632             :     } else {
   13633         405 :       gotValue = true;
   13634             : 
   13635         405 :       if (storage != &aValue) {
   13636             :         // Simplify any numbers in the Times_L position (which are
   13637             :         // not simplified by the check above).
   13638          98 :         MOZ_ASSERT(storage == &aValue.GetArrayValue()->Item(1),
   13639             :                    "unexpected relationship to current storage");
   13640          98 :         nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
   13641          98 :         if (variantMask & VARIANT_INTEGER) {
   13642             :           mozilla::css::ReduceIntegerCalcOps ops;
   13643           0 :           int integer = mozilla::css::ComputeCalc(leftValue, ops);
   13644           0 :           leftValue.SetIntValue(integer, eCSSUnit_Integer);
   13645             :         } else {
   13646             :           mozilla::css::ReduceNumberCalcOps ops;
   13647          98 :           float number = mozilla::css::ComputeCalc(leftValue, ops);
   13648          98 :           leftValue.SetFloatValue(number, eCSSUnit_Number);
   13649             :         }
   13650             :       }
   13651             :     }
   13652             : 
   13653         540 :     bool hadWS = RequireWhitespace();
   13654         540 :     if (!GetToken(false)) {
   13655           0 :       *aHadFinalWS = hadWS;
   13656         405 :       break;
   13657             :     }
   13658             :     nsCSSUnit unit;
   13659         540 :     if (mToken.IsSymbol('*')) {
   13660         109 :       unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
   13661         109 :       afterDivision = false;
   13662         431 :     } else if (mToken.IsSymbol('/')) {
   13663          26 :       if (variantMask & VARIANT_INTEGER) {
   13664             :         // Integers aren't mixed with anything else (see the assert at the top
   13665             :         // of CSSParserImpl::ParseCalc).
   13666             :         // We don't allow division at all in calc()s for expressions where an
   13667             :         // integer is expected, because calc() division can't be resolved to
   13668             :         // an integer, as implied by spec text about '/' here:
   13669             :         // https://drafts.csswg.org/css-values-3/#calc-type-checking
   13670             :         // We've consumed the '/' token, but it doesn't matter as we're in an
   13671             :         // error-handling situation where we've already consumed a lot of
   13672             :         // other tokens (e.g. the token before the '/'). ParseVariant will
   13673             :         // indicate this with CSSParseResult::Error.
   13674           0 :         return false;
   13675             :       }
   13676          26 :       unit = eCSSUnit_Calc_Divided;
   13677          26 :       afterDivision = true;
   13678             :     } else {
   13679         405 :       UngetToken();
   13680         405 :       *aHadFinalWS = hadWS;
   13681         405 :       break;
   13682             :     }
   13683             : 
   13684         270 :     RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
   13685         135 :     arr->Item(0) = aValue;
   13686         135 :     storage = &arr->Item(1);
   13687         135 :     aValue.SetArrayValue(arr, unit);
   13688         135 :   }
   13689             : 
   13690             :   // Adjust aVariantMask (see comments above function) to reflect which
   13691             :   // option we took.
   13692         405 :   if (aVariantMask & VARIANT_NUMBER) {
   13693          25 :     if (gotValue) {
   13694          25 :       aVariantMask &= ~int32_t(VARIANT_NUMBER);
   13695             :     } else {
   13696           0 :       aVariantMask = VARIANT_NUMBER;
   13697             :     }
   13698             :   } else {
   13699         380 :     if (!gotValue) {
   13700             :       // We had to find a value, but we didn't.
   13701           0 :       return false;
   13702             :     }
   13703             :   }
   13704             : 
   13705         405 :   return true;
   13706             : }
   13707             : 
   13708             : //  * If aVariantMask is VARIANT_NUMBER, this function parses the
   13709             : //    <number-term> production.
   13710             : //  * If aVariantMask does not contain VARIANT_NUMBER, this function
   13711             : //    parses the <value-term> production.
   13712             : //  * Otherwise (VARIANT_NUMBER and other bits) this function parses
   13713             : //    whichever one of the productions matches ***and modifies
   13714             : //    aVariantMask*** to reflect which one it has parsed by either
   13715             : //    removing VARIANT_NUMBER or removing all other bits.
   13716             : bool
   13717         563 : CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask)
   13718             : {
   13719         563 :   MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
   13720         563 :   if (!GetToken(true))
   13721           0 :     return false;
   13722             :   // Either an additive expression in parentheses...
   13723        1101 :   if (mToken.IsSymbol('(') ||
   13724             :       // Treat nested calc() as plain parenthesis.
   13725         538 :       IsCSSTokenCalcFunction(mToken)) {
   13726          48 :     if (!ParseCalcAdditiveExpression(aValue, aVariantMask) ||
   13727          23 :         !ExpectSymbol(')', true)) {
   13728           2 :       SkipUntil(')');
   13729           2 :       return false;
   13730             :     }
   13731          23 :     return true;
   13732             :   }
   13733             :   // ... or just a value
   13734         538 :   UngetToken();
   13735         538 :   if (aVariantMask & VARIANT_INTEGER) {
   13736             :     // Integers aren't mixed with anything else (see the assert at the
   13737             :     // top of CSSParserImpl::ParseCalc).
   13738           0 :     if (ParseVariant(aValue, aVariantMask, nullptr) != CSSParseResult::Ok) {
   13739           0 :       return false;
   13740             :     }
   13741             :   } else {
   13742             :     // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
   13743             :     // always gets picked up (we want to catch unitless zeroes using
   13744             :     // VARIANT_NUMBER and then error out)
   13745         538 :     if (ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr) !=
   13746             :         CSSParseResult::Ok) {
   13747          21 :       return false;
   13748             :     }
   13749             :     // ...and do the VARIANT_NUMBER check ourselves.
   13750         517 :     if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
   13751           0 :       return false;
   13752             :     }
   13753             :   }
   13754             :   // If we did the value parsing, we need to adjust aVariantMask to
   13755             :   // reflect which option we took (see above).
   13756         517 :   if (aVariantMask & VARIANT_NUMBER) {
   13757         517 :     if (aValue.GetUnit() == eCSSUnit_Number) {
   13758         135 :       aVariantMask = VARIANT_NUMBER;
   13759             :     } else {
   13760         382 :       aVariantMask &= ~int32_t(VARIANT_NUMBER);
   13761             :     }
   13762             :   }
   13763         517 :   return true;
   13764             : }
   13765             : 
   13766             : // This function consumes all consecutive whitespace and returns whether
   13767             : // there was any.
   13768             : bool
   13769         717 : CSSParserImpl::RequireWhitespace()
   13770             : {
   13771         717 :   if (!GetToken(false))
   13772           0 :     return false;
   13773         717 :   if (mToken.mType != eCSSToken_Whitespace) {
   13774         228 :     UngetToken();
   13775         228 :     return false;
   13776             :   }
   13777             :   // Skip any additional whitespace tokens.
   13778         489 :   if (GetToken(true)) {
   13779         489 :     UngetToken();
   13780             :   }
   13781         489 :   return true;
   13782             : }
   13783             : 
   13784             : bool
   13785          89 : CSSParserImpl::ParseRect(nsCSSPropertyID aPropID)
   13786             : {
   13787         178 :   nsCSSValue val;
   13788          89 :   if (ParseSingleTokenVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) {
   13789          10 :     AppendValue(aPropID, val);
   13790          10 :     return true;
   13791             :   }
   13792             : 
   13793          79 :   if (! GetToken(true)) {
   13794           0 :     return false;
   13795             :   }
   13796             : 
   13797         158 :   if (mToken.mType == eCSSToken_Function &&
   13798          79 :       mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
   13799          79 :     nsCSSRect& rect = val.SetRectValue();
   13800             :     bool useCommas;
   13801         395 :     NS_FOR_CSS_SIDES(side) {
   13802         316 :       if (!ParseSingleTokenVariant(rect.*(nsCSSRect::sides[side]),
   13803             :                                    VARIANT_AL, nullptr)) {
   13804           0 :         return false;
   13805             :       }
   13806         316 :       if (side == 0) {
   13807          79 :         useCommas = ExpectSymbol(',', true);
   13808         237 :       } else if (useCommas && side < 3) {
   13809             :         // Skip optional commas between elements, but only if the first
   13810             :         // separator was a comma.
   13811         158 :         if (!ExpectSymbol(',', true)) {
   13812           0 :           return false;
   13813             :         }
   13814             :       }
   13815             :     }
   13816          79 :     if (!ExpectSymbol(')', true)) {
   13817           0 :       return false;
   13818             :     }
   13819             :   } else {
   13820           0 :     UngetToken();
   13821           0 :     return false;
   13822             :   }
   13823             : 
   13824          79 :   AppendValue(aPropID, val);
   13825          79 :   return true;
   13826             : }
   13827             : 
   13828             : bool
   13829           0 : CSSParserImpl::ParseColumns()
   13830             : {
   13831             :   // We use a similar "fake value" hack to ParseListStyle, because
   13832             :   // "auto" is acceptable for both column-count and column-width.
   13833             :   // If the fake "auto" value is found, and one of the real values isn't,
   13834             :   // that means the fake auto value is meant for the real value we didn't
   13835             :   // find.
   13836             :   static const nsCSSPropertyID columnIDs[] = {
   13837             :     eCSSPropertyExtra_x_auto_value,
   13838             :     eCSSProperty_column_count,
   13839             :     eCSSProperty_column_width
   13840             :   };
   13841           0 :   const int32_t numProps = MOZ_ARRAY_LENGTH(columnIDs);
   13842             : 
   13843           0 :   nsCSSValue values[numProps];
   13844           0 :   int32_t found = ParseChoice(values, columnIDs, numProps);
   13845           0 :   if (found < 1) {
   13846           0 :     return false;
   13847             :   }
   13848           0 :   if ((found & (1|2|4)) == (1|2|4) &&
   13849           0 :       values[0].GetUnit() ==  eCSSUnit_Auto) {
   13850             :     // We filled all 3 values, which is invalid
   13851           0 :     return false;
   13852             :   }
   13853             : 
   13854           0 :   if ((found & 2) == 0) {
   13855             :     // Provide auto column-count
   13856           0 :     values[1].SetAutoValue();
   13857             :   }
   13858           0 :   if ((found & 4) == 0) {
   13859             :     // Provide auto column-width
   13860           0 :     values[2].SetAutoValue();
   13861             :   }
   13862             : 
   13863             :   // Start at index 1 to skip the fake auto value.
   13864           0 :   for (int32_t index = 1; index < numProps; index++) {
   13865           0 :     AppendValue(columnIDs[index], values[index]);
   13866             :   }
   13867           0 :   return true;
   13868             : }
   13869             : 
   13870             : #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
   13871             :                          VARIANT_KEYWORD)
   13872             : bool
   13873          27 : CSSParserImpl::ParseContent()
   13874             : {
   13875             :   // We need to divide the 'content' keywords into two classes for
   13876             :   // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable.
   13877             :   static const KTableEntry kContentListKWs[] = {
   13878             :     { eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE },
   13879             :     { eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE },
   13880             :     { eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE },
   13881             :     { eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE },
   13882             :     { eCSSKeyword_UNKNOWN, -1 }
   13883             :   };
   13884             : 
   13885             :   static const KTableEntry kContentSolitaryKWs[] = {
   13886             :     { eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT },
   13887             :     { eCSSKeyword_UNKNOWN, -1 }
   13888             :   };
   13889             : 
   13890             :   // Verify that these two lists add up to the size of
   13891             :   // nsCSSProps::kContentKTable.
   13892          27 :   MOZ_ASSERT(nsCSSProps::kContentKTable[
   13893             :                ArrayLength(kContentListKWs) +
   13894             :                ArrayLength(kContentSolitaryKWs) - 2].mKeyword ==
   13895             :                  eCSSKeyword_UNKNOWN &&
   13896             :              nsCSSProps::kContentKTable[
   13897             :                ArrayLength(kContentListKWs) +
   13898             :                ArrayLength(kContentSolitaryKWs) - 2].mValue == -1,
   13899             :              "content keyword tables out of sync");
   13900             : 
   13901          54 :   nsCSSValue value;
   13902             :   // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must
   13903             :   // be alone
   13904          27 :   if (!ParseSingleTokenVariant(value, VARIANT_HMK | VARIANT_NONE,
   13905             :                                kContentSolitaryKWs)) {
   13906          25 :     nsCSSValueList* cur = value.SetListValue();
   13907             :     for (;;) {
   13908          25 :       if (ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs) !=
   13909             :           CSSParseResult::Ok) {
   13910           0 :         return false;
   13911             :       }
   13912          25 :       if (CheckEndProperty()) {
   13913          25 :         break;
   13914             :       }
   13915           0 :       cur->mNext = new nsCSSValueList;
   13916           0 :       cur = cur->mNext;
   13917             :     }
   13918             :   }
   13919          27 :   AppendValue(eCSSProperty_content, value);
   13920          27 :   return true;
   13921             : }
   13922             : 
   13923             : bool
   13924           0 : CSSParserImpl::ParseCounterData(nsCSSPropertyID aPropID)
   13925             : {
   13926             :   static const nsCSSKeyword kCounterDataKTable[] = {
   13927             :     eCSSKeyword_none,
   13928             :     eCSSKeyword_UNKNOWN
   13929             :   };
   13930           0 :   nsCSSValue value;
   13931           0 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
   13932             :                                nullptr)) {
   13933           0 :     if (!GetToken(true)) {
   13934           0 :       return false;
   13935             :     }
   13936           0 :     if (mToken.mType != eCSSToken_Ident) {
   13937           0 :       UngetToken();
   13938           0 :       return false;
   13939             :     }
   13940             : 
   13941           0 :     nsCSSValuePairList *cur = value.SetPairListValue();
   13942             :     for (;;) {
   13943           0 :       if (!ParseCustomIdent(cur->mXValue, mToken.mIdent, kCounterDataKTable)) {
   13944           0 :         return false;
   13945             :       }
   13946           0 :       int32_t value = aPropID == eCSSProperty_counter_increment ? 1 : 0;
   13947           0 :       if (GetToken(true)) {
   13948           0 :         if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
   13949           0 :           value = mToken.mInteger;
   13950             :         } else {
   13951           0 :           UngetToken();
   13952             :         }
   13953             :       }
   13954           0 :       cur->mYValue.SetIntValue(value, eCSSUnit_Integer);
   13955           0 :       if (!GetToken(true)) {
   13956           0 :         break;
   13957             :       }
   13958           0 :       if (mToken.mType != eCSSToken_Ident) {
   13959           0 :         UngetToken();
   13960           0 :         break;
   13961             :       }
   13962           0 :       cur->mNext = new nsCSSValuePairList;
   13963           0 :       cur = cur->mNext;
   13964           0 :     }
   13965             :   }
   13966           0 :   AppendValue(aPropID, value);
   13967           0 :   return true;
   13968             : }
   13969             : 
   13970             : bool
   13971          67 : CSSParserImpl::ParseCursor()
   13972             : {
   13973         134 :   nsCSSValue value;
   13974             :   // 'inherit', 'initial' and 'unset' must be alone
   13975          67 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   13976          57 :     nsCSSValueList* cur = value.SetListValue();
   13977             :     for (;;) {
   13978          57 :       if (!ParseSingleTokenVariant(cur->mValue, VARIANT_UK,
   13979             :                                    nsCSSProps::kCursorKTable)) {
   13980           0 :         return false;
   13981             :       }
   13982          57 :       if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last
   13983          57 :         break;
   13984             :       }
   13985             : 
   13986             :       // We have a URL, so make a value array with three values.
   13987           0 :       RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
   13988           0 :       val->Item(0) = cur->mValue;
   13989             : 
   13990             :       // Parse optional x and y position of cursor hotspot (css3-ui).
   13991           0 :       if (ParseSingleTokenVariant(val->Item(1), VARIANT_NUMBER, nullptr)) {
   13992             :         // If we have one number, we must have two.
   13993           0 :         if (!ParseSingleTokenVariant(val->Item(2), VARIANT_NUMBER, nullptr)) {
   13994           0 :           return false;
   13995             :         }
   13996             :       }
   13997           0 :       cur->mValue.SetArrayValue(val, eCSSUnit_Array);
   13998             : 
   13999           0 :       if (!ExpectSymbol(',', true)) { // url must not be last
   14000           0 :         return false;
   14001             :       }
   14002           0 :       cur->mNext = new nsCSSValueList;
   14003           0 :       cur = cur->mNext;
   14004           0 :     }
   14005             :   }
   14006          67 :   AppendValue(eCSSProperty_cursor, value);
   14007          67 :   return true;
   14008             : }
   14009             : 
   14010             : 
   14011             : bool
   14012          31 : CSSParserImpl::ParseFont()
   14013             : {
   14014          62 :   nsCSSValue  family;
   14015          31 :   if (ParseSingleTokenVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
   14016          76 :     if (eCSSUnit_Inherit == family.GetUnit() ||
   14017          50 :         eCSSUnit_Initial == family.GetUnit() ||
   14018          24 :         eCSSUnit_Unset == family.GetUnit()) {
   14019           2 :       AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
   14020           2 :       AppendValue(eCSSProperty_font_family, family);
   14021           2 :       AppendValue(eCSSProperty_font_style, family);
   14022           2 :       AppendValue(eCSSProperty_font_weight, family);
   14023           2 :       AppendValue(eCSSProperty_font_size, family);
   14024           2 :       AppendValue(eCSSProperty_line_height, family);
   14025           2 :       AppendValue(eCSSProperty_font_stretch, family);
   14026           2 :       AppendValue(eCSSProperty_font_size_adjust, family);
   14027           2 :       AppendValue(eCSSProperty_font_feature_settings, family);
   14028           2 :       AppendValue(eCSSProperty_font_language_override, family);
   14029           2 :       AppendValue(eCSSProperty_font_kerning, family);
   14030           2 :       AppendValue(eCSSProperty_font_variant_alternates, family);
   14031           2 :       AppendValue(eCSSProperty_font_variant_caps, family);
   14032           2 :       AppendValue(eCSSProperty_font_variant_east_asian, family);
   14033           2 :       AppendValue(eCSSProperty_font_variant_ligatures, family);
   14034           2 :       AppendValue(eCSSProperty_font_variant_numeric, family);
   14035           2 :       AppendValue(eCSSProperty_font_variant_position, family);
   14036             :     }
   14037             :     else {
   14038          24 :       AppendValue(eCSSProperty__x_system_font, family);
   14039          48 :       nsCSSValue systemFont(eCSSUnit_System_Font);
   14040          24 :       AppendValue(eCSSProperty_font_family, systemFont);
   14041          24 :       AppendValue(eCSSProperty_font_style, systemFont);
   14042          24 :       AppendValue(eCSSProperty_font_weight, systemFont);
   14043          24 :       AppendValue(eCSSProperty_font_size, systemFont);
   14044          24 :       AppendValue(eCSSProperty_line_height, systemFont);
   14045          24 :       AppendValue(eCSSProperty_font_stretch, systemFont);
   14046          24 :       AppendValue(eCSSProperty_font_size_adjust, systemFont);
   14047          24 :       AppendValue(eCSSProperty_font_feature_settings, systemFont);
   14048          24 :       AppendValue(eCSSProperty_font_language_override, systemFont);
   14049          24 :       AppendValue(eCSSProperty_font_kerning, systemFont);
   14050          24 :       AppendValue(eCSSProperty_font_variant_alternates, systemFont);
   14051          24 :       AppendValue(eCSSProperty_font_variant_caps, systemFont);
   14052          24 :       AppendValue(eCSSProperty_font_variant_east_asian, systemFont);
   14053          24 :       AppendValue(eCSSProperty_font_variant_ligatures, systemFont);
   14054          24 :       AppendValue(eCSSProperty_font_variant_numeric, systemFont);
   14055          24 :       AppendValue(eCSSProperty_font_variant_position, systemFont);
   14056             :     }
   14057          26 :     return true;
   14058             :   }
   14059             : 
   14060             :   // Get optional font-style, font-variant, font-weight, font-stretch
   14061             :   // (in any order)
   14062             : 
   14063             :   // Indexes into fontIDs[] and values[] arrays.
   14064           5 :   const int kFontStyleIndex = 0;
   14065           5 :   const int kFontVariantIndex = 1;
   14066           5 :   const int kFontWeightIndex = 2;
   14067           5 :   const int kFontStretchIndex = 3;
   14068             : 
   14069             :   // The order of the initializers here must match the order of the indexes
   14070             :   // defined above!
   14071             :   static const nsCSSPropertyID fontIDs[] = {
   14072             :     eCSSProperty_font_style,
   14073             :     eCSSProperty_font_variant_caps,
   14074             :     eCSSProperty_font_weight,
   14075             :     eCSSProperty_font_stretch
   14076             :   };
   14077             : 
   14078           5 :   const int32_t numProps = MOZ_ARRAY_LENGTH(fontIDs);
   14079          10 :   nsCSSValue  values[numProps];
   14080           5 :   int32_t found = ParseChoice(values, fontIDs, numProps);
   14081          10 :   if (found < 0 ||
   14082          10 :       eCSSUnit_Inherit == values[kFontStyleIndex].GetUnit() ||
   14083          15 :       eCSSUnit_Initial == values[kFontStyleIndex].GetUnit() ||
   14084           5 :       eCSSUnit_Unset == values[kFontStyleIndex].GetUnit()) { // illegal data
   14085           0 :     return false;
   14086             :   }
   14087           5 :   if ((found & (1 << kFontStyleIndex)) == 0) {
   14088             :     // Provide default font-style
   14089             :     values[kFontStyleIndex].SetIntValue(NS_FONT_STYLE_NORMAL,
   14090           5 :                                         eCSSUnit_Enumerated);
   14091             :   }
   14092           5 :   if ((found & (1 << kFontVariantIndex)) == 0) {
   14093             :     // Provide default font-variant
   14094           5 :     values[kFontVariantIndex].SetNormalValue();
   14095             :   } else {
   14096           0 :     if (values[kFontVariantIndex].GetUnit() == eCSSUnit_Enumerated &&
   14097           0 :         values[kFontVariantIndex].GetIntValue() !=
   14098             :         NS_FONT_VARIANT_CAPS_SMALLCAPS) {
   14099             :       // only normal or small-caps is allowed in font shorthand
   14100             :       // this also assumes other values for font-variant-caps never overlap
   14101             :       // possible values for style or weight
   14102           0 :       return false;
   14103             :     }
   14104             :   }
   14105           5 :   if ((found & (1 << kFontWeightIndex)) == 0) {
   14106             :     // Provide default font-weight
   14107             :     values[kFontWeightIndex].SetIntValue(NS_FONT_WEIGHT_NORMAL,
   14108           5 :                                          eCSSUnit_Enumerated);
   14109             :   }
   14110           5 :   if ((found & (1 << kFontStretchIndex)) == 0) {
   14111             :     // Provide default font-stretch
   14112             :     values[kFontStretchIndex].SetIntValue(NS_FONT_STRETCH_NORMAL,
   14113           5 :                                           eCSSUnit_Enumerated);
   14114             :   }
   14115             : 
   14116             :   // Get mandatory font-size
   14117          10 :   nsCSSValue  size;
   14118           5 :   if (!ParseSingleTokenNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP,
   14119             :                                           nsCSSProps::kFontSizeKTable)) {
   14120           2 :     return false;
   14121             :   }
   14122             : 
   14123             :   // Get optional "/" line-height
   14124           6 :   nsCSSValue  lineHeight;
   14125           3 :   if (ExpectSymbol('/', true)) {
   14126           0 :     if (ParseNonNegativeVariant(lineHeight,
   14127             :                                 VARIANT_NUMBER | VARIANT_LP |
   14128             :                                   VARIANT_NORMAL | VARIANT_CALC,
   14129             :                                 nullptr) != CSSParseResult::Ok) {
   14130           0 :       return false;
   14131             :     }
   14132             :   }
   14133             :   else {
   14134           3 :     lineHeight.SetNormalValue();
   14135             :   }
   14136             : 
   14137             :   // Get final mandatory font-family
   14138           6 :   nsAutoParseCompoundProperty compound(this);
   14139           3 :   if (ParseFamily(family)) {
   14140           9 :     if (eCSSUnit_Inherit != family.GetUnit() &&
   14141           6 :         eCSSUnit_Initial != family.GetUnit() &&
   14142           3 :         eCSSUnit_Unset != family.GetUnit()) {
   14143           3 :       AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
   14144           3 :       AppendValue(eCSSProperty_font_family, family);
   14145           3 :       AppendValue(eCSSProperty_font_style, values[kFontStyleIndex]);
   14146           3 :       AppendValue(eCSSProperty_font_variant_caps, values[kFontVariantIndex]);
   14147           3 :       AppendValue(eCSSProperty_font_weight, values[kFontWeightIndex]);
   14148           3 :       AppendValue(eCSSProperty_font_size, size);
   14149           3 :       AppendValue(eCSSProperty_line_height, lineHeight);
   14150           3 :       AppendValue(eCSSProperty_font_stretch, values[kFontStretchIndex]);
   14151           3 :       AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
   14152           3 :       AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal));
   14153           3 :       AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal));
   14154             :       AppendValue(eCSSProperty_font_kerning,
   14155           3 :                   nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated));
   14156             :       AppendValue(eCSSProperty_font_variant_alternates,
   14157           3 :                   nsCSSValue(eCSSUnit_Normal));
   14158             :       AppendValue(eCSSProperty_font_variant_east_asian,
   14159           3 :                   nsCSSValue(eCSSUnit_Normal));
   14160             :       AppendValue(eCSSProperty_font_variant_ligatures,
   14161           3 :                   nsCSSValue(eCSSUnit_Normal));
   14162             :       AppendValue(eCSSProperty_font_variant_numeric,
   14163           3 :                   nsCSSValue(eCSSUnit_Normal));
   14164             :       AppendValue(eCSSProperty_font_variant_position,
   14165           3 :                   nsCSSValue(eCSSUnit_Normal));
   14166           3 :       return true;
   14167             :     }
   14168             :   }
   14169           0 :   return false;
   14170             : }
   14171             : 
   14172             : bool
   14173           0 : CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue)
   14174             : {
   14175           0 :   if (!ParseSingleTokenVariant(aValue, VARIANT_HK | VARIANT_NONE,
   14176             :                                nsCSSProps::kFontSynthesisKTable)) {
   14177           0 :     return false;
   14178             :   }
   14179             : 
   14180             :   // first value 'none' ==> done
   14181           0 :   if (eCSSUnit_None == aValue.GetUnit() ||
   14182           0 :       eCSSUnit_Initial == aValue.GetUnit() ||
   14183           0 :       eCSSUnit_Inherit == aValue.GetUnit() ||
   14184           0 :       eCSSUnit_Unset == aValue.GetUnit())
   14185             :   {
   14186           0 :     return true;
   14187             :   }
   14188             : 
   14189             :   // look for a second value
   14190           0 :   int32_t intValue = aValue.GetIntValue();
   14191           0 :   nsCSSValue nextValue;
   14192             : 
   14193           0 :   if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) {
   14194           0 :     int32_t nextIntValue = nextValue.GetIntValue();
   14195           0 :     if (nextIntValue & intValue) {
   14196           0 :       return false;
   14197             :     }
   14198           0 :     aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
   14199             :   }
   14200             : 
   14201           0 :   return true;
   14202             : }
   14203             : 
   14204             : // font-variant-alternates allows for a combination of multiple
   14205             : // simple enumerated values and functional values.  Functional values have
   14206             : // parameter lists with one or more idents which are later resolved
   14207             : // based on values defined in @font-feature-value rules.
   14208             : //
   14209             : // font-variant-alternates: swash(flowing) historical-forms styleset(alt-g, alt-m);
   14210             : //
   14211             : // So for this the nsCSSValue is set to a pair value, with one
   14212             : // value for a bitmask of both simple and functional property values
   14213             : // and another value containing a ValuePairList with lists of idents
   14214             : // for each functional property value.
   14215             : //
   14216             : // pairValue
   14217             : //   o intValue
   14218             : //       NS_FONT_VARIANT_ALTERNATES_SWASH |
   14219             : //       NS_FONT_VARIANT_ALTERNATES_STYLESET
   14220             : //   o valuePairList, each element with
   14221             : //     - intValue - indicates which alternate
   14222             : //     - string or valueList of strings
   14223             : //
   14224             : // Note: when only 'historical-forms' is specified, there are no
   14225             : // functional values to store, in which case the valuePairList is a
   14226             : // single element dummy list.  In all other cases, the length of the
   14227             : // list will match the number of functional values.
   14228             : 
   14229             : #define MAX_ALLOWED_FEATURES 512
   14230             : 
   14231             : static uint16_t
   14232           0 : MaxElementsForAlternateType(nsCSSKeyword keyword)
   14233             : {
   14234           0 :   uint16_t maxElems = 1;
   14235           0 :   if (keyword == eCSSKeyword_styleset ||
   14236             :       keyword == eCSSKeyword_character_variant) {
   14237           0 :     maxElems = MAX_ALLOWED_FEATURES;
   14238             :   }
   14239           0 :   return maxElems;
   14240             : }
   14241             : 
   14242             : bool
   14243           0 : CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature,
   14244             :                                     nsCSSValue& aValue)
   14245             : {
   14246           0 :   if (!GetToken(true)) {
   14247           0 :     return false;
   14248             :   }
   14249             : 
   14250           0 :   bool isIdent = (mToken.mType == eCSSToken_Ident);
   14251           0 :   if (mToken.mType != eCSSToken_Function && !isIdent) {
   14252           0 :     UngetToken();
   14253           0 :     return false;
   14254             :   }
   14255             : 
   14256             :   // ident ==> simple enumerated prop val (e.g. historical-forms)
   14257             :   // function ==> e.g. swash(flowing) styleset(alt-g, alt-m)
   14258             : 
   14259           0 :   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
   14260           0 :   if (!nsCSSProps::FindKeyword(keyword,
   14261             :                                (isIdent ?
   14262             :                                 nsCSSProps::kFontVariantAlternatesKTable :
   14263             :                                 nsCSSProps::kFontVariantAlternatesFuncsKTable),
   14264             :                                aWhichFeature))
   14265             :   {
   14266             :     // failed, pop token
   14267           0 :     UngetToken();
   14268           0 :     return false;
   14269             :   }
   14270             : 
   14271           0 :   if (isIdent) {
   14272           0 :     aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated);
   14273           0 :     return true;
   14274             :   }
   14275             : 
   14276           0 :   return ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER,
   14277           0 :                        1, MaxElementsForAlternateType(keyword), aValue);
   14278             : }
   14279             : 
   14280             : bool
   14281           0 : CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue)
   14282             : {
   14283           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
   14284             :                               nullptr)) {
   14285           0 :     return true;
   14286             :   }
   14287             : 
   14288             :   // iterate through parameters
   14289           0 :   nsCSSValue listValue;
   14290           0 :   int32_t feature, featureFlags = 0;
   14291             : 
   14292             :   // if no functional values, this may be a list with a single, unused element
   14293           0 :   listValue.SetListValue();
   14294             : 
   14295           0 :   nsCSSValueList* list = nullptr;
   14296           0 :   nsCSSValue value;
   14297           0 :   while (ParseSingleAlternate(feature, value)) {
   14298             : 
   14299             :     // check to make sure value not already set
   14300           0 :     if (feature == 0 ||
   14301           0 :         feature & featureFlags) {
   14302           0 :       return false;
   14303             :     }
   14304             : 
   14305           0 :     featureFlags |= feature;
   14306             : 
   14307             :     // if function, need to add to the list of functions
   14308           0 :     if (value.GetUnit() == eCSSUnit_Function) {
   14309           0 :       if (!list) {
   14310           0 :         list = listValue.GetListValue();
   14311             :       } else {
   14312           0 :         list->mNext = new nsCSSValueList;
   14313           0 :         list = list->mNext;
   14314             :       }
   14315           0 :       list->mValue = value;
   14316             :     }
   14317             :   }
   14318             : 
   14319           0 :   if (featureFlags == 0) {
   14320             :     // ParseSingleAlternate failed the first time through the loop.
   14321           0 :     return false;
   14322             :   }
   14323             : 
   14324           0 :   nsCSSValue featureValue;
   14325           0 :   featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated);
   14326           0 :   aValue.SetPairValue(featureValue, listValue);
   14327             : 
   14328           0 :   return true;
   14329             : }
   14330             : 
   14331             : bool
   14332           0 : CSSParserImpl::MergeBitmaskValue(int32_t aNewValue,
   14333             :                                  const int32_t aMasks[],
   14334             :                                  int32_t& aMergedValue)
   14335             : {
   14336             :   // check to make sure value not already set
   14337           0 :   if (aNewValue & aMergedValue) {
   14338           0 :     return false;
   14339             :   }
   14340             : 
   14341           0 :   const int32_t *m = aMasks;
   14342           0 :   int32_t c = 0;
   14343             : 
   14344           0 :   while (*m != MASK_END_VALUE) {
   14345           0 :     if (*m & aNewValue) {
   14346           0 :       c = aMergedValue & *m;
   14347           0 :       break;
   14348             :     }
   14349           0 :     m++;
   14350             :   }
   14351             : 
   14352           0 :   if (c) {
   14353           0 :     return false;
   14354             :   }
   14355             : 
   14356           0 :   aMergedValue |= aNewValue;
   14357           0 :   return true;
   14358             : }
   14359             : 
   14360             : // aMasks - array of masks for mutually-exclusive property values,
   14361             : //          e.g. proportial-nums, tabular-nums
   14362             : 
   14363             : bool
   14364           4 : CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue,
   14365             :                                   const KTableEntry aKeywordTable[],
   14366             :                                   const int32_t aMasks[])
   14367             : {
   14368             :   // Parse at least one keyword
   14369           4 :   if (!ParseEnum(aValue, aKeywordTable)) {
   14370           0 :     return false;
   14371             :   }
   14372             : 
   14373             :   // look for more values
   14374           8 :   nsCSSValue nextValue;
   14375           4 :   int32_t mergedValue = aValue.GetIntValue();
   14376             : 
   14377           4 :   while (ParseEnum(nextValue, aKeywordTable))
   14378             :   {
   14379           0 :     if (!MergeBitmaskValue(nextValue.GetIntValue(), aMasks, mergedValue)) {
   14380           0 :       return false;
   14381             :     }
   14382             :   }
   14383             : 
   14384           4 :   aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated);
   14385             : 
   14386           4 :   return true;
   14387             : }
   14388             : 
   14389             : static const int32_t maskEastAsian[] = {
   14390             :   NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK,
   14391             :   NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK,
   14392             :   MASK_END_VALUE
   14393             : };
   14394             : 
   14395             : bool
   14396           2 : CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue)
   14397             : {
   14398           2 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
   14399             :                               nullptr)) {
   14400           0 :     return true;
   14401             :   }
   14402             : 
   14403           2 :   NS_ASSERTION(maskEastAsian[ArrayLength(maskEastAsian) - 1] ==
   14404             :                  MASK_END_VALUE,
   14405             :                "incorrectly terminated array");
   14406             : 
   14407             :   return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable,
   14408           2 :                             maskEastAsian);
   14409             : }
   14410             : 
   14411             : bool
   14412           0 : CSSParserImpl::ParseContain(nsCSSValue& aValue)
   14413             : {
   14414           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE,
   14415             :                               nullptr)) {
   14416           0 :     return true;
   14417             :   }
   14418             :   static const int32_t maskContain[] = { MASK_END_VALUE };
   14419           0 :   if (!ParseBitmaskValues(aValue, nsCSSProps::kContainKTable, maskContain)) {
   14420           0 :     return false;
   14421             :   }
   14422           0 :   if (aValue.GetIntValue() & NS_STYLE_CONTAIN_STRICT) {
   14423           0 :     if (aValue.GetIntValue() != NS_STYLE_CONTAIN_STRICT) {
   14424             :       // Disallow any other keywords in combination with 'strict'.
   14425           0 :       return false;
   14426             :     }
   14427             :     // Strict implies layout, style, and paint.
   14428             :     // However, for serialization purposes, we keep the strict bit around.
   14429             :     aValue.SetIntValue(NS_STYLE_CONTAIN_STRICT |
   14430           0 :         NS_STYLE_CONTAIN_ALL_BITS, eCSSUnit_Enumerated);
   14431             :   }
   14432           0 :   return true;
   14433             : }
   14434             : 
   14435             : static const int32_t maskLigatures[] = {
   14436             :   NS_FONT_VARIANT_LIGATURES_COMMON_MASK,
   14437             :   NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK,
   14438             :   NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK,
   14439             :   NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK,
   14440             :   MASK_END_VALUE
   14441             : };
   14442             : 
   14443             : bool
   14444           0 : CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue)
   14445             : {
   14446           0 :   if (ParseSingleTokenVariant(aValue,
   14447             :                               VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE,
   14448             :                               nullptr)) {
   14449           0 :     return true;
   14450             :   }
   14451             : 
   14452           0 :   NS_ASSERTION(maskLigatures[ArrayLength(maskLigatures) - 1] ==
   14453             :                  MASK_END_VALUE,
   14454             :                "incorrectly terminated array");
   14455             : 
   14456             :   return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable,
   14457           0 :                             maskLigatures);
   14458             : }
   14459             : 
   14460             : static const int32_t maskNumeric[] = {
   14461             :   NS_FONT_VARIANT_NUMERIC_FIGURE_MASK,
   14462             :   NS_FONT_VARIANT_NUMERIC_SPACING_MASK,
   14463             :   NS_FONT_VARIANT_NUMERIC_FRACTION_MASK,
   14464             :   MASK_END_VALUE
   14465             : };
   14466             : 
   14467             : bool
   14468           2 : CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue)
   14469             : {
   14470           2 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
   14471             :                               nullptr)) {
   14472           0 :     return true;
   14473             :   }
   14474             : 
   14475           2 :   NS_ASSERTION(maskNumeric[ArrayLength(maskNumeric) - 1] ==
   14476             :                  MASK_END_VALUE,
   14477             :                "incorrectly terminated array");
   14478             : 
   14479             :   return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable,
   14480           2 :                             maskNumeric);
   14481             : }
   14482             : 
   14483             : bool
   14484           3 : CSSParserImpl::ParseFontVariant()
   14485             : {
   14486             :   // parse single values - normal/inherit/none
   14487           6 :   nsCSSValue value;
   14488           6 :   nsCSSValue normal(eCSSUnit_Normal);
   14489             : 
   14490           3 :   if (ParseSingleTokenVariant(value,
   14491             :                               VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE,
   14492             :                               nullptr)) {
   14493           2 :     AppendValue(eCSSProperty_font_variant_ligatures, value);
   14494           2 :     if (eCSSUnit_None == value.GetUnit()) {
   14495             :       // 'none' applies the value 'normal' to all properties other
   14496             :       // than 'font-variant-ligatures'
   14497           0 :       value.SetNormalValue();
   14498             :     }
   14499           2 :     AppendValue(eCSSProperty_font_variant_alternates, value);
   14500           2 :     AppendValue(eCSSProperty_font_variant_caps, value);
   14501           2 :     AppendValue(eCSSProperty_font_variant_east_asian, value);
   14502           2 :     AppendValue(eCSSProperty_font_variant_numeric, value);
   14503           2 :     AppendValue(eCSSProperty_font_variant_position, value);
   14504           2 :     return true;
   14505             :   }
   14506             : 
   14507             :   // set each of the individual subproperties
   14508           1 :   int32_t altFeatures = 0, capsFeatures = 0, eastAsianFeatures = 0,
   14509           1 :           ligFeatures = 0, numericFeatures = 0, posFeatures = 0;
   14510           2 :   nsCSSValue altListValue;
   14511           1 :   nsCSSValueList* altList = nullptr;
   14512             : 
   14513             :   // if no functional values, this may be a list with a single, unused element
   14514           1 :   altListValue.SetListValue();
   14515             : 
   14516           1 :   bool foundValid = false; // found at least one proper value
   14517           3 :   while (GetToken(true)) {
   14518             :     // only an ident or a function at this point
   14519           2 :     bool isFunction = (mToken.mType == eCSSToken_Function);
   14520           2 :     if (mToken.mType != eCSSToken_Ident && !isFunction) {
   14521           1 :       UngetToken();
   14522           1 :       break;
   14523             :     }
   14524             : 
   14525           1 :     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
   14526           1 :     if (keyword == eCSSKeyword_UNKNOWN) {
   14527           0 :       UngetToken();
   14528           0 :       return false;
   14529             :     }
   14530             : 
   14531             :     int32_t feature;
   14532             : 
   14533             :     // function? ==> font-variant-alternates
   14534           1 :     if (isFunction) {
   14535           0 :       if (!nsCSSProps::FindKeyword(keyword,
   14536             :                                    nsCSSProps::kFontVariantAlternatesFuncsKTable,
   14537           0 :                                    feature) ||
   14538           0 :           (feature & altFeatures)) {
   14539           0 :         UngetToken();
   14540           0 :         return false;
   14541             :       }
   14542             : 
   14543           0 :       altFeatures |= feature;
   14544           0 :       nsCSSValue funcValue;
   14545           0 :       if (!ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER, 1,
   14546           0 :                          MaxElementsForAlternateType(keyword), funcValue) ||
   14547           0 :           funcValue.GetUnit() != eCSSUnit_Function) {
   14548           0 :         UngetToken();
   14549           0 :         return false;
   14550             :       }
   14551             : 
   14552           0 :       if (!altList) {
   14553           0 :         altList = altListValue.GetListValue();
   14554             :       } else {
   14555           0 :         altList->mNext = new nsCSSValueList;
   14556           0 :         altList = altList->mNext;
   14557             :       }
   14558           0 :       altList->mValue = funcValue;
   14559           1 :     } else if (nsCSSProps::FindKeyword(keyword,
   14560             :                                        nsCSSProps::kFontVariantCapsKTable,
   14561             :                                        feature)) {
   14562           1 :       if (capsFeatures != 0) {
   14563             :         // multiple values for font-variant-caps
   14564           0 :         UngetToken();
   14565           0 :         return false;
   14566             :       }
   14567           1 :       capsFeatures = feature;
   14568           0 :     } else if (nsCSSProps::FindKeyword(keyword,
   14569             :                                        nsCSSProps::kFontVariantAlternatesKTable,
   14570             :                                        feature)) {
   14571           0 :       if (feature & altFeatures) {
   14572             :         // same value repeated
   14573           0 :         UngetToken();
   14574           0 :         return false;
   14575             :       }
   14576           0 :       altFeatures |= feature;
   14577           0 :     } else if (nsCSSProps::FindKeyword(keyword,
   14578             :                                        nsCSSProps::kFontVariantEastAsianKTable,
   14579             :                                        feature)) {
   14580           0 :       if (!MergeBitmaskValue(feature, maskEastAsian, eastAsianFeatures)) {
   14581             :         // multiple mutually exclusive values
   14582           0 :         UngetToken();
   14583           0 :         return false;
   14584             :       }
   14585           0 :     } else if (nsCSSProps::FindKeyword(keyword,
   14586             :                                        nsCSSProps::kFontVariantLigaturesKTable,
   14587             :                                        feature)) {
   14588           0 :       if (keyword == eCSSKeyword_none ||
   14589           0 :           !MergeBitmaskValue(feature, maskLigatures, ligFeatures)) {
   14590             :         // none or multiple mutually exclusive values
   14591           0 :         UngetToken();
   14592           0 :         return false;
   14593             :       }
   14594           0 :     } else if (nsCSSProps::FindKeyword(keyword,
   14595             :                                        nsCSSProps::kFontVariantNumericKTable,
   14596             :                                        feature)) {
   14597           0 :       if (!MergeBitmaskValue(feature, maskNumeric, numericFeatures)) {
   14598             :         // multiple mutually exclusive values
   14599           0 :         UngetToken();
   14600           0 :         return false;
   14601             :       }
   14602           0 :     } else if (nsCSSProps::FindKeyword(keyword,
   14603             :                                        nsCSSProps::kFontVariantPositionKTable,
   14604             :                                        feature)) {
   14605           0 :       if (posFeatures != 0) {
   14606             :         // multiple values for font-variant-caps
   14607           0 :         UngetToken();
   14608           0 :         return false;
   14609             :       }
   14610           0 :       posFeatures = feature;
   14611             :     } else {
   14612             :       // bogus keyword, bail...
   14613           0 :       UngetToken();
   14614           0 :       return false;
   14615             :     }
   14616             : 
   14617           1 :     foundValid = true;
   14618             :   }
   14619             : 
   14620           1 :   if (!foundValid) {
   14621           0 :     return false;
   14622             :   }
   14623             : 
   14624           1 :   if (altFeatures) {
   14625           0 :     nsCSSValue featureValue;
   14626           0 :     featureValue.SetIntValue(altFeatures, eCSSUnit_Enumerated);
   14627           0 :     value.SetPairValue(featureValue, altListValue);
   14628           0 :     AppendValue(eCSSProperty_font_variant_alternates, value);
   14629             :   } else {
   14630           1 :     AppendValue(eCSSProperty_font_variant_alternates, normal);
   14631             :   }
   14632             : 
   14633           1 :   if (capsFeatures) {
   14634           1 :     value.SetIntValue(capsFeatures, eCSSUnit_Enumerated);
   14635           1 :     AppendValue(eCSSProperty_font_variant_caps, value);
   14636             :   } else {
   14637           0 :     AppendValue(eCSSProperty_font_variant_caps, normal);
   14638             :   }
   14639             : 
   14640           1 :   if (eastAsianFeatures) {
   14641           0 :     value.SetIntValue(eastAsianFeatures, eCSSUnit_Enumerated);
   14642           0 :     AppendValue(eCSSProperty_font_variant_east_asian, value);
   14643             :   } else {
   14644           1 :     AppendValue(eCSSProperty_font_variant_east_asian, normal);
   14645             :   }
   14646             : 
   14647           1 :   if (ligFeatures) {
   14648           0 :     value.SetIntValue(ligFeatures, eCSSUnit_Enumerated);
   14649           0 :     AppendValue(eCSSProperty_font_variant_ligatures, value);
   14650             :   } else {
   14651           1 :     AppendValue(eCSSProperty_font_variant_ligatures, normal);
   14652             :   }
   14653             : 
   14654           1 :   if (numericFeatures) {
   14655           0 :     value.SetIntValue(numericFeatures, eCSSUnit_Enumerated);
   14656           0 :     AppendValue(eCSSProperty_font_variant_numeric, value);
   14657             :   } else {
   14658           1 :     AppendValue(eCSSProperty_font_variant_numeric, normal);
   14659             :   }
   14660             : 
   14661           1 :   if (posFeatures) {
   14662           0 :     value.SetIntValue(posFeatures, eCSSUnit_Enumerated);
   14663           0 :     AppendValue(eCSSProperty_font_variant_position, value);
   14664             :   } else {
   14665           1 :     AppendValue(eCSSProperty_font_variant_position, normal);
   14666             :   }
   14667             : 
   14668           1 :   return true;
   14669             : }
   14670             : 
   14671             : bool
   14672          54 : CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
   14673             : {
   14674          54 :   if (ParseSingleTokenVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT,
   14675             :                               nsCSSProps::kFontWeightKTable)) {
   14676          49 :     if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
   14677           3 :       int32_t intValue = aValue.GetIntValue();
   14678           3 :       if ((100 <= intValue) &&
   14679           3 :           (intValue <= 900) &&
   14680           3 :           (0 == (intValue % 100))) {
   14681           3 :         return true;
   14682             :       } else {
   14683           0 :         UngetToken();
   14684           0 :         return false;
   14685             :       }
   14686             :     }
   14687          46 :     return true;
   14688             :   }
   14689           5 :   return false;
   14690             : }
   14691             : 
   14692             : bool
   14693          19 : CSSParserImpl::ParseOneFamily(nsAString& aFamily,
   14694             :                               bool& aOneKeyword,
   14695             :                               bool& aQuoted)
   14696             : {
   14697          19 :   if (!GetToken(true))
   14698           0 :     return false;
   14699             : 
   14700          19 :   nsCSSToken* tk = &mToken;
   14701             : 
   14702          19 :   aOneKeyword = false;
   14703          19 :   aQuoted = false;
   14704          19 :   if (eCSSToken_Ident == tk->mType) {
   14705          19 :     aOneKeyword = true;
   14706          19 :     aFamily.Append(tk->mIdent);
   14707             :     for (;;) {
   14708          19 :       if (!GetToken(false))
   14709           0 :         break;
   14710             : 
   14711          19 :       if (eCSSToken_Ident == tk->mType) {
   14712           0 :         aOneKeyword = false;
   14713             :         // We had at least another keyword before.
   14714             :         // "If a sequence of identifiers is given as a font family name,
   14715             :         //  the computed value is the name converted to a string by joining
   14716             :         //  all the identifiers in the sequence by single spaces."
   14717             :         // -- CSS 2.1, section 15.3
   14718             :         // Whitespace tokens do not actually matter,
   14719             :         // identifier tokens can be separated by comments.
   14720           0 :         aFamily.Append(char16_t(' '));
   14721           0 :         aFamily.Append(tk->mIdent);
   14722          19 :       } else if (eCSSToken_Whitespace != tk->mType) {
   14723          19 :         UngetToken();
   14724          19 :         break;
   14725             :       }
   14726             :     }
   14727          19 :     return true;
   14728             : 
   14729           0 :   } else if (eCSSToken_String == tk->mType) {
   14730           0 :     aQuoted = true;
   14731           0 :     aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
   14732           0 :     return true;
   14733             : 
   14734             :   } else {
   14735           0 :     UngetToken();
   14736           0 :     return false;
   14737             :   }
   14738             : }
   14739             : 
   14740             : 
   14741             : static bool
   14742          19 : AppendGeneric(nsCSSKeyword aKeyword, FontFamilyList *aFamilyList)
   14743             : {
   14744          19 :   switch (aKeyword) {
   14745             :     case eCSSKeyword_serif:
   14746           0 :       aFamilyList->Append(FontFamilyName(eFamily_serif));
   14747           0 :       return true;
   14748             :     case eCSSKeyword_sans_serif:
   14749           5 :       aFamilyList->Append(FontFamilyName(eFamily_sans_serif));
   14750           5 :       return true;
   14751             :     case eCSSKeyword_monospace:
   14752           1 :       aFamilyList->Append(FontFamilyName(eFamily_monospace));
   14753           1 :       return true;
   14754             :     case eCSSKeyword_cursive:
   14755           0 :       aFamilyList->Append(FontFamilyName(eFamily_cursive));
   14756           0 :       return true;
   14757             :     case eCSSKeyword_fantasy:
   14758           0 :       aFamilyList->Append(FontFamilyName(eFamily_fantasy));
   14759           0 :       return true;
   14760             :     case eCSSKeyword__moz_fixed:
   14761          10 :       aFamilyList->Append(FontFamilyName(eFamily_moz_fixed));
   14762          10 :       return true;
   14763             :     default:
   14764           3 :       break;
   14765             :   }
   14766             : 
   14767           3 :   return false;
   14768             : }
   14769             : 
   14770             : bool
   14771          16 : CSSParserImpl::ParseFamily(nsCSSValue& aValue)
   14772             : {
   14773             :   RefPtr<css::FontFamilyListRefCnt> familyList =
   14774          32 :     new css::FontFamilyListRefCnt();
   14775          32 :   nsAutoString family;
   14776             :   bool single, quoted;
   14777             : 
   14778             :   // keywords only have meaning in the first position
   14779          16 :   if (!ParseOneFamily(family, single, quoted))
   14780           0 :     return false;
   14781             : 
   14782             :   // check for keywords, but only when keywords appear by themselves
   14783             :   // i.e. not in compounds such as font-family: default blah;
   14784          16 :   bool foundGeneric = false;
   14785          16 :   if (single) {
   14786          16 :     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
   14787          16 :     switch (keyword) {
   14788             :       case eCSSKeyword_inherit:
   14789           0 :         aValue.SetInheritValue();
   14790           0 :         return true;
   14791             :       case eCSSKeyword_default:
   14792             :         // 605231 - don't parse unquoted 'default' reserved keyword
   14793           0 :         return false;
   14794             :       case eCSSKeyword_initial:
   14795           0 :         aValue.SetInitialValue();
   14796           0 :         return true;
   14797             :       case eCSSKeyword_unset:
   14798           0 :         if (nsLayoutUtils::UnsetValueEnabled()) {
   14799           0 :           aValue.SetUnsetValue();
   14800           0 :           return true;
   14801             :         }
   14802           0 :         break;
   14803             :       case eCSSKeyword__moz_use_system_font:
   14804           0 :         if (!IsParsingCompoundProperty()) {
   14805           0 :           aValue.SetSystemFontValue();
   14806           0 :           return true;
   14807             :         }
   14808           0 :         break;
   14809             :       default:
   14810          16 :         foundGeneric = AppendGeneric(keyword, familyList);
   14811             :     }
   14812             :   }
   14813             : 
   14814          16 :   if (!foundGeneric) {
   14815           1 :     familyList->Append(
   14816           2 :       FontFamilyName(family, (quoted ? eQuotedName : eUnquotedName)));
   14817             :   }
   14818             : 
   14819             :   for (;;) {
   14820          19 :     if (!ExpectSymbol(',', true))
   14821          16 :       break;
   14822             : 
   14823           6 :     nsAutoString nextFamily;
   14824           3 :     if (!ParseOneFamily(nextFamily, single, quoted))
   14825           0 :       return false;
   14826             : 
   14827             :     // at this point unquoted keywords are not allowed
   14828             :     // as font family names but can appear within names
   14829           3 :     foundGeneric = false;
   14830           3 :     if (single) {
   14831           3 :       nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily);
   14832           3 :       switch (keyword) {
   14833             :         case eCSSKeyword_inherit:
   14834             :         case eCSSKeyword_initial:
   14835             :         case eCSSKeyword_default:
   14836             :         case eCSSKeyword__moz_use_system_font:
   14837           0 :           return false;
   14838             :         case eCSSKeyword_unset:
   14839           0 :           if (nsLayoutUtils::UnsetValueEnabled()) {
   14840           0 :             return false;
   14841             :           }
   14842           0 :           break;
   14843             :         default:
   14844           3 :           foundGeneric = AppendGeneric(keyword, familyList);
   14845           3 :           break;
   14846             :       }
   14847             :     }
   14848             : 
   14849           3 :     if (!foundGeneric) {
   14850           2 :       familyList->Append(
   14851           4 :         FontFamilyName(nextFamily, (quoted ? eQuotedName : eUnquotedName)));
   14852             :     }
   14853           3 :   }
   14854             : 
   14855          16 :   if (familyList->IsEmpty()) {
   14856           0 :     return false;
   14857             :   }
   14858             : 
   14859          16 :   aValue.SetFontFamilyListValue(familyList);
   14860          16 :   return true;
   14861             : }
   14862             : 
   14863             : // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
   14864             : // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
   14865             : // local-src: 'local(' ( string | ident ) ')'
   14866             : 
   14867             : bool
   14868           0 : CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
   14869             : {
   14870             :   // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>?
   14871           0 :   InfallibleTArray<nsCSSValue> values;
   14872           0 :   nsCSSValue cur;
   14873             :   for (;;) {
   14874           0 :     if (!GetToken(true))
   14875           0 :       break;
   14876             : 
   14877           0 :     if (mToken.mType == eCSSToken_URL) {
   14878           0 :       SetValueToURL(cur, mToken.mIdent);
   14879           0 :       values.AppendElement(cur);
   14880           0 :       if (!ParseFontSrcFormat(values))
   14881           0 :         return false;
   14882             : 
   14883           0 :     } else if (mToken.mType == eCSSToken_Function &&
   14884           0 :                mToken.mIdent.LowerCaseEqualsLiteral("local")) {
   14885             :       // css3-fonts does not specify a formal grammar for local().
   14886             :       // The text permits both unquoted identifiers and quoted
   14887             :       // strings.  We resolve this ambiguity in the spec by
   14888             :       // assuming that the appropriate production is a single
   14889             :       // <family-name>, possibly surrounded by whitespace.
   14890             : 
   14891           0 :       nsAutoString family;
   14892             :       bool single, quoted;
   14893           0 :       if (!ParseOneFamily(family, single, quoted)) {
   14894           0 :         SkipUntil(')');
   14895           0 :         return false;
   14896             :       }
   14897           0 :       if (!ExpectSymbol(')', true)) {
   14898           0 :         SkipUntil(')');
   14899           0 :         return false;
   14900             :       }
   14901             : 
   14902             :       // reject generics
   14903           0 :       if (single) {
   14904           0 :         nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
   14905           0 :         switch (keyword) {
   14906             :           case eCSSKeyword_serif:
   14907             :           case eCSSKeyword_sans_serif:
   14908             :           case eCSSKeyword_monospace:
   14909             :           case eCSSKeyword_cursive:
   14910             :           case eCSSKeyword_fantasy:
   14911             :           case eCSSKeyword__moz_fixed:
   14912           0 :             return false;
   14913             :           default:
   14914           0 :             break;
   14915             :         }
   14916             :       }
   14917             : 
   14918           0 :       cur.SetStringValue(family, eCSSUnit_Local_Font);
   14919           0 :       values.AppendElement(cur);
   14920             :     } else {
   14921             :       // We don't know what to do with this token; unget it and error out
   14922           0 :       UngetToken();
   14923           0 :       return false;
   14924             :     }
   14925             : 
   14926           0 :     if (!ExpectSymbol(',', true))
   14927           0 :       break;
   14928           0 :   }
   14929             : 
   14930           0 :   if (values.Length() == 0)
   14931           0 :     return false;
   14932             : 
   14933             :   RefPtr<nsCSSValue::Array> srcVals
   14934           0 :     = nsCSSValue::Array::Create(values.Length());
   14935             : 
   14936             :   uint32_t i;
   14937           0 :   for (i = 0; i < values.Length(); i++)
   14938           0 :     srcVals->Item(i) = values[i];
   14939           0 :   aValue.SetArrayValue(srcVals, eCSSUnit_Array);
   14940           0 :   return true;
   14941             : }
   14942             : 
   14943             : bool
   14944           0 : CSSParserImpl::ParseFontSrcFormat(InfallibleTArray<nsCSSValue> & values)
   14945             : {
   14946           0 :   if (!GetToken(true))
   14947           0 :     return true; // EOF harmless here
   14948           0 :   if (mToken.mType != eCSSToken_Function ||
   14949           0 :       !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
   14950           0 :     UngetToken();
   14951           0 :     return true;
   14952             :   }
   14953             : 
   14954           0 :   do {
   14955           0 :     if (!GetToken(true))
   14956           0 :       return false; // EOF - no need for SkipUntil
   14957             : 
   14958           0 :     if (mToken.mType != eCSSToken_String) {
   14959           0 :       UngetToken();
   14960           0 :       SkipUntil(')');
   14961           0 :       return false;
   14962             :     }
   14963             : 
   14964           0 :     nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
   14965           0 :     values.AppendElement(cur);
   14966             :   } while (ExpectSymbol(',', true));
   14967             : 
   14968           0 :   if (!ExpectSymbol(')', true)) {
   14969           0 :     SkipUntil(')');
   14970           0 :     return false;
   14971             :   }
   14972             : 
   14973           0 :   return true;
   14974             : }
   14975             : 
   14976             : // font-ranges: urange ( ',' urange )*
   14977             : bool
   14978           0 : CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
   14979             : {
   14980           0 :   InfallibleTArray<uint32_t> ranges;
   14981             :   for (;;) {
   14982           0 :     if (!GetToken(true))
   14983           0 :       break;
   14984             : 
   14985           0 :     if (mToken.mType != eCSSToken_URange) {
   14986           0 :       UngetToken();
   14987           0 :       break;
   14988             :     }
   14989             : 
   14990             :     // An invalid range token is a parsing error, causing the entire
   14991             :     // descriptor to be ignored.
   14992           0 :     if (!mToken.mIntegerValid)
   14993           0 :       return false;
   14994             : 
   14995           0 :     uint32_t low = mToken.mInteger;
   14996           0 :     uint32_t high = mToken.mInteger2;
   14997             : 
   14998             :     // A range that descends, or high end exceeds the current range of
   14999             :     // Unicode (U+0-10FFFF) invalidates the descriptor.
   15000           0 :     if (low > high || high > 0x10FFFF) {
   15001           0 :       return false;
   15002             :     }
   15003           0 :     ranges.AppendElement(low);
   15004           0 :     ranges.AppendElement(high);
   15005             : 
   15006           0 :     if (!ExpectSymbol(',', true))
   15007           0 :       break;
   15008           0 :   }
   15009             : 
   15010           0 :   if (ranges.Length() == 0)
   15011           0 :     return false;
   15012             : 
   15013             :   RefPtr<nsCSSValue::Array> srcVals
   15014           0 :     = nsCSSValue::Array::Create(ranges.Length());
   15015             : 
   15016           0 :   for (uint32_t i = 0; i < ranges.Length(); i++)
   15017           0 :     srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer);
   15018           0 :   aValue.SetArrayValue(srcVals, eCSSUnit_Array);
   15019           0 :   return true;
   15020             : }
   15021             : 
   15022             : // font-feature-settings: normal | <feature-tag-value> [, <feature-tag-value>]*
   15023             : // <feature-tag-value> = <string> [ <integer> | on | off ]?
   15024             : 
   15025             : // minimum - "tagx", "tagy", "tagz"
   15026             : // edge error case - "tagx" on 1, "tagx" "tagy", "tagx" -1, "tagx" big
   15027             : 
   15028             : // pair value is always x = string, y = int
   15029             : 
   15030             : // font feature tags must be four ASCII characters
   15031             : #define FEATURE_TAG_LENGTH   4
   15032             : 
   15033             : static bool
   15034           0 : ValidFontFeatureTag(const nsString& aTag)
   15035             : {
   15036           0 :   if (aTag.Length() != FEATURE_TAG_LENGTH) {
   15037           0 :     return false;
   15038             :   }
   15039             :   uint32_t i;
   15040           0 :   for (i = 0; i < FEATURE_TAG_LENGTH; i++) {
   15041           0 :     uint32_t ch = aTag[i];
   15042           0 :     if (ch < 0x20 || ch > 0x7e) {
   15043           0 :       return false;
   15044             :     }
   15045             :   }
   15046           0 :   return true;
   15047             : }
   15048             : 
   15049             : bool
   15050           0 : CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue)
   15051             : {
   15052           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
   15053             :                               nullptr)) {
   15054           0 :     return true;
   15055             :   }
   15056             : 
   15057           0 :   auto resultHead = MakeUnique<nsCSSValuePairList>();
   15058           0 :   nsCSSValuePairList* cur = resultHead.get();
   15059             : 
   15060             :   for (;;) {
   15061             :     // feature tag
   15062           0 :     if (!GetToken(true)) {
   15063           0 :       return false;
   15064             :     }
   15065             : 
   15066           0 :     if (mToken.mType != eCSSToken_String ||
   15067           0 :         !ValidFontFeatureTag(mToken.mIdent)) {
   15068           0 :       UngetToken();
   15069           0 :       return false;
   15070             :     }
   15071           0 :     cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
   15072             : 
   15073           0 :     if (!GetToken(true)) {
   15074           0 :       cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
   15075           0 :       break;
   15076             :     }
   15077             : 
   15078             :     // optional value or on/off keyword
   15079           0 :     if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid &&
   15080           0 :         mToken.mInteger >= 0) {
   15081           0 :       cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
   15082           0 :     } else if (mToken.mType == eCSSToken_Ident &&
   15083           0 :                mToken.mIdent.LowerCaseEqualsLiteral("on")) {
   15084           0 :       cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
   15085           0 :     } else if (mToken.mType == eCSSToken_Ident &&
   15086           0 :                mToken.mIdent.LowerCaseEqualsLiteral("off")) {
   15087           0 :       cur->mYValue.SetIntValue(0, eCSSUnit_Integer);
   15088             :     } else {
   15089             :       // something other than value/on/off, set default value
   15090           0 :       cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
   15091           0 :       UngetToken();
   15092             :     }
   15093             : 
   15094           0 :     if (!ExpectSymbol(',', true)) {
   15095           0 :       break;
   15096             :     }
   15097             : 
   15098           0 :     cur->mNext = new nsCSSValuePairList;
   15099           0 :     cur = cur->mNext;
   15100             :   }
   15101             : 
   15102           0 :   aValue.AdoptPairListValue(Move(resultHead));
   15103             : 
   15104           0 :   return true;
   15105             : }
   15106             : 
   15107             : bool
   15108           0 : CSSParserImpl::ParseFontVariationSettings(nsCSSValue& aValue)
   15109             : {
   15110           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
   15111             :                               nullptr)) {
   15112           0 :     return true;
   15113             :   }
   15114             : 
   15115           0 :   auto resultHead = MakeUnique<nsCSSValuePairList>();
   15116           0 :   nsCSSValuePairList* cur = resultHead.get();
   15117             : 
   15118             :   for (;;) {
   15119             :     // variation tag
   15120           0 :     if (!GetToken(true)) {
   15121           0 :       return false;
   15122             :     }
   15123             : 
   15124             :     // variation tags are subject to the same validation as feature tags
   15125           0 :     if (mToken.mType != eCSSToken_String ||
   15126           0 :         !ValidFontFeatureTag(mToken.mIdent)) {
   15127           0 :       UngetToken();
   15128           0 :       return false;
   15129             :     }
   15130           0 :     cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
   15131             : 
   15132           0 :     if (!GetToken(true)) {
   15133           0 :       return false;
   15134             :     }
   15135             : 
   15136           0 :     if (mToken.mType == eCSSToken_Number) {
   15137           0 :       cur->mYValue.SetFloatValue(mToken.mNumber, eCSSUnit_Number);
   15138             :     } else {
   15139           0 :       UngetToken();
   15140           0 :       return false;
   15141             :     }
   15142             : 
   15143           0 :     if (!ExpectSymbol(',', true)) {
   15144           0 :       break;
   15145             :     }
   15146             : 
   15147           0 :     cur->mNext = new nsCSSValuePairList;
   15148           0 :     cur = cur->mNext;
   15149             :   }
   15150             : 
   15151           0 :   aValue.AdoptPairListValue(Move(resultHead));
   15152             : 
   15153           0 :   return true;
   15154             : }
   15155             : 
   15156             : bool
   15157           2 : CSSParserImpl::ParseListStyle()
   15158             : {
   15159             :   // 'list-style' can accept 'none' for two different subproperties,
   15160             :   // 'list-style-type' and 'list-style-image'.  In order to accept
   15161             :   // 'none' as the value of either but still allow another value for
   15162             :   // either, we need to ensure that the first 'none' we find gets
   15163             :   // allocated to a dummy property instead. Since parse function for
   15164             :   // 'list-style-type' could accept values for 'list-style-position',
   15165             :   // we put position in front of type.
   15166             :   static const nsCSSPropertyID listStyleIDs[] = {
   15167             :     eCSSPropertyExtra_x_none_value,
   15168             :     eCSSProperty_list_style_position,
   15169             :     eCSSProperty_list_style_type,
   15170             :     eCSSProperty_list_style_image
   15171             :   };
   15172             : 
   15173           4 :   nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)];
   15174             :   int32_t found =
   15175           2 :     ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs));
   15176           2 :   if (found < 1) {
   15177           0 :     return false;
   15178             :   }
   15179             : 
   15180           2 :   if ((found & (1|4|8)) == (1|4|8)) {
   15181           0 :     if (values[0].GetUnit() == eCSSUnit_None) {
   15182             :       // We found a 'none' plus another value for both of
   15183             :       // 'list-style-type' and 'list-style-image'.  This is a parse
   15184             :       // error, since the 'none' has to count for at least one of them.
   15185           0 :       return false;
   15186             :     } else {
   15187           0 :       NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] &&
   15188             :                    values[0] == values[2] && values[0] == values[3],
   15189             :                    "should be a special value");
   15190             :     }
   15191             :   }
   15192             : 
   15193           2 :   if ((found & 2) == 0) {
   15194             :     values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
   15195           0 :                           eCSSUnit_Enumerated);
   15196             :   }
   15197           2 :   if ((found & 4) == 0) {
   15198             :     // Provide default values
   15199           0 :     nsIAtom* type = (found & 1) ? nsGkAtoms::none : nsGkAtoms::disc;
   15200           0 :     values[2].SetAtomIdentValue(do_AddRef(type));
   15201             :   }
   15202           2 :   if ((found & 8) == 0) {
   15203           2 :     values[3].SetNoneValue();
   15204             :   }
   15205             : 
   15206             :   // Start at 1 to avoid appending fake value.
   15207           8 :   for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) {
   15208           6 :     AppendValue(listStyleIDs[index], values[index]);
   15209             :   }
   15210           2 :   return true;
   15211             : }
   15212             : 
   15213             : bool
   15214          12 : CSSParserImpl::ParseListStyleType(nsCSSValue& aValue)
   15215             : {
   15216          12 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_STRING,
   15217             :                               nullptr)) {
   15218           0 :     return true;
   15219             :   }
   15220             : 
   15221          12 :   if (ParseCounterStyleNameValue(aValue) || ParseSymbols(aValue)) {
   15222          12 :     return true;
   15223             :   }
   15224             : 
   15225           0 :   return false;
   15226             : }
   15227             : 
   15228             : bool
   15229         246 : CSSParserImpl::ParseMargin()
   15230             : {
   15231             :   static const nsCSSPropertyID kMarginSideIDs[] = {
   15232             :     eCSSProperty_margin_top,
   15233             :     eCSSProperty_margin_right,
   15234             :     eCSSProperty_margin_bottom,
   15235             :     eCSSProperty_margin_left
   15236             :   };
   15237             : 
   15238         246 :   return ParseBoxProperties(kMarginSideIDs);
   15239             : }
   15240             : 
   15241             : bool
   15242           2 : CSSParserImpl::ParseObjectPosition()
   15243             : {
   15244           4 :   nsCSSValue value;
   15245           2 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) &&
   15246           0 :       !ParsePositionValue(value)) {
   15247           0 :     return false;
   15248             :   }
   15249           2 :   AppendValue(eCSSProperty_object_position, value);
   15250           2 :   return true;
   15251             : }
   15252             : 
   15253             : bool
   15254          27 : CSSParserImpl::ParseOutline()
   15255             : {
   15256          27 :   const int32_t numProps = 3;
   15257             :   static const nsCSSPropertyID kOutlineIDs[] = {
   15258             :     eCSSProperty_outline_color,
   15259             :     eCSSProperty_outline_style,
   15260             :     eCSSProperty_outline_width
   15261             :   };
   15262             : 
   15263          54 :   nsCSSValue  values[numProps];
   15264          27 :   int32_t found = ParseChoice(values, kOutlineIDs, numProps);
   15265          27 :   if (found < 1) {
   15266           0 :     return false;
   15267             :   }
   15268             : 
   15269             :   // Provide default values
   15270          27 :   if ((found & 1) == 0) { // Provide default outline-color
   15271          20 :     values[0].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
   15272             :   }
   15273          27 :   if ((found & 2) == 0) { // Provide default outline-style
   15274           1 :     values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
   15275             :   }
   15276          27 :   if ((found & 4) == 0) { // Provide default outline-width
   15277           1 :     values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
   15278             :   }
   15279             : 
   15280             :   int32_t index;
   15281         108 :   for (index = 0; index < numProps; index++) {
   15282          81 :     AppendValue(kOutlineIDs[index], values[index]);
   15283             :   }
   15284          27 :   return true;
   15285             : }
   15286             : 
   15287             : bool
   15288          71 : CSSParserImpl::ParseOverflow()
   15289             : {
   15290         142 :   nsCSSValue overflow;
   15291          71 :   if (!ParseSingleTokenVariant(overflow, VARIANT_HK,
   15292             :                                nsCSSProps::kOverflowKTable)) {
   15293           0 :     return false;
   15294             :   }
   15295             : 
   15296         142 :   nsCSSValue overflowX(overflow);
   15297         142 :   nsCSSValue overflowY(overflow);
   15298          71 :   if (eCSSUnit_Enumerated == overflow.GetUnit())
   15299          67 :     switch(overflow.GetIntValue()) {
   15300             :       case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
   15301           0 :         overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
   15302           0 :         overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
   15303           0 :         break;
   15304             :       case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
   15305           0 :         overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
   15306           0 :         overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
   15307           0 :         break;
   15308             :     }
   15309          71 :   AppendValue(eCSSProperty_overflow_x, overflowX);
   15310          71 :   AppendValue(eCSSProperty_overflow_y, overflowY);
   15311          71 :   return true;
   15312             : }
   15313             : 
   15314             : bool
   15315         343 : CSSParserImpl::ParsePadding()
   15316             : {
   15317             :   static const nsCSSPropertyID kPaddingSideIDs[] = {
   15318             :     eCSSProperty_padding_top,
   15319             :     eCSSProperty_padding_right,
   15320             :     eCSSProperty_padding_bottom,
   15321             :     eCSSProperty_padding_left
   15322             :   };
   15323             : 
   15324         343 :   return ParseBoxProperties(kPaddingSideIDs);
   15325             : }
   15326             : 
   15327             : bool
   15328           0 : CSSParserImpl::ParseQuotes()
   15329             : {
   15330           0 :   nsCSSValue value;
   15331           0 :   if (!ParseSingleTokenVariant(value, VARIANT_HOS, nullptr)) {
   15332           0 :     return false;
   15333             :   }
   15334           0 :   if (value.GetUnit() == eCSSUnit_String) {
   15335           0 :     nsCSSValue open = value;
   15336           0 :     nsCSSValuePairList* quotes = value.SetPairListValue();
   15337             :     for (;;) {
   15338           0 :       quotes->mXValue = open;
   15339             :       // get mandatory close
   15340           0 :       if (!ParseSingleTokenVariant(quotes->mYValue, VARIANT_STRING, nullptr)) {
   15341           0 :         return false;
   15342             :       }
   15343             :       // look for another open
   15344           0 :       if (!ParseSingleTokenVariant(open, VARIANT_STRING, nullptr)) {
   15345           0 :         break;
   15346             :       }
   15347           0 :       quotes->mNext = new nsCSSValuePairList;
   15348           0 :       quotes = quotes->mNext;
   15349             :     }
   15350             :   }
   15351           0 :   AppendValue(eCSSProperty_quotes, value);
   15352           0 :   return true;
   15353             : }
   15354             : 
   15355             : bool
   15356          16 : CSSParserImpl::ParseTextDecoration()
   15357             : {
   15358             :   static const nsCSSPropertyID kTextDecorationIDs[] = {
   15359             :     eCSSProperty_text_decoration_line,
   15360             :     eCSSProperty_text_decoration_style,
   15361             :     eCSSProperty_text_decoration_color
   15362             :   };
   15363          16 :   const int32_t numProps = MOZ_ARRAY_LENGTH(kTextDecorationIDs);
   15364          32 :   nsCSSValue values[numProps];
   15365             : 
   15366          16 :   int32_t found = ParseChoice(values, kTextDecorationIDs, numProps);
   15367          16 :   if (found < 1) {
   15368           0 :     return false;
   15369             :   }
   15370             : 
   15371             :   // Provide default values
   15372          16 :   if ((found & 1) == 0) { // Provide default text-decoration-line
   15373             :     values[0].SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE,
   15374           0 :                           eCSSUnit_Enumerated);
   15375             :   }
   15376          16 :   if ((found & 2) == 0) { // Provide default text-decoration-style
   15377             :     values[1].SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
   15378          10 :                           eCSSUnit_Enumerated);
   15379             :   }
   15380          16 :   if ((found & 4) == 0) { // Provide default text-decoration-color
   15381          12 :     values[2].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
   15382             :   }
   15383             : 
   15384          64 :   for (int32_t index = 0; index < numProps; index++) {
   15385          48 :     AppendValue(kTextDecorationIDs[index], values[index]);
   15386             :   }
   15387          16 :   return true;
   15388             : }
   15389             : 
   15390             : bool
   15391           2 : CSSParserImpl::ParseTextEmphasis()
   15392             : {
   15393             :   static constexpr nsCSSPropertyID kTextEmphasisIDs[] = {
   15394             :     eCSSProperty_text_emphasis_style,
   15395             :     eCSSProperty_text_emphasis_color
   15396             :   };
   15397           2 :   constexpr int32_t numProps = MOZ_ARRAY_LENGTH(kTextEmphasisIDs);
   15398           4 :   nsCSSValue values[numProps];
   15399             : 
   15400           2 :   int32_t found = ParseChoice(values, kTextEmphasisIDs, numProps);
   15401           2 :   if (found < 1) {
   15402           0 :     return false;
   15403             :   }
   15404             : 
   15405           2 :   if (!(found & 1)) { // Provide default text-emphasis-style
   15406           0 :     values[0].SetNoneValue();
   15407             :   }
   15408           2 :   if (!(found & 2)) { // Provide default text-emphasis-color
   15409           2 :     values[1].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
   15410             :   }
   15411             : 
   15412           6 :   for (int32_t index = 0; index < numProps; index++) {
   15413           4 :     AppendValue(kTextEmphasisIDs[index], values[index]);
   15414             :   }
   15415           2 :   return true;
   15416             : }
   15417             : 
   15418             : bool
   15419           0 : CSSParserImpl::ParseTextEmphasisPosition(nsCSSValue& aValue)
   15420             : {
   15421             :   static_assert((NS_STYLE_TEXT_EMPHASIS_POSITION_OVER ^
   15422             :                  NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER ^
   15423             :                  NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT ^
   15424             :                  NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT) ==
   15425             :                 (NS_STYLE_TEXT_EMPHASIS_POSITION_OVER |
   15426             :                  NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER |
   15427             :                  NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT |
   15428             :                  NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT),
   15429             :                 "text-emphasis-position constants should be bitmasks");
   15430             : 
   15431           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
   15432           0 :     return true;
   15433             :   }
   15434             : 
   15435           0 :   nsCSSValue first, second;
   15436           0 :   const auto& kTable = nsCSSProps::kTextEmphasisPositionKTable;
   15437           0 :   if (!ParseSingleTokenVariant(first, VARIANT_KEYWORD, kTable) ||
   15438           0 :       !ParseSingleTokenVariant(second, VARIANT_KEYWORD, kTable)) {
   15439           0 :     return false;
   15440             :   }
   15441             : 
   15442           0 :   auto firstValue = first.GetIntValue();
   15443           0 :   auto secondValue = second.GetIntValue();
   15444           0 :   if ((firstValue == NS_STYLE_TEXT_EMPHASIS_POSITION_OVER ||
   15445           0 :       firstValue == NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER) ==
   15446           0 :       (secondValue == NS_STYLE_TEXT_EMPHASIS_POSITION_OVER ||
   15447             :       secondValue == NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER)) {
   15448           0 :     return false;
   15449             :   }
   15450             : 
   15451           0 :   aValue.SetIntValue(firstValue | secondValue, eCSSUnit_Enumerated);
   15452           0 :   return true;
   15453             : }
   15454             : 
   15455             : bool
   15456           2 : CSSParserImpl::ParseTextEmphasisStyle(nsCSSValue& aValue)
   15457             : {
   15458             :   static_assert((NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK ^
   15459             :                  NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK) ==
   15460             :                 (NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK |
   15461             :                  NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK),
   15462             :                 "text-emphasis-style shape and fill constants "
   15463             :                 "should not intersect");
   15464             :   static_assert(NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED == 0,
   15465             :                 "Making 'filled' zero ensures that if neither 'filled' nor "
   15466             :                 "'open' is specified, we compute it to 'filled' per spec");
   15467             : 
   15468           2 :   if (ParseSingleTokenVariant(aValue, VARIANT_HOS, nullptr)) {
   15469           2 :     return true;
   15470             :   }
   15471             : 
   15472           0 :   nsCSSValue first, second;
   15473           0 :   const auto& fillKTable = nsCSSProps::kTextEmphasisStyleFillKTable;
   15474           0 :   const auto& shapeKTable = nsCSSProps::kTextEmphasisStyleShapeKTable;
   15475             : 
   15476             :   // Parse a fill value and/or a shape value, in either order.
   15477             :   // (Require at least one of them, and treat the second as optional.)
   15478           0 :   if (ParseSingleTokenVariant(first, VARIANT_KEYWORD, fillKTable)) {
   15479           0 :     Unused << ParseSingleTokenVariant(second, VARIANT_KEYWORD, shapeKTable);
   15480           0 :   } else if (ParseSingleTokenVariant(first, VARIANT_KEYWORD, shapeKTable)) {
   15481           0 :     Unused << ParseSingleTokenVariant(second, VARIANT_KEYWORD, fillKTable);
   15482             :   } else {
   15483           0 :     return false;
   15484             :   }
   15485             : 
   15486           0 :   auto value = first.GetIntValue();
   15487           0 :   if (second.GetUnit() == eCSSUnit_Enumerated) {
   15488           0 :     value |= second.GetIntValue();
   15489             :   }
   15490           0 :   aValue.SetIntValue(value, eCSSUnit_Enumerated);
   15491           0 :   return true;
   15492             : }
   15493             : 
   15494             : bool
   15495          57 : CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableEntry aTable[])
   15496             : {
   15497          57 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
   15498             :     // 'inherit', 'initial' and 'unset' must be alone
   15499           7 :     return true;
   15500             :   }
   15501             : 
   15502         100 :   nsCSSValue left;
   15503          50 :   if (!ParseSingleTokenVariant(left, VARIANT_KEYWORD, aTable)) {
   15504           0 :     return false;
   15505             :   }
   15506             : 
   15507          50 :   if (!nsLayoutUtils::IsTextAlignUnsafeValueEnabled()) {
   15508          50 :     aValue = left;
   15509          50 :     return true;
   15510             :   }
   15511             : 
   15512           0 :   nsCSSValue right;
   15513           0 :   if (ParseSingleTokenVariant(right, VARIANT_KEYWORD, aTable)) {
   15514             :     // 'true' must be combined with some other value than 'true'.
   15515           0 :     if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE &&
   15516           0 :         right.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) {
   15517           0 :       return false;
   15518             :     }
   15519           0 :     aValue.SetPairValue(left, right);
   15520             :   } else {
   15521             :     // Single value 'true' is not allowed.
   15522           0 :     if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) {
   15523           0 :       return false;
   15524             :     }
   15525           0 :     aValue = left;
   15526             :   }
   15527           0 :   return true;
   15528             : }
   15529             : 
   15530             : bool
   15531          57 : CSSParserImpl::ParseTextAlign(nsCSSValue& aValue)
   15532             : {
   15533          57 :   return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable);
   15534             : }
   15535             : 
   15536             : bool
   15537           0 : CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue)
   15538             : {
   15539           0 :   return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable);
   15540             : }
   15541             : 
   15542             : bool
   15543          18 : CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
   15544             : {
   15545             :   static_assert((NS_STYLE_TEXT_DECORATION_LINE_NONE ^
   15546             :                  NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE ^
   15547             :                  NS_STYLE_TEXT_DECORATION_LINE_OVERLINE ^
   15548             :                  NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH ^
   15549             :                  NS_STYLE_TEXT_DECORATION_LINE_BLINK) ==
   15550             :                 (NS_STYLE_TEXT_DECORATION_LINE_NONE |
   15551             :                  NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE |
   15552             :                  NS_STYLE_TEXT_DECORATION_LINE_OVERLINE |
   15553             :                  NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH |
   15554             :                  NS_STYLE_TEXT_DECORATION_LINE_BLINK),
   15555             :                 "text decoration constants need to be bitmasks");
   15556          18 :   if (ParseSingleTokenVariant(aValue, VARIANT_HK,
   15557             :                               nsCSSProps::kTextDecorationLineKTable)) {
   15558          16 :     if (eCSSUnit_Enumerated == aValue.GetUnit()) {
   15559          12 :       int32_t intValue = aValue.GetIntValue();
   15560          12 :       if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
   15561             :         // look for more keywords
   15562          24 :         nsCSSValue  keyword;
   15563             :         int32_t index;
   15564          12 :         for (index = 0; index < 3; index++) {
   15565          12 :           if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) {
   15566           0 :             int32_t newValue = keyword.GetIntValue();
   15567           0 :             if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE ||
   15568           0 :                 newValue & intValue) {
   15569             :               // 'none' keyword in conjuction with others is not allowed, and
   15570             :               // duplicate keyword is not allowed.
   15571           0 :               return false;
   15572             :             }
   15573           0 :             intValue |= newValue;
   15574             :           }
   15575             :           else {
   15576          12 :             break;
   15577             :           }
   15578             :         }
   15579          12 :         aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
   15580             :       }
   15581             :     }
   15582          16 :     return true;
   15583             :   }
   15584           2 :   return false;
   15585             : }
   15586             : 
   15587             : bool
   15588          18 : CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
   15589             : {
   15590          18 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
   15591             :     // 'inherit', 'initial' and 'unset' must be alone
   15592          15 :     return true;
   15593             :   }
   15594             : 
   15595           6 :   nsCSSValue left;
   15596           3 :   if (!ParseSingleTokenVariant(left, VARIANT_KEYWORD | VARIANT_STRING,
   15597             :                                nsCSSProps::kTextOverflowKTable))
   15598           0 :     return false;
   15599             : 
   15600           6 :   nsCSSValue right;
   15601           3 :   if (ParseSingleTokenVariant(right, VARIANT_KEYWORD | VARIANT_STRING,
   15602             :                               nsCSSProps::kTextOverflowKTable))
   15603           0 :     aValue.SetPairValue(left, right);
   15604             :   else {
   15605           3 :     aValue = left;
   15606             :   }
   15607           3 :   return true;
   15608             : }
   15609             : 
   15610             : bool
   15611           0 : CSSParserImpl::ParseTouchAction(nsCSSValue& aValue)
   15612             : {
   15613             :   // Avaliable values of property touch-action:
   15614             :   // auto | none | [pan-x || pan-y] | manipulation
   15615             : 
   15616           0 :   if (!ParseSingleTokenVariant(aValue, VARIANT_HK,
   15617             :                                nsCSSProps::kTouchActionKTable)) {
   15618           0 :     return false;
   15619             :   }
   15620             : 
   15621             :   // Auto and None keywords aren't allowed in conjunction with others.
   15622             :   // Also inherit, initial and unset values are available.
   15623           0 :   if (eCSSUnit_Enumerated != aValue.GetUnit()) {
   15624           0 :     return true;
   15625             :   }
   15626             : 
   15627           0 :   int32_t intValue = aValue.GetIntValue();
   15628           0 :   nsCSSValue nextValue;
   15629           0 :   if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) {
   15630           0 :     int32_t nextIntValue = nextValue.GetIntValue();
   15631             : 
   15632             :     // duplicates aren't allowed.
   15633           0 :     if (nextIntValue & intValue) {
   15634           0 :       return false;
   15635             :     }
   15636             : 
   15637             :     // Auto and None and Manipulation is not allowed in conjunction with others.
   15638           0 :     if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE |
   15639             :                                      NS_STYLE_TOUCH_ACTION_AUTO |
   15640             :                                      NS_STYLE_TOUCH_ACTION_MANIPULATION)) {
   15641           0 :       return false;
   15642             :     }
   15643             : 
   15644           0 :     aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
   15645             :   }
   15646             : 
   15647           0 :   return true;
   15648             : }
   15649             : 
   15650             : bool
   15651           0 : CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue)
   15652             : {
   15653           0 :   if (!ParseSingleTokenVariant(aValue, VARIANT_HK,
   15654             :                                nsCSSProps::kTextCombineUprightKTable)) {
   15655           0 :     return false;
   15656             :   }
   15657             : 
   15658             :   // if 'digits', need to check for an explicit number [2, 3, 4]
   15659           0 :   if (eCSSUnit_Enumerated == aValue.GetUnit() &&
   15660           0 :       aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) {
   15661           0 :     if (!nsLayoutUtils::TextCombineUprightDigitsEnabled()) {
   15662           0 :       return false;
   15663             :     }
   15664           0 :     if (!GetToken(true)) {
   15665           0 :       return true;
   15666             :     }
   15667           0 :     if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
   15668           0 :       switch (mToken.mInteger) {
   15669             :         case 2:  // already set, nothing to do
   15670           0 :           break;
   15671             :         case 3:
   15672             :           aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3,
   15673           0 :                              eCSSUnit_Enumerated);
   15674           0 :           break;
   15675             :         case 4:
   15676             :           aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4,
   15677           0 :                              eCSSUnit_Enumerated);
   15678           0 :           break;
   15679             :         default:
   15680             :           // invalid digits value
   15681           0 :           return false;
   15682             :       }
   15683             :     } else {
   15684           0 :       UngetToken();
   15685             :     }
   15686             :   }
   15687           0 :   return true;
   15688             : }
   15689             : 
   15690             : ///////////////////////////////////////////////////////
   15691             : // transform Parsing Implementation
   15692             : 
   15693             : /* Reads a function list of arguments and consumes the closing parenthesis.
   15694             :  * Do not call this function directly; it's meant to be called from
   15695             :  * ParseFunction.
   15696             :  */
   15697             : bool
   15698         121 : CSSParserImpl::ParseFunctionInternals(const uint32_t aVariantMask[],
   15699             :                                       uint32_t aVariantMaskAll,
   15700             :                                       uint16_t aMinElems,
   15701             :                                       uint16_t aMaxElems,
   15702             :                                       InfallibleTArray<nsCSSValue> &aOutput)
   15703             : {
   15704         121 :   NS_ASSERTION((aVariantMask && !aVariantMaskAll) ||
   15705             :                (!aVariantMask && aVariantMaskAll),
   15706             :                "only one of the two variant mask parameters can be set");
   15707             : 
   15708         127 :   for (uint16_t index = 0; index < aMaxElems; ++index) {
   15709         127 :     nsCSSValue newValue;
   15710         127 :     uint32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index];
   15711         127 :     if (ParseVariant(newValue, m, nullptr) != CSSParseResult::Ok) {
   15712           0 :       break;
   15713             :     }
   15714             : 
   15715         127 :     if (nsCSSValue::IsFloatUnit(newValue.GetUnit())) {
   15716             :       // Clamp infinity or -infinity values to max float or -max float to avoid
   15717             :       // calculations with infinity.
   15718         214 :       newValue.SetFloatValue(
   15719         214 :         mozilla::clamped(newValue.GetFloatValue(),
   15720         214 :                          -std::numeric_limits<float>::max(),
   15721         321 :                           std::numeric_limits<float>::max()),
   15722         107 :         newValue.GetUnit());
   15723             :     }
   15724             : 
   15725         127 :     aOutput.AppendElement(newValue);
   15726             : 
   15727         127 :     if (ExpectSymbol(',', true)) {
   15728             :       // Move on to the next argument if we see a comma.
   15729           6 :       continue;
   15730             :     }
   15731             : 
   15732         121 :     if (ExpectSymbol(')', true)) {
   15733             :       // Make sure we've read enough symbols if we see a closing parenthesis.
   15734         121 :       return (index + 1) >= aMinElems;
   15735             :     }
   15736             : 
   15737             :     // Only a comma or a closing parenthesis is valid after an argument.
   15738           0 :     break;
   15739             :   }
   15740             : 
   15741             :   // If we're here, we've hit an error without seeing a closing parenthesis or
   15742             :   // we've read too many elements without seeing a closing parenthesis.
   15743           0 :   SkipUntil(')');
   15744           0 :   return false;
   15745             : }
   15746             : 
   15747             : /* Parses a function [ input of the form (a [, b]*) ] and stores it
   15748             :  * as an nsCSSValue that holds a function of the form
   15749             :  * function-name arg1 arg2 ... argN
   15750             :  *
   15751             :  * On error, the return value is false.
   15752             :  *
   15753             :  * @param aFunction The name of the function that we're reading.
   15754             :  * @param aAllowedTypes An array of values corresponding to the legal
   15755             :  *        types for each element in the function.  The zeroth element in the
   15756             :  *        array corresponds to the first function parameter, etc.  The length
   15757             :  *        of this array _must_ be greater than or equal to aMaxElems or the
   15758             :  *        behavior is undefined.  If not null, aAllowTypesAll must be 0.
   15759             :  * @param aAllowedTypesAll If set, every element tested for these types
   15760             :  * @param aMinElems Minimum number of elements to read.  Reading fewer than
   15761             :  *        this many elements will result in the function failing.
   15762             :  * @param aMaxElems Maximum number of elements to read.  Reading more than
   15763             :  *        this many elements will result in the function failing.
   15764             :  * @param aValue (out) The value that was parsed.
   15765             :  */
   15766             : bool
   15767         121 : CSSParserImpl::ParseFunction(nsCSSKeyword aFunction,
   15768             :                              const uint32_t aAllowedTypes[],
   15769             :                              uint32_t aAllowedTypesAll,
   15770             :                              uint16_t aMinElems, uint16_t aMaxElems,
   15771             :                              nsCSSValue &aValue)
   15772             : {
   15773         121 :   NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) ||
   15774             :                (!aAllowedTypes && aAllowedTypesAll),
   15775             :                "only one of the two allowed type parameter can be set");
   15776             :   typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
   15777             : 
   15778             :   /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
   15779             :    * elements stored in the the nsCSSValue::Array.
   15780             :    */
   15781             :   static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
   15782             : 
   15783             :   /* Read in a list of values as an array, failing if we can't or if
   15784             :    * it's out of bounds.
   15785             :    *
   15786             :    * We reserve 16 entries in the foundValues array in order to avoid
   15787             :    * having to resize the array dynamically when parsing some well-formed
   15788             :    * functions.  The number 16 is coming from the number of arguments that
   15789             :    * matrix3d() accepts.
   15790             :    */
   15791         242 :   AutoTArray<nsCSSValue, 16> foundValues;
   15792         121 :   if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems,
   15793             :                               aMaxElems, foundValues)) {
   15794           0 :     return false;
   15795             :   }
   15796             : 
   15797             :   /*
   15798             :    * In case the user has given us more than 2^16 - 2 arguments,
   15799             :    * we'll truncate them at 2^16 - 2 arguments.
   15800             :    */
   15801         121 :   uint16_t numArgs = std::min(foundValues.Length(), MAX_ALLOWED_ELEMS);
   15802             :   RefPtr<nsCSSValue::Array> convertedArray =
   15803         242 :     aValue.InitFunction(aFunction, numArgs);
   15804             : 
   15805             :   /* Copy things over. */
   15806         248 :   for (uint16_t index = 0; index < numArgs; ++index)
   15807         127 :     convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
   15808             : 
   15809             :   /* Return it! */
   15810         121 :   return true;
   15811             : }
   15812             : 
   15813             : /**
   15814             :  * Given a token, determines the minimum and maximum number of function
   15815             :  * parameters to read, along with the mask that should be used to read
   15816             :  * those function parameters.  If the token isn't a transform function,
   15817             :  * returns an error.
   15818             :  *
   15819             :  * @param aToken The token identifying the function.
   15820             :  * @param aIsPrefixed If true, parse matrices using the matrix syntax
   15821             :  *   for -moz-transform.
   15822             :  * @param aDisallowRelativeValues If true, only allow variants that are
   15823             :  *   numbers or have non-relative dimensions.
   15824             :  * @param aMinElems [out] The minimum number of elements to read.
   15825             :  * @param aMaxElems [out] The maximum number of elements to read
   15826             :  * @param aVariantMask [out] The variant mask to use during parsing
   15827             :  * @return Whether the information was loaded successfully.
   15828             :  */
   15829         108 : static bool GetFunctionParseInformation(nsCSSKeyword aToken,
   15830             :                                         bool aIsPrefixed,
   15831             :                                         bool aDisallowRelativeValues,
   15832             :                                         uint16_t &aMinElems,
   15833             :                                         uint16_t &aMaxElems,
   15834             :                                         const uint32_t *& aVariantMask)
   15835             : {
   15836             : /* These types represent the common variant masks that will be used to
   15837             :    * parse out the individual functions.  The order in the enumeration
   15838             :    * must match the order in which the masks are declared.
   15839             :    */
   15840             :   enum { eLengthPercentCalc,
   15841             :          eLengthCalc,
   15842             :          eAbsoluteLengthCalc,
   15843             :          eTwoLengthPercentCalcs,
   15844             :          eTwoAbsoluteLengthCalcs,
   15845             :          eTwoLengthPercentCalcsOneLengthCalc,
   15846             :          eThreeAbsoluteLengthCalc,
   15847             :          eAngle,
   15848             :          eTwoAngles,
   15849             :          eNumber,
   15850             :          eNonNegativeLength,
   15851             :          eNonNegativeAbsoluteLength,
   15852             :          eTwoNumbers,
   15853             :          eThreeNumbers,
   15854             :          eThreeNumbersOneAngle,
   15855             :          eMatrix,
   15856             :          eMatrixPrefixed,
   15857             :          eMatrix3d,
   15858             :          eMatrix3dPrefixed,
   15859             :          eNumVariantMasks };
   15860             :   static const int32_t kMaxElemsPerFunction = 16;
   15861             :   static const uint32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
   15862             :     {VARIANT_LPCALC},
   15863             :     {VARIANT_LCALC},
   15864             :     {VARIANT_LB},
   15865             :     {VARIANT_LPCALC, VARIANT_LPCALC},
   15866             :     {VARIANT_LBCALC, VARIANT_LBCALC},
   15867             :     {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LCALC},
   15868             :     {VARIANT_LBCALC, VARIANT_LBCALC, VARIANT_LBCALC},
   15869             :     {VARIANT_ANGLE_OR_ZERO},
   15870             :     {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
   15871             :     {VARIANT_NUMBER},
   15872             :     {VARIANT_LENGTH|VARIANT_NONNEGATIVE_DIMENSION},
   15873             :     {VARIANT_LB|VARIANT_NONNEGATIVE_DIMENSION},
   15874             :     {VARIANT_NUMBER, VARIANT_NUMBER},
   15875             :     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
   15876             :     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
   15877             :     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
   15878             :      VARIANT_NUMBER, VARIANT_NUMBER},
   15879             :     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
   15880             :      VARIANT_LPNCALC, VARIANT_LPNCALC},
   15881             :     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
   15882             :      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
   15883             :      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
   15884             :      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
   15885             :     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
   15886             :      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
   15887             :      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
   15888             :      VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
   15889             :   // Map from a mask to a congruent mask that excludes relative variants.
   15890             :   static const int32_t kNonRelativeVariantMap[eNumVariantMasks] = {
   15891             :     eAbsoluteLengthCalc,
   15892             :     eAbsoluteLengthCalc,
   15893             :     eAbsoluteLengthCalc,
   15894             :     eTwoAbsoluteLengthCalcs,
   15895             :     eTwoAbsoluteLengthCalcs,
   15896             :     eThreeAbsoluteLengthCalc,
   15897             :     eThreeAbsoluteLengthCalc,
   15898             :     eAngle,
   15899             :     eTwoAngles,
   15900             :     eNumber,
   15901             :     eNonNegativeAbsoluteLength,
   15902             :     eNonNegativeAbsoluteLength,
   15903             :     eTwoNumbers,
   15904             :     eThreeNumbers,
   15905             :     eThreeNumbersOneAngle,
   15906             :     eMatrix,
   15907             :     eMatrix,
   15908             :     eMatrix3d,
   15909             :     eMatrix3d };
   15910             : 
   15911             : #ifdef DEBUG
   15912             :   static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
   15913             :     {1, 1, 1, 2, 2, 3, 3, 1, 2, 1, 1, 1, 2, 3, 4, 6, 6, 16, 16};
   15914             : #endif
   15915             : 
   15916         108 :   int32_t variantIndex = eNumVariantMasks;
   15917             : 
   15918         108 :   switch (aToken) {
   15919             :   case eCSSKeyword_translatex:
   15920             :   case eCSSKeyword_translatey:
   15921             :     /* Exactly one length or percent. */
   15922          33 :     variantIndex = eLengthPercentCalc;
   15923          33 :     aMinElems = 1U;
   15924          33 :     aMaxElems = 1U;
   15925          33 :     break;
   15926             :   case eCSSKeyword_translatez:
   15927             :     /* Exactly one length */
   15928           0 :     variantIndex = eLengthCalc;
   15929           0 :     aMinElems = 1U;
   15930           0 :     aMaxElems = 1U;
   15931           0 :     break;
   15932             :   case eCSSKeyword_translate3d:
   15933             :     /* Exactly two lengthds or percents and a number */
   15934           0 :     variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
   15935           0 :     aMinElems = 3U;
   15936           0 :     aMaxElems = 3U;
   15937           0 :     break;
   15938             :   case eCSSKeyword_scalez:
   15939             :   case eCSSKeyword_scalex:
   15940             :   case eCSSKeyword_scaley:
   15941             :     /* Exactly one scale factor. */
   15942          23 :     variantIndex = eNumber;
   15943          23 :     aMinElems = 1U;
   15944          23 :     aMaxElems = 1U;
   15945          23 :     break;
   15946             :   case eCSSKeyword_scale3d:
   15947             :     /* Exactly three scale factors. */
   15948           0 :     variantIndex = eThreeNumbers;
   15949           0 :     aMinElems = 3U;
   15950           0 :     aMaxElems = 3U;
   15951           0 :     break;
   15952             :   case eCSSKeyword_rotatex:
   15953             :   case eCSSKeyword_rotatey:
   15954             :   case eCSSKeyword_rotate:
   15955             :   case eCSSKeyword_rotatez:
   15956             :     /* Exactly one angle. */
   15957          13 :     variantIndex = eAngle;
   15958          13 :     aMinElems = 1U;
   15959          13 :     aMaxElems = 1U;
   15960          13 :     break;
   15961             :   case eCSSKeyword_rotate3d:
   15962           0 :     variantIndex = eThreeNumbersOneAngle;
   15963           0 :     aMinElems = 4U;
   15964           0 :     aMaxElems = 4U;
   15965           0 :     break;
   15966             :   case eCSSKeyword_translate:
   15967             :     /* One or two lengths or percents. */
   15968          12 :     variantIndex = eTwoLengthPercentCalcs;
   15969          12 :     aMinElems = 1U;
   15970          12 :     aMaxElems = 2U;
   15971          12 :     break;
   15972             :   case eCSSKeyword_skew:
   15973             :     /* Exactly one or two angles. */
   15974           0 :     variantIndex = eTwoAngles;
   15975           0 :     aMinElems = 1U;
   15976           0 :     aMaxElems = 2U;
   15977           0 :     break;
   15978             :   case eCSSKeyword_scale:
   15979             :     /* One or two scale factors. */
   15980          26 :     variantIndex = eTwoNumbers;
   15981          26 :     aMinElems = 1U;
   15982          26 :     aMaxElems = 2U;
   15983          26 :     break;
   15984             :   case eCSSKeyword_skewx:
   15985             :     /* Exactly one angle. */
   15986           0 :     variantIndex = eAngle;
   15987           0 :     aMinElems = 1U;
   15988           0 :     aMaxElems = 1U;
   15989           0 :     break;
   15990             :   case eCSSKeyword_skewy:
   15991             :     /* Exactly one angle. */
   15992           0 :     variantIndex = eAngle;
   15993           0 :     aMinElems = 1U;
   15994           0 :     aMaxElems = 1U;
   15995           0 :     break;
   15996             :   case eCSSKeyword_matrix:
   15997             :     /* Six values, all numbers. */
   15998           0 :     variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
   15999           0 :     aMinElems = 6U;
   16000           0 :     aMaxElems = 6U;
   16001           0 :     break;
   16002             :   case eCSSKeyword_matrix3d:
   16003             :     /* 16 matrix values, all numbers */
   16004           0 :     variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
   16005           0 :     aMinElems = 16U;
   16006           0 :     aMaxElems = 16U;
   16007           0 :     break;
   16008             :   case eCSSKeyword_perspective:
   16009             :     /* Exactly one scale number. */
   16010           1 :     variantIndex = eNonNegativeLength;
   16011           1 :     aMinElems = 1U;
   16012           1 :     aMaxElems = 1U;
   16013           1 :     break;
   16014             :   default:
   16015             :     /* Oh dear, we didn't match.  Report an error. */
   16016           0 :     return false;
   16017             :   }
   16018             : 
   16019         108 :   if (aDisallowRelativeValues) {
   16020           0 :     variantIndex = kNonRelativeVariantMap[variantIndex];
   16021             :   }
   16022             : 
   16023         108 :   NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
   16024         108 :   NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
   16025         108 :   NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
   16026         108 :   NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
   16027         108 :   NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
   16028             : #ifdef DEBUG
   16029         108 :   NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
   16030             :                "Invalid aMaxElems for this variant mask.");
   16031             : #endif
   16032             : 
   16033             :   // Convert the index into a mask.
   16034         108 :   aVariantMask = kVariantMasks[variantIndex];
   16035             : 
   16036         108 :   return true;
   16037             : }
   16038             : 
   16039           1 : bool CSSParserImpl::ParseWillChange()
   16040             : {
   16041           2 :   nsCSSValue listValue;
   16042           1 :   nsCSSValueList* currentListValue = listValue.SetListValue();
   16043           1 :   bool first = true;
   16044             :   for (;;) {
   16045             :     const uint32_t variantMask = VARIANT_IDENTIFIER |
   16046             :                                  VARIANT_INHERIT |
   16047             :                                  VARIANT_NONE |
   16048             :                                  VARIANT_ALL |
   16049           1 :                                  VARIANT_AUTO;
   16050           1 :     nsCSSValue value;
   16051           1 :     if (!ParseSingleTokenVariant(value, variantMask, nullptr)) {
   16052           0 :       return false;
   16053             :     }
   16054             : 
   16055           2 :     if (value.GetUnit() == eCSSUnit_None ||
   16056           1 :         value.GetUnit() == eCSSUnit_All)
   16057             :     {
   16058           0 :       return false;
   16059             :     }
   16060             : 
   16061           1 :     if (value.GetUnit() != eCSSUnit_Ident) {
   16062           0 :       if (first) {
   16063           0 :         AppendValue(eCSSProperty_will_change, value);
   16064           0 :         return true;
   16065             :       } else {
   16066           0 :         return false;
   16067             :       }
   16068             :     }
   16069             : 
   16070           1 :     value.AtomizeIdentValue();
   16071           1 :     nsIAtom* atom = value.GetAtomValue();
   16072           1 :     if (atom == nsGkAtoms::_default || atom == nsGkAtoms::willChange) {
   16073           0 :       return false;
   16074             :     }
   16075             : 
   16076           1 :     currentListValue->mValue = Move(value);
   16077             : 
   16078           1 :     if (!ExpectSymbol(',', true)) {
   16079           1 :       break;
   16080             :     }
   16081           0 :     currentListValue->mNext = new nsCSSValueList;
   16082           0 :     currentListValue = currentListValue->mNext;
   16083           0 :     first = false;
   16084           0 :   }
   16085             : 
   16086           1 :   AppendValue(eCSSProperty_will_change, listValue);
   16087           1 :   return true;
   16088             : }
   16089             : 
   16090             : /* Reads a single transform function from the tokenizer stream, reporting an
   16091             :  * error if something goes wrong.
   16092             :  */
   16093             : bool
   16094         108 : CSSParserImpl::ParseSingleTransform(bool aIsPrefixed,
   16095             :                                     bool aDisallowRelativeValues,
   16096             :                                     nsCSSValue& aValue)
   16097             : {
   16098         108 :   if (!GetToken(true))
   16099           0 :     return false;
   16100             : 
   16101         108 :   if (mToken.mType != eCSSToken_Function) {
   16102           0 :     UngetToken();
   16103           0 :     return false;
   16104             :   }
   16105             : 
   16106             :   const uint32_t* variantMask;
   16107             :   uint16_t minElems, maxElems;
   16108         108 :   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
   16109             : 
   16110         108 :   if (!GetFunctionParseInformation(keyword, aIsPrefixed,
   16111             :                                    aDisallowRelativeValues,
   16112             :                                    minElems, maxElems,
   16113             :                                    variantMask))
   16114           0 :     return false;
   16115             : 
   16116         108 :   return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue);
   16117             : }
   16118             : 
   16119             : /* Parses a transform property list by continuously reading in properties
   16120             :  * and constructing a matrix from it.
   16121             :  * aProperty can be transform or -moz-window-transform.
   16122             :  * FIXME: For -moz-window-transform, it would be nice to reject non-2d
   16123             :  * transforms at parse time, because the implementation only supports 2d
   16124             :  * transforms. Instead, at the moment, non-2d transforms are treated as the
   16125             :  * identity transform very late in the pipeline.
   16126             :  */
   16127             : bool
   16128          84 : CSSParserImpl::ParseTransform(bool aIsPrefixed, nsCSSPropertyID aProperty,
   16129             :                               bool aDisallowRelativeValues)
   16130             : {
   16131         168 :   nsCSSValue value;
   16132             :   // 'inherit', 'initial', 'unset' and 'none' must be alone
   16133          84 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
   16134             :                                nullptr)) {
   16135          80 :     nsCSSValueSharedList* list = new nsCSSValueSharedList;
   16136          80 :     value.SetSharedListValue(list);
   16137          80 :     list->mHead = new nsCSSValueList;
   16138          80 :     nsCSSValueList* cur = list->mHead;
   16139             :     for (;;) {
   16140         136 :       if (!ParseSingleTransform(aIsPrefixed, aDisallowRelativeValues,
   16141             :                                 cur->mValue)) {
   16142           0 :         return false;
   16143             :       }
   16144         108 :       if (CheckEndProperty()) {
   16145          80 :         break;
   16146             :       }
   16147          28 :       cur->mNext = new nsCSSValueList;
   16148          28 :       cur = cur->mNext;
   16149             :     }
   16150             :   }
   16151          84 :   AppendValue(aProperty, value);
   16152          84 :   return true;
   16153             : }
   16154             : 
   16155             : /* Reads a polygon function's argument list.
   16156             :  */
   16157             : bool
   16158           0 : CSSParserImpl::ParsePolygonFunction(nsCSSValue& aValue)
   16159             : {
   16160           0 :   uint16_t numArgs = 1;
   16161             : 
   16162           0 :   nsCSSValue fillRuleValue;
   16163           0 :   if (ParseEnum(fillRuleValue, nsCSSProps::kFillRuleKTable)) {
   16164           0 :     numArgs++;
   16165             : 
   16166             :     // The fill-rule must be comma separated from the polygon points.
   16167           0 :     if (!ExpectSymbol(',', true)) {
   16168           0 :       REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
   16169           0 :       SkipUntil(')');
   16170           0 :       return false;
   16171             :     }
   16172             :   }
   16173             : 
   16174           0 :   nsCSSValue coordinates;
   16175           0 :   nsCSSValuePairList* item = coordinates.SetPairListValue();
   16176             :   for (;;) {
   16177           0 :     nsCSSValue xValue, yValue;
   16178           0 :     if (ParseVariant(xValue, VARIANT_LPCALC, nullptr) != CSSParseResult::Ok ||
   16179           0 :         ParseVariant(yValue, VARIANT_LPCALC, nullptr) != CSSParseResult::Ok) {
   16180           0 :       REPORT_UNEXPECTED_TOKEN(PECoordinatePair);
   16181           0 :       SkipUntil(')');
   16182           0 :       return false;
   16183             :     }
   16184           0 :     item->mXValue = xValue;
   16185           0 :     item->mYValue = yValue;
   16186             : 
   16187             :     // See whether to continue or whether to look for end of function.
   16188           0 :     if (!ExpectSymbol(',', true)) {
   16189             :       // We need to read the closing parenthesis.
   16190           0 :       if (!ExpectSymbol(')', true)) {
   16191           0 :         REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
   16192           0 :         SkipUntil(')');
   16193           0 :         return false;
   16194             :       }
   16195           0 :       break;
   16196             :     }
   16197           0 :     item->mNext = new nsCSSValuePairList;
   16198           0 :     item = item->mNext;
   16199           0 :   }
   16200             : 
   16201             :   RefPtr<nsCSSValue::Array> functionArray =
   16202           0 :     aValue.InitFunction(eCSSKeyword_polygon, numArgs);
   16203           0 :   functionArray->Item(numArgs) = coordinates;
   16204           0 :   if (numArgs > 1) {
   16205           0 :     functionArray->Item(1) = fillRuleValue;
   16206             :   }
   16207             : 
   16208           0 :   return true;
   16209             : }
   16210             : 
   16211             : bool
   16212           0 : CSSParserImpl::ParseCircleOrEllipseFunction(nsCSSKeyword aKeyword,
   16213             :                                             nsCSSValue& aValue)
   16214             : {
   16215           0 :   nsCSSValue radiusX, radiusY, position;
   16216           0 :   bool hasRadius = false, hasPosition = false;
   16217             : 
   16218             :   int32_t mask = VARIANT_LPCALC | VARIANT_NONNEGATIVE_DIMENSION |
   16219           0 :                  VARIANT_KEYWORD;
   16220             :   CSSParseResult result =
   16221           0 :     ParseVariant(radiusX, mask, nsCSSProps::kShapeRadiusKTable);
   16222           0 :   if (result == CSSParseResult::Error) {
   16223           0 :     return false;
   16224           0 :   } else if (result == CSSParseResult::Ok) {
   16225           0 :     if (aKeyword == eCSSKeyword_ellipse) {
   16226           0 :       if (ParseVariant(radiusY, mask, nsCSSProps::kShapeRadiusKTable) !=
   16227             :           CSSParseResult::Ok) {
   16228           0 :         REPORT_UNEXPECTED_TOKEN(PEExpectedRadius);
   16229           0 :         SkipUntil(')');
   16230           0 :         return false;
   16231             :       }
   16232             :     }
   16233           0 :     hasRadius = true;
   16234             :   }
   16235             : 
   16236           0 :   if (!ExpectSymbol(')', true)) {
   16237           0 :     if (!GetToken(true)) {
   16238           0 :       REPORT_UNEXPECTED_EOF(PEPositionEOF);
   16239           0 :       return false;
   16240             :     }
   16241             : 
   16242           0 :     if (mToken.mType != eCSSToken_Ident ||
   16243           0 :         !mToken.mIdent.LowerCaseEqualsLiteral("at") ||
   16244           0 :         !ParsePositionValueForBasicShape(position)) {
   16245           0 :       REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
   16246           0 :       SkipUntil(')');
   16247           0 :       return false;
   16248             :     }
   16249           0 :     if (!ExpectSymbol(')', true)) {
   16250           0 :       REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
   16251           0 :       SkipUntil(')');
   16252           0 :       return false;
   16253             :     }
   16254           0 :     hasPosition = true;
   16255             :   }
   16256             : 
   16257           0 :   size_t count = aKeyword == eCSSKeyword_circle ? 2 : 3;
   16258             :   RefPtr<nsCSSValue::Array> functionArray =
   16259           0 :     aValue.InitFunction(aKeyword, count);
   16260           0 :   if (hasRadius) {
   16261           0 :     functionArray->Item(1) = radiusX;
   16262           0 :     if (aKeyword == eCSSKeyword_ellipse) {
   16263           0 :       functionArray->Item(2) = radiusY;
   16264             :     }
   16265             :   }
   16266           0 :   if (hasPosition) {
   16267           0 :     functionArray->Item(count) = position;
   16268             :   }
   16269             : 
   16270           0 :   return true;
   16271             : }
   16272             : 
   16273             : bool
   16274           0 : CSSParserImpl::ParseInsetFunction(nsCSSValue& aValue)
   16275             : {
   16276             :   RefPtr<nsCSSValue::Array> functionArray =
   16277           0 :     aValue.InitFunction(eCSSKeyword_inset, 5);
   16278             : 
   16279           0 :   int count = 0;
   16280           0 :   while (count < 4) {
   16281             :     CSSParseResult result =
   16282           0 :       ParseVariant(functionArray->Item(count + 1), VARIANT_LPCALC, nullptr);
   16283           0 :     if (result == CSSParseResult::Error) {
   16284           0 :       count = 0;
   16285           0 :       break;
   16286           0 :     } else if (result == CSSParseResult::NotFound) {
   16287           0 :       break;
   16288             :     }
   16289           0 :     ++count;
   16290             :   }
   16291             : 
   16292           0 :   if (count == 0) {
   16293           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedShapeArg);
   16294           0 :     SkipUntil(')');
   16295           0 :     return false;
   16296             :   }
   16297             : 
   16298           0 :   if (!ExpectSymbol(')', true)) {
   16299           0 :     if (!GetToken(true)) {
   16300           0 :       NS_NOTREACHED("ExpectSymbol should have returned true");
   16301           0 :       return false;
   16302             :     }
   16303             : 
   16304           0 :     RefPtr<nsCSSValue::Array> radiusArray = nsCSSValue::Array::Create(4);
   16305           0 :     functionArray->Item(5).SetArrayValue(radiusArray, eCSSUnit_Array);
   16306           0 :     if (mToken.mType != eCSSToken_Ident ||
   16307           0 :         !mToken.mIdent.LowerCaseEqualsLiteral("round") ||
   16308           0 :         !ParseBoxCornerRadiiInternals(radiusArray->ItemStorage())) {
   16309           0 :       REPORT_UNEXPECTED_TOKEN(PEExpectedRadius);
   16310           0 :       SkipUntil(')');
   16311           0 :       return false;
   16312             :     }
   16313             : 
   16314           0 :     if (!ExpectSymbol(')', true)) {
   16315           0 :       REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
   16316           0 :       SkipUntil(')');
   16317           0 :       return false;
   16318             :     }
   16319             :   }
   16320             : 
   16321           0 :   return true;
   16322             : }
   16323             : 
   16324             : bool
   16325           0 : CSSParserImpl::ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens)
   16326             : {
   16327           0 :   if (!GetToken(true)) {
   16328           0 :     return false;
   16329             :   }
   16330             : 
   16331           0 :   if (mToken.mType != eCSSToken_Function) {
   16332           0 :     UngetToken();
   16333           0 :     return false;
   16334             :   }
   16335             : 
   16336             :   // Specific shape function parsing always consumes tokens.
   16337           0 :   *aConsumedTokens = true;
   16338           0 :   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
   16339           0 :   switch (keyword) {
   16340             :   case eCSSKeyword_polygon:
   16341           0 :     return ParsePolygonFunction(aValue);
   16342             :   case eCSSKeyword_circle:
   16343             :   case eCSSKeyword_ellipse:
   16344           0 :     return ParseCircleOrEllipseFunction(keyword, aValue);
   16345             :   case eCSSKeyword_inset:
   16346           0 :     return ParseInsetFunction(aValue);
   16347             :   default:
   16348           0 :     return false;
   16349             :   }
   16350             : }
   16351             : 
   16352             : bool
   16353           0 : CSSParserImpl::ParseReferenceBoxAndBasicShape(
   16354             :   nsCSSValue& aValue,
   16355             :   const KTableEntry aBoxKeywordTable[])
   16356             : {
   16357           0 :   nsCSSValue referenceBox;
   16358           0 :   bool hasBox = ParseEnum(referenceBox, aBoxKeywordTable);
   16359             : 
   16360           0 :   const bool boxCameFirst = hasBox;
   16361             : 
   16362           0 :   nsCSSValue basicShape;
   16363           0 :   bool basicShapeConsumedTokens = false;
   16364           0 :   bool hasShape = ParseBasicShape(basicShape, &basicShapeConsumedTokens);
   16365             : 
   16366             :   // Parsing wasn't successful if ParseBasicShape consumed tokens but failed
   16367             :   // or if the token was neither a reference box nor a basic shape.
   16368           0 :   if ((!hasShape && basicShapeConsumedTokens) || (!hasBox && !hasShape)) {
   16369           0 :     return false;
   16370             :   }
   16371             : 
   16372             :   // Check if the second argument is a reference box if the first wasn't.
   16373           0 :   if (!hasBox) {
   16374           0 :     hasBox = ParseEnum(referenceBox, aBoxKeywordTable);
   16375             :   }
   16376             : 
   16377             :   RefPtr<nsCSSValue::Array> fullValue =
   16378           0 :     nsCSSValue::Array::Create((hasBox && hasShape) ? 2 : 1);
   16379             : 
   16380           0 :   if (hasBox && hasShape) {
   16381           0 :     fullValue->Item(boxCameFirst ? 0 : 1) = referenceBox;
   16382           0 :     fullValue->Item(boxCameFirst ? 1 : 0) = basicShape;
   16383           0 :   } else if (hasBox) {
   16384           0 :     fullValue->Item(0) = referenceBox;
   16385             :   } else {
   16386           0 :     MOZ_ASSERT(hasShape, "should've bailed if we got neither box nor shape");
   16387           0 :     fullValue->Item(0) = basicShape;
   16388             :   }
   16389             : 
   16390           0 :   aValue.SetArrayValue(fullValue, eCSSUnit_Array);
   16391           0 :   return true;
   16392             : }
   16393             : 
   16394             : // Parse a clip-path url to a <clipPath> element or a basic shape.
   16395             : bool
   16396           8 : CSSParserImpl::ParseClipPath(nsCSSValue& aValue)
   16397             : {
   16398           8 :   if (ParseSingleTokenVariant(aValue, VARIANT_HUO, nullptr)) {
   16399           8 :     return true;
   16400             :   }
   16401             : 
   16402           0 :   if (!nsLayoutUtils::CSSClipPathShapesEnabled()) {
   16403             :     // With CSS Clip Path Shapes disabled, we should only accept
   16404             :     // SVG clipPath reference and none.
   16405           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
   16406           0 :     return false;
   16407             :   }
   16408             : 
   16409             :   return ParseReferenceBoxAndBasicShape(
   16410           0 :     aValue, nsCSSProps::kClipPathGeometryBoxKTable);
   16411             : }
   16412             : 
   16413             : // none | [ <basic-shape> || <shape-box> ] | <image>
   16414             : bool
   16415           0 : CSSParserImpl::ParseShapeOutside(nsCSSValue& aValue)
   16416             : {
   16417           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_HUO, nullptr)) {
   16418             :     // 'inherit', 'initial', 'unset', 'none', and <image> url must be alone.
   16419           0 :     return true;
   16420             :   }
   16421             : 
   16422             :   return ParseReferenceBoxAndBasicShape(
   16423           0 :     aValue, nsCSSProps::kShapeOutsideShapeBoxKTable);
   16424             : }
   16425             : 
   16426           4 : bool CSSParserImpl::ParseTransformOrigin(nsCSSPropertyID aProperty)
   16427             : {
   16428           8 :   nsCSSValuePair position;
   16429           4 :   if (!ParseBoxPositionValues(position, true))
   16430           0 :     return false;
   16431             : 
   16432             :   // Unlike many other uses of pairs, this position should always be stored
   16433             :   // as a pair, even if the values are the same, so it always serializes as
   16434             :   // a pair, and to keep the computation code simple.
   16435          10 :   if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
   16436           6 :       position.mXValue.GetUnit() == eCSSUnit_Initial ||
   16437           2 :       position.mXValue.GetUnit() == eCSSUnit_Unset) {
   16438           2 :     MOZ_ASSERT(position.mXValue == position.mYValue,
   16439             :                "inherit/initial/unset only half?");
   16440           2 :     AppendValue(aProperty, position.mXValue);
   16441             :   } else {
   16442           4 :     nsCSSValue value;
   16443           2 :     if (aProperty != eCSSProperty_transform_origin) {
   16444           0 :       value.SetPairValue(position.mXValue, position.mYValue);
   16445             :     } else {
   16446           4 :       nsCSSValue depth;
   16447             :       CSSParseResult result =
   16448           2 :         ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr);
   16449           2 :       if (result == CSSParseResult::Error) {
   16450           0 :         return false;
   16451           2 :       } else if (result == CSSParseResult::NotFound) {
   16452           2 :         depth.SetFloatValue(0.0f, eCSSUnit_Pixel);
   16453             :       }
   16454           2 :       value.SetTripletValue(position.mXValue, position.mYValue, depth);
   16455             :     }
   16456             : 
   16457           2 :     AppendValue(aProperty, value);
   16458             :   }
   16459           4 :   return true;
   16460             : }
   16461             : 
   16462             : /**
   16463             :  * Reads a drop-shadow value. At the moment the Filter Effects specification
   16464             :  * just expects one shadow item. Should this ever change to a list of shadow
   16465             :  * items, use ParseShadowList instead.
   16466             :  */
   16467             : bool
   16468           4 : CSSParserImpl::ParseDropShadow(nsCSSValue* aValue)
   16469             : {
   16470             :   // Use nsCSSValueList to reuse the shadow resolving code in
   16471             :   // nsRuleNode and nsComputedDOMStyle.
   16472           8 :   nsCSSValue shadow;
   16473           4 :   nsCSSValueList* cur = shadow.SetListValue();
   16474           4 :   if (!ParseShadowItem(cur->mValue, false))
   16475           0 :     return false;
   16476             : 
   16477           4 :   if (!ExpectSymbol(')', true))
   16478           0 :     return false;
   16479             : 
   16480           4 :   nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1);
   16481             : 
   16482             :   // Copy things over.
   16483           4 :   dropShadow->Item(1) = shadow;
   16484             : 
   16485           4 :   return true;
   16486             : }
   16487             : 
   16488             : /**
   16489             :  * Reads a single url or filter function from the tokenizer stream, reporting an
   16490             :  * error if something goes wrong.
   16491             :  */
   16492             : bool
   16493          18 : CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
   16494             : {
   16495          18 :   if (ParseSingleTokenVariant(*aValue, VARIANT_URL, nullptr)) {
   16496           1 :     return true;
   16497             :   }
   16498             : 
   16499          17 :   if (!nsLayoutUtils::CSSFiltersEnabled()) {
   16500             :     // With CSS Filters disabled, we should only accept an SVG reference filter.
   16501           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
   16502           0 :     return false;
   16503             :   }
   16504             : 
   16505          17 :   if (!GetToken(true)) {
   16506           0 :     REPORT_UNEXPECTED_EOF(PEFilterEOF);
   16507           0 :     return false;
   16508             :   }
   16509             : 
   16510          17 :   if (mToken.mType != eCSSToken_Function) {
   16511           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
   16512           0 :     UngetToken();
   16513           0 :     return false;
   16514             :   }
   16515             : 
   16516          17 :   nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
   16517             :   // Parse drop-shadow independently of the other filter functions
   16518             :   // because of its more complex characteristics.
   16519          17 :   if (functionName == eCSSKeyword_drop_shadow) {
   16520           4 :     if (ParseDropShadow(aValue)) {
   16521           4 :       return true;
   16522             :     } else {
   16523             :       // Unrecognized filter function.
   16524           0 :       REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
   16525           0 :       SkipUntil(')');
   16526           0 :       return false;
   16527             :     }
   16528             :   }
   16529             : 
   16530             :   // Set up the parsing rules based on the filter function.
   16531          13 :   uint32_t variantMask = VARIANT_PN;
   16532          13 :   bool rejectNegativeArgument = true;
   16533          13 :   bool clampArgumentToOne = false;
   16534          13 :   switch (functionName) {
   16535             :     case eCSSKeyword_blur:
   16536           0 :       variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
   16537             :       // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
   16538           0 :       rejectNegativeArgument = false;
   16539           0 :       break;
   16540             :     case eCSSKeyword_brightness:
   16541             :     case eCSSKeyword_contrast:
   16542             :     case eCSSKeyword_saturate:
   16543           6 :       break;
   16544             :     case eCSSKeyword_grayscale:
   16545             :     case eCSSKeyword_invert:
   16546             :     case eCSSKeyword_sepia:
   16547             :     case eCSSKeyword_opacity:
   16548           7 :       clampArgumentToOne = true;
   16549           7 :       break;
   16550             :     case eCSSKeyword_hue_rotate:
   16551           0 :       variantMask = VARIANT_ANGLE;
   16552           0 :       rejectNegativeArgument = false;
   16553           0 :       break;
   16554             :     default:
   16555             :       // Unrecognized filter function.
   16556           0 :       REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
   16557           0 :       SkipUntil(')');
   16558           0 :       return false;
   16559             :   }
   16560             : 
   16561             :   // Parse the function.
   16562          13 :   uint16_t minElems = 1U;
   16563          13 :   uint16_t maxElems = 1U;
   16564          13 :   uint32_t allVariants = 0;
   16565          13 :   if (!ParseFunction(functionName, &variantMask, allVariants,
   16566             :                      minElems, maxElems, *aValue)) {
   16567           0 :     REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError);
   16568           0 :     return false;
   16569             :   }
   16570             : 
   16571             :   // Get the first and only argument to the filter function.
   16572          13 :   MOZ_ASSERT(aValue->GetUnit() == eCSSUnit_Function,
   16573             :              "expected a filter function");
   16574          13 :   MOZ_ASSERT(aValue->UnitHasArrayValue(),
   16575             :              "filter function should be an array");
   16576          13 :   MOZ_ASSERT(aValue->GetArrayValue()->Count() == 2,
   16577             :              "filter function should have exactly one argument");
   16578          13 :   nsCSSValue& arg = aValue->GetArrayValue()->Item(1);
   16579             : 
   16580          26 :   if (rejectNegativeArgument &&
   16581          39 :       ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) ||
   16582          13 :        (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) {
   16583           0 :     REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
   16584           0 :     return false;
   16585             :   }
   16586             : 
   16587          13 :   if (clampArgumentToOne) {
   16588           7 :     if (arg.GetUnit() == eCSSUnit_Number &&
   16589           0 :         arg.GetFloatValue() > 1.0f) {
   16590           0 :       arg.SetFloatValue(1.0f, arg.GetUnit());
   16591          14 :     } else if (arg.GetUnit() == eCSSUnit_Percent &&
   16592           7 :                arg.GetPercentValue() > 1.0f) {
   16593           0 :       arg.SetPercentValue(1.0f);
   16594             :     }
   16595             :   }
   16596             : 
   16597          13 :   return true;
   16598             : }
   16599             : 
   16600             : /**
   16601             :  * Parses a filter property value by continuously reading in urls and/or filter
   16602             :  * functions and constructing a list.
   16603             :  *
   16604             :  * When CSS Filters are enabled, the filter property accepts one or more SVG
   16605             :  * reference filters and/or CSS filter functions.
   16606             :  * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%);
   16607             :  *
   16608             :  * When CSS Filters are disabled, the filter property only accepts one SVG
   16609             :  * reference filter.
   16610             :  * e.g. filter: url(#my-filter);
   16611             :  */
   16612             : bool
   16613          14 : CSSParserImpl::ParseFilter()
   16614             : {
   16615          28 :   nsCSSValue value;
   16616             :   // 'inherit', 'initial', 'unset' and 'none' must be alone
   16617          14 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
   16618             :                                nullptr)) {
   16619           8 :     nsCSSValueList* cur = value.SetListValue();
   16620          28 :     while (cur) {
   16621          18 :       if (!ParseSingleFilter(&cur->mValue)) {
   16622           0 :         return false;
   16623             :       }
   16624          18 :       if (CheckEndProperty()) {
   16625           8 :         break;
   16626             :       }
   16627          10 :       if (!nsLayoutUtils::CSSFiltersEnabled()) {
   16628             :         // With CSS Filters disabled, we should only accept one SVG reference
   16629             :         // filter.
   16630           0 :         REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
   16631           0 :         return false;
   16632             :       }
   16633          10 :       cur->mNext = new nsCSSValueList;
   16634          10 :       cur = cur->mNext;
   16635             :     }
   16636             :   }
   16637          14 :   AppendValue(eCSSProperty_filter, value);
   16638          14 :   return true;
   16639             : }
   16640             : 
   16641             : bool
   16642           7 : CSSParserImpl::ParseTransitionProperty()
   16643             : {
   16644          14 :   nsCSSValue value;
   16645             :   // 'inherit', 'initial', 'unset' and 'none' must be alone
   16646           7 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
   16647             :                                nullptr)) {
   16648             :     // Accept a list of arbitrary identifiers.  They should be
   16649             :     // CSS properties, but we want to accept any so that we
   16650             :     // accept properties that we don't know about yet, e.g.
   16651             :     // transition-property: invalid-property, left, opacity;
   16652           7 :     nsCSSValueList* cur = value.SetListValue();
   16653             :     for (;;) {
   16654          18 :       if (!ParseSingleTokenVariant(cur->mValue,
   16655             :                                    VARIANT_IDENTIFIER | VARIANT_ALL,
   16656             :                                    nullptr)) {
   16657           0 :         return false;
   16658             :       }
   16659          18 :       if (cur->mValue.GetUnit() == eCSSUnit_Ident) {
   16660          36 :         nsDependentString str(cur->mValue.GetStringBufferValue());
   16661             :         // Exclude 'none', 'inherit', 'initial' and 'unset' according to the
   16662             :         // same rules as for 'counter-reset' in CSS 2.1.
   16663          54 :         if (str.LowerCaseEqualsLiteral("none") ||
   16664          36 :             str.LowerCaseEqualsLiteral("inherit") ||
   16665          54 :             str.LowerCaseEqualsLiteral("initial") ||
   16666          18 :             (str.LowerCaseEqualsLiteral("unset") &&
   16667           0 :              nsLayoutUtils::UnsetValueEnabled())) {
   16668           0 :           return false;
   16669             :         }
   16670             :       }
   16671          18 :       if (!ExpectSymbol(',', true)) {
   16672           7 :         break;
   16673             :       }
   16674          11 :       cur->mNext = new nsCSSValueList;
   16675          11 :       cur = cur->mNext;
   16676          11 :     }
   16677             :   }
   16678           7 :   AppendValue(eCSSProperty_transition_property, value);
   16679           7 :   return true;
   16680             : }
   16681             : 
   16682             : bool
   16683           0 : CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue)
   16684             : {
   16685           0 :   NS_ASSERTION(!mHavePushBack &&
   16686             :                mToken.mType == eCSSToken_Function &&
   16687             :                mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"),
   16688             :                "unexpected initial state");
   16689             : 
   16690           0 :   RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(4);
   16691             : 
   16692             :   float x1, x2, y1, y2;
   16693           0 :   if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) ||
   16694           0 :       !ParseTransitionTimingFunctionValueComponent(y1, ',', false) ||
   16695           0 :       !ParseTransitionTimingFunctionValueComponent(x2, ',', true) ||
   16696           0 :       !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) {
   16697           0 :     return false;
   16698             :   }
   16699             : 
   16700           0 :   val->Item(0).SetFloatValue(x1, eCSSUnit_Number);
   16701           0 :   val->Item(1).SetFloatValue(y1, eCSSUnit_Number);
   16702           0 :   val->Item(2).SetFloatValue(x2, eCSSUnit_Number);
   16703           0 :   val->Item(3).SetFloatValue(y2, eCSSUnit_Number);
   16704             : 
   16705           0 :   aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier);
   16706             : 
   16707           0 :   return true;
   16708             : }
   16709             : 
   16710             : bool
   16711           0 : CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent,
   16712             :                                                            char aStop,
   16713             :                                                            bool aIsXPoint)
   16714             : {
   16715           0 :   if (!GetToken(true)) {
   16716           0 :     return false;
   16717             :   }
   16718           0 :   nsCSSToken* tk = &mToken;
   16719           0 :   if (tk->mType == eCSSToken_Number) {
   16720           0 :     float num = tk->mNumber;
   16721             : 
   16722             :     // Clamp infinity or -infinity values to max float or -max float to avoid
   16723             :     // calculations with infinity.
   16724           0 :     num = mozilla::clamped(num, -std::numeric_limits<float>::max(),
   16725           0 :                                  std::numeric_limits<float>::max());
   16726             : 
   16727             :     // X control point should be inside [0, 1] range.
   16728           0 :     if (aIsXPoint && (num < 0.0 || num > 1.0)) {
   16729           0 :       return false;
   16730             :     }
   16731           0 :     aComponent = num;
   16732           0 :     if (ExpectSymbol(aStop, true)) {
   16733           0 :       return true;
   16734             :     }
   16735             :   }
   16736           0 :   return false;
   16737             : }
   16738             : 
   16739             : bool
   16740           3 : CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
   16741             : {
   16742           3 :   NS_ASSERTION(!mHavePushBack &&
   16743             :                mToken.mType == eCSSToken_Function &&
   16744             :                mToken.mIdent.LowerCaseEqualsLiteral("steps"),
   16745             :                "unexpected initial state");
   16746             : 
   16747           6 :   RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
   16748             : 
   16749           3 :   if (!ParseSingleTokenOneOrLargerVariant(val->Item(0), VARIANT_INTEGER,
   16750             :                                           nullptr)) {
   16751           0 :     return false;
   16752             :   }
   16753             : 
   16754           3 :   int32_t type = -1;  // indicates an implicit end value
   16755           3 :   if (ExpectSymbol(',', true)) {
   16756           0 :     if (!GetToken(true)) {
   16757           0 :       return false;
   16758             :     }
   16759           0 :     if (mToken.mType == eCSSToken_Ident) {
   16760           0 :       if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
   16761           0 :         type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
   16762           0 :       } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) {
   16763           0 :         type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
   16764             :       }
   16765             :     }
   16766           0 :     if (type == -1) {
   16767           0 :       UngetToken();
   16768           0 :       return false;
   16769             :     }
   16770             :   }
   16771           3 :   val->Item(1).SetIntValue(type, eCSSUnit_Enumerated);
   16772             : 
   16773           3 :   if (!ExpectSymbol(')', true)) {
   16774           0 :     return false;
   16775             :   }
   16776             : 
   16777           3 :   aValue.SetArrayValue(val, eCSSUnit_Steps);
   16778           3 :   return true;
   16779             : }
   16780             : 
   16781             : bool
   16782           0 : CSSParserImpl::ParseTransitionFramesTimingFunctionValues(nsCSSValue& aValue)
   16783             : {
   16784           0 :   NS_ASSERTION(!mHavePushBack &&
   16785             :                mToken.mType == eCSSToken_Function &&
   16786             :                mToken.mIdent.LowerCaseEqualsLiteral("frames"),
   16787             :                "unexpected initial state");
   16788             : 
   16789           0 :   nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
   16790           0 :   MOZ_ASSERT(functionName == eCSSKeyword_frames);
   16791             : 
   16792           0 :   nsCSSValue frameNumber;
   16793           0 :   if (!ParseSingleTokenOneOrLargerVariant(frameNumber, VARIANT_INTEGER,
   16794             :                                           nullptr)) {
   16795           0 :     return false;
   16796             :   }
   16797           0 :   MOZ_ASSERT(frameNumber.GetIntValue() >= 1,
   16798             :              "Parsing function should've enforced OneOrLarger, per its name");
   16799             : 
   16800             :   // The number of frames must be a positive integer greater than one.
   16801           0 :   if (frameNumber.GetIntValue() == 1) {
   16802           0 :     return false;
   16803             :   }
   16804             : 
   16805           0 :   if (!ExpectSymbol(')', true)) {
   16806           0 :     return false;
   16807             :   }
   16808             : 
   16809           0 :   RefPtr<nsCSSValue::Array> val = aValue.InitFunction(functionName, 1);
   16810           0 :   val->Item(1) = frameNumber;
   16811           0 :   return true;
   16812             : }
   16813             : 
   16814             : static nsCSSValueList*
   16815         564 : AppendValueToList(nsCSSValue& aContainer,
   16816             :                   nsCSSValueList* aTail,
   16817             :                   const nsCSSValue& aValue)
   16818             : {
   16819             :   nsCSSValueList* entry;
   16820         564 :   if (aContainer.GetUnit() == eCSSUnit_Null) {
   16821         496 :     MOZ_ASSERT(!aTail, "should not have an entry");
   16822         496 :     entry = aContainer.SetListValue();
   16823             :   } else {
   16824          68 :     MOZ_ASSERT(!aTail->mNext, "should not have a next entry");
   16825          68 :     MOZ_ASSERT(aContainer.GetUnit() == eCSSUnit_List, "not a list");
   16826          68 :     entry = new nsCSSValueList;
   16827          68 :     aTail->mNext = entry;
   16828             :   }
   16829         564 :   entry->mValue = aValue;
   16830         564 :   return entry;
   16831             : }
   16832             : 
   16833             : CSSParserImpl::ParseAnimationOrTransitionShorthandResult
   16834         117 : CSSParserImpl::ParseAnimationOrTransitionShorthand(
   16835             :                  const nsCSSPropertyID* aProperties,
   16836             :                  const nsCSSValue* aInitialValues,
   16837             :                  nsCSSValue* aValues,
   16838             :                  size_t aNumProperties)
   16839             : {
   16840         234 :   nsCSSValue tempValue;
   16841             :   // first see if 'inherit', 'initial' or 'unset' is specified.  If one is,
   16842             :   // it can be the only thing specified, so don't attempt to parse any
   16843             :   // additional properties
   16844         117 :   if (ParseSingleTokenVariant(tempValue, VARIANT_INHERIT, nullptr)) {
   16845           0 :     for (uint32_t i = 0; i < aNumProperties; ++i) {
   16846           0 :       AppendValue(aProperties[i], tempValue);
   16847             :     }
   16848           0 :     return eParseAnimationOrTransitionShorthand_Inherit;
   16849             :   }
   16850             : 
   16851             :   static const size_t maxNumProperties = 8;
   16852         117 :   MOZ_ASSERT(aNumProperties <= maxNumProperties,
   16853             :              "can't handle this many properties");
   16854             :   nsCSSValueList *cur[maxNumProperties];
   16855             :   bool parsedProperty[maxNumProperties];
   16856             : 
   16857         629 :   for (size_t i = 0; i < aNumProperties; ++i) {
   16858         512 :     cur[i] = nullptr;
   16859             :   }
   16860         117 :   bool atEOP = false; // at end of property?
   16861             :   for (;;) { // loop over comma-separated transitions or animations
   16862             :     // whether a particular subproperty was specified for this
   16863             :     // transition or animation
   16864         128 :     bool haveAnyProperty = false;
   16865         708 :     for (size_t i = 0; i < aNumProperties; ++i) {
   16866         580 :       parsedProperty[i] = false;
   16867             :     }
   16868             :     for (;;) { // loop over values within a transition or animation
   16869         591 :       bool foundProperty = false;
   16870             :       // check to see if we're at the end of one full transition or
   16871             :       // animation definition (either because we hit a comma or because
   16872             :       // we hit the end of the property definition)
   16873         591 :       if (ExpectSymbol(',', true))
   16874          11 :         break;
   16875         580 :       if (CheckEndProperty()) {
   16876         109 :         atEOP = true;
   16877         109 :         break;
   16878             :       }
   16879             : 
   16880             :       // else, try to parse the next transition or animation sub-property
   16881        1340 :       for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) {
   16882        1332 :         if (!parsedProperty[i]) {
   16883             :           // if we haven't found this property yet, try to parse it
   16884             :           CSSParseResult result =
   16885         957 :             ParseSingleValueProperty(tempValue, aProperties[i]);
   16886         957 :           if (result == CSSParseResult::Error) {
   16887           0 :             return eParseAnimationOrTransitionShorthand_Error;
   16888             :           }
   16889         957 :           if (result == CSSParseResult::Ok) {
   16890         463 :             parsedProperty[i] = true;
   16891         463 :             cur[i] = AppendValueToList(aValues[i], cur[i], tempValue);
   16892         463 :             foundProperty = true;
   16893         463 :             haveAnyProperty = true;
   16894         463 :             break; // out of inner loop; continue looking for next sub-property
   16895             :           }
   16896             :         }
   16897             :       }
   16898         471 :       if (!foundProperty) {
   16899             :         // We're not at a ',' or at the end of the property, but we couldn't
   16900             :         // parse any of the sub-properties, so the declaration is invalid.
   16901           8 :         return eParseAnimationOrTransitionShorthand_Error;
   16902             :       }
   16903         463 :     }
   16904             : 
   16905         120 :     if (!haveAnyProperty) {
   16906             :       // Got an empty item.
   16907           0 :       return eParseAnimationOrTransitionShorthand_Error;
   16908             :     }
   16909             : 
   16910             :     // We hit the end of the property or the end of one transition
   16911             :     // or animation definition, add its components to the list.
   16912         668 :     for (uint32_t i = 0; i < aNumProperties; ++i) {
   16913             :       // If all of the subproperties were not explicitly specified, fill
   16914             :       // in the missing ones with initial values.
   16915         548 :       if (!parsedProperty[i]) {
   16916         101 :         cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]);
   16917             :       }
   16918             :     }
   16919             : 
   16920         120 :     if (atEOP)
   16921         109 :       break;
   16922             :     // else we just hit a ',' so continue parsing the next compound transition
   16923          11 :   }
   16924             : 
   16925         109 :   return eParseAnimationOrTransitionShorthand_Values;
   16926             : }
   16927             : 
   16928             : bool
   16929         106 : CSSParserImpl::ParseTransition()
   16930             : {
   16931             :   static const nsCSSPropertyID kTransitionProperties[] = {
   16932             :     eCSSProperty_transition_duration,
   16933             :     eCSSProperty_transition_timing_function,
   16934             :     // Must check 'transition-delay' after 'transition-duration', since
   16935             :     // that's our assumption about what the spec means for the shorthand
   16936             :     // syntax (the first time given is the duration, and the second
   16937             :     // given is the delay).
   16938             :     eCSSProperty_transition_delay,
   16939             :     // Must check 'transition-property' after
   16940             :     // 'transition-timing-function' since 'transition-property' accepts
   16941             :     // any keyword.
   16942             :     eCSSProperty_transition_property
   16943             :   };
   16944             :   static const uint32_t numProps = MOZ_ARRAY_LENGTH(kTransitionProperties);
   16945             :   // this is a shorthand property that accepts -property, -delay,
   16946             :   // -duration, and -timing-function with some components missing.
   16947             :   // there can be multiple transitions, separated with commas
   16948             : 
   16949         212 :   nsCSSValue initialValues[numProps];
   16950         106 :   initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
   16951             :   initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
   16952         106 :                                eCSSUnit_Enumerated);
   16953         106 :   initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
   16954         106 :   initialValues[3].SetAllValue();
   16955             : 
   16956         212 :   nsCSSValue values[numProps];
   16957             : 
   16958             :   ParseAnimationOrTransitionShorthandResult spres =
   16959             :     ParseAnimationOrTransitionShorthand(kTransitionProperties,
   16960         106 :                                         initialValues, values, numProps);
   16961         106 :   if (spres != eParseAnimationOrTransitionShorthand_Values) {
   16962           8 :     return spres != eParseAnimationOrTransitionShorthand_Error;
   16963             :   }
   16964             : 
   16965             :   // Make two checks on the list for 'transition-property':
   16966             :   //   + If there is more than one item, then none of the items can be
   16967             :   //     'none'.
   16968             :   //   + None of the items can be 'inherit', 'initial' or 'unset'.
   16969             :   {
   16970          98 :     MOZ_ASSERT(kTransitionProperties[3] == eCSSProperty_transition_property,
   16971             :                "array index mismatch");
   16972          98 :     nsCSSValueList *l = values[3].GetListValue();
   16973          98 :     bool multipleItems = !!l->mNext;
   16974          97 :     do {
   16975         103 :       const nsCSSValue& val = l->mValue;
   16976         103 :       if (val.GetUnit() == eCSSUnit_None) {
   16977           6 :         if (multipleItems) {
   16978             :           // This is a syntax error.
   16979           0 :           return false;
   16980             :         }
   16981             : 
   16982             :         // Unbox a solitary 'none'.
   16983           6 :         values[3].SetNoneValue();
   16984           6 :         break;
   16985             :       }
   16986          97 :       if (val.GetUnit() == eCSSUnit_Ident) {
   16987         194 :         nsDependentString str(val.GetStringBufferValue());
   16988         291 :         if (str.EqualsLiteral("inherit") ||
   16989         194 :             str.EqualsLiteral("initial") ||
   16990          97 :             (str.EqualsLiteral("unset") &&
   16991           0 :              nsLayoutUtils::UnsetValueEnabled())) {
   16992           0 :           return false;
   16993             :         }
   16994             :       }
   16995          97 :     } while ((l = l->mNext));
   16996             :   }
   16997             : 
   16998             :   // Save all parsed transition sub-properties in mTempData
   16999         490 :   for (uint32_t i = 0; i < numProps; ++i) {
   17000         392 :     AppendValue(kTransitionProperties[i], values[i]);
   17001             :   }
   17002          98 :   return true;
   17003             : }
   17004             : 
   17005             : bool
   17006          11 : CSSParserImpl::ParseAnimation()
   17007             : {
   17008             :   static const nsCSSPropertyID kAnimationProperties[] = {
   17009             :     eCSSProperty_animation_duration,
   17010             :     eCSSProperty_animation_timing_function,
   17011             :     // Must check 'animation-delay' after 'animation-duration', since
   17012             :     // that's our assumption about what the spec means for the shorthand
   17013             :     // syntax (the first time given is the duration, and the second
   17014             :     // given is the delay).
   17015             :     eCSSProperty_animation_delay,
   17016             :     eCSSProperty_animation_direction,
   17017             :     eCSSProperty_animation_fill_mode,
   17018             :     eCSSProperty_animation_iteration_count,
   17019             :     eCSSProperty_animation_play_state,
   17020             :     // Must check 'animation-name' after 'animation-timing-function',
   17021             :     // 'animation-direction', 'animation-fill-mode',
   17022             :     // 'animation-iteration-count', and 'animation-play-state' since
   17023             :     // 'animation-name' accepts any keyword.
   17024             :     eCSSProperty_animation_name
   17025             :   };
   17026             :   static const uint32_t numProps = MOZ_ARRAY_LENGTH(kAnimationProperties);
   17027             :   // this is a shorthand property that accepts -property, -delay,
   17028             :   // -duration, and -timing-function with some components missing.
   17029             :   // there can be multiple animations, separated with commas
   17030             : 
   17031          22 :   nsCSSValue initialValues[numProps];
   17032          11 :   initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
   17033             :   initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
   17034          11 :                                eCSSUnit_Enumerated);
   17035          11 :   initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
   17036             :   initialValues[3].SetIntValue(static_cast<int32_t>(dom::PlaybackDirection::Normal),
   17037          11 :                                eCSSUnit_Enumerated);
   17038             :   initialValues[4].SetIntValue(static_cast<int32_t>(dom::FillMode::None),
   17039          11 :                                eCSSUnit_Enumerated);
   17040          11 :   initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
   17041          11 :   initialValues[6].SetIntValue(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING, eCSSUnit_Enumerated);
   17042          11 :   initialValues[7].SetNoneValue();
   17043             : 
   17044          22 :   nsCSSValue values[numProps];
   17045             : 
   17046             :   ParseAnimationOrTransitionShorthandResult spres =
   17047             :     ParseAnimationOrTransitionShorthand(kAnimationProperties,
   17048          11 :                                         initialValues, values, numProps);
   17049          11 :   if (spres != eParseAnimationOrTransitionShorthand_Values) {
   17050           0 :     return spres != eParseAnimationOrTransitionShorthand_Error;
   17051             :   }
   17052             : 
   17053             :   // Save all parsed animation sub-properties in mTempData
   17054          99 :   for (uint32_t i = 0; i < numProps; ++i) {
   17055          88 :     AppendValue(kAnimationProperties[i], values[i]);
   17056             :   }
   17057          11 :   return true;
   17058             : }
   17059             : 
   17060             : bool
   17061          43 : CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow)
   17062             : {
   17063             :   // A shadow list item is an array, with entries in this sequence:
   17064             :   enum {
   17065             :     IndexX,
   17066             :     IndexY,
   17067             :     IndexRadius,
   17068             :     IndexSpread,  // only for box-shadow
   17069             :     IndexColor,
   17070             :     IndexInset    // only for box-shadow
   17071             :   };
   17072             : 
   17073          86 :   RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6);
   17074             : 
   17075          43 :   if (aIsBoxShadow) {
   17076             :     // Optional inset keyword (ignore errors)
   17077          35 :     Unused << ParseSingleTokenVariant(val->Item(IndexInset), VARIANT_KEYWORD,
   17078             :                                       nsCSSProps::kBoxShadowTypeKTable);
   17079             :   }
   17080             : 
   17081          86 :   nsCSSValue xOrColor;
   17082          43 :   bool haveColor = false;
   17083          43 :   if (ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC,
   17084             :                    nullptr) != CSSParseResult::Ok) {
   17085           0 :     return false;
   17086             :   }
   17087          43 :   if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) {
   17088          43 :     val->Item(IndexX) = xOrColor;
   17089             :   } else {
   17090             :     // Must be a color (as string or color value)
   17091           0 :     NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident ||
   17092             :                  xOrColor.GetUnit() == eCSSUnit_EnumColor ||
   17093             :                  xOrColor.IsNumericColorUnit(),
   17094             :                  "Must be a color value");
   17095           0 :     val->Item(IndexColor) = xOrColor;
   17096           0 :     haveColor = true;
   17097             : 
   17098             :     // X coordinate mandatory after color
   17099           0 :     if (ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC,
   17100             :                      nullptr) != CSSParseResult::Ok) {
   17101           0 :       return false;
   17102             :     }
   17103             :   }
   17104             : 
   17105             :   // Y coordinate; mandatory
   17106          43 :   if (ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC,
   17107             :                    nullptr) != CSSParseResult::Ok) {
   17108           0 :     return false;
   17109             :   }
   17110             : 
   17111             :   // Optional radius. Ignore errors except if they pass a negative
   17112             :   // value which we must reject. If we use ParseNonNegativeVariant
   17113             :   // we can't tell the difference between an unspecified radius
   17114             :   // and a negative radius.
   17115             :   CSSParseResult result =
   17116          43 :     ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC,
   17117          43 :                  nullptr);
   17118          43 :   if (result == CSSParseResult::Error) {
   17119           0 :     return false;
   17120          43 :   } else if (result == CSSParseResult::Ok) {
   17121          80 :     if (val->Item(IndexRadius).IsLengthUnit() &&
   17122          40 :         val->Item(IndexRadius).GetFloatValue() < 0) {
   17123           0 :       return false;
   17124             :     }
   17125             :   }
   17126             : 
   17127          43 :   if (aIsBoxShadow) {
   17128             :     // Optional spread
   17129          35 :     if (ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC,
   17130             :                      nullptr) == CSSParseResult::Error) {
   17131           0 :       return false;
   17132             :     }
   17133             :   }
   17134             : 
   17135          43 :   if (!haveColor) {
   17136             :     // Optional color
   17137          43 :     if (ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr) ==
   17138             :         CSSParseResult::Error) {
   17139           0 :       return false;
   17140             :     }
   17141             :   }
   17142             : 
   17143          43 :   if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) {
   17144             :     // Optional inset keyword (ignore errors)
   17145          34 :     Unused << ParseSingleTokenVariant(val->Item(IndexInset), VARIANT_KEYWORD,
   17146             :                                       nsCSSProps::kBoxShadowTypeKTable);
   17147             :   }
   17148             : 
   17149          43 :   aValue.SetArrayValue(val, eCSSUnit_Array);
   17150          43 :   return true;
   17151             : }
   17152             : 
   17153             : bool
   17154          69 : CSSParserImpl::ParseShadowList(nsCSSPropertyID aProperty)
   17155             : {
   17156         138 :   nsAutoParseCompoundProperty compound(this);
   17157          69 :   bool isBoxShadow = aProperty == eCSSProperty_box_shadow;
   17158             : 
   17159         138 :   nsCSSValue value;
   17160             :   // 'inherit', 'initial', 'unset' and 'none' must be alone
   17161          69 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
   17162             :                                nullptr)) {
   17163          33 :     nsCSSValueList* cur = value.SetListValue();
   17164             :     for (;;) {
   17165          45 :       if (!ParseShadowItem(cur->mValue, isBoxShadow)) {
   17166           0 :         return false;
   17167             :       }
   17168          39 :       if (!ExpectSymbol(',', true)) {
   17169          33 :         break;
   17170             :       }
   17171           6 :       cur->mNext = new nsCSSValueList;
   17172           6 :       cur = cur->mNext;
   17173             :     }
   17174             :   }
   17175          69 :   AppendValue(aProperty, value);
   17176          69 :   return true;
   17177             : }
   17178             : 
   17179             : int32_t
   17180          78 : CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix)
   17181             : {
   17182          78 :   NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
   17183             : 
   17184          78 :   int32_t nameSpaceID = kNameSpaceID_Unknown;
   17185          78 :   if (mNameSpaceMap) {
   17186             :     // user-specified identifiers are case-sensitive (bug 416106)
   17187         156 :     nsCOMPtr<nsIAtom> prefix = NS_Atomize(aPrefix);
   17188          78 :     nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
   17189             :   }
   17190             :   // else no declared namespaces
   17191             : 
   17192          78 :   if (nameSpaceID == kNameSpaceID_Unknown) {   // unknown prefix, dump it
   17193           0 :     REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, aPrefix);
   17194             :   }
   17195             : 
   17196          78 :   return nameSpaceID;
   17197             : }
   17198             : 
   17199             : void
   17200        8187 : CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
   17201             : {
   17202        8187 :   if (mNameSpaceMap) {
   17203        6353 :     aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nullptr));
   17204             :   } else {
   17205        1834 :     aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
   17206             :   }
   17207        8187 : }
   17208             : 
   17209             : bool
   17210         141 : CSSParserImpl::ParsePaint(nsCSSPropertyID aPropID)
   17211             : {
   17212         282 :   nsCSSValue x, y;
   17213             : 
   17214         141 :   if (ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL | VARIANT_KEYWORD,
   17215             :                    nsCSSProps::kContextPatternKTable) != CSSParseResult::Ok) {
   17216          14 :     return false;
   17217             :   }
   17218             : 
   17219         127 :   bool hasFallback = false;
   17220         254 :   bool canHaveFallback = x.GetUnit() == eCSSUnit_URL ||
   17221         254 :                          x.GetUnit() == eCSSUnit_Enumerated;
   17222         127 :   if (canHaveFallback) {
   17223             :     CSSParseResult result =
   17224          30 :       ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr);
   17225          30 :     if (result == CSSParseResult::Error) {
   17226           0 :       return false;
   17227             :     }
   17228          30 :     hasFallback = (result != CSSParseResult::NotFound);
   17229             :   }
   17230             : 
   17231         127 :   if (hasFallback) {
   17232           0 :     nsCSSValue val;
   17233           0 :     val.SetPairValue(x, y);
   17234           0 :     AppendValue(aPropID, val);
   17235             :   } else {
   17236         127 :     AppendValue(aPropID, x);
   17237             :   }
   17238         127 :   return true;
   17239             : }
   17240             : 
   17241             : bool
   17242           2 : CSSParserImpl::ParseDasharray()
   17243             : {
   17244           4 :   nsCSSValue value;
   17245             : 
   17246             :   // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
   17247           2 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE |
   17248             :                                       VARIANT_OPENTYPE_SVG_KEYWORD,
   17249             :                                nsCSSProps::kStrokeContextValueKTable)) {
   17250           0 :     nsCSSValueList *cur = value.SetListValue();
   17251             :     for (;;) {
   17252           0 :       if (!ParseSingleTokenNonNegativeVariant(cur->mValue, VARIANT_LPN,
   17253             :                                               nullptr)) {
   17254           0 :         return false;
   17255             :       }
   17256           0 :       if (CheckEndProperty()) {
   17257           0 :         break;
   17258             :       }
   17259             :       // skip optional commas between elements
   17260           0 :       (void)ExpectSymbol(',', true);
   17261             : 
   17262           0 :       cur->mNext = new nsCSSValueList;
   17263           0 :       cur = cur->mNext;
   17264             :     }
   17265             :   }
   17266           2 :   AppendValue(eCSSProperty_stroke_dasharray, value);
   17267           2 :   return true;
   17268             : }
   17269             : 
   17270             : bool
   17271           0 : CSSParserImpl::ParseMarker()
   17272             : {
   17273           0 :   nsCSSValue marker;
   17274           0 :   if (ParseSingleValueProperty(marker, eCSSProperty_marker_end) ==
   17275             :       CSSParseResult::Ok) {
   17276           0 :     AppendValue(eCSSProperty_marker_end, marker);
   17277           0 :     AppendValue(eCSSProperty_marker_mid, marker);
   17278           0 :     AppendValue(eCSSProperty_marker_start, marker);
   17279           0 :     return true;
   17280             :   }
   17281           0 :   return false;
   17282             : }
   17283             : 
   17284             : bool
   17285           0 : CSSParserImpl::ParsePaintOrder()
   17286             : {
   17287             :   static_assert
   17288             :     ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) > NS_STYLE_PAINT_ORDER_LAST_VALUE,
   17289             :      "bitfield width insufficient for paint-order constants");
   17290             : 
   17291             :   static const KTableEntry kPaintOrderKTable[] = {
   17292             :     { eCSSKeyword_normal,  NS_STYLE_PAINT_ORDER_NORMAL },
   17293             :     { eCSSKeyword_fill,    NS_STYLE_PAINT_ORDER_FILL },
   17294             :     { eCSSKeyword_stroke,  NS_STYLE_PAINT_ORDER_STROKE },
   17295             :     { eCSSKeyword_markers, NS_STYLE_PAINT_ORDER_MARKERS },
   17296             :     { eCSSKeyword_UNKNOWN, -1 }
   17297             :   };
   17298             : 
   17299             :   static_assert(MOZ_ARRAY_LENGTH(kPaintOrderKTable) ==
   17300             :                   NS_STYLE_PAINT_ORDER_LAST_VALUE + 2,
   17301             :                 "missing paint-order values in kPaintOrderKTable");
   17302             : 
   17303           0 :   nsCSSValue value;
   17304           0 :   if (!ParseSingleTokenVariant(value, VARIANT_HK, kPaintOrderKTable)) {
   17305           0 :     return false;
   17306             :   }
   17307             : 
   17308           0 :   uint32_t seen = 0;
   17309           0 :   uint32_t order = 0;
   17310           0 :   uint32_t position = 0;
   17311             : 
   17312             :   // Ensure that even cast to a signed int32_t when stored in CSSValue,
   17313             :   // we have enough space for the entire paint-order value.
   17314             :   static_assert
   17315             :     (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE < 32,
   17316             :      "seen and order not big enough");
   17317             : 
   17318           0 :   if (value.GetUnit() == eCSSUnit_Enumerated) {
   17319           0 :     uint32_t component = static_cast<uint32_t>(value.GetIntValue());
   17320           0 :     if (component != NS_STYLE_PAINT_ORDER_NORMAL) {
   17321           0 :       bool parsedOK = true;
   17322             :       for (;;) {
   17323           0 :         if (seen & (1 << component)) {
   17324             :           // Already seen this component.
   17325           0 :           UngetToken();
   17326           0 :           parsedOK = false;
   17327           0 :           break;
   17328             :         }
   17329           0 :         seen |= (1 << component);
   17330           0 :         order |= (component << position);
   17331           0 :         position += NS_STYLE_PAINT_ORDER_BITWIDTH;
   17332           0 :         if (!ParseEnum(value, kPaintOrderKTable)) {
   17333           0 :           break;
   17334             :         }
   17335           0 :         component = value.GetIntValue();
   17336           0 :         if (component == NS_STYLE_PAINT_ORDER_NORMAL) {
   17337             :           // Can't have "normal" in the middle of the list of paint components.
   17338           0 :           UngetToken();
   17339           0 :           parsedOK = false;
   17340           0 :           break;
   17341             :         }
   17342             :       }
   17343             : 
   17344             :       // Fill in the remaining paint-order components in the order of their
   17345             :       // constant values.
   17346           0 :       if (parsedOK) {
   17347           0 :         for (component = 1;
   17348           0 :              component <= NS_STYLE_PAINT_ORDER_LAST_VALUE;
   17349             :              component++) {
   17350           0 :           if (!(seen & (1 << component))) {
   17351           0 :             order |= (component << position);
   17352           0 :             position += NS_STYLE_PAINT_ORDER_BITWIDTH;
   17353             :           }
   17354             :         }
   17355             :       }
   17356             :     }
   17357             : 
   17358             :     static_assert(NS_STYLE_PAINT_ORDER_NORMAL == 0,
   17359             :                   "unexpected value for NS_STYLE_PAINT_ORDER_NORMAL");
   17360           0 :     value.SetIntValue(static_cast<int32_t>(order), eCSSUnit_Enumerated);
   17361             :   }
   17362             : 
   17363           0 :   AppendValue(eCSSProperty_paint_order, value);
   17364           0 :   return true;
   17365             : }
   17366             : 
   17367             : bool
   17368         473 : CSSParserImpl::BackslashDropped()
   17369             : {
   17370         473 :   return mScanner->GetEOFCharacters() &
   17371         473 :          nsCSSScanner::eEOFCharacters_DropBackslash;
   17372             : }
   17373             : 
   17374             : void
   17375         141 : CSSParserImpl::AppendImpliedEOFCharacters(nsAString& aResult)
   17376             : {
   17377         141 :   nsCSSScanner::AppendImpliedEOFCharacters(mScanner->GetEOFCharacters(),
   17378         141 :                                            aResult);
   17379         141 : }
   17380             : 
   17381             : bool
   17382           0 : CSSParserImpl::ParseAll()
   17383             : {
   17384           0 :   nsCSSValue value;
   17385           0 :   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
   17386           0 :     return false;
   17387             :   }
   17388             : 
   17389             :   // It's unlikely we'll want to use 'all' from within a UA style sheet, so
   17390             :   // instead of computing the correct EnabledState value we just expand out
   17391             :   // to all content-visible properties.
   17392           0 :   CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all,
   17393             :                                        CSSEnabledState::eForAllContent) {
   17394           0 :     AppendValue(*p, value);
   17395             :   }
   17396           0 :   return true;
   17397             : }
   17398             : 
   17399             : bool
   17400          74 : CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
   17401             :                                         nsString& aValue)
   17402             : {
   17403             :   CSSVariableDeclarations::Type type;
   17404         148 :   nsString variableValue;
   17405             :   bool dropBackslash;
   17406         148 :   nsString impliedCharacters;
   17407             : 
   17408             :   // Record the token stream while parsing a variable value.
   17409          74 :   if (!mInSupportsCondition) {
   17410          74 :     mScanner->StartRecording();
   17411             :   }
   17412          74 :   if (!ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
   17413             :                                nullptr, nullptr)) {
   17414           0 :     if (!mInSupportsCondition) {
   17415           0 :       mScanner->StopRecording();
   17416             :     }
   17417           0 :     return false;
   17418             :   }
   17419             : 
   17420          74 :   if (!mInSupportsCondition) {
   17421          74 :     if (type == CSSVariableDeclarations::eTokenStream) {
   17422             :       // This was indeed a token stream value, so store it in variableValue.
   17423          74 :       mScanner->StopRecording(variableValue);
   17424          74 :       if (dropBackslash) {
   17425           0 :         MOZ_ASSERT(!variableValue.IsEmpty() &&
   17426             :                    variableValue[variableValue.Length() - 1] == '\\');
   17427           0 :         variableValue.Truncate(variableValue.Length() - 1);
   17428             :       }
   17429          74 :       variableValue.Append(impliedCharacters);
   17430             :     } else {
   17431             :       // This was either 'inherit' or 'initial'; we don't need the recorded
   17432             :       // input.
   17433           0 :       mScanner->StopRecording();
   17434             :     }
   17435             :   }
   17436             : 
   17437          74 :   if (mHavePushBack && type == CSSVariableDeclarations::eTokenStream) {
   17438             :     // If we came to the end of a valid variable declaration and a token was
   17439             :     // pushed back, then it would have been ended by '!', ')', ';', ']' or '}'.
   17440             :     // We need to remove it from the recorded variable value.
   17441          74 :     MOZ_ASSERT(mToken.IsSymbol('!') ||
   17442             :                mToken.IsSymbol(')') ||
   17443             :                mToken.IsSymbol(';') ||
   17444             :                mToken.IsSymbol(']') ||
   17445             :                mToken.IsSymbol('}'));
   17446          74 :     if (!mInSupportsCondition) {
   17447          74 :       MOZ_ASSERT(!variableValue.IsEmpty());
   17448          74 :       MOZ_ASSERT(variableValue[variableValue.Length() - 1] == mToken.mSymbol);
   17449          74 :       variableValue.Truncate(variableValue.Length() - 1);
   17450             :     }
   17451             :   }
   17452             : 
   17453          74 :   *aType = type;
   17454          74 :   aValue = variableValue;
   17455          74 :   return true;
   17456             : }
   17457             : 
   17458             : bool
   17459           0 : CSSParserImpl::ParseScrollSnapType()
   17460             : {
   17461           0 :   nsCSSValue value;
   17462           0 :   if (!ParseSingleTokenVariant(value, VARIANT_HK,
   17463             :                                nsCSSProps::kScrollSnapTypeKTable)) {
   17464           0 :     return false;
   17465             :   }
   17466           0 :   AppendValue(eCSSProperty_scroll_snap_type_x, value);
   17467           0 :   AppendValue(eCSSProperty_scroll_snap_type_y, value);
   17468           0 :   return true;
   17469             : }
   17470             : 
   17471             : bool
   17472           0 : CSSParserImpl::ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSPropertyID aPropID)
   17473             : {
   17474           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE,
   17475             :                               nullptr)) {
   17476           0 :     return true;
   17477             :   }
   17478           0 :   if (!GetToken(true)) {
   17479           0 :     return false;
   17480             :   }
   17481           0 :   if (mToken.mType == eCSSToken_Function &&
   17482           0 :       nsCSSKeywords::LookupKeyword(mToken.mIdent) == eCSSKeyword_repeat) {
   17483           0 :     nsCSSValue lengthValue;
   17484           0 :     if (ParseNonNegativeVariant(lengthValue,
   17485             :                                 VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_CALC,
   17486             :                                 nullptr) != CSSParseResult::Ok) {
   17487           0 :       REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
   17488           0 :       SkipUntil(')');
   17489           0 :       return false;
   17490             :     }
   17491           0 :     if (!ExpectSymbol(')', true)) {
   17492           0 :       REPORT_UNEXPECTED(PEExpectedCloseParen);
   17493           0 :       SkipUntil(')');
   17494           0 :       return false;
   17495             :     }
   17496             :     RefPtr<nsCSSValue::Array> functionArray =
   17497           0 :       aValue.InitFunction(eCSSKeyword_repeat, 1);
   17498           0 :     functionArray->Item(1) = lengthValue;
   17499           0 :     return true;
   17500             :   }
   17501           0 :   UngetToken();
   17502           0 :   return false;
   17503             : }
   17504             : 
   17505             : 
   17506             : bool
   17507           0 : CSSParserImpl::ParseScrollSnapDestination(nsCSSValue& aValue)
   17508             : {
   17509           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
   17510           0 :     return true;
   17511             :   }
   17512           0 :   nsCSSValue itemValue;
   17513           0 :   if (!ParsePositionValue(aValue)) {
   17514           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
   17515           0 :     return false;
   17516             :   }
   17517           0 :   return true;
   17518             : }
   17519             : 
   17520             : // This function is very similar to ParseImageLayerPosition, and ParseImageLayerSize.
   17521             : bool
   17522           0 : CSSParserImpl::ParseScrollSnapCoordinate(nsCSSValue& aValue)
   17523             : {
   17524           0 :   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE,
   17525             :                               nullptr)) {
   17526           0 :     return true;
   17527             :   }
   17528           0 :   nsCSSValue itemValue;
   17529           0 :   if (!ParsePositionValue(itemValue)) {
   17530           0 :     REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
   17531           0 :     return false;
   17532             :   }
   17533           0 :   nsCSSValueList* item = aValue.SetListValue();
   17534             :   for (;;) {
   17535           0 :     item->mValue = itemValue;
   17536           0 :     if (!ExpectSymbol(',', true)) {
   17537           0 :       break;
   17538             :     }
   17539           0 :     if (!ParsePositionValue(itemValue)) {
   17540           0 :       REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
   17541           0 :       return false;
   17542             :     }
   17543           0 :     item->mNext = new nsCSSValueList;
   17544           0 :     item = item->mNext;
   17545             :   }
   17546           0 :   return true;
   17547             : }
   17548             : 
   17549             : bool
   17550         473 : CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
   17551             :                                        bool* aDropBackslash,
   17552             :                                        nsString& aImpliedCharacters,
   17553             :                                        void (*aFunc)(const nsAString&, void*),
   17554             :                                        void* aData)
   17555             : {
   17556             :   // A property value is invalid if it contains variable references and also:
   17557             :   //
   17558             :   //   * has unbalanced parens, brackets or braces
   17559             :   //   * has any BAD_STRING or BAD_URL tokens
   17560             :   //   * has any ';' or '!' tokens at the top level of a variable reference's
   17561             :   //     fallback
   17562             :   //
   17563             :   // If the property is a custom property (i.e. a variable declaration), then
   17564             :   // it is also invalid if it consists of no tokens, such as:
   17565             :   //
   17566             :   //   --invalid:;
   17567             :   //
   17568             :   // Note that is valid for a custom property to have a value that consists
   17569             :   // solely of white space, such as:
   17570             :   //
   17571             :   //   --valid: ;
   17572             : 
   17573             :   // Stack of closing characters for currently open constructs.
   17574         946 :   StopSymbolCharStack stack;
   17575             : 
   17576             :   // Indexes into ')' characters in |stack| that correspond to "var(".  This
   17577             :   // is used to stop parsing when we encounter a '!' or ';' at the top level
   17578             :   // of a variable reference's fallback.
   17579         946 :   AutoTArray<uint32_t, 16> references;
   17580             : 
   17581         473 :   if (!GetToken(false)) {
   17582             :     // Variable value was empty since we reached EOF.
   17583           0 :     REPORT_UNEXPECTED_EOF(PEVariableEOF);
   17584           0 :     return false;
   17585             :   }
   17586             : 
   17587         473 :   if (mToken.mType == eCSSToken_Symbol &&
   17588           0 :       (mToken.mSymbol == '!' ||
   17589           0 :        mToken.mSymbol == ')' ||
   17590           0 :        mToken.mSymbol == ';' ||
   17591           0 :        mToken.mSymbol == ']' ||
   17592           0 :        mToken.mSymbol == '}')) {
   17593             :     // Variable value was empty since we reached the end of the construct.
   17594           0 :     UngetToken();
   17595           0 :     REPORT_UNEXPECTED_TOKEN(PEVariableEmpty);
   17596           0 :     return false;
   17597             :   }
   17598             : 
   17599         473 :   if (mToken.mType == eCSSToken_Whitespace) {
   17600         473 :     if (!GetToken(true)) {
   17601             :       // Variable value was white space only.  This is valid.
   17602           0 :       MOZ_ASSERT(!BackslashDropped());
   17603           0 :       *aType = CSSVariableDeclarations::eTokenStream;
   17604           0 :       *aDropBackslash = false;
   17605           0 :       AppendImpliedEOFCharacters(aImpliedCharacters);
   17606           0 :       return true;
   17607             :     }
   17608             :   }
   17609             : 
   17610             :   // Look for 'initial', 'inherit' or 'unset' as the first non-white space
   17611             :   // token.
   17612         473 :   CSSVariableDeclarations::Type type = CSSVariableDeclarations::eTokenStream;
   17613         473 :   if (mToken.mType == eCSSToken_Ident) {
   17614          59 :     if (mToken.mIdent.LowerCaseEqualsLiteral("initial")) {
   17615           0 :       type = CSSVariableDeclarations::eInitial;
   17616          59 :     } else if (mToken.mIdent.LowerCaseEqualsLiteral("inherit")) {
   17617           0 :       type = CSSVariableDeclarations::eInherit;
   17618          59 :     } else if (mToken.mIdent.LowerCaseEqualsLiteral("unset")) {
   17619           0 :       type = CSSVariableDeclarations::eUnset;
   17620             :     }
   17621             :   }
   17622             : 
   17623         473 :   if (type != CSSVariableDeclarations::eTokenStream) {
   17624           0 :     if (!GetToken(true)) {
   17625             :       // Variable value was 'initial' or 'inherit' followed by EOF.
   17626           0 :       MOZ_ASSERT(!BackslashDropped());
   17627           0 :       *aType = type;
   17628           0 :       *aDropBackslash = false;
   17629           0 :       AppendImpliedEOFCharacters(aImpliedCharacters);
   17630           0 :       return true;
   17631             :     }
   17632           0 :     UngetToken();
   17633           0 :     if (mToken.mType == eCSSToken_Symbol &&
   17634           0 :         (mToken.mSymbol == '!' ||
   17635           0 :          mToken.mSymbol == ')' ||
   17636           0 :          mToken.mSymbol == ';' ||
   17637           0 :          mToken.mSymbol == ']' ||
   17638           0 :          mToken.mSymbol == '}')) {
   17639             :       // Variable value was 'initial' or 'inherit' followed by the end
   17640             :       // of the declaration.
   17641           0 :       MOZ_ASSERT(!BackslashDropped());
   17642           0 :       *aType = type;
   17643           0 :       *aDropBackslash = false;
   17644           0 :       return true;
   17645             :     }
   17646             :   }
   17647             : 
   17648        1323 :   do {
   17649        1655 :     switch (mToken.mType) {
   17650             :       case eCSSToken_Symbol:
   17651         677 :         if (mToken.mSymbol == '(') {
   17652           5 :           stack.AppendElement(')');
   17653         672 :         } else if (mToken.mSymbol == '[') {
   17654           0 :           stack.AppendElement(']');
   17655         672 :         } else if (mToken.mSymbol == '{') {
   17656           0 :           stack.AppendElement('}');
   17657        1056 :         } else if (mToken.mSymbol == ';' ||
   17658         384 :                    mToken.mSymbol == '!') {
   17659         331 :           if (stack.IsEmpty()) {
   17660         331 :             UngetToken();
   17661         331 :             MOZ_ASSERT(!BackslashDropped());
   17662         331 :             *aType = CSSVariableDeclarations::eTokenStream;
   17663         331 :             *aDropBackslash = false;
   17664         331 :             return true;
   17665           0 :           } else if (!references.IsEmpty() &&
   17666           0 :                      references.LastElement() == stack.Length() - 1) {
   17667           0 :             REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback);
   17668           0 :             SkipUntilAllOf(stack);
   17669           0 :             return false;
   17670             :           }
   17671         574 :         } else if (mToken.mSymbol == ')' ||
   17672         466 :                    mToken.mSymbol == ']' ||
   17673         233 :                    mToken.mSymbol == '}') {
   17674             :           for (;;) {
   17675         109 :             if (stack.IsEmpty()) {
   17676           1 :               UngetToken();
   17677           1 :               MOZ_ASSERT(!BackslashDropped());
   17678           1 :               *aType = CSSVariableDeclarations::eTokenStream;
   17679           1 :               *aDropBackslash = false;
   17680           1 :               return true;
   17681             :             }
   17682         108 :             char16_t c = stack.LastElement();
   17683         108 :             stack.TruncateLength(stack.Length() - 1);
   17684         126 :             if (!references.IsEmpty() &&
   17685          18 :                 references.LastElement() == stack.Length()) {
   17686          18 :               references.TruncateLength(references.Length() - 1);
   17687             :             }
   17688         108 :             if (mToken.mSymbol == c) {
   17689         108 :               break;
   17690             :             }
   17691           0 :           }
   17692             :         }
   17693         345 :         break;
   17694             : 
   17695             :       case eCSSToken_Function:
   17696         370 :         if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
   17697         285 :           if (!GetToken(true)) {
   17698             :             // EOF directly after "var(".
   17699           0 :             REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF);
   17700           0 :             return false;
   17701             :           }
   17702         570 :           if (mToken.mType != eCSSToken_Ident ||
   17703         285 :               !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
   17704             :             // There must be an identifier directly after the "var(" and
   17705             :             // it must be a custom property name.
   17706           0 :             UngetToken();
   17707           0 :             REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName);
   17708           0 :             SkipUntil(')');
   17709           0 :             SkipUntilAllOf(stack);
   17710           0 :             return false;
   17711             :           }
   17712         285 :           if (aFunc) {
   17713          11 :             MOZ_ASSERT(Substring(mToken.mIdent, 0,
   17714             :                                  CSS_CUSTOM_NAME_PREFIX_LENGTH).
   17715             :                          EqualsLiteral("--"));
   17716             :             // remove '--'
   17717             :             const nsDependentSubstring varName =
   17718          22 :               Substring(mToken.mIdent, CSS_CUSTOM_NAME_PREFIX_LENGTH);
   17719          11 :             aFunc(varName, aData);
   17720             :           }
   17721         285 :           if (!GetToken(true)) {
   17722             :             // EOF right after "var(<ident>".
   17723           0 :             stack.AppendElement(')');
   17724         285 :           } else if (mToken.IsSymbol(',')) {
   17725             :             // Variable reference with fallback.
   17726          18 :             if (!GetToken(false) || mToken.IsSymbol(')')) {
   17727             :               // Comma must be followed by at least one fallback token.
   17728           0 :               REPORT_UNEXPECTED(PEExpectedVariableFallback);
   17729           0 :               SkipUntilAllOf(stack);
   17730           0 :               return false;
   17731             :             }
   17732          18 :             UngetToken();
   17733          18 :             references.AppendElement(stack.Length());
   17734          18 :             stack.AppendElement(')');
   17735         267 :           } else if (mToken.IsSymbol(')')) {
   17736             :             // Correctly closed variable reference.
   17737             :           } else {
   17738             :             // Malformed variable reference.
   17739           0 :             REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen);
   17740           0 :             SkipUntil(')');
   17741           0 :             SkipUntilAllOf(stack);
   17742           0 :             return false;
   17743             :           }
   17744             :         } else {
   17745          85 :           stack.AppendElement(')');
   17746             :         }
   17747         370 :         break;
   17748             : 
   17749             :       case eCSSToken_Bad_String:
   17750           0 :         SkipUntilAllOf(stack);
   17751           0 :         return false;
   17752             : 
   17753             :       case eCSSToken_Bad_URL:
   17754           0 :         SkipUntil(')');
   17755           0 :         SkipUntilAllOf(stack);
   17756           0 :         return false;
   17757             : 
   17758             :       default:
   17759         608 :         break;
   17760             :     }
   17761             :   } while (GetToken(true));
   17762             : 
   17763             :   // Append any implied closing characters.
   17764         141 :   *aDropBackslash = BackslashDropped();
   17765         141 :   AppendImpliedEOFCharacters(aImpliedCharacters);
   17766         141 :   uint32_t i = stack.Length();
   17767         141 :   while (i--) {
   17768           0 :     aImpliedCharacters.Append(stack[i]);
   17769             :   }
   17770             : 
   17771         141 :   *aType = type;
   17772         141 :   return true;
   17773             : }
   17774             : 
   17775             : bool
   17776           0 : CSSParserImpl::IsValueValidForProperty(const nsCSSPropertyID aPropID,
   17777             :                                        const nsAString& aPropValue)
   17778             : {
   17779           0 :   mData.AssertInitialState();
   17780           0 :   mTempData.AssertInitialState();
   17781             : 
   17782           0 :   nsCSSScanner scanner(aPropValue, 0);
   17783           0 :   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr);
   17784           0 :   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
   17785             : 
   17786             :   // We normally would need to pass in a sheet principal to InitScanner,
   17787             :   // because we might parse a URL value.  However, we will never use the
   17788             :   // parsed nsCSSValue (and so whether we have a sheet principal or not
   17789             :   // doesn't really matter), so to avoid failing the assertion in
   17790             :   // SetValueToURL, we set mSheetPrincipalRequired to false to declare
   17791             :   // that it's safe to skip the assertion.
   17792           0 :   AutoRestore<bool> autoRestore(mSheetPrincipalRequired);
   17793           0 :   mSheetPrincipalRequired = false;
   17794             : 
   17795           0 :   nsAutoSuppressErrors suppressErrors(this);
   17796             : 
   17797           0 :   mSection = eCSSSection_General;
   17798             : 
   17799             :   // Check for unknown properties
   17800           0 :   if (eCSSProperty_UNKNOWN == aPropID) {
   17801           0 :     ReleaseScanner();
   17802           0 :     return false;
   17803             :   }
   17804             : 
   17805             :   // Check that the property and value parse successfully
   17806           0 :   bool parsedOK = ParseProperty(aPropID);
   17807             : 
   17808             :   // Check for priority
   17809           0 :   parsedOK = parsedOK && ParsePriority() != ePriority_Error;
   17810             : 
   17811             :   // We should now be at EOF
   17812           0 :   parsedOK = parsedOK && !GetToken(true);
   17813             : 
   17814           0 :   mTempData.ClearProperty(aPropID);
   17815           0 :   mTempData.AssertInitialState();
   17816           0 :   mData.AssertInitialState();
   17817             : 
   17818           0 :   CLEAR_ERROR();
   17819           0 :   ReleaseScanner();
   17820             : 
   17821           0 :   return parsedOK;
   17822             : }
   17823             : 
   17824             : } // namespace
   17825             : 
   17826             : // Recycling of parser implementation objects
   17827             : 
   17828             : static CSSParserImpl* gFreeList = nullptr;
   17829             : 
   17830             : /* static */ void
   17831           3 : nsCSSParser::Startup()
   17832             : {
   17833             :   Preferences::AddBoolVarCache(&sOpentypeSVGEnabled,
   17834           3 :                                "gfx.font_rendering.opentype_svg.enabled");
   17835             :   Preferences::AddBoolVarCache(&sWebkitPrefixedAliasesEnabled,
   17836           3 :                                "layout.css.prefixes.webkit");
   17837             :   Preferences::AddBoolVarCache(&sWebkitDevicePixelRatioEnabled,
   17838           3 :                                "layout.css.prefixes.device-pixel-ratio-webkit");
   17839             :   Preferences::AddBoolVarCache(&sMozGradientsEnabled,
   17840           3 :                                "layout.css.prefixes.gradients");
   17841             :   Preferences::AddBoolVarCache(&sControlCharVisibility,
   17842           3 :                                "layout.css.control-characters.visible");
   17843             :   Preferences::AddBoolVarCache(&sFramesTimingFunctionEnabled,
   17844           3 :                                "layout.css.frames-timing.enabled");
   17845           3 : }
   17846             : 
   17847       12025 : nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
   17848       12025 :                          CSSStyleSheet* aSheet)
   17849             : {
   17850       12025 :   CSSParserImpl *impl = gFreeList;
   17851       12025 :   if (impl) {
   17852       12021 :     gFreeList = impl->mNextFree;
   17853       12021 :     impl->mNextFree = nullptr;
   17854             :   } else {
   17855           4 :     impl = new CSSParserImpl();
   17856             :   }
   17857             : 
   17858       12025 :   if (aLoader) {
   17859         300 :     impl->SetChildLoader(aLoader);
   17860         300 :     impl->SetQuirkMode(aLoader->GetCompatibilityMode() ==
   17861         300 :                        eCompatibility_NavQuirks);
   17862             :   }
   17863       12025 :   if (aSheet) {
   17864          60 :     impl->SetStyleSheet(aSheet);
   17865             :   }
   17866             : 
   17867       12025 :   mImpl = static_cast<void*>(impl);
   17868       12025 : }
   17869             : 
   17870       24050 : nsCSSParser::~nsCSSParser()
   17871             : {
   17872       12025 :   CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl);
   17873       12025 :   impl->Reset();
   17874       12025 :   impl->mNextFree = gFreeList;
   17875       12025 :   gFreeList = impl;
   17876       12025 : }
   17877             : 
   17878             : /* static */ void
   17879           0 : nsCSSParser::Shutdown()
   17880             : {
   17881           0 :   CSSParserImpl *tofree = gFreeList;
   17882             :   CSSParserImpl *next;
   17883           0 :   while (tofree)
   17884             :     {
   17885           0 :       next = tofree->mNextFree;
   17886           0 :       delete tofree;
   17887           0 :       tofree = next;
   17888             :     }
   17889           0 : }
   17890             : 
   17891             : // Wrapper methods
   17892             : 
   17893             : nsresult
   17894          60 : nsCSSParser::ParseSheet(const nsAString& aInput,
   17895             :                         nsIURI*          aSheetURI,
   17896             :                         nsIURI*          aBaseURI,
   17897             :                         nsIPrincipal*    aSheetPrincipal,
   17898             :                         uint32_t         aLineNumber,
   17899             :                         css::LoaderReusableStyleSheets* aReusableSheets)
   17900             : {
   17901          60 :   return static_cast<CSSParserImpl*>(mImpl)->
   17902             :     ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
   17903          60 :                aReusableSheets);
   17904             : }
   17905             : 
   17906             : already_AddRefed<css::Declaration>
   17907           7 : nsCSSParser::ParseStyleAttribute(const nsAString&  aAttributeValue,
   17908             :                                  nsIURI*           aDocURI,
   17909             :                                  nsIURI*           aBaseURI,
   17910             :                                  nsIPrincipal*     aNodePrincipal)
   17911             : {
   17912           7 :   return static_cast<CSSParserImpl*>(mImpl)->
   17913           7 :     ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI, aNodePrincipal);
   17914             : }
   17915             : 
   17916             : nsresult
   17917           0 : nsCSSParser::ParseDeclarations(const nsAString&  aBuffer,
   17918             :                                nsIURI*           aSheetURI,
   17919             :                                nsIURI*           aBaseURI,
   17920             :                                nsIPrincipal*     aSheetPrincipal,
   17921             :                                css::Declaration* aDeclaration,
   17922             :                                bool*           aChanged)
   17923             : {
   17924           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   17925             :     ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal,
   17926           0 :                       aDeclaration, aChanged);
   17927             : }
   17928             : 
   17929             : nsresult
   17930           0 : nsCSSParser::ParseRule(const nsAString&        aRule,
   17931             :                        nsIURI*                 aSheetURI,
   17932             :                        nsIURI*                 aBaseURI,
   17933             :                        nsIPrincipal*           aSheetPrincipal,
   17934             :                        css::Rule**             aResult)
   17935             : {
   17936           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   17937           0 :     ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult);
   17938             : }
   17939             : 
   17940             : void
   17941         103 : nsCSSParser::ParseProperty(const nsCSSPropertyID aPropID,
   17942             :                            const nsAString&    aPropValue,
   17943             :                            nsIURI*             aSheetURI,
   17944             :                            nsIURI*             aBaseURI,
   17945             :                            nsIPrincipal*       aSheetPrincipal,
   17946             :                            css::Declaration*   aDeclaration,
   17947             :                            bool*               aChanged,
   17948             :                            bool                aIsImportant,
   17949             :                            bool                aIsSVGMode)
   17950             : {
   17951         103 :   static_cast<CSSParserImpl*>(mImpl)->
   17952         103 :     ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
   17953             :                   aSheetPrincipal, aDeclaration, aChanged,
   17954         103 :                   aIsImportant, aIsSVGMode);
   17955         103 : }
   17956             : 
   17957             : void
   17958           0 : nsCSSParser::ParseLonghandProperty(const nsCSSPropertyID aPropID,
   17959             :                                    const nsAString&    aPropValue,
   17960             :                                    nsIURI*             aSheetURI,
   17961             :                                    nsIURI*             aBaseURI,
   17962             :                                    nsIPrincipal*       aSheetPrincipal,
   17963             :                                    nsCSSValue&         aResult)
   17964             : {
   17965           0 :   static_cast<CSSParserImpl*>(mImpl)->
   17966             :     ParseLonghandProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
   17967           0 :                           aSheetPrincipal, aResult);
   17968           0 : }
   17969             : 
   17970             : bool
   17971           0 : nsCSSParser::ParseTransformProperty(const nsAString& aPropValue,
   17972             :                                     bool             aDisallowRelativeValues,
   17973             :                                     nsCSSValue&      aResult)
   17974             : {
   17975           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   17976           0 :     ParseTransformProperty(aPropValue, aDisallowRelativeValues, aResult);
   17977             : }
   17978             : 
   17979             : void
   17980           0 : nsCSSParser::ParseVariable(const nsAString&    aVariableName,
   17981             :                            const nsAString&    aPropValue,
   17982             :                            nsIURI*             aSheetURI,
   17983             :                            nsIURI*             aBaseURI,
   17984             :                            nsIPrincipal*       aSheetPrincipal,
   17985             :                            css::Declaration*   aDeclaration,
   17986             :                            bool*               aChanged,
   17987             :                            bool                aIsImportant)
   17988             : {
   17989           0 :   static_cast<CSSParserImpl*>(mImpl)->
   17990           0 :     ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI,
   17991           0 :                   aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
   17992           0 : }
   17993             : 
   17994             : void
   17995           3 : nsCSSParser::ParseMediaList(const nsAString& aBuffer,
   17996             :                             nsIURI*            aURI,
   17997             :                             uint32_t           aLineNumber,
   17998             :                             nsMediaList*       aMediaList)
   17999             : {
   18000           3 :   static_cast<CSSParserImpl*>(mImpl)->
   18001           3 :     ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList);
   18002           3 : }
   18003             : 
   18004             : bool
   18005           0 : nsCSSParser::ParseSourceSizeList(const nsAString& aBuffer,
   18006             :                                  nsIURI* aURI,
   18007             :                                  uint32_t aLineNumber,
   18008             :                                  InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
   18009             :                                  InfallibleTArray<nsCSSValue>& aValues)
   18010             : {
   18011           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18012           0 :     ParseSourceSizeList(aBuffer, aURI, aLineNumber, aQueries, aValues);
   18013             : }
   18014             : 
   18015             : bool
   18016           0 : nsCSSParser::ParseFontFamilyListString(const nsAString& aBuffer,
   18017             :                                        nsIURI*            aURI,
   18018             :                                        uint32_t           aLineNumber,
   18019             :                                        nsCSSValue&        aValue)
   18020             : {
   18021           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18022           0 :     ParseFontFamilyListString(aBuffer, aURI, aLineNumber, aValue);
   18023             : }
   18024             : 
   18025             : bool
   18026         152 : nsCSSParser::ParseColorString(const nsAString& aBuffer,
   18027             :                               nsIURI*            aURI,
   18028             :                               uint32_t           aLineNumber,
   18029             :                               nsCSSValue&        aValue,
   18030             :                               bool               aSuppressErrors /* false */)
   18031             : {
   18032         152 :   return static_cast<CSSParserImpl*>(mImpl)->
   18033         304 :     ParseColorString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors);
   18034             : }
   18035             : 
   18036             : bool
   18037           0 : nsCSSParser::ParseMarginString(const nsAString& aBuffer,
   18038             :                                nsIURI*            aURI,
   18039             :                                uint32_t           aLineNumber,
   18040             :                                nsCSSValue&        aValue,
   18041             :                                bool               aSuppressErrors /* false */)
   18042             : {
   18043           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18044           0 :     ParseMarginString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors);
   18045             : }
   18046             : 
   18047             : nsresult
   18048           7 : nsCSSParser::ParseSelectorString(const nsAString&  aSelectorString,
   18049             :                                  nsIURI*             aURI,
   18050             :                                  uint32_t            aLineNumber,
   18051             :                                  nsCSSSelectorList** aSelectorList)
   18052             : {
   18053           7 :   return static_cast<CSSParserImpl*>(mImpl)->
   18054           7 :     ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList);
   18055             : }
   18056             : 
   18057             : already_AddRefed<nsCSSKeyframeRule>
   18058           0 : nsCSSParser::ParseKeyframeRule(const nsAString& aBuffer,
   18059             :                                nsIURI*            aURI,
   18060             :                                uint32_t           aLineNumber)
   18061             : {
   18062           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18063           0 :     ParseKeyframeRule(aBuffer, aURI, aLineNumber);
   18064             : }
   18065             : 
   18066             : bool
   18067           0 : nsCSSParser::ParseKeyframeSelectorString(const nsAString& aSelectorString,
   18068             :                                          nsIURI*            aURI,
   18069             :                                          uint32_t           aLineNumber,
   18070             :                                          InfallibleTArray<float>& aSelectorList)
   18071             : {
   18072           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18073             :     ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber,
   18074           0 :                                 aSelectorList);
   18075             : }
   18076             : 
   18077             : bool
   18078           0 : nsCSSParser::EvaluateSupportsDeclaration(const nsAString& aProperty,
   18079             :                                          const nsAString& aValue,
   18080             :                                          nsIURI* aDocURL,
   18081             :                                          nsIURI* aBaseURL,
   18082             :                                          nsIPrincipal* aDocPrincipal)
   18083             : {
   18084           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18085             :     EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL,
   18086           0 :                                 aDocPrincipal);
   18087             : }
   18088             : 
   18089             : bool
   18090           0 : nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition,
   18091             :                                        nsIURI* aDocURL,
   18092             :                                        nsIURI* aBaseURL,
   18093             :                                        nsIPrincipal* aDocPrincipal,
   18094             :                                        SupportsParsingSettings aSettings)
   18095             : {
   18096           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18097             :     EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL,
   18098           0 :                               aDocPrincipal, aSettings);
   18099             : }
   18100             : 
   18101             : bool
   18102         141 : nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue,
   18103             :                                          VariableEnumFunc aFunc,
   18104             :                                          void* aData)
   18105             : {
   18106         141 :   return static_cast<CSSParserImpl*>(mImpl)->
   18107         141 :     EnumerateVariableReferences(aPropertyValue, aFunc, aData);
   18108             : }
   18109             : 
   18110             : bool
   18111         141 : nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue,
   18112             :                                   const CSSVariableValues* aVariables,
   18113             :                                   nsString& aResult,
   18114             :                                   nsCSSTokenSerializationType& aFirstToken,
   18115             :                                   nsCSSTokenSerializationType& aLastToken)
   18116             : {
   18117         141 :   return static_cast<CSSParserImpl*>(mImpl)->
   18118             :     ResolveVariableValue(aPropertyValue, aVariables,
   18119         141 :                          aResult, aFirstToken, aLastToken);
   18120             : }
   18121             : 
   18122             : void
   18123         883 : nsCSSParser::ParsePropertyWithVariableReferences(
   18124             :                                             nsCSSPropertyID aPropertyID,
   18125             :                                             nsCSSPropertyID aShorthandPropertyID,
   18126             :                                             const nsAString& aValue,
   18127             :                                             const CSSVariableValues* aVariables,
   18128             :                                             nsRuleData* aRuleData,
   18129             :                                             nsIURI* aDocURL,
   18130             :                                             nsIURI* aBaseURL,
   18131             :                                             nsIPrincipal* aDocPrincipal,
   18132             :                                             CSSStyleSheet* aSheet,
   18133             :                                             uint32_t aLineNumber,
   18134             :                                             uint32_t aLineOffset)
   18135             : {
   18136         883 :   static_cast<CSSParserImpl*>(mImpl)->
   18137             :     ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID,
   18138             :                                         aValue, aVariables, aRuleData, aDocURL,
   18139             :                                         aBaseURL, aDocPrincipal, aSheet,
   18140         883 :                                         aLineNumber, aLineOffset);
   18141         883 : }
   18142             : 
   18143             : already_AddRefed<nsIAtom>
   18144           0 : nsCSSParser::ParseCounterStyleName(const nsAString& aBuffer, nsIURI* aURL)
   18145             : {
   18146           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18147           0 :     ParseCounterStyleName(aBuffer, aURL);
   18148             : }
   18149             : 
   18150             : bool
   18151           0 : nsCSSParser::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
   18152             :                                     const nsAString& aBuffer,
   18153             :                                     nsIURI* aSheetURL,
   18154             :                                     nsIURI* aBaseURL,
   18155             :                                     nsIPrincipal* aSheetPrincipal,
   18156             :                                     nsCSSValue& aValue)
   18157             : {
   18158           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18159             :     ParseCounterDescriptor(aDescID, aBuffer,
   18160           0 :                            aSheetURL, aBaseURL, aSheetPrincipal, aValue);
   18161             : }
   18162             : 
   18163             : bool
   18164           0 : nsCSSParser::ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
   18165             :                                      const nsAString& aBuffer,
   18166             :                                      nsIURI* aSheetURL,
   18167             :                                      nsIURI* aBaseURL,
   18168             :                                      nsIPrincipal* aSheetPrincipal,
   18169             :                                      nsCSSValue& aValue)
   18170             : {
   18171           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18172             :     ParseFontFaceDescriptor(aDescID, aBuffer,
   18173           0 :                            aSheetURL, aBaseURL, aSheetPrincipal, aValue);
   18174             : }
   18175             : 
   18176             : bool
   18177           0 : nsCSSParser::IsValueValidForProperty(const nsCSSPropertyID aPropID,
   18178             :                                      const nsAString&    aPropValue)
   18179             : {
   18180           0 :   return static_cast<CSSParserImpl*>(mImpl)->
   18181           0 :     IsValueValidForProperty(aPropID, aPropValue);
   18182             : }
   18183             : 
   18184             : /* static */
   18185             : uint8_t
   18186         101 : nsCSSParser::ControlCharVisibilityDefault()
   18187             : {
   18188             :   return sControlCharVisibility
   18189             :     ? NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE
   18190         101 :     : NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN;
   18191             : }

Generated by: LCOV version 1.13