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 "mozilla/ArrayUtils.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/Move.h"
12 #include "mozilla/MathAlgorithms.h"
13 #include "mozilla/TypedEnumBits.h"
14 
15 #include <algorithm> // for std::stable_sort
16 #include <limits> // for std::numeric_limits
17 
18 #include "nsCSSParser.h"
19 #include "nsAlgorithm.h"
20 #include "nsCSSProps.h"
21 #include "nsCSSKeywords.h"
22 #include "nsCSSScanner.h"
23 #include "mozilla/css/ErrorReporter.h"
24 #include "mozilla/css/Loader.h"
25 #include "mozilla/css/StyleRule.h"
26 #include "mozilla/css/ImportRule.h"
27 #include "nsCSSRules.h"
28 #include "mozilla/css/NameSpaceRule.h"
infer_shape(X, n_points=None)29 #include "nsTArray.h"
30 #include "mozilla/StyleSheetInlines.h"
31 #include "mozilla/css/Declaration.h"
32 #include "nsStyleConsts.h"
33 #include "nsNetUtil.h"
34 #include "nsCOMPtr.h"
35 #include "nsString.h"
36 #include "nsIAtom.h"
37 #include "nsColor.h"
38 #include "nsCSSPseudoClasses.h"
39 #include "nsCSSPseudoElements.h"
40 #include "nsCSSAnonBoxes.h"
41 #include "nsNameSpaceManager.h"
42 #include "nsXMLNameSpaceMap.h"
43 #include "nsError.h"
44 #include "nsIMediaList.h"
45 #include "nsStyleUtil.h"
46 #include "nsIPrincipal.h"
47 #include "nsICSSUnprefixingService.h"
48 #include "mozilla/Sprintf.h"
49 #include "nsContentUtils.h"
50 #include "nsAutoPtr.h"
51 #include "CSSCalc.h"
52 #include "nsMediaFeatures.h"
53 #include "nsLayoutUtils.h"
54 #include "mozilla/Preferences.h"
55 #include "nsRuleData.h"
56 #include "mozilla/CSSVariableValues.h"
57 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h"
58 #include "mozilla/dom/URL.h"
59 #include "gfxFontFamilyList.h"
60 
61 using namespace mozilla;
62 using namespace mozilla::css;
63 
conditioned_vars(varnames)64 typedef nsCSSProps::KTableEntry KTableEntry;
65 
66 // pref-backed bool values (hooked up in nsCSSParser::Startup)
gp_wrapper(cls)67 static bool sOpentypeSVGEnabled;
make_getter(name)68 static bool sWebkitPrefixedAliasesEnabled;
getter(self)69 static bool sWebkitDevicePixelRatioEnabled;
70 static bool sUnprefixingServiceEnabled;
71 #ifdef NIGHTLY_BUILD
72 static bool sUnprefixingServiceGloballyWhitelisted;
73 #endif
74 static bool sMozGradientsEnabled;
75 static bool sControlCharVisibility;
76 
77 const uint32_t
78 nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
79 #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \
80                  stylestruct_, stylestructoffset_, animtype_)                 \
81   parsevariant_,
82 #define CSS_PROP_LIST_INCLUDE_LOGICAL
make_setter(name)83 #include "nsCSSPropList.h"
84 #undef CSS_PROP_LIST_INCLUDE_LOGICAL
85 #undef CSS_PROP
86 };
87 
88 // Maximum number of repetitions for the repeat() function
89 // in the grid-template-rows and grid-template-columns properties,
90 // to limit high memory usage from small stylesheets.
91 // Must be a positive integer. Should be large-ish.
92 #define GRID_TEMPLATE_MAX_REPETITIONS 10000
93 
94 // End-of-array marker for mask arguments to ParseBitmaskValues
95 #define MASK_END_VALUE  (-1)
96 
97 enum class CSSParseResult : int32_t {
plot_gp_dist( ax, samples: np.ndarray, x: np.ndarray, plot_samples=True, palette="Reds", fill_alpha=0.8, samples_alpha=0.1, fill_kwargs=None, samples_kwargs=None, )98   // Parsed something successfully:
99   Ok,
100   // Did not find what we were looking for, but did not consume any token:
101   NotFound,
102   // Unexpected token or token value, too late for UngetToken() to be enough:
103   Error
104 };
105 
106 enum class GridTrackSizeFlags {
107   eDefaultTrackSize = 0x0,
108   eFixedTrackSize   = 0x1, // parse a <fixed-size> instead of <track-size>
109 };
110 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackSizeFlags)
111 
112 enum class GridTrackListFlags {
113   eDefaultTrackList  = 0x0, // parse a <track-list>
114   eExplicitTrackList = 0x1, // parse an <explicit-track-list> instead
115 };
116 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackListFlags)
117 
118 namespace {
119 
120 // Rule processing function
121 typedef void (* RuleAppendFunc) (css::Rule* aRule, void* aData);
122 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer);
123 static void AppendRuleToSheet(css::Rule* aRule, void* aParser);
124 
125 struct CSSParserInputState {
126   nsCSSScannerPosition mPosition;
127   nsCSSToken mToken;
128   bool mHavePushBack;
129 };
130 
131 static_assert(css::eAuthorSheetFeatures == 0 &&
132               css::eUserSheetFeatures == 1 &&
133               css::eAgentSheetFeatures == 2,
134               "sheet parsing mode constants won't fit "
135               "in CSSParserImpl::mParsingMode");
136 
137 // Your basic top-down recursive descent style parser
138 // The exposed methods and members of this class are precisely those
139 // needed by nsCSSParser, far below.
140 class CSSParserImpl {
141 public:
142   CSSParserImpl();
143   ~CSSParserImpl();
144 
145   nsresult SetStyleSheet(CSSStyleSheet* aSheet);
146 
147   nsIDocument* GetDocument();
148 
149   nsresult SetQuirkMode(bool aQuirkMode);
150 
151   nsresult SetChildLoader(mozilla::css::Loader* aChildLoader);
152 
153   // Clears everything set by the above Set*() functions.
154   void Reset();
155 
156   nsresult ParseSheet(const nsAString& aInput,
157                       nsIURI*          aSheetURI,
158                       nsIURI*          aBaseURI,
159                       nsIPrincipal*    aSheetPrincipal,
160                       uint32_t         aLineNumber,
161                       css::LoaderReusableStyleSheets* aReusableSheets);
162 
163   already_AddRefed<css::Declaration>
164            ParseStyleAttribute(const nsAString&  aAttributeValue,
165                                nsIURI*           aDocURL,
166                                nsIURI*           aBaseURL,
167                                nsIPrincipal*     aNodePrincipal);
168 
169   nsresult ParseDeclarations(const nsAString&  aBuffer,
170                              nsIURI*           aSheetURL,
171                              nsIURI*           aBaseURL,
172                              nsIPrincipal*     aSheetPrincipal,
173                              css::Declaration* aDeclaration,
174                              bool*           aChanged);
175 
176   nsresult ParseRule(const nsAString&        aRule,
177                      nsIURI*                 aSheetURL,
178                      nsIURI*                 aBaseURL,
179                      nsIPrincipal*           aSheetPrincipal,
180                      css::Rule**             aResult);
181 
182   void ParseProperty(const nsCSSPropertyID aPropID,
183                      const nsAString& aPropValue,
184                      nsIURI* aSheetURL,
185                      nsIURI* aBaseURL,
186                      nsIPrincipal* aSheetPrincipal,
187                      css::Declaration* aDeclaration,
188                      bool* aChanged,
189                      bool aIsImportant,
190                      bool aIsSVGMode);
191   void ParseLonghandProperty(const nsCSSPropertyID aPropID,
192                              const nsAString& aPropValue,
193                              nsIURI* aSheetURL,
194                              nsIURI* aBaseURL,
195                              nsIPrincipal* aSheetPrincipal,
196                              nsCSSValue& aValue);
197 
198   bool ParseTransformProperty(const nsAString& aPropValue,
199                               bool aDisallowRelativeValues,
200                               nsCSSValue& aResult);
201 
202   void ParseMediaList(const nsSubstring& aBuffer,
203                       nsIURI* aURL, // for error reporting
204                       uint32_t aLineNumber, // for error reporting
205                       nsMediaList* aMediaList,
206                       bool aHTMLMode);
207 
208   bool ParseSourceSizeList(const nsAString& aBuffer,
209                            nsIURI* aURI, // for error reporting
210                            uint32_t aLineNumber, // for error reporting
211                            InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
212                            InfallibleTArray<nsCSSValue>& aValues,
213                            bool aHTMLMode);
214 
215   void ParseVariable(const nsAString& aVariableName,
216                      const nsAString& aPropValue,
217                      nsIURI* aSheetURL,
218                      nsIURI* aBaseURL,
219                      nsIPrincipal* aSheetPrincipal,
220                      css::Declaration* aDeclaration,
221                      bool* aChanged,
222                      bool aIsImportant);
223 
224   bool ParseFontFamilyListString(const nsSubstring& aBuffer,
225                                  nsIURI* aURL, // for error reporting
226                                  uint32_t aLineNumber, // for error reporting
227                                  nsCSSValue& aValue);
228 
229   bool ParseColorString(const nsSubstring& aBuffer,
230                         nsIURI* aURL, // for error reporting
231                         uint32_t aLineNumber, // for error reporting
232                         nsCSSValue& aValue,
233                         bool aSuppressErrors /* false */);
234 
235   bool ParseMarginString(const nsSubstring& aBuffer,
236                          nsIURI* aURL, // for error reporting
237                          uint32_t aLineNumber, // for error reporting
238                          nsCSSValue& aValue,
239                          bool aSuppressErrors /* false */);
240 
241   nsresult ParseSelectorString(const nsSubstring& aSelectorString,
242                                nsIURI* aURL, // for error reporting
243                                uint32_t aLineNumber, // for error reporting
244                                nsCSSSelectorList **aSelectorList);
245 
246   already_AddRefed<nsCSSKeyframeRule>
247   ParseKeyframeRule(const nsSubstring& aBuffer,
248                     nsIURI*            aURL,
249                     uint32_t           aLineNumber);
250 
251   bool ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
252                                    nsIURI* aURL, // for error reporting
253                                    uint32_t aLineNumber, // for error reporting
254                                    InfallibleTArray<float>& aSelectorList);
255 
256   bool EvaluateSupportsDeclaration(const nsAString& aProperty,
257                                    const nsAString& aValue,
258                                    nsIURI* aDocURL,
259                                    nsIURI* aBaseURL,
260                                    nsIPrincipal* aDocPrincipal);
261 
262   bool EvaluateSupportsCondition(const nsAString& aCondition,
263                                  nsIURI* aDocURL,
264                                  nsIURI* aBaseURL,
265                                  nsIPrincipal* aDocPrincipal);
266 
267   bool ParseCounterStyleName(const nsAString& aBuffer,
268                              nsIURI* aURL,
269                              nsAString& aName);
270 
271   bool ParseCounterDescriptor(nsCSSCounterDesc aDescID,
272                               const nsAString& aBuffer,
273                               nsIURI* aSheetURL,
274                               nsIURI* aBaseURL,
275                               nsIPrincipal* aSheetPrincipal,
276                               nsCSSValue& aValue);
277 
278   bool ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
279                                const nsAString& aBuffer,
280                                nsIURI* aSheetURL,
281                                nsIURI* aBaseURL,
282                                nsIPrincipal* aSheetPrincipal,
283                                nsCSSValue& aValue);
284 
285   bool IsValueValidForProperty(const nsCSSPropertyID aPropID,
286                                const nsAString& aPropValue);
287 
288   typedef nsCSSParser::VariableEnumFunc VariableEnumFunc;
289 
290   /**
291    * Parses a CSS token stream value and invokes a callback function for each
292    * variable reference that is encountered.
293    *
294    * @param aPropertyValue The CSS token stream value.
295    * @param aFunc The callback function to invoke; its parameters are the
296    *   variable name found and the aData argument passed in to this function.
297    * @param aData User data to pass in to the callback.
298    * @return Whether aPropertyValue could be parsed as a valid CSS token stream
299    *   value (e.g., without syntactic errors in variable references).
300    */
301   bool EnumerateVariableReferences(const nsAString& aPropertyValue,
302                                    VariableEnumFunc aFunc,
303                                    void* aData);
304 
305   /**
306    * Parses aPropertyValue as a CSS token stream value and resolves any
307    * variable references using the variables in aVariables.
308    *
309    * @param aPropertyValue The CSS token stream value.
310    * @param aVariables The set of variable values to use when resolving variable
311    *   references.
312    * @param aResult Out parameter that gets the resolved value.
313    * @param aFirstToken Out parameter that gets the type of the first token in
314    *   aResult.
315    * @param aLastToken Out parameter that gets the type of the last token in
316    *   aResult.
317    * @return Whether aResult could be parsed successfully and variable reference
318    *   substitution succeeded.
319    */
320   bool ResolveVariableValue(const nsAString& aPropertyValue,
321                             const CSSVariableValues* aVariables,
322                             nsString& aResult,
323                             nsCSSTokenSerializationType& aFirstToken,
324                             nsCSSTokenSerializationType& aLastToken);
325 
326   /**
327    * Parses a string as a CSS token stream value for particular property,
328    * resolving any variable references.  The parsed property value is stored
329    * in the specified nsRuleData object.  If aShorthandPropertyID has a value
330    * other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
331    * otherwise, aPropertyID will be parsed.  Either way, only aPropertyID,
332    * a longhand property, will be copied over to the rule data.
333    *
334    * If the property cannot be parsed, it will be treated as if 'initial' or
335    * 'inherit' were specified, for non-inherited and inherited properties
336    * respectively.
337    *
338    * @param aPropertyID The ID of the longhand property whose value is to be
339    *   copied to the rule data.
340    * @param aShorthandPropertyID The ID of the shorthand property to be parsed.
341    *   If a longhand property is to be parsed, aPropertyID is that property,
342    *   and aShorthandPropertyID must be eCSSProperty_UNKNOWN.
343    * @param aValue The CSS token stream value.
344    * @param aVariables The set of variable values to use when resolving variable
345    *   references.
346    * @param aRuleData The rule data object into which parsed property value for
347    *   aPropertyID will be stored.
348    */
349   void ParsePropertyWithVariableReferences(nsCSSPropertyID aPropertyID,
350                                            nsCSSPropertyID aShorthandPropertyID,
351                                            const nsAString& aValue,
352                                            const CSSVariableValues* aVariables,
353                                            nsRuleData* aRuleData,
354                                            nsIURI* aDocURL,
355                                            nsIURI* aBaseURL,
356                                            nsIPrincipal* aDocPrincipal,
357                                            CSSStyleSheet* aSheet,
358                                            uint32_t aLineNumber,
359                                            uint32_t aLineOffset);
360 
361   bool AgentRulesEnabled() const {
362     return mParsingMode == css::eAgentSheetFeatures;
363   }
364   bool ChromeRulesEnabled() const {
365     return mIsChrome;
366   }
367   bool UserRulesEnabled() const {
368     return mParsingMode == css::eAgentSheetFeatures ||
369            mParsingMode == css::eUserSheetFeatures;
370   }
371 
372   CSSEnabledState EnabledState() const {
373     static_assert(int(CSSEnabledState::eForAllContent) == 0,
374                   "CSSEnabledState::eForAllContent should be zero for "
375                   "this bitfield to work");
376     CSSEnabledState enabledState = CSSEnabledState::eForAllContent;
377     if (AgentRulesEnabled()) {
378       enabledState |= CSSEnabledState::eInUASheets;
379     }
380     if (mIsChrome) {
381       enabledState |= CSSEnabledState::eInChrome;
382     }
383     return enabledState;
384   }
385 
386   nsCSSPropertyID LookupEnabledProperty(const nsAString& aProperty) {
387     return nsCSSProps::LookupProperty(aProperty, EnabledState());
388   }
389 
390 protected:
391   class nsAutoParseCompoundProperty;
392   friend class nsAutoParseCompoundProperty;
393 
394   class nsAutoFailingSupportsRule;
395   friend class nsAutoFailingSupportsRule;
396 
397   class nsAutoSuppressErrors;
398   friend class nsAutoSuppressErrors;
399 
400   void AppendRule(css::Rule* aRule);
401   friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule
402 
403   /**
404    * This helper class automatically calls SetParsingCompoundProperty in its
405    * constructor and takes care of resetting it to false in its destructor.
406    */
407   class nsAutoParseCompoundProperty {
408     public:
409       explicit nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
410       {
411         NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
412                      "already parsing compound property");
413         NS_ASSERTION(aParser, "Null parser?");
414         aParser->SetParsingCompoundProperty(true);
415       }
416 
417       ~nsAutoParseCompoundProperty()
418       {
419         mParser->SetParsingCompoundProperty(false);
420       }
421     private:
422       CSSParserImpl* mParser;
423   };
424 
425   /**
426    * This helper class conditionally sets mInFailingSupportsRule to
427    * true if aCondition = false, and resets it to its original value in its
428    * destructor.  If we are already somewhere within a failing @supports
429    * rule, passing in aCondition = true does not change mInFailingSupportsRule.
430    */
431   class nsAutoFailingSupportsRule {
432     public:
433       nsAutoFailingSupportsRule(CSSParserImpl* aParser,
434                                 bool aCondition)
435         : mParser(aParser),
436           mOriginalValue(aParser->mInFailingSupportsRule)
437       {
438         if (!aCondition) {
439           mParser->mInFailingSupportsRule = true;
440         }
441       }
442 
443       ~nsAutoFailingSupportsRule()
444       {
445         mParser->mInFailingSupportsRule = mOriginalValue;
446       }
447 
448     private:
449       CSSParserImpl* mParser;
450       bool mOriginalValue;
451   };
452 
453   /**
454    * Auto class to set aParser->mSuppressErrors to the specified value
455    * and restore it to its original value later.
456    */
457   class nsAutoSuppressErrors {
458     public:
459       explicit nsAutoSuppressErrors(CSSParserImpl* aParser,
460                                     bool aSuppressErrors = true)
461         : mParser(aParser),
462           mOriginalValue(aParser->mSuppressErrors)
463       {
464         mParser->mSuppressErrors = aSuppressErrors;
465       }
466 
467       ~nsAutoSuppressErrors()
468       {
469         mParser->mSuppressErrors = mOriginalValue;
470       }
471 
472     private:
473       CSSParserImpl* mParser;
474       bool mOriginalValue;
475   };
476 
477   /**
478    * RAII class to set aParser->mInSupportsCondition to true and restore it
479    * to false later.
480    */
481   class MOZ_RAII nsAutoInSupportsCondition
482   {
483   public:
484     explicit nsAutoInSupportsCondition(CSSParserImpl* aParser)
485       : mParser(aParser)
486     {
487       MOZ_ASSERT(!aParser->mInSupportsCondition,
488                  "nsAutoInSupportsCondition is not designed to be used "
489                  "re-entrantly");
490       mParser->mInSupportsCondition = true;
491     }
492 
493     ~nsAutoInSupportsCondition()
494     {
495       mParser->mInSupportsCondition = false;
496     }
497 
498   private:
499     CSSParserImpl* const mParser;
500   };
501 
502   // the caller must hold on to aString until parsing is done
503   void InitScanner(nsCSSScanner& aScanner,
504                    css::ErrorReporter& aReporter,
505                    nsIURI* aSheetURI, nsIURI* aBaseURI,
506                    nsIPrincipal* aSheetPrincipal);
507   void ReleaseScanner(void);
508 
509   /**
510    * This is a RAII class which behaves like an "AutoRestore<>" for our parser
511    * input state. When instantiated, this class saves the current parser input
512    * state (in a CSSParserInputState object), and it restores the parser to
513    * that state when destructed, unless "DoNotRestore()" has been called.
514   */
515   class MOZ_RAII nsAutoCSSParserInputStateRestorer {
516     public:
517       explicit nsAutoCSSParserInputStateRestorer(CSSParserImpl* aParser
518                                                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
519         : mParser(aParser),
520           mShouldRestore(true)
521       {
522         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
523         mParser->SaveInputState(mSavedState);
524       }
525 
526       void DoNotRestore()
527       {
528         mShouldRestore = false;
529       }
530 
531       ~nsAutoCSSParserInputStateRestorer()
532       {
533         if (mShouldRestore) {
534           mParser->RestoreSavedInputState(mSavedState);
535         }
536       }
537 
538     private:
539       MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
540       CSSParserImpl* mParser;
541       CSSParserInputState mSavedState;
542       bool mShouldRestore;
543   };
544 
545   /**
546    * This is a RAII class which creates a temporary nsCSSScanner for the given
547    * string, and reconfigures aParser to use *that* scanner instead of its
548    * existing scanner, until we go out of scope.  (This allows us to rewrite
549    * a portion of a stylesheet using a temporary string, and switch to parsing
550    * that rewritten section, and then resume parsing the original stylesheet.)
551    *
552    * aParser must have a non-null nsCSSScanner (which we'll be temporarily
553    * replacing) and ErrorReporter (which this class will co-opt for the
554    * temporary parser). While we're in scope, we also suppress error reporting,
555    * so it doesn't really matter which reporter we use. We suppress reporting
556    * because this class is only used with CSS that is synthesized & didn't
557    * come directly from an author, and it would be confusing if we reported
558    * syntax errors for CSS that an author didn't provide.
559    *
560    * XXXdholbert we could also change this & report errors, if needed. Might
561    * want to customize the error reporting somehow though.
562    */
563   class MOZ_RAII nsAutoScannerChanger {
564     public:
565       nsAutoScannerChanger(CSSParserImpl* aParser,
566                            const nsAString& aStringToScan
567                            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
568         : mParser(aParser),
569           mOriginalScanner(aParser->mScanner),
570           mStringScanner(aStringToScan, 0),
571           mParserStateRestorer(aParser),
572           mErrorSuppresser(aParser)
573       {
574         MOZ_ASSERT(mOriginalScanner,
575                    "Shouldn't use nsAutoScannerChanger unless we already "
576                    "have a scanner");
577         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
578 
579         // Set & setup the new scanner:
580         mParser->mScanner = &mStringScanner;
581         mStringScanner.SetErrorReporter(mParser->mReporter);
582 
583         // We might've had push-back on our original scanner (and if we did,
584         // that fact is saved via mParserStateRestorer).  But we don't have
585         // push-back in mStringScanner, so clear that flag.
586         mParser->mHavePushBack = false;
587       }
588 
589       ~nsAutoScannerChanger()
590       {
591         // Restore original scanner. All other cleanup is done by RAII members.
592         mParser->mScanner = mOriginalScanner;
593       }
594 
595     private:
596       MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
597       CSSParserImpl* mParser;
598       nsCSSScanner *mOriginalScanner;
599       nsCSSScanner mStringScanner;
600       nsAutoCSSParserInputStateRestorer mParserStateRestorer;
601       nsAutoSuppressErrors mErrorSuppresser;
602   };
603 
604 
605   bool IsSVGMode() const {
606     return mScanner->IsSVGMode();
607   }
608 
609   /**
610    * Saves the current input state, which includes any currently pushed
611    * back token, and the current position of the scanner.
612    */
613   void SaveInputState(CSSParserInputState& aState);
614 
615   /**
616    * Restores the saved input state by pushing back any saved pushback
617    * token and calling RestoreSavedPosition on the scanner.
618    */
619   void RestoreSavedInputState(const CSSParserInputState& aState);
620 
621   bool GetToken(bool aSkipWS);
622   void UngetToken();
623   bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum);
624   void AssertNextTokenAt(uint32_t aLine, uint32_t aCol)
625   {
626     // Beware that this method will call GetToken/UngetToken (in
627     // GetNextTokenLocation) in DEBUG builds, but not in non-DEBUG builds.
628     DebugOnly<uint32_t> lineAfter, colAfter;
629     MOZ_ASSERT(GetNextTokenLocation(true, &lineAfter, &colAfter) &&
630                lineAfter == aLine && colAfter == aCol,
631                "shouldn't have consumed any tokens");
632   }
633 
634   bool ExpectSymbol(char16_t aSymbol, bool aSkipWS);
635   bool ExpectEndProperty();
636   bool CheckEndProperty();
637   nsSubstring* NextIdent();
638 
639   // returns true when the stop symbol is found, and false for EOF
640   bool SkipUntil(char16_t aStopSymbol);
641   void SkipUntilOneOf(const char16_t* aStopSymbolChars);
642   // For each character in aStopSymbolChars from the end of the array
643   // to the start, calls SkipUntil with that character.
644   typedef AutoTArray<char16_t, 16> StopSymbolCharStack;
645   void SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars);
646   // returns true if the stop symbol or EOF is found, and false for an
647   // unexpected ')', ']' or '}'; this not safe to call outside variable
648   // resolution, as it doesn't handle mismatched content
649   bool SkipBalancedContentUntil(char16_t aStopSymbol);
650 
651   void SkipRuleSet(bool aInsideBraces);
652   bool SkipAtRule(bool aInsideBlock);
653   bool SkipDeclaration(bool aCheckForBraces);
654 
655   void PushGroup(css::GroupRule* aRule);
656   void PopGroup();
657 
658   bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData,
659                     bool aInsideBraces = false);
660   bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData,
661                    bool aInAtRule);
662   bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
663   bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
664   bool ParseURLOrString(nsString& aURL);
665   bool GatherMedia(nsMediaList* aMedia, bool aInAtRule);
666 
667   enum eMediaQueryType { eMediaQueryNormal,
668                          // Parsing an at rule
669                          eMediaQueryAtRule,
670                          // Attempt to consume a single media-condition and
671                          // stop. Note that the spec defines "expression and/or
672                          // expression" as one condition but "expression,
673                          // expression" as two.
674                          eMediaQuerySingleCondition };
675   bool ParseMediaQuery(eMediaQueryType aMode, nsMediaQuery **aQuery,
676                        bool *aHitStop);
677   bool ParseMediaQueryExpression(nsMediaQuery* aQuery);
678   void ProcessImport(const nsString& aURLSpec,
679                      nsMediaList* aMedia,
680                      RuleAppendFunc aAppendFunc,
681                      void* aProcessData,
682                      uint32_t aLineNumber,
683                      uint32_t aColumnNumber);
684   bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc,
685                       void* aProcessData);
686   bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
687   bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
688   bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
689   void ProcessNameSpace(const nsString& aPrefix,
690                         const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
691                         void* aProcessData,
692                         uint32_t aLineNumber, uint32_t aColumnNumber);
693 
694   bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
695   bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
696                                   void* aProcessData);
697   bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule);
698   bool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
699   bool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
700                                 nsCSSValue& aValue);
701 
702   bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
703   bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData);
704   already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule();
705   bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList);
706 
707   bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData);
708   bool ParseSupportsCondition(bool& aConditionMet);
709   bool ParseSupportsConditionNegation(bool& aConditionMet);
710   bool ParseSupportsConditionInParens(bool& aConditionMet);
711   bool ParseSupportsMozBoolPrefName(bool& aConditionMet);
712   bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet);
713   bool ParseSupportsConditionTerms(bool& aConditionMet);
714   enum SupportsConditionTermOperator { eAnd, eOr };
715   bool ParseSupportsConditionTermsAfterOperator(
716                                        bool& aConditionMet,
717                                        SupportsConditionTermOperator aOperator);
718 
719   bool ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aProcessData);
720   bool ParseCounterStyleName(nsAString& aName, bool aForDefinition);
721   bool ParseCounterStyleNameValue(nsCSSValue& aValue);
722   bool ParseCounterDescriptor(nsCSSCounterStyleRule *aRule);
723   bool ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
724                                    nsCSSValue& aValue);
725   bool ParseCounterRange(nsCSSValuePair& aPair);
726 
727   /**
728    * Parses the current input stream for a CSS token stream value and resolves
729    * any variable references using the variables in aVariables.
730    *
731    * @param aVariables The set of variable values to use when resolving variable
732    *   references.
733    * @param aResult Out parameter that, if the function returns true, will be
734    *   replaced with the resolved value.
735    * @return Whether aResult could be parsed successfully and variable reference
736    *   substitution succeeded.
737    */
738   bool ResolveValueWithVariableReferences(
739                               const CSSVariableValues* aVariables,
740                               nsString& aResult,
741                               nsCSSTokenSerializationType& aResultFirstToken,
742                               nsCSSTokenSerializationType& aResultLastToken);
743   // Helper function for ResolveValueWithVariableReferences.
744   bool ResolveValueWithVariableReferencesRec(
745                              nsString& aResult,
746                              nsCSSTokenSerializationType& aResultFirstToken,
747                              nsCSSTokenSerializationType& aResultLastToken,
748                              const CSSVariableValues* aVariables);
749 
750   enum nsSelectorParsingStatus {
751     // we have parsed a selector and we saw a token that cannot be
752     // part of a selector:
753     eSelectorParsingStatus_Done,
754     // we should continue parsing the selector:
755     eSelectorParsingStatus_Continue,
756     // we saw an unexpected token or token value,
757     // or we saw end-of-file with an unfinished selector:
758     eSelectorParsingStatus_Error
759   };
760   nsSelectorParsingStatus ParseIDSelector(int32_t&       aDataMask,
761                                           nsCSSSelector& aSelector);
762 
763   nsSelectorParsingStatus ParseClassSelector(int32_t&       aDataMask,
764                                              nsCSSSelector& aSelector);
765 
766   // aPseudoElement and aPseudoElementArgs are the location where
767   // pseudo-elements (as opposed to pseudo-classes) are stored;
768   // pseudo-classes are stored on aSelector.  aPseudoElement and
769   // aPseudoElementArgs must be non-null iff !aIsNegated.
770   nsSelectorParsingStatus ParsePseudoSelector(int32_t&       aDataMask,
771                                               nsCSSSelector& aSelector,
772                                               bool           aIsNegated,
773                                               nsIAtom**      aPseudoElement,
774                                               nsAtomList**   aPseudoElementArgs,
775                                               CSSPseudoElementType* aPseudoElementType);
776 
777   nsSelectorParsingStatus ParseAttributeSelector(int32_t&       aDataMask,
778                                                  nsCSSSelector& aSelector);
779 
780   nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t&       aDataMask,
781                                                        nsCSSSelector& aSelector,
782                                                        bool           aIsNegated);
783 
784   nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
785                                                        CSSPseudoClassType aType);
786 
787   nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
788                                                          CSSPseudoClassType aType);
789 
790   nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
791                                                               CSSPseudoClassType aType);
792 
793   nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t&       aDataMask,
794                                                      nsCSSSelector& aSelector);
795 
796   // If aStopChar is non-zero, the selector list is done when we hit
797   // aStopChar.  Otherwise, it's done when we hit EOF.
798   bool ParseSelectorList(nsCSSSelectorList*& aListHead,
799                            char16_t aStopChar);
800   bool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
801   bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator);
802 
803   enum {
804     eParseDeclaration_InBraces           = 1 << 0,
805     eParseDeclaration_AllowImportant     = 1 << 1,
806     // The declaration we're parsing was generated by the CSSUnprefixingService:
807     eParseDeclaration_FromUnprefixingSvc = 1 << 2
808   };
809   enum nsCSSContextType {
810     eCSSContext_General,
811     eCSSContext_Page
812   };
813 
814   already_AddRefed<css::Declaration>
815     ParseDeclarationBlock(uint32_t aFlags,
816                           nsCSSContextType aContext = eCSSContext_General);
817   bool ParseDeclaration(css::Declaration* aDeclaration,
818                         uint32_t aFlags,
819                         bool aMustCallValueAppended,
820                         bool* aChanged,
821                         nsCSSContextType aContext = eCSSContext_General);
822 
823   // A "prefix-aware" wrapper for nsCSSKeywords::LookupKeyword().
824   // Use this instead of LookupKeyword() if you might be parsing an unprefixed
825   // property (like "display") for which we emulate a vendor-prefixed value
826   // (like "-webkit-box").
827   nsCSSKeyword LookupKeywordPrefixAware(nsAString& aKeywordStr,
828                                         const KTableEntry aKeywordTable[]);
829 
830   bool ShouldUseUnprefixingService() const;
831   bool ParsePropertyWithUnprefixingService(const nsAString& aPropertyName,
832                                            css::Declaration* aDeclaration,
833                                            uint32_t aFlags,
834                                            bool aMustCallValueAppended,
835                                            bool* aChanged,
836                                            nsCSSContextType aContext);
837   // When we detect a webkit-prefixed gradient expression, this function can be
838   // used to parse its body into outparam |aValue|, with the help of the
839   // CSSUnprefixingService.
840   // Only call if ShouldUseUnprefixingService() returns true.
841   bool ParseWebkitPrefixedGradientWithService(nsAString& aPrefixedFuncName,
842                                               nsCSSValue& aValue);
843 
844   bool ParseProperty(nsCSSPropertyID aPropID);
845   bool ParsePropertyByFunction(nsCSSPropertyID aPropID);
846   CSSParseResult ParseSingleValueProperty(nsCSSValue& aValue,
847                                           nsCSSPropertyID aPropID);
848   bool ParseSingleValuePropertyByFunction(nsCSSValue& aValue,
849                                           nsCSSPropertyID aPropID);
850 
851   // This is similar to ParseSingleValueProperty but only works for
852   // properties that are parsed with ParseBoxProperties or
853   // ParseGroupedBoxProperty.
854   //
855   // Only works with variants with the following flags:
856   // A, C, H, K, L, N, P, CALC.
857   CSSParseResult ParseBoxProperty(nsCSSValue& aValue,
858                                   nsCSSPropertyID aPropID);
859 
860   enum PriorityParsingStatus {
861     ePriority_None,
862     ePriority_Important,
863     ePriority_Error
864   };
865   PriorityParsingStatus ParsePriority();
866 
867 #ifdef MOZ_XUL
868   bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs);
869 #endif
870 
871   // Property specific parsing routines
872   bool ParseImageLayers(const nsCSSPropertyID aTable[]);
873 
874   struct ImageLayersShorthandParseState {
875     nsCSSValue&  mColor;
876     nsCSSValueList* mImage;
877     nsCSSValuePairList* mRepeat;
878     nsCSSValueList* mAttachment;   // A property for background layer only
879     nsCSSValueList* mClip;
880     nsCSSValueList* mOrigin;
881     nsCSSValueList* mPositionX;
882     nsCSSValueList* mPositionY;
883     nsCSSValuePairList* mSize;
884     nsCSSValueList* mComposite;    // A property for mask layer only
885     nsCSSValueList* mMode;         // A property for mask layer only
886     ImageLayersShorthandParseState(
887         nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
888         nsCSSValueList* aAttachment, nsCSSValueList* aClip,
889         nsCSSValueList* aOrigin,
890         nsCSSValueList* aPositionX, nsCSSValueList* aPositionY,
891         nsCSSValuePairList* aSize, nsCSSValueList* aComposite,
892         nsCSSValueList* aMode) :
893         mColor(aColor), mImage(aImage), mRepeat(aRepeat),
894         mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
895         mPositionX(aPositionX), mPositionY(aPositionY),
896         mSize(aSize), mComposite(aComposite),
897         mMode(aMode) {};
898   };
899 
900   bool IsFunctionTokenValidForImageLayerImage(const nsCSSToken& aToken) const;
901   bool ParseImageLayersItem(ImageLayersShorthandParseState& aState,
902                             const nsCSSPropertyID aTable[]);
903 
904   bool ParseValueList(nsCSSPropertyID aPropID); // a single value prop-id
905   bool ParseImageLayerRepeat(nsCSSPropertyID aPropID);
906   bool ParseImageLayerRepeatValues(nsCSSValuePair& aValue);
907   bool ParseImageLayerPosition(const nsCSSPropertyID aTable[]);
908   bool ParseImageLayerPositionCoord(nsCSSPropertyID aPropID, bool aIsHorizontal);
909 
910   // ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
911   // which is still used by some properties. See ParsePositionValue
912   // for the css3-background syntax.
913   bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit,
914                               bool aAllowExplicitCenter = true); // deprecated
915 
916   // ParsePositionValue parses a CSS <position> value, which is used by
917   // the 'background-position' property.
918   bool ParsePositionValue(nsCSSValue& aOut);
919   bool ParsePositionValueSeparateCoords(nsCSSValue& aOutX, nsCSSValue& aOutY);
920 
921   bool ParseImageLayerPositionCoordItem(nsCSSValue& aOut, bool aIsHorizontal);
922   bool ParseImageLayerSize(nsCSSPropertyID aPropID);
923   bool ParseImageLayerSizeValues(nsCSSValuePair& aOut);
924   bool ParseBorderColor();
925   bool ParseBorderColors(nsCSSPropertyID aProperty);
926   void SetBorderImageInitialValues();
927   bool ParseBorderImageRepeat(bool aAcceptsInherit);
928   // If ParseBorderImageSlice returns false, aConsumedTokens indicates
929   // whether or not any tokens were consumed (in other words, was the property
930   // in error or just not present).  If ParseBorderImageSlice returns true
931   // aConsumedTokens is always true.
932   bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens);
933   bool ParseBorderImageWidth(bool aAcceptsInherit);
934   bool ParseBorderImageOutset(bool aAcceptsInherit);
935   bool ParseBorderImage();
936   bool ParseBorderSpacing();
937   bool ParseBorderSide(const nsCSSPropertyID aPropIDs[],
938                          bool aSetAllSides);
939   bool ParseBorderStyle();
940   bool ParseBorderWidth();
941 
942   bool ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask);
943   bool ParseCalcAdditiveExpression(nsCSSValue& aValue,
944                                    uint32_t& aVariantMask);
945   bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
946                                          uint32_t& aVariantMask,
947                                          bool *aHadFinalWS);
948   bool ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask);
949   bool RequireWhitespace();
950 
951   // For "flex" shorthand property, defined in CSS Flexbox spec
952   bool ParseFlex();
953   // For "flex-flow" shorthand property, defined in CSS Flexbox spec
954   bool ParseFlexFlow();
955 
956   // CSS Grid
957   bool ParseGridAutoFlow();
958 
959   // Parse a <line-names> expression.
960   // If successful, either leave aValue untouched,
961   // to indicate that we parsed the empty list,
962   // or set it to a eCSSUnit_List of eCSSUnit_Ident.
963   //
964   // To parse an optional <line-names> (ie. if not finding an open bracket
965   // is considered the same as an empty list),
966   // treat CSSParseResult::NotFound the same as CSSParseResult::Ok.
967   //
968   // If aValue is already a eCSSUnit_List, append to that list.
969   CSSParseResult ParseGridLineNames(nsCSSValue& aValue);
970   bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr);
971   bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue);
972 
973   CSSParseResult ParseGridTrackBreadth(nsCSSValue& aValue);
974   // eFixedTrackSize in aFlags makes it parse a <fixed-size>.
975   CSSParseResult ParseGridTrackSize(nsCSSValue& aValue,
976     GridTrackSizeFlags aFlags = GridTrackSizeFlags::eDefaultTrackSize);
977 
978   bool ParseGridAutoColumnsRows(nsCSSPropertyID aPropID);
979   bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr);
980   bool ParseGridTrackRepeatIntro(bool            aForSubgrid,
981                                  int32_t*        aRepetitions,
982                                  Maybe<int32_t>* aRepeatAutoEnum);
983 
984   // Assuming a [ <line-names>? ] has already been parsed,
985   // parse the rest of a <track-list>.
986   //
987   // This exists because [ <line-names>? ] is ambiguous in the 'grid-template'
988   // shorthand: it can be either the start of a <track-list> (in
989   // a <'grid-template-rows'>) or of the intertwined syntax that sets both
990   // grid-template-rows and grid-template-areas.
991   //
992   // On success, |aValue| will be a list of odd length >= 3,
993   // starting with a <line-names> (which is itself a list)
994   // and alternating between that and <track-size>.
995   bool ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
996     const nsCSSValue& aFirstLineNames,
997     GridTrackListFlags aFlags = GridTrackListFlags::eDefaultTrackList);
998 
999   bool ParseGridTrackList(nsCSSPropertyID aPropID,
1000     GridTrackListFlags aFlags = GridTrackListFlags::eDefaultTrackList);
1001   bool ParseGridTemplateColumnsRows(nsCSSPropertyID aPropID);
1002 
1003   // |aAreaIndices| is a lookup table to help us parse faster,
1004   // mapping area names to indices in |aResult.mNamedAreas|.
1005   bool ParseGridTemplateAreasLine(const nsAutoString& aInput,
1006                                   css::GridTemplateAreasValue* aResult,
1007                                   nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices);
1008   bool ParseGridTemplateAreas();
1009   bool ParseGridTemplateColumnsOrAutoFlow(bool aForGridShorthand);
1010   bool ParseGridTemplate(bool aForGridShorthand = false);
1011   bool ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames);
1012   bool ParseGrid();
1013   CSSParseResult ParseGridShorthandAutoProps(int32_t aAutoFlowAxis);
1014   bool ParseGridLine(nsCSSValue& aValue);
1015   bool ParseGridColumnRowStartEnd(nsCSSPropertyID aPropID);
1016   bool ParseGridColumnRow(nsCSSPropertyID aStartPropID,
1017                           nsCSSPropertyID aEndPropID);
1018   bool ParseGridArea();
1019   bool ParseGridGap();
1020 
1021   bool ParseInitialLetter();
1022 
1023   // parsing 'align/justify-items/self' from the css-align spec
1024   bool ParseAlignJustifyPosition(nsCSSValue& aResult,
1025                                  const KTableEntry aTable[]);
1026   bool ParseJustifyItems();
1027   bool ParseAlignItems();
1028   bool ParseAlignJustifySelf(nsCSSPropertyID aPropID);
1029   // parsing 'align/justify-content' from the css-align spec
1030   bool ParseAlignJustifyContent(nsCSSPropertyID aPropID);
1031   bool ParsePlaceContent();
1032   bool ParsePlaceItems();
1033   bool ParsePlaceSelf();
1034 
1035   // for 'clip' and '-moz-image-region'
1036   bool ParseRect(nsCSSPropertyID aPropID);
1037   bool ParseColumns();
1038   bool ParseContain(nsCSSValue& aValue);
1039   bool ParseContent();
1040   bool ParseCounterData(nsCSSPropertyID aPropID);
1041   bool ParseCursor();
1042   bool ParseFont();
1043   bool ParseFontSynthesis(nsCSSValue& aValue);
1044   bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue);
1045   bool ParseFontVariantAlternates(nsCSSValue& aValue);
1046   bool MergeBitmaskValue(int32_t aNewValue, const int32_t aMasks[],
1047                          int32_t& aMergedValue);
1048   bool ParseBitmaskValues(nsCSSValue& aValue,
1049                           const KTableEntry aKeywordTable[],
1050                           const int32_t aMasks[]);
1051   bool ParseFontVariantEastAsian(nsCSSValue& aValue);
1052   bool ParseFontVariantLigatures(nsCSSValue& aValue);
1053   bool ParseFontVariantNumeric(nsCSSValue& aValue);
1054   bool ParseFontVariant();
1055   bool ParseFontWeight(nsCSSValue& aValue);
1056   bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword, bool& aQuoted);
1057   bool ParseFamily(nsCSSValue& aValue);
1058   bool ParseFontFeatureSettings(nsCSSValue& aValue);
1059   bool ParseFontSrc(nsCSSValue& aValue);
1060   bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
1061   bool ParseFontRanges(nsCSSValue& aValue);
1062   bool ParseListStyle();
1063   bool ParseListStyleType(nsCSSValue& aValue);
1064   bool ParseMargin();
1065   bool ParseClipPath(nsCSSValue& aValue);
1066   bool ParseTransform(bool aIsPrefixed, bool aDisallowRelativeValues = false);
1067   bool ParseObjectPosition();
1068   bool ParseOutline();
1069   bool ParseOverflow();
1070   bool ParsePadding();
1071   bool ParseQuotes();
1072   bool ParseTextAlign(nsCSSValue& aValue,
1073                       const KTableEntry aTable[]);
1074   bool ParseTextAlign(nsCSSValue& aValue);
1075   bool ParseTextAlignLast(nsCSSValue& aValue);
1076   bool ParseTextDecoration();
1077   bool ParseTextDecorationLine(nsCSSValue& aValue);
1078   bool ParseTextEmphasis();
1079   bool ParseTextEmphasisPosition(nsCSSValue& aValue);
1080   bool ParseTextEmphasisStyle(nsCSSValue& aValue);
1081   bool ParseTextCombineUpright(nsCSSValue& aValue);
1082   bool ParseTextOverflow(nsCSSValue& aValue);
1083   bool ParseTouchAction(nsCSSValue& aValue);
1084 
1085   bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow);
1086   bool ParseShadowList(nsCSSPropertyID aProperty);
1087   bool ParseShapeOutside(nsCSSValue& aValue);
1088   bool ParseTransitionProperty();
1089   bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
1090   bool ParseTransitionTimingFunctionValueComponent(float& aComponent,
1091                                                      char aStop,
1092                                                      bool aIsXPoint);
1093   bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
1094   enum ParseAnimationOrTransitionShorthandResult {
1095     eParseAnimationOrTransitionShorthand_Values,
1096     eParseAnimationOrTransitionShorthand_Inherit,
1097     eParseAnimationOrTransitionShorthand_Error
1098   };
1099   ParseAnimationOrTransitionShorthandResult
1100     ParseAnimationOrTransitionShorthand(const nsCSSPropertyID* aProperties,
1101                                         const nsCSSValue* aInitialValues,
1102                                         nsCSSValue* aValues,
1103                                         size_t aNumProperties);
1104   bool ParseTransition();
1105   bool ParseAnimation();
1106   bool ParseWillChange();
1107 
1108   bool ParsePaint(nsCSSPropertyID aPropID);
1109   bool ParseDasharray();
1110   bool ParseMarker();
1111   bool ParsePaintOrder();
1112   bool ParseAll();
1113   bool ParseScrollSnapType();
1114   bool ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSPropertyID aPropID);
1115   bool ParseScrollSnapDestination(nsCSSValue& aValue);
1116   bool ParseScrollSnapCoordinate(nsCSSValue& aValue);
1117   bool ParseWebkitTextStroke();
1118 
1119   /**
1120    * Parses a variable value from a custom property declaration.
1121    *
1122    * @param aType Out parameter into which will be stored the type of variable
1123    *   value, indicating whether the parsed value was a token stream or one of
1124    *   the CSS-wide keywords.
1125    * @param aValue Out parameter into which will be stored the token stream
1126    *   as a string, if the parsed custom property did take a token stream.
1127    * @return Whether parsing succeeded.
1128    */
1129   bool ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
1130                                 nsString& aValue);
1131 
1132   /**
1133    * Parses a CSS variable value.  This could be 'initial', 'inherit', 'unset'
1134    * or a token stream, which may or may not include variable references.
1135    *
1136    * @param aType Out parameter into which the type of the variable value
1137    *   will be stored.
1138    * @param aDropBackslash Out parameter indicating whether during variable
1139    *   value parsing there was a trailing backslash before EOF that must
1140    *   be dropped when serializing the variable value.
1141    * @param aImpliedCharacters Out parameter appended to which will be any
1142    *   characters that were implied when encountering EOF and which
1143    *   must be included at the end of the serialized variable value.
1144    * @param aFunc A callback function to invoke when a variable reference
1145    *   is encountered.  May be null.  Arguments are the variable name
1146    *   and the aData argument passed in to this function.
1147    * @param User data to pass in to the callback.
1148    * @return Whether parsing succeeded.
1149    */
1150   bool ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
1151                                bool* aDropBackslash,
1152                                nsString& aImpliedCharacters,
1153                                void (*aFunc)(const nsAString&, void*),
1154                                void* aData);
1155 
1156   /**
1157    * Returns whether the scanner dropped a backslash just before EOF.
1158    */
1159   bool BackslashDropped();
1160 
1161   /**
1162    * Calls AppendImpliedEOFCharacters on mScanner.
1163    */
1164   void AppendImpliedEOFCharacters(nsAString& aResult);
1165 
1166   // Reused utility parsing routines
1167   void AppendValue(nsCSSPropertyID aPropID, const nsCSSValue& aValue);
1168   bool ParseBoxProperties(const nsCSSPropertyID aPropIDs[]);
1169   bool ParseGroupedBoxProperty(int32_t aVariantMask,
1170                                nsCSSValue& aValue,
1171                                uint32_t aRestrictions);
1172   bool ParseBoxCornerRadius(const nsCSSPropertyID aPropID);
1173   bool ParseBoxCornerRadiiInternals(nsCSSValue array[]);
1174   bool ParseBoxCornerRadii(const nsCSSPropertyID aPropIDs[]);
1175 
1176   int32_t ParseChoice(nsCSSValue aValues[],
1177                       const nsCSSPropertyID aPropIDs[], int32_t aNumIDs);
1178 
1179   CSSParseResult ParseColor(nsCSSValue& aValue);
1180 
1181   template<typename ComponentType>
1182   bool ParseRGBColor(ComponentType& aR,
1183                      ComponentType& aG,
1184                      ComponentType& aB,
1185                      ComponentType& aA);
1186   bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
1187                      float& aOpacity);
1188 
1189   // The ParseColorOpacityAndCloseParen methods below attempt to parse an
1190   // optional [ separator <alpha-value> ] expression, followed by a
1191   // close-parenthesis, at the end of a css color function (e.g. "rgba()" or
1192   // "hsla()"). If these functions simply encounter a close-parenthesis (without
1193   // any [separator <alpha-value>]), they will still succeed (i.e. return true),
1194   // with outparam 'aOpacity' set to a default opacity value (fully-opaque).
1195   //
1196   // The range of opacity component is [0, 255], and the default opacity value
1197   // is 255 (fully-opaque) for this function.
1198   bool ParseColorOpacityAndCloseParen(uint8_t& aOpacity,
1199                                       char aSeparator);
1200   // Similar to the previous one, but the range of opacity component is
1201   // [0.0f, 1.0f] and the default opacity value is 1.0f (fully-opaque).
1202   bool ParseColorOpacityAndCloseParen(float& aOpacity,
1203                                       char aSeparator);
1204 
1205   // Parse a <number> color component. The range of color component is [0, 255].
1206   // If |aSeparator| is provided, this function will also attempt to parse that
1207   // character after parsing the color component.
1208   bool ParseColorComponent(uint8_t& aComponent, Maybe<char> aSeparator);
1209   // Similar to the previous one, but parse a <percentage> color component.
1210   // The range of color component is [0.0f, 1.0f].
1211   bool ParseColorComponent(float& aComponent, Maybe<char> aSeparator);
1212 
1213   // Parse a <hue> component.
1214   //   <hue> = <number> | <angle>
1215   // The unit of outparam 'aAngle' is degree. Assume the unit to be degree if an
1216   // unitless <number> is parsed.
1217   bool ParseHue(float& aAngle);
1218 
1219   bool ParseEnum(nsCSSValue& aValue,
1220                  const KTableEntry aKeywordTable[]);
1221 
1222   // A special ParseEnum for the CSS Box Alignment properties that have
1223   // 'baseline' values.  In addition to the keywords in aKeywordTable, it also
1224   // parses 'first baseline' and 'last baseline' as a single value.
1225   // (aKeywordTable must contain 'baseline')
1226   bool ParseAlignEnum(nsCSSValue& aValue, const KTableEntry aKeywordTable[]);
1227 
1228   // Variant parsing methods
1229   CSSParseResult ParseVariant(nsCSSValue& aValue,
1230                               uint32_t aVariantMask,
1231                               const KTableEntry aKeywordTable[]);
1232   CSSParseResult ParseVariantWithRestrictions(nsCSSValue& aValue,
1233                                               int32_t aVariantMask,
1234                                               const KTableEntry aKeywordTable[],
1235                                               uint32_t aRestrictions);
1236   CSSParseResult ParseNonNegativeVariant(nsCSSValue& aValue,
1237                                          int32_t aVariantMask,
1238                                          const KTableEntry aKeywordTable[]);
1239   CSSParseResult ParseOneOrLargerVariant(nsCSSValue& aValue,
1240                                          int32_t aVariantMask,
1241                                          const KTableEntry aKeywordTable[]);
1242 
1243   // Variant parsing methods that are guaranteed to UngetToken any token
1244   // consumed on failure
1245   bool ParseSingleTokenVariant(nsCSSValue& aValue,
1246                                int32_t aVariantMask,
1247                                const KTableEntry aKeywordTable[])
1248   {
1249     MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS),
1250                "use ParseVariant for variants in VARIANT_MULTIPLE_TOKENS");
1251     CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable);
1252     MOZ_ASSERT(result != CSSParseResult::Error);
1253     return result == CSSParseResult::Ok;
1254   }
1255   bool ParseSingleTokenVariantWithRestrictions(
1256       nsCSSValue& aValue,
1257       int32_t aVariantMask,
1258       const KTableEntry aKeywordTable[],
1259       uint32_t aRestrictions)
1260   {
1261     MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS),
1262                "use ParseVariantWithRestrictions for variants in "
1263                "VARIANT_MULTIPLE_TOKENS");
1264     CSSParseResult result =
1265       ParseVariantWithRestrictions(aValue, aVariantMask, aKeywordTable,
1266                                    aRestrictions);
1267     MOZ_ASSERT(result != CSSParseResult::Error);
1268     return result == CSSParseResult::Ok;
1269   }
1270   bool ParseSingleTokenNonNegativeVariant(nsCSSValue& aValue,
1271                                           int32_t aVariantMask,
1272                                           const KTableEntry aKeywordTable[])
1273   {
1274     MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS),
1275                "use ParseNonNegativeVariant for variants in "
1276                "VARIANT_MULTIPLE_TOKENS");
1277     CSSParseResult result =
1278       ParseNonNegativeVariant(aValue, aVariantMask, aKeywordTable);
1279     MOZ_ASSERT(result != CSSParseResult::Error);
1280     return result == CSSParseResult::Ok;
1281   }
1282   bool ParseSingleTokenOneOrLargerVariant(nsCSSValue& aValue,
1283                                           int32_t aVariantMask,
1284                                           const KTableEntry aKeywordTable[])
1285   {
1286     MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS),
1287                "use ParseOneOrLargerVariant for variants in "
1288                "VARIANT_MULTIPLE_TOKENS");
1289     CSSParseResult result =
1290       ParseOneOrLargerVariant(aValue, aVariantMask, aKeywordTable);
1291     MOZ_ASSERT(result != CSSParseResult::Error);
1292     return result == CSSParseResult::Ok;
1293   }
1294 
1295   // Helpers for some common ParseSingleTokenNonNegativeVariant calls.
1296   bool ParseNonNegativeInteger(nsCSSValue& aValue)
1297   {
1298     return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_INTEGER, nullptr);
1299   }
1300   bool ParseNonNegativeNumber(nsCSSValue& aValue)
1301   {
1302     return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_NUMBER, nullptr);
1303   }
1304 
1305   // Helpers for some common ParseSingleTokenOneOrLargerVariant calls.
1306   bool ParseOneOrLargerInteger(nsCSSValue& aValue)
1307   {
1308     return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_INTEGER, nullptr);
1309   }
1310   bool ParseOneOrLargerNumber(nsCSSValue& aValue)
1311   {
1312     return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_NUMBER, nullptr);
1313   }
1314 
1315   // http://dev.w3.org/csswg/css-values/#custom-idents
1316   // Parse an identifier that is none of:
1317   // * a CSS-wide keyword
1318   // * "default"
1319   // * a keyword in |aExcludedKeywords|
1320   // * a keyword in |aPropertyKTable|
1321   //
1322   // |aExcludedKeywords| is an array of nsCSSKeyword
1323   // that ends with a eCSSKeyword_UNKNOWN marker.
1324   //
1325   // |aPropertyKTable| can be used if some of the keywords to exclude
1326   // also appear in an existing nsCSSProps::KTableEntry,
1327   // to avoid duplicating them.
1328   bool ParseCustomIdent(nsCSSValue& aValue,
1329                         const nsAutoString& aIdentValue,
1330                         const nsCSSKeyword aExcludedKeywords[] = nullptr,
1331                         const nsCSSProps::KTableEntry aPropertyKTable[] = nullptr);
1332   bool ParseCounter(nsCSSValue& aValue);
1333   bool ParseAttr(nsCSSValue& aValue);
1334   bool ParseSymbols(nsCSSValue& aValue);
1335   bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL);
1336   bool TranslateDimension(nsCSSValue& aValue, uint32_t aVariantMask,
1337                             float aNumber, const nsString& aUnit);
1338   bool ParseImageOrientation(nsCSSValue& aAngle);
1339   bool ParseImageRect(nsCSSValue& aImage);
1340   bool ParseElement(nsCSSValue& aValue);
1341   bool ParseColorStop(nsCSSValueGradient* aGradient);
1342 
1343   enum GradientParsingFlags {
1344     eGradient_Repeating    = 1 << 0, // repeating-{linear|radial}-gradient
1345     eGradient_MozLegacy    = 1 << 1, // -moz-{linear|radial}-gradient
1346     eGradient_WebkitLegacy = 1 << 2, // -webkit-{linear|radial}-gradient
1347 
1348     // Mask to catch both "legacy" flags:
1349     eGradient_AnyLegacy = eGradient_MozLegacy | eGradient_WebkitLegacy
1350   };
1351   bool ParseLinearGradient(nsCSSValue& aValue, uint8_t aFlags);
1352   bool ParseRadialGradient(nsCSSValue& aValue, uint8_t aFlags);
1353   bool IsLegacyGradientLine(const nsCSSTokenType& aType,
1354                             const nsString& aId);
1355   bool ParseGradientColorStops(nsCSSValueGradient* aGradient,
1356                                nsCSSValue& aValue);
1357 
1358   // For the ancient "-webkit-gradient(linear|radial, ...)" syntax:
1359   bool ParseWebkitGradientPointComponent(nsCSSValue& aComponent,
1360                                          bool aIsHorizontal);
1361   bool ParseWebkitGradientPoint(nsCSSValuePair& aPoint);
1362   bool ParseWebkitGradientRadius(float& aRadius);
1363   bool ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient);
1364   bool ParseWebkitGradientColorStops(nsCSSValueGradient* aGradient);
1365   void FinalizeLinearWebkitGradient(nsCSSValueGradient* aGradient,
1366                                     const nsCSSValuePair& aStartPoint,
1367                                     const nsCSSValuePair& aSecondPoint);
1368   void FinalizeRadialWebkitGradient(nsCSSValueGradient* aGradient,
1369                                     const nsCSSValuePair& aFirstCenter,
1370                                     const nsCSSValuePair& aSecondCenter,
1371                                     const float aFirstRadius,
1372                                     const float aSecondRadius);
1373   bool ParseWebkitGradient(nsCSSValue& aValue);
1374 
1375   void SetParsingCompoundProperty(bool aBool) {
1376     mParsingCompoundProperty = aBool;
1377   }
1378   bool IsParsingCompoundProperty(void) const {
1379     return mParsingCompoundProperty;
1380   }
1381 
1382   /* Functions for basic shapes */
1383   bool ParseReferenceBoxAndBasicShape(nsCSSValue& aValue,
1384                                       const KTableEntry aBoxKeywordTable[]);
1385   bool ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens);
1386   bool ParsePolygonFunction(nsCSSValue& aValue);
1387   bool ParseCircleOrEllipseFunction(nsCSSKeyword, nsCSSValue& aValue);
1388   bool ParseInsetFunction(nsCSSValue& aValue);
1389   // We parse position values differently for basic-shape, by expanding defaults
1390   // and replacing keywords with percentages
1391   bool ParsePositionValueForBasicShape(nsCSSValue& aOut);
1392 
1393 
1394   /* Functions for transform Parsing */
1395   bool ParseSingleTransform(bool aIsPrefixed, bool aDisallowRelativeValues,
1396                             nsCSSValue& aValue);
1397   bool ParseFunction(nsCSSKeyword aFunction, const uint32_t aAllowedTypes[],
1398                      uint32_t aVariantMaskAll, uint16_t aMinElems,
1399                      uint16_t aMaxElems, nsCSSValue &aValue);
1400   bool ParseFunctionInternals(const uint32_t aVariantMask[],
1401                               uint32_t aVariantMaskAll,
1402                               uint16_t aMinElems,
1403                               uint16_t aMaxElems,
1404                               InfallibleTArray<nsCSSValue>& aOutput);
1405 
1406   /* Functions for transform-origin/perspective-origin Parsing */
1407   bool ParseTransformOrigin(bool aPerspective);
1408 
1409   /* Functions for filter parsing */
1410   bool ParseFilter();
1411   bool ParseSingleFilter(nsCSSValue* aValue);
1412   bool ParseDropShadow(nsCSSValue* aValue);
1413 
1414   /* Find and return the namespace ID associated with aPrefix.
1415      If aPrefix has not been declared in an @namespace rule, returns
1416      kNameSpaceID_Unknown. */
1417   int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
1418 
1419   /* Find the correct default namespace, and set it on aSelector. */
1420   void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
1421 
1422   // Current token. The value is valid after calling GetToken and invalidated
1423   // by UngetToken.
1424   nsCSSToken mToken;
1425 
1426   // Our scanner.
1427   nsCSSScanner* mScanner;
1428 
1429   // Our error reporter.
1430   css::ErrorReporter* mReporter;
1431 
1432   // The URI to be used as a base for relative URIs.
1433   nsCOMPtr<nsIURI> mBaseURI;
1434 
1435   // The URI to be used as an HTTP "Referer" and for error reporting.
1436   nsCOMPtr<nsIURI> mSheetURI;
1437 
1438   // The principal of the sheet involved
1439   nsCOMPtr<nsIPrincipal> mSheetPrincipal;
1440 
1441   // The sheet we're parsing into
1442   RefPtr<CSSStyleSheet> mSheet;
1443 
1444   // Used for @import rules
1445   css::Loader* mChildLoader; // not ref counted, it owns us
1446 
1447   // Any sheets we may reuse when parsing an @import.
1448   css::LoaderReusableStyleSheets* mReusableSheets;
1449 
1450   // Sheet section we're in.  This is used to enforce correct ordering of the
1451   // various rule types (eg the fact that a @charset rule must come before
1452   // anything else).  Note that there are checks of similar things in various
1453   // places in CSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
1454   enum nsCSSSection {
1455     eCSSSection_Charset,
1456     eCSSSection_Import,
1457     eCSSSection_NameSpace,
1458     eCSSSection_General
1459   };
1460   nsCSSSection  mSection;
1461 
1462   nsXMLNameSpaceMap *mNameSpaceMap;  // weak, mSheet owns it
1463 
1464   // After an UngetToken is done this flag is true. The next call to
1465   // GetToken clears the flag.
1466   bool mHavePushBack : 1;
1467 
1468   // True if we are in quirks mode; false in standards or almost standards mode
1469   bool          mNavQuirkMode : 1;
1470 
1471   // True when the hashless color quirk applies.
1472   bool mHashlessColorQuirk : 1;
1473 
1474   // True when the unitless length quirk applies.
1475   bool mUnitlessLengthQuirk : 1;
1476 
1477   // Controls access to nonstandard style constructs that are not safe
1478   // for use on the public Web but necessary in UA sheets and/or
1479   // useful in user sheets.  The only values stored in this field are
1480   // 0, 1, and 2; three bits are allocated to avoid issues should the
1481   // enum type be signed.
1482   css::SheetParsingMode mParsingMode : 3;
1483 
1484   // True if we are in parsing rules for the chrome.
1485   bool mIsChrome : 1;
1486 
1487   // True if viewport units should be allowed.
1488   bool mViewportUnitsEnabled : 1;
1489 
1490   // True for parsing media lists for HTML attributes, where we have to
1491   // ignore CSS comments.
1492   bool mHTMLMediaMode : 1;
1493 
1494   // This flag is set when parsing a non-box shorthand; it's used to not apply
1495   // some quirks during shorthand parsing
1496   bool          mParsingCompoundProperty : 1;
1497 
1498   // True if we are in the middle of parsing an @supports condition.
1499   // This is used to avoid recording the input stream when variable references
1500   // are encountered in a property declaration in the @supports condition.
1501   bool mInSupportsCondition : 1;
1502 
1503   // True if we are somewhere within a @supports rule whose condition is
1504   // false.
1505   bool mInFailingSupportsRule : 1;
1506 
1507   // True if we will suppress all parse errors (except unexpected EOFs).
1508   // This is used to prevent errors for declarations inside a failing
1509   // @supports rule.
1510   bool mSuppressErrors : 1;
1511 
1512   // True if any parsing of URL values requires a sheet principal to have
1513   // been passed in the nsCSSScanner constructor.  This is usually the case.
1514   // It can be set to false, for example, when we create an nsCSSParser solely
1515   // to parse a property value to test it for syntactic correctness.  When
1516   // false, an assertion that mSheetPrincipal is non-null is skipped.  Should
1517   // not be set to false if any nsCSSValues created during parsing can escape
1518   // out of the parser.
1519   bool mSheetPrincipalRequired;
1520 
1521   // This enum helps us track whether we've unprefixed "display: -webkit-box"
1522   // (treating it as "display: flex") in an earlier declaration within a series
1523   // of declarations.  (This only impacts behavior when the function
1524   // "ShouldUseUnprefixingService()" returns true, and that should only happen
1525   // for a short whitelist of origins.)
1526   enum WebkitBoxUnprefixState : uint8_t {
1527     eNotParsingDecls, // We are *not* currently parsing a sequence of
1528                       // CSS declarations. (default state)
1529 
1530     // The next two enum values indicate that we *are* currently parsing a
1531     // sequence of declarations (in ParseDeclarations or ParseDeclarationBlock)
1532     // and...
1533     eHaveNotUnprefixed, // ...we have not unprefixed 'display:-webkit-box' in
1534                         // this sequence of CSS declarations.
1535     eHaveUnprefixed // ...we *have* unprefixed 'display:-webkit-box' earlier in
1536                     // this sequence of CSS declarations.
1537   };
1538   WebkitBoxUnprefixState mWebkitBoxUnprefixState;
1539 
1540   // Stack of rule groups; used for @media and such.
1541   InfallibleTArray<RefPtr<css::GroupRule> > mGroupStack;
1542 
1543   // During the parsing of a property (which may be a shorthand), the data
1544   // are stored in |mTempData|.  (It is needed to ensure that parser
1545   // errors cause the data to be ignored, and to ensure that a
1546   // non-'!important' declaration does not override an '!important'
1547   // one.)
1548   nsCSSExpandedDataBlock mTempData;
1549 
1550   // All data from successfully parsed properties are placed into |mData|.
1551   nsCSSExpandedDataBlock mData;
1552 
1553 public:
1554   // Used from nsCSSParser constructors and destructors
1555   CSSParserImpl* mNextFree;
1556 };
1557 
1558 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer)
1559 {
1560   css::Rule **pointer = static_cast<css::Rule**>(aPointer);
1561   NS_ADDREF(*pointer = aRule);
1562 }
1563 
1564 static void AppendRuleToSheet(css::Rule* aRule, void* aParser)
1565 {
1566   CSSParserImpl* parser = (CSSParserImpl*) aParser;
1567   parser->AppendRule(aRule);
1568 }
1569 
1570 #define REPORT_UNEXPECTED(msg_) \
1571   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_); }
1572 
1573 #define REPORT_UNEXPECTED_P(msg_, param_) \
1574   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_); }
1575 
1576 #define REPORT_UNEXPECTED_P_V(msg_, param_, value_) \
1577   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_, value_); }
1578 
1579 #define REPORT_UNEXPECTED_TOKEN(msg_) \
1580   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken); }
1581 
1582 #define REPORT_UNEXPECTED_TOKEN_CHAR(msg_, ch_) \
1583   { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken, ch_); }
1584 
1585 #define REPORT_UNEXPECTED_EOF(lf_) \
1586   mReporter->ReportUnexpectedEOF(#lf_)
1587 
1588 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
1589   mReporter->ReportUnexpectedEOF(ch_)
1590 
1591 #define OUTPUT_ERROR() \
1592   mReporter->OutputError()
1593 
1594 #define OUTPUT_ERROR_WITH_POSITION(linenum_, lineoff_) \
1595   mReporter->OutputError(linenum_, lineoff_)
1596 
1597 #define CLEAR_ERROR() \
1598   mReporter->ClearError()
1599 
1600 CSSParserImpl::CSSParserImpl()
1601   : mToken(),
1602     mScanner(nullptr),
1603     mReporter(nullptr),
1604     mChildLoader(nullptr),
1605     mReusableSheets(nullptr),
1606     mSection(eCSSSection_Charset),
1607     mNameSpaceMap(nullptr),
1608     mHavePushBack(false),
1609     mNavQuirkMode(false),
1610     mHashlessColorQuirk(false),
1611     mUnitlessLengthQuirk(false),
1612     mParsingMode(css::eAuthorSheetFeatures),
1613     mIsChrome(false),
1614     mViewportUnitsEnabled(true),
1615     mHTMLMediaMode(false),
1616     mParsingCompoundProperty(false),
1617     mInSupportsCondition(false),
1618     mInFailingSupportsRule(false),
1619     mSuppressErrors(false),
1620     mSheetPrincipalRequired(true),
1621     mWebkitBoxUnprefixState(eNotParsingDecls),
1622     mNextFree(nullptr)
1623 {
1624 }
1625 
1626 CSSParserImpl::~CSSParserImpl()
1627 {
1628   mData.AssertInitialState();
1629   mTempData.AssertInitialState();
1630 }
1631 
1632 nsresult
1633 CSSParserImpl::SetStyleSheet(CSSStyleSheet* aSheet)
1634 {
1635   if (aSheet != mSheet) {
1636     // Switch to using the new sheet, if any
1637     mGroupStack.Clear();
1638     mSheet = aSheet;
1639     if (mSheet) {
1640       mNameSpaceMap = mSheet->GetNameSpaceMap();
1641     } else {
1642       mNameSpaceMap = nullptr;
1643     }
1644   } else if (mSheet) {
1645     mNameSpaceMap = mSheet->GetNameSpaceMap();
1646   }
1647 
1648   return NS_OK;
1649 }
1650 
1651 nsIDocument*
1652 CSSParserImpl::GetDocument()
1653 {
1654   if (!mSheet) {
1655     return nullptr;
1656   }
1657   return mSheet->GetDocument();
1658 }
1659 
1660 nsresult
1661 CSSParserImpl::SetQuirkMode(bool aQuirkMode)
1662 {
1663   mNavQuirkMode = aQuirkMode;
1664   return NS_OK;
1665 }
1666 
1667 nsresult
1668 CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader)
1669 {
1670   mChildLoader = aChildLoader;  // not ref counted, it owns us
1671   return NS_OK;
1672 }
1673 
1674 void
1675 CSSParserImpl::Reset()
1676 {
1677   NS_ASSERTION(!mScanner, "resetting with scanner active");
1678   SetStyleSheet(nullptr);
1679   SetQuirkMode(false);
1680   SetChildLoader(nullptr);
1681 }
1682 
1683 void
1684 CSSParserImpl::InitScanner(nsCSSScanner& aScanner,
1685                            css::ErrorReporter& aReporter,
1686                            nsIURI* aSheetURI, nsIURI* aBaseURI,
1687                            nsIPrincipal* aSheetPrincipal)
1688 {
1689   NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
1690   NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
1691   NS_PRECONDITION(!mScanner, "already have scanner");
1692 
1693   mScanner = &aScanner;
1694   mReporter = &aReporter;
1695   mScanner->SetErrorReporter(mReporter);
1696 
1697   mBaseURI = aBaseURI;
1698   mSheetURI = aSheetURI;
1699   mSheetPrincipal = aSheetPrincipal;
1700   mHavePushBack = false;
1701 }
1702 
1703 void
1704 CSSParserImpl::ReleaseScanner()
1705 {
1706   mScanner = nullptr;
1707   mReporter = nullptr;
1708   mBaseURI = nullptr;
1709   mSheetURI = nullptr;
1710   mSheetPrincipal = nullptr;
1711 }
1712 
1713 nsresult
1714 CSSParserImpl::ParseSheet(const nsAString& aInput,
1715                           nsIURI*          aSheetURI,
1716                           nsIURI*          aBaseURI,
1717                           nsIPrincipal*    aSheetPrincipal,
1718                           uint32_t         aLineNumber,
1719                           css::LoaderReusableStyleSheets* aReusableSheets)
1720 {
1721   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1722   NS_PRECONDITION(aBaseURI, "need base URI");
1723   NS_PRECONDITION(aSheetURI, "need sheet URI");
1724   NS_PRECONDITION(mSheet, "Must have sheet to parse into");
1725   NS_ENSURE_STATE(mSheet);
1726 
1727 #ifdef DEBUG
1728   nsIURI* uri = mSheet->GetSheetURI();
1729   bool equal;
1730   NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
1731                "Sheet URI does not match passed URI");
1732   NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
1733                                                         &equal)) &&
1734                equal,
1735                "Sheet principal does not match passed principal");
1736 #endif
1737 
1738   nsCSSScanner scanner(aInput, aLineNumber);
1739   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1740   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1741 
1742   int32_t ruleCount = mSheet->StyleRuleCount();
1743   if (0 < ruleCount) {
1744     const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1);
1745     if (lastRule) {
1746       switch (lastRule->GetType()) {
1747         case css::Rule::CHARSET_RULE:
1748         case css::Rule::IMPORT_RULE:
1749           mSection = eCSSSection_Import;
1750           break;
1751         case css::Rule::NAMESPACE_RULE:
1752           mSection = eCSSSection_NameSpace;
1753           break;
1754         default:
1755           mSection = eCSSSection_General;
1756           break;
1757       }
1758     }
1759   }
1760   else {
1761     mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
1762   }
1763 
1764   mParsingMode = mSheet->ParsingMode();
1765   mIsChrome = dom::IsChromeURI(aSheetURI);
1766   mReusableSheets = aReusableSheets;
1767 
1768   nsCSSToken* tk = &mToken;
1769   for (;;) {
1770     // Get next non-whitespace token
1771     if (!GetToken(true)) {
1772       OUTPUT_ERROR();
1773       break;
1774     }
1775     if (eCSSToken_HTMLComment == tk->mType) {
1776       continue; // legal here only
1777     }
1778     if (eCSSToken_AtKeyword == tk->mType) {
1779       ParseAtRule(AppendRuleToSheet, this, false);
1780       continue;
1781     }
1782     UngetToken();
1783     if (ParseRuleSet(AppendRuleToSheet, this)) {
1784       mSection = eCSSSection_General;
1785     }
1786   }
1787   ReleaseScanner();
1788 
1789   mParsingMode = css::eAuthorSheetFeatures;
1790   mIsChrome = false;
1791   mReusableSheets = nullptr;
1792 
1793   return NS_OK;
1794 }
1795 
1796 /**
1797  * Determines whether the identifier contained in the given string is a
1798  * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
1799  */
1800 static bool
1801 NonMozillaVendorIdentifier(const nsAString& ident)
1802 {
1803   return (ident.First() == char16_t('-') &&
1804           !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
1805          ident.First() == char16_t('_');
1806 
1807 }
1808 
1809 already_AddRefed<css::Declaration>
1810 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
1811                                    nsIURI*          aDocURI,
1812                                    nsIURI*          aBaseURI,
1813                                    nsIPrincipal*    aNodePrincipal)
1814 {
1815   NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
1816   NS_PRECONDITION(aBaseURI, "need base URI");
1817 
1818   // XXX line number?
1819   nsCSSScanner scanner(aAttributeValue, 0);
1820   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI);
1821   InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal);
1822 
1823   mSection = eCSSSection_General;
1824 
1825   uint32_t parseFlags = eParseDeclaration_AllowImportant;
1826 
1827   RefPtr<css::Declaration> declaration = ParseDeclarationBlock(parseFlags);
1828 
1829   ReleaseScanner();
1830 
1831   return declaration.forget();
1832 }
1833 
1834 nsresult
1835 CSSParserImpl::ParseDeclarations(const nsAString&  aBuffer,
1836                                  nsIURI*           aSheetURI,
1837                                  nsIURI*           aBaseURI,
1838                                  nsIPrincipal*     aSheetPrincipal,
1839                                  css::Declaration* aDeclaration,
1840                                  bool*           aChanged)
1841 {
1842   *aChanged = false;
1843 
1844   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1845 
1846   nsCSSScanner scanner(aBuffer, 0);
1847   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1848   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1849 
1850   MOZ_ASSERT(mWebkitBoxUnprefixState == eNotParsingDecls,
1851              "Someone forgot to clear mWebkitBoxUnprefixState!");
1852   AutoRestore<WebkitBoxUnprefixState> autoRestore(mWebkitBoxUnprefixState);
1853   mWebkitBoxUnprefixState = eHaveNotUnprefixed;
1854 
1855   mSection = eCSSSection_General;
1856 
1857   mData.AssertInitialState();
1858   aDeclaration->ClearData();
1859   // We could check if it was already empty, but...
1860   *aChanged = true;
1861 
1862   for (;;) {
1863     // If we cleared the old decl, then we want to be calling
1864     // ValueAppended as we parse.
1865     if (!ParseDeclaration(aDeclaration, eParseDeclaration_AllowImportant,
1866                           true, aChanged)) {
1867       if (!SkipDeclaration(false)) {
1868         break;
1869       }
1870     }
1871   }
1872 
1873   aDeclaration->CompressFrom(&mData);
1874   ReleaseScanner();
1875   return NS_OK;
1876 }
1877 
1878 nsresult
1879 CSSParserImpl::ParseRule(const nsAString&        aRule,
1880                          nsIURI*                 aSheetURI,
1881                          nsIURI*                 aBaseURI,
1882                          nsIPrincipal*           aSheetPrincipal,
1883                          css::Rule**             aResult)
1884 {
1885   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1886   NS_PRECONDITION(aBaseURI, "need base URI");
1887 
1888   *aResult = nullptr;
1889 
1890   nsCSSScanner scanner(aRule, 0);
1891   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1892   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1893 
1894   mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
1895 
1896   nsCSSToken* tk = &mToken;
1897   // Get first non-whitespace token
1898   nsresult rv = NS_OK;
1899   if (!GetToken(true)) {
1900     REPORT_UNEXPECTED(PEParseRuleWSOnly);
1901     OUTPUT_ERROR();
1902     rv = NS_ERROR_DOM_SYNTAX_ERR;
1903   } else {
1904     if (eCSSToken_AtKeyword == tk->mType) {
1905       // FIXME: perhaps aInsideBlock should be true when we are?
1906       ParseAtRule(AssignRuleToPointer, aResult, false);
1907     } else {
1908       UngetToken();
1909       ParseRuleSet(AssignRuleToPointer, aResult);
1910     }
1911 
1912     if (*aResult && GetToken(true)) {
1913       // garbage after rule
1914       REPORT_UNEXPECTED_TOKEN(PERuleTrailing);
1915       NS_RELEASE(*aResult);
1916     }
1917 
1918     if (!*aResult) {
1919       rv = NS_ERROR_DOM_SYNTAX_ERR;
1920       OUTPUT_ERROR();
1921     }
1922   }
1923 
1924   ReleaseScanner();
1925   return rv;
1926 }
1927 
1928 void
1929 CSSParserImpl::ParseLonghandProperty(const nsCSSPropertyID aPropID,
1930                                      const nsAString& aPropValue,
1931                                      nsIURI* aSheetURL,
1932                                      nsIURI* aBaseURL,
1933                                      nsIPrincipal* aSheetPrincipal,
1934                                      nsCSSValue& aValue)
1935 {
1936   MOZ_ASSERT(aPropID < eCSSProperty_COUNT_no_shorthands,
1937              "ParseLonghandProperty must only take a longhand property");
1938 
1939   RefPtr<css::Declaration> declaration = new css::Declaration;
1940   declaration->InitializeEmpty();
1941 
1942   bool changed;
1943   ParseProperty(aPropID, aPropValue, aSheetURL, aBaseURL, aSheetPrincipal,
1944                 declaration, &changed,
1945                 /* aIsImportant */ false,
1946                 /* aIsSVGMode */ false);
1947 
1948   if (changed) {
1949     aValue = *declaration->GetNormalBlock()->ValueFor(aPropID);
1950   } else {
1951     aValue.Reset();
1952   }
1953 }
1954 
1955 bool
1956 CSSParserImpl::ParseTransformProperty(const nsAString& aPropValue,
1957                                       bool aDisallowRelativeValues,
1958                                       nsCSSValue& aValue)
1959 {
1960   RefPtr<css::Declaration> declaration = new css::Declaration();
1961   declaration->InitializeEmpty();
1962 
1963   mData.AssertInitialState();
1964   mTempData.AssertInitialState();
1965 
1966   nsCSSScanner scanner(aPropValue, 0);
1967   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr);
1968   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
1969 
1970   bool parsedOK = ParseTransform(false, aDisallowRelativeValues);
1971   // We should now be at EOF
1972   if (parsedOK && GetToken(true)) {
1973     parsedOK = false;
1974   }
1975 
1976   bool changed = false;
1977   if (parsedOK) {
1978     declaration->ExpandTo(&mData);
1979     changed = mData.TransferFromBlock(mTempData, eCSSProperty_transform,
1980                                       EnabledState(), false,
1981                                       true, false, declaration,
1982                                       GetDocument());
1983     declaration->CompressFrom(&mData);
1984   }
1985 
1986   if (changed) {
1987     aValue = *declaration->GetNormalBlock()->ValueFor(eCSSProperty_transform);
1988   } else {
1989     aValue.Reset();
1990   }
1991 
1992   ReleaseScanner();
1993 
1994   return parsedOK;
1995 }
1996 
1997 void
1998 CSSParserImpl::ParseProperty(const nsCSSPropertyID aPropID,
1999                              const nsAString& aPropValue,
2000                              nsIURI* aSheetURI,
2001                              nsIURI* aBaseURI,
2002                              nsIPrincipal* aSheetPrincipal,
2003                              css::Declaration* aDeclaration,
2004                              bool* aChanged,
2005                              bool aIsImportant,
2006                              bool aIsSVGMode)
2007 {
2008   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
2009   NS_PRECONDITION(aBaseURI, "need base URI");
2010   NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
2011   MOZ_ASSERT(aPropID != eCSSPropertyExtra_variable);
2012 
2013   mData.AssertInitialState();
2014   mTempData.AssertInitialState();
2015   aDeclaration->AssertMutable();
2016 
2017   nsCSSScanner scanner(aPropValue, 0);
2018   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
2019   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
2020   mSection = eCSSSection_General;
2021   scanner.SetSVGMode(aIsSVGMode);
2022 
2023   *aChanged = false;
2024 
2025   // Check for unknown or preffed off properties
2026   if (eCSSProperty_UNKNOWN == aPropID ||
2027       !nsCSSProps::IsEnabled(aPropID, EnabledState())) {
2028     NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
2029     REPORT_UNEXPECTED_P(PEUnknownProperty, propName);
2030     REPORT_UNEXPECTED(PEDeclDropped);
2031     OUTPUT_ERROR();
2032     ReleaseScanner();
2033     return;
2034   }
2035 
2036   bool parsedOK = ParseProperty(aPropID);
2037   // We should now be at EOF
2038   if (parsedOK && GetToken(true)) {
2039     REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
2040     parsedOK = false;
2041   }
2042 
2043   if (!parsedOK) {
2044     NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
2045     REPORT_UNEXPECTED_P(PEValueParsingError, propName);
2046     REPORT_UNEXPECTED(PEDeclDropped);
2047     OUTPUT_ERROR();
2048     mTempData.ClearProperty(aPropID);
2049   } else {
2050 
2051     // We know we don't need to force a ValueAppended call for the new
2052     // value.  So if we are not processing a shorthand, and there's
2053     // already a value for this property in the declaration at the
2054     // same importance level, then we can just copy our parsed value
2055     // directly into the declaration without going through the whole
2056     // expand/compress thing.
2057     if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData,
2058                                        aChanged)) {
2059       // Do it the slow way
2060       aDeclaration->ExpandTo(&mData);
2061       *aChanged = mData.TransferFromBlock(mTempData, aPropID,
2062                                           EnabledState(), aIsImportant,
2063                                           true, false, aDeclaration,
2064                                           GetDocument());
2065       aDeclaration->CompressFrom(&mData);
2066     }
2067     CLEAR_ERROR();
2068   }
2069 
2070   mTempData.AssertInitialState();
2071 
2072   ReleaseScanner();
2073 }
2074 
2075 void
2076 CSSParserImpl::ParseVariable(const nsAString& aVariableName,
2077                              const nsAString& aPropValue,
2078                              nsIURI* aSheetURI,
2079                              nsIURI* aBaseURI,
2080                              nsIPrincipal* aSheetPrincipal,
2081                              css::Declaration* aDeclaration,
2082                              bool* aChanged,
2083                              bool aIsImportant)
2084 {
2085   NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
2086   NS_PRECONDITION(aBaseURI, "need base URI");
2087   NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
2088   NS_PRECONDITION(nsLayoutUtils::CSSVariablesEnabled(),
2089                   "expected Variables to be enabled");
2090 
2091   mData.AssertInitialState();
2092   mTempData.AssertInitialState();
2093   aDeclaration->AssertMutable();
2094 
2095   nsCSSScanner scanner(aPropValue, 0);
2096   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
2097   InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
2098   mSection = eCSSSection_General;
2099 
2100   *aChanged = false;
2101 
2102   CSSVariableDeclarations::Type variableType;
2103   nsString variableValue;
2104 
2105   bool parsedOK = ParseVariableDeclaration(&variableType, variableValue);
2106 
2107   // We should now be at EOF
2108   if (parsedOK && GetToken(true)) {
2109     REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
2110     parsedOK = false;
2111   }
2112 
2113   if (!parsedOK) {
2114     REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("--") +
2115                                              aVariableName);
2116     REPORT_UNEXPECTED(PEDeclDropped);
2117     OUTPUT_ERROR();
2118   } else {
2119     CLEAR_ERROR();
2120     aDeclaration->AddVariable(aVariableName, variableType,
2121                               variableValue, aIsImportant, true);
2122     *aChanged = true;
2123   }
2124 
2125   mTempData.AssertInitialState();
2126 
2127   ReleaseScanner();
2128 }
2129 
2130 void
2131 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
2132                               nsIURI* aURI, // for error reporting
2133                               uint32_t aLineNumber, // for error reporting
2134                               nsMediaList* aMediaList,
2135                               bool aHTMLMode)
2136 {
2137   // XXX Are there cases where the caller wants to keep what it already
2138   // has in case of parser error?  If GatherMedia ever changes to return
2139   // a value other than true, we probably should avoid modifying aMediaList.
2140   aMediaList->Clear();
2141 
2142   // fake base URI since media lists don't have URIs in them
2143   nsCSSScanner scanner(aBuffer, aLineNumber);
2144   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2145   InitScanner(scanner, reporter, aURI, aURI, nullptr);
2146 
2147   mHTMLMediaMode = aHTMLMode;
2148 
2149     // XXXldb We need to make the scanner not skip CSS comments!  (Or
2150     // should we?)
2151 
2152   // For aHTMLMode, we used to follow the parsing rules in
2153   // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
2154   // which wouldn't work for media queries since they remove all but the
2155   // first word.  However, they're changed in
2156   // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
2157   // (as of 2008-05-29) which says that the media attribute just points
2158   // to a media query.  (The main substative difference is the relative
2159   // precedence of commas and paretheses.)
2160 
2161   DebugOnly<bool> parsedOK = GatherMedia(aMediaList, false);
2162   NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid "
2163                          "trashing aMediaList");
2164 
2165   CLEAR_ERROR();
2166   ReleaseScanner();
2167   mHTMLMediaMode = false;
2168 }
2169 
2170 //  <source-size-list> = <source-size>#?
2171 //  <source-size> = <media-condition>? <length>
2172 bool
2173 CSSParserImpl::ParseSourceSizeList(const nsAString& aBuffer,
2174                                    nsIURI* aURI, // for error reporting
2175                                    uint32_t aLineNumber, // for error reporting
2176                                    InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
2177                                    InfallibleTArray<nsCSSValue>& aValues,
2178                                    bool aHTMLMode)
2179 {
2180   aQueries.Clear();
2181   aValues.Clear();
2182 
2183   // fake base URI since media value lists don't have URIs in them
2184   nsCSSScanner scanner(aBuffer, aLineNumber);
2185   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2186   InitScanner(scanner, reporter, aURI, aURI, nullptr);
2187 
2188   // See ParseMediaList comment about HTML mode
2189   mHTMLMediaMode = aHTMLMode;
2190 
2191   // https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-sizes-attribute
2192   bool hitEnd = false;
2193   do {
2194     bool hitError = false;
2195     // Parse single <media-condition> <source-size-value>
2196     do {
2197       nsAutoPtr<nsMediaQuery> query;
2198       nsCSSValue value;
2199 
2200       bool hitStop;
2201       if (!ParseMediaQuery(eMediaQuerySingleCondition, getter_Transfers(query),
2202                            &hitStop)) {
2203         NS_ASSERTION(!hitStop, "should return true when hit stop");
2204         hitError = true;
2205         break;
2206       }
2207 
2208       if (!query) {
2209         REPORT_UNEXPECTED_EOF(PEParseSourceSizeListEOF);
2210         NS_ASSERTION(hitStop,
2211                      "should return hitStop or an error if returning no query");
2212         hitError = true;
2213         break;
2214       }
2215 
2216       if (hitStop) {
2217         // Empty conditions (e.g. just a bare value) should be treated as always
2218         // matching (a query with no expressions fails to match, so a negated one
2219         // always matches.)
2220         query->SetNegated();
2221       }
2222 
2223       // https://html.spec.whatwg.org/multipage/embedded-content.html#source-size-value
2224       // Percentages are not allowed in a <source-size-value>, to avoid
2225       // confusion about what it would be relative to.
2226       if (ParseNonNegativeVariant(value, VARIANT_LCALC, nullptr) !=
2227           CSSParseResult::Ok) {
2228         hitError = true;
2229         break;
2230       }
2231 
2232       if (GetToken(true)) {
2233         if (!mToken.IsSymbol(',')) {
2234           REPORT_UNEXPECTED_TOKEN(PEParseSourceSizeListNotComma);
2235           hitError = true;
2236           break;
2237         }
2238       } else {
2239         hitEnd = true;
2240       }
2241 
2242       aQueries.AppendElement(query.forget());
2243       aValues.AppendElement(value);
2244     } while(0);
2245 
2246     if (hitError) {
2247       OUTPUT_ERROR();
2248 
2249       // Per spec, we just skip the current entry if there was a parse error.
2250       // Jumps to next entry of <source-size-list> which is a comma-separated list.
2251       if (!SkipUntil(',')) {
2252         hitEnd = true;
2253       }
2254     }
2255   } while (!hitEnd);
2256 
2257   CLEAR_ERROR();
2258   ReleaseScanner();
2259   mHTMLMediaMode = false;
2260 
2261   return !aQueries.IsEmpty();
2262 }
2263 
2264 bool
2265 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
2266                                 nsIURI* aURI, // for error reporting
2267                                 uint32_t aLineNumber, // for error reporting
2268                                 nsCSSValue& aValue,
2269                                 bool aSuppressErrors /* false */)
2270 {
2271   nsCSSScanner scanner(aBuffer, aLineNumber);
2272   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2273   InitScanner(scanner, reporter, aURI, aURI, nullptr);
2274 
2275   nsAutoSuppressErrors suppressErrors(this, aSuppressErrors);
2276 
2277   // Parse a color, and check that there's nothing else after it.
2278   bool colorParsed = ParseColor(aValue) == CSSParseResult::Ok &&
2279                      !GetToken(true);
2280 
2281   if (aSuppressErrors) {
2282     CLEAR_ERROR();
2283   } else {
2284     OUTPUT_ERROR();
2285   }
2286 
2287   ReleaseScanner();
2288   return colorParsed;
2289 }
2290 
2291 bool
2292 CSSParserImpl::ParseMarginString(const nsSubstring& aBuffer,
2293                                  nsIURI* aURI, // for error reporting
2294                                  uint32_t aLineNumber, // for error reporting
2295                                  nsCSSValue& aValue,
2296                                  bool aSuppressErrors /* false */)
2297 {
2298   nsCSSScanner scanner(aBuffer, aLineNumber);
2299   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2300   InitScanner(scanner, reporter, aURI, aURI, nullptr);
2301 
2302   nsAutoSuppressErrors suppressErrors(this, aSuppressErrors);
2303 
2304   // Parse a margin, and check that there's nothing else after it.
2305   bool marginParsed = ParseGroupedBoxProperty(VARIANT_LP, aValue, 0) && !GetToken(true);
2306 
2307   if (aSuppressErrors) {
2308     CLEAR_ERROR();
2309   } else {
2310     OUTPUT_ERROR();
2311   }
2312 
2313   ReleaseScanner();
2314   return marginParsed;
2315 }
2316 
2317 bool
2318 CSSParserImpl::ParseFontFamilyListString(const nsSubstring& aBuffer,
2319                                          nsIURI* aURI, // for error reporting
2320                                          uint32_t aLineNumber, // for error reporting
2321                                          nsCSSValue& aValue)
2322 {
2323   nsCSSScanner scanner(aBuffer, aLineNumber);
2324   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2325   InitScanner(scanner, reporter, aURI, aURI, nullptr);
2326 
2327   // Parse a font family list, and check that there's nothing else after it.
2328   bool familyParsed = ParseFamily(aValue) && !GetToken(true);
2329   OUTPUT_ERROR();
2330   ReleaseScanner();
2331   return familyParsed;
2332 }
2333 
2334 nsresult
2335 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
2336                                    nsIURI* aURI, // for error reporting
2337                                    uint32_t aLineNumber, // for error reporting
2338                                    nsCSSSelectorList **aSelectorList)
2339 {
2340   nsCSSScanner scanner(aSelectorString, aLineNumber);
2341   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2342   InitScanner(scanner, reporter, aURI, aURI, nullptr);
2343 
2344   bool success = ParseSelectorList(*aSelectorList, char16_t(0));
2345 
2346   // We deliberately do not call OUTPUT_ERROR here, because all our
2347   // callers map a failure return to a JS exception, and if that JS
2348   // exception is caught, people don't want to see parser diagnostics;
2349   // see e.g. http://bugs.jquery.com/ticket/7535
2350   // It would be nice to be able to save the parser diagnostics into
2351   // the exception, so that if it _isn't_ caught we can report them
2352   // along with the usual uncaught-exception message, but we don't
2353   // have any way to do that at present; see bug 631621.
2354   CLEAR_ERROR();
2355   ReleaseScanner();
2356 
2357   if (success) {
2358     NS_ASSERTION(*aSelectorList, "Should have list!");
2359     return NS_OK;
2360   }
2361 
2362   NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
2363 
2364   return NS_ERROR_DOM_SYNTAX_ERR;
2365 }
2366 
2367 
2368 already_AddRefed<nsCSSKeyframeRule>
2369 CSSParserImpl::ParseKeyframeRule(const nsSubstring&  aBuffer,
2370                                  nsIURI*             aURI,
2371                                  uint32_t            aLineNumber)
2372 {
2373   nsCSSScanner scanner(aBuffer, aLineNumber);
2374   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2375   InitScanner(scanner, reporter, aURI, aURI, nullptr);
2376 
2377   RefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule();
2378   if (GetToken(true)) {
2379     // extra garbage at the end
2380     result = nullptr;
2381   }
2382 
2383   OUTPUT_ERROR();
2384   ReleaseScanner();
2385 
2386   return result.forget();
2387 }
2388 
2389 bool
2390 CSSParserImpl::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
2391                                            nsIURI* aURI, // for error reporting
2392                                            uint32_t aLineNumber, // for error reporting
2393                                            InfallibleTArray<float>& aSelectorList)
2394 {
2395   MOZ_ASSERT(aSelectorList.IsEmpty(), "given list should start empty");
2396 
2397   nsCSSScanner scanner(aSelectorString, aLineNumber);
2398   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2399   InitScanner(scanner, reporter, aURI, aURI, nullptr);
2400 
2401   bool success = ParseKeyframeSelectorList(aSelectorList) &&
2402                  // must consume entire input string
2403                  !GetToken(true);
2404 
2405   OUTPUT_ERROR();
2406   ReleaseScanner();
2407 
2408   if (success) {
2409     NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty");
2410   } else {
2411     aSelectorList.Clear();
2412   }
2413 
2414   return success;
2415 }
2416 
2417 bool
2418 CSSParserImpl::EvaluateSupportsDeclaration(const nsAString& aProperty,
2419                                            const nsAString& aValue,
2420                                            nsIURI* aDocURL,
2421                                            nsIURI* aBaseURL,
2422                                            nsIPrincipal* aDocPrincipal)
2423 {
2424   nsCSSPropertyID propID = LookupEnabledProperty(aProperty);
2425   if (propID == eCSSProperty_UNKNOWN) {
2426     return false;
2427   }
2428 
2429   nsCSSScanner scanner(aValue, 0);
2430   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
2431   InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2432   nsAutoSuppressErrors suppressErrors(this);
2433 
2434   bool parsedOK;
2435 
2436   if (propID == eCSSPropertyExtra_variable) {
2437     MOZ_ASSERT(Substring(aProperty, 0,
2438                          CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
2439     const nsDependentSubstring varName =
2440       Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH);  // remove '--'
2441     CSSVariableDeclarations::Type variableType;
2442     nsString variableValue;
2443     parsedOK = ParseVariableDeclaration(&variableType, variableValue) &&
2444                !GetToken(true);
2445   } else {
2446     parsedOK = ParseProperty(propID) && !GetToken(true);
2447 
2448     mTempData.ClearProperty(propID);
2449     mTempData.AssertInitialState();
2450   }
2451 
2452   CLEAR_ERROR();
2453   ReleaseScanner();
2454 
2455   return parsedOK;
2456 }
2457 
2458 bool
2459 CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration,
2460                                          nsIURI* aDocURL,
2461                                          nsIURI* aBaseURL,
2462                                          nsIPrincipal* aDocPrincipal)
2463 {
2464   nsCSSScanner scanner(aDeclaration, 0);
2465   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
2466   InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2467   nsAutoSuppressErrors suppressErrors(this);
2468 
2469   bool conditionMet;
2470   bool parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true);
2471 
2472   CLEAR_ERROR();
2473   ReleaseScanner();
2474 
2475   return parsedOK && conditionMet;
2476 }
2477 
2478 bool
2479 CSSParserImpl::EnumerateVariableReferences(const nsAString& aPropertyValue,
2480                                            VariableEnumFunc aFunc,
2481                                            void* aData)
2482 {
2483   nsCSSScanner scanner(aPropertyValue, 0);
2484   css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
2485   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
2486   nsAutoSuppressErrors suppressErrors(this);
2487 
2488   CSSVariableDeclarations::Type type;
2489   bool dropBackslash;
2490   nsString impliedCharacters;
2491   bool result = ParseValueWithVariables(&type, &dropBackslash,
2492                                         impliedCharacters, aFunc, aData) &&
2493                 !GetToken(true);
2494 
2495   ReleaseScanner();
2496 
2497   return result;
2498 }
2499 
2500 static bool
2501 SeparatorRequiredBetweenTokens(nsCSSTokenSerializationType aToken1,
2502                                nsCSSTokenSerializationType aToken2)
2503 {
2504   // The two lines marked with (*) do not correspond to entries in
2505   // the table in the css-syntax spec but which we need to handle,
2506   // as we treat them as whole tokens.
2507   switch (aToken1) {
2508     case eCSSTokenSerialization_Ident:
2509       return aToken2 == eCSSTokenSerialization_Ident ||
2510              aToken2 == eCSSTokenSerialization_Function ||
2511              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
2512              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
2513              aToken2 == eCSSTokenSerialization_Number ||
2514              aToken2 == eCSSTokenSerialization_Percentage ||
2515              aToken2 == eCSSTokenSerialization_Dimension ||
2516              aToken2 == eCSSTokenSerialization_URange ||
2517              aToken2 == eCSSTokenSerialization_CDC ||
2518              aToken2 == eCSSTokenSerialization_Symbol_OpenParen;
2519     case eCSSTokenSerialization_AtKeyword_or_Hash:
2520     case eCSSTokenSerialization_Dimension:
2521       return aToken2 == eCSSTokenSerialization_Ident ||
2522              aToken2 == eCSSTokenSerialization_Function ||
2523              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
2524              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
2525              aToken2 == eCSSTokenSerialization_Number ||
2526              aToken2 == eCSSTokenSerialization_Percentage ||
2527              aToken2 == eCSSTokenSerialization_Dimension ||
2528              aToken2 == eCSSTokenSerialization_URange ||
2529              aToken2 == eCSSTokenSerialization_CDC;
2530     case eCSSTokenSerialization_Symbol_Hash:
2531     case eCSSTokenSerialization_Symbol_Minus:
2532       return aToken2 == eCSSTokenSerialization_Ident ||
2533              aToken2 == eCSSTokenSerialization_Function ||
2534              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
2535              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
2536              aToken2 == eCSSTokenSerialization_Number ||
2537              aToken2 == eCSSTokenSerialization_Percentage ||
2538              aToken2 == eCSSTokenSerialization_Dimension ||
2539              aToken2 == eCSSTokenSerialization_URange;
2540     case eCSSTokenSerialization_Number:
2541       return aToken2 == eCSSTokenSerialization_Ident ||
2542              aToken2 == eCSSTokenSerialization_Function ||
2543              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
2544              aToken2 == eCSSTokenSerialization_Number ||
2545              aToken2 == eCSSTokenSerialization_Percentage ||
2546              aToken2 == eCSSTokenSerialization_Dimension ||
2547              aToken2 == eCSSTokenSerialization_URange;
2548     case eCSSTokenSerialization_Symbol_At:
2549       return aToken2 == eCSSTokenSerialization_Ident ||
2550              aToken2 == eCSSTokenSerialization_Function ||
2551              aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
2552              aToken2 == eCSSTokenSerialization_Symbol_Minus ||
2553              aToken2 == eCSSTokenSerialization_URange;
2554     case eCSSTokenSerialization_URange:
2555       return aToken2 == eCSSTokenSerialization_Ident ||
2556              aToken2 == eCSSTokenSerialization_Function ||
2557              aToken2 == eCSSTokenSerialization_Number ||
2558              aToken2 == eCSSTokenSerialization_Percentage ||
2559              aToken2 == eCSSTokenSerialization_Dimension ||
2560              aToken2 == eCSSTokenSerialization_Symbol_Question;
2561     case eCSSTokenSerialization_Symbol_Dot_or_Plus:
2562       return aToken2 == eCSSTokenSerialization_Number ||
2563              aToken2 == eCSSTokenSerialization_Percentage ||
2564              aToken2 == eCSSTokenSerialization_Dimension;
2565     case eCSSTokenSerialization_Symbol_Assorted:
2566     case eCSSTokenSerialization_Symbol_Asterisk:
2567       return aToken2 == eCSSTokenSerialization_Symbol_Equals;
2568     case eCSSTokenSerialization_Symbol_Bar:
2569       return aToken2 == eCSSTokenSerialization_Symbol_Equals ||
2570              aToken2 == eCSSTokenSerialization_Symbol_Bar ||
2571              aToken2 == eCSSTokenSerialization_DashMatch;              // (*)
2572     case eCSSTokenSerialization_Symbol_Slash:
2573       return aToken2 == eCSSTokenSerialization_Symbol_Asterisk ||
2574              aToken2 == eCSSTokenSerialization_ContainsMatch;          // (*)
2575     default:
2576       MOZ_ASSERT(aToken1 == eCSSTokenSerialization_Nothing ||
2577                  aToken1 == eCSSTokenSerialization_Whitespace ||
2578                  aToken1 == eCSSTokenSerialization_Percentage ||
2579                  aToken1 == eCSSTokenSerialization_URL_or_BadURL ||
2580                  aToken1 == eCSSTokenSerialization_Function ||
2581                  aToken1 == eCSSTokenSerialization_CDC ||
2582                  aToken1 == eCSSTokenSerialization_Symbol_OpenParen ||
2583                  aToken1 == eCSSTokenSerialization_Symbol_Question ||
2584                  aToken1 == eCSSTokenSerialization_Symbol_Assorted ||
2585                  aToken1 == eCSSTokenSerialization_Symbol_Asterisk ||
2586                  aToken1 == eCSSTokenSerialization_Symbol_Equals ||
2587                  aToken1 == eCSSTokenSerialization_Symbol_Bar ||
2588                  aToken1 == eCSSTokenSerialization_Symbol_Slash ||
2589                  aToken1 == eCSSTokenSerialization_Other,
2590                  "unexpected nsCSSTokenSerializationType value");
2591       return false;
2592   }
2593 }
2594 
2595 /**
2596  * Appends aValue to aResult, possibly inserting an empty CSS
2597  * comment between the two to ensure that tokens from both strings
2598  * remain separated.
2599  */
2600 static void
2601 AppendTokens(nsAString& aResult,
2602              nsCSSTokenSerializationType& aResultFirstToken,
2603              nsCSSTokenSerializationType& aResultLastToken,
2604              nsCSSTokenSerializationType aValueFirstToken,
2605              nsCSSTokenSerializationType aValueLastToken,
2606              const nsAString& aValue)
2607 {
2608   if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
2609     aResult.AppendLiteral("/**/");
2610   }
2611   aResult.Append(aValue);
2612   if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
2613     aResultFirstToken = aValueFirstToken;
2614   }
2615   if (aValueLastToken != eCSSTokenSerialization_Nothing) {
2616     aResultLastToken = aValueLastToken;
2617   }
2618 }
2619 
2620 /**
2621  * Stops the given scanner recording, and appends the recorded result
2622  * to aResult, possibly inserting an empty CSS comment between the two to
2623  * ensure that tokens from both strings remain separated.
2624  */
2625 static void
2626 StopRecordingAndAppendTokens(nsString& aResult,
2627                              nsCSSTokenSerializationType& aResultFirstToken,
2628                              nsCSSTokenSerializationType& aResultLastToken,
2629                              nsCSSTokenSerializationType aValueFirstToken,
2630                              nsCSSTokenSerializationType aValueLastToken,
2631                              nsCSSScanner* aScanner)
2632 {
2633   if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
2634     aResult.AppendLiteral("/**/");
2635   }
2636   aScanner->StopRecording(aResult);
2637   if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
2638     aResultFirstToken = aValueFirstToken;
2639   }
2640   if (aValueLastToken != eCSSTokenSerialization_Nothing) {
2641     aResultLastToken = aValueLastToken;
2642   }
2643 }
2644 
2645 bool
2646 CSSParserImpl::ResolveValueWithVariableReferencesRec(
2647                                      nsString& aResult,
2648                                      nsCSSTokenSerializationType& aResultFirstToken,
2649                                      nsCSSTokenSerializationType& aResultLastToken,
2650                                      const CSSVariableValues* aVariables)
2651 {
2652   // This function assumes we are already recording, and will leave the scanner
2653   // recording when it returns.
2654   MOZ_ASSERT(mScanner->IsRecording());
2655   MOZ_ASSERT(aResult.IsEmpty());
2656 
2657   // Stack of closing characters for currently open constructs.
2658   AutoTArray<char16_t, 16> stack;
2659 
2660   // The resolved value for this ResolveValueWithVariableReferencesRec call.
2661   nsString value;
2662 
2663   // The length of the scanner's recording before the currently parsed token.
2664   // This is used so that when we encounter a "var(" token, we can strip
2665   // it off the end of the recording, regardless of how long the token was.
2666   // (With escapes, it could be longer than four characters.)
2667   uint32_t lengthBeforeVar = 0;
2668 
2669   // Tracking the type of token that appears at the start and end of |value|
2670   // and that appears at the start and end of the scanner recording.  These are
2671   // used to determine whether we need to insert "/**/" when pasting token
2672   // streams together.
2673   nsCSSTokenSerializationType valueFirstToken = eCSSTokenSerialization_Nothing,
2674                               valueLastToken  = eCSSTokenSerialization_Nothing,
2675                               recFirstToken   = eCSSTokenSerialization_Nothing,
2676                               recLastToken    = eCSSTokenSerialization_Nothing;
2677 
2678 #define UPDATE_RECORDING_TOKENS(type)                    \
2679   if (recFirstToken == eCSSTokenSerialization_Nothing) { \
2680     recFirstToken = type;                                \
2681   }                                                      \
2682   recLastToken = type;
2683 
2684   while (GetToken(false)) {
2685     switch (mToken.mType) {
2686       case eCSSToken_Symbol: {
2687         nsCSSTokenSerializationType type = eCSSTokenSerialization_Other;
2688         if (mToken.mSymbol == '(') {
2689           stack.AppendElement(')');
2690           type = eCSSTokenSerialization_Symbol_OpenParen;
2691         } else if (mToken.mSymbol == '[') {
2692           stack.AppendElement(']');
2693         } else if (mToken.mSymbol == '{') {
2694           stack.AppendElement('}');
2695         } else if (mToken.mSymbol == ';') {
2696           if (stack.IsEmpty()) {
2697             // A ';' that is at the top level of the value or at the top level
2698             // of a variable reference's fallback is invalid.
2699             return false;
2700           }
2701         } else if (mToken.mSymbol == '!') {
2702           if (stack.IsEmpty()) {
2703             // An '!' that is at the top level of the value or at the top level
2704             // of a variable reference's fallback is invalid.
2705             return false;
2706           }
2707         } else if (mToken.mSymbol == ')' &&
2708                    stack.IsEmpty()) {
2709           // We're closing a "var(".
2710           nsString finalTokens;
2711           mScanner->StopRecording(finalTokens);
2712           MOZ_ASSERT(finalTokens[finalTokens.Length() - 1] == ')');
2713           finalTokens.Truncate(finalTokens.Length() - 1);
2714           aResult.Append(value);
2715 
2716           AppendTokens(aResult, valueFirstToken, valueLastToken,
2717                        recFirstToken, recLastToken, finalTokens);
2718 
2719           mScanner->StartRecording();
2720           UngetToken();
2721           aResultFirstToken = valueFirstToken;
2722           aResultLastToken = valueLastToken;
2723           return true;
2724         } else if (mToken.mSymbol == ')' ||
2725                    mToken.mSymbol == ']' ||
2726                    mToken.mSymbol == '}') {
2727           if (stack.IsEmpty() ||
2728               stack.LastElement() != mToken.mSymbol) {
2729             // A mismatched closing bracket is invalid.
2730             return false;
2731           }
2732           stack.TruncateLength(stack.Length() - 1);
2733         } else if (mToken.mSymbol == '#') {
2734           type = eCSSTokenSerialization_Symbol_Hash;
2735         } else if (mToken.mSymbol == '@') {
2736           type = eCSSTokenSerialization_Symbol_At;
2737         } else if (mToken.mSymbol == '.' ||
2738                    mToken.mSymbol == '+') {
2739           type = eCSSTokenSerialization_Symbol_Dot_or_Plus;
2740         } else if (mToken.mSymbol == '-') {
2741           type = eCSSTokenSerialization_Symbol_Minus;
2742         } else if (mToken.mSymbol == '?') {
2743           type = eCSSTokenSerialization_Symbol_Question;
2744         } else if (mToken.mSymbol == '$' ||
2745                    mToken.mSymbol == '^' ||
2746                    mToken.mSymbol == '~') {
2747           type = eCSSTokenSerialization_Symbol_Assorted;
2748         } else if (mToken.mSymbol == '=') {
2749           type = eCSSTokenSerialization_Symbol_Equals;
2750         } else if (mToken.mSymbol == '|') {
2751           type = eCSSTokenSerialization_Symbol_Bar;
2752         } else if (mToken.mSymbol == '/') {
2753           type = eCSSTokenSerialization_Symbol_Slash;
2754         } else if (mToken.mSymbol == '*') {
2755           type = eCSSTokenSerialization_Symbol_Asterisk;
2756         }
2757         UPDATE_RECORDING_TOKENS(type);
2758         break;
2759       }
2760 
2761       case eCSSToken_Function:
2762         if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
2763           // Save the tokens before the "var(" to our resolved value.
2764           nsString recording;
2765           mScanner->StopRecording(recording);
2766           recording.Truncate(lengthBeforeVar);
2767           AppendTokens(value, valueFirstToken, valueLastToken,
2768                        recFirstToken, recLastToken, recording);
2769           recFirstToken = eCSSTokenSerialization_Nothing;
2770           recLastToken = eCSSTokenSerialization_Nothing;
2771 
2772           if (!GetToken(true) ||
2773               mToken.mType != eCSSToken_Ident ||
2774               !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
2775             // "var(" must be followed by an identifier, and it must be a
2776             // custom property name.
2777             return false;
2778           }
2779 
2780           // Turn the custom property name into a variable name by removing the
2781           // '--' prefix.
2782           MOZ_ASSERT(Substring(mToken.mIdent, 0,
2783                                CSS_CUSTOM_NAME_PREFIX_LENGTH).
2784                        EqualsLiteral("--"));
2785           nsDependentString variableName(mToken.mIdent,
2786                                          CSS_CUSTOM_NAME_PREFIX_LENGTH);
2787 
2788           // Get the value of the identified variable.  Note that we
2789           // check if the variable value is the empty string, as that means
2790           // that the variable was invalid at computed value time due to
2791           // unresolveable variable references or cycles.
2792           nsString variableValue;
2793           nsCSSTokenSerializationType varFirstToken, varLastToken;
2794           bool valid = aVariables->Get(variableName, variableValue,
2795                                        varFirstToken, varLastToken) &&
2796                        !variableValue.IsEmpty();
2797 
2798           if (!GetToken(true) ||
2799               mToken.IsSymbol(')')) {
2800             mScanner->StartRecording();
2801             if (!valid) {
2802               // Invalid variable with no fallback.
2803               return false;
2804             }
2805             // Valid variable with no fallback.
2806             AppendTokens(value, valueFirstToken, valueLastToken,
2807                          varFirstToken, varLastToken, variableValue);
2808           } else if (mToken.IsSymbol(',')) {
2809             mScanner->StartRecording();
2810             if (!GetToken(false) ||
2811                 mToken.IsSymbol(')')) {
2812               // Comma must be followed by at least one fallback token.
2813               return false;
2814             }
2815             UngetToken();
2816             if (valid) {
2817               // Valid variable with ignored fallback.
2818               mScanner->StopRecording();
2819               AppendTokens(value, valueFirstToken, valueLastToken,
2820                            varFirstToken, varLastToken, variableValue);
2821               bool ok = SkipBalancedContentUntil(')');
2822               mScanner->StartRecording();
2823               if (!ok) {
2824                 return false;
2825               }
2826             } else {
2827               nsString fallback;
2828               if (!ResolveValueWithVariableReferencesRec(fallback,
2829                                                          varFirstToken,
2830                                                          varLastToken,
2831                                                          aVariables)) {
2832                 // Fallback value had invalid tokens or an invalid variable reference
2833                 // that itself had no fallback.
2834                 return false;
2835               }
2836               AppendTokens(value, valueFirstToken, valueLastToken,
2837                            varFirstToken, varLastToken, fallback);
2838               // Now we're either at the pushed back ')' that finished the
2839               // fallback or at EOF.
2840               DebugOnly<bool> gotToken = GetToken(false);
2841               MOZ_ASSERT(!gotToken || mToken.IsSymbol(')'));
2842             }
2843           } else {
2844             // Expected ',' or ')' after the variable name.
2845             mScanner->StartRecording();
2846             return false;
2847           }
2848         } else {
2849           stack.AppendElement(')');
2850           UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function);
2851         }
2852         break;
2853 
2854       case eCSSToken_Bad_String:
2855       case eCSSToken_Bad_URL:
2856         return false;
2857 
2858       case eCSSToken_Whitespace:
2859         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace);
2860         break;
2861 
2862       case eCSSToken_AtKeyword:
2863       case eCSSToken_Hash:
2864         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash);
2865         break;
2866 
2867       case eCSSToken_Number:
2868         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number);
2869         break;
2870 
2871       case eCSSToken_Dimension:
2872         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension);
2873         break;
2874 
2875       case eCSSToken_Ident:
2876         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident);
2877         break;
2878 
2879       case eCSSToken_Percentage:
2880         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage);
2881         break;
2882 
2883       case eCSSToken_URange:
2884         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange);
2885         break;
2886 
2887       case eCSSToken_URL:
2888         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL);
2889         break;
2890 
2891       case eCSSToken_HTMLComment:
2892         if (mToken.mIdent[0] == '-') {
2893           UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC);
2894         } else {
2895           UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
2896         }
2897         break;
2898 
2899       case eCSSToken_Dashmatch:
2900         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch);
2901         break;
2902 
2903       case eCSSToken_Containsmatch:
2904         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch);
2905         break;
2906 
2907       default:
2908         MOZ_FALLTHROUGH_ASSERT("unexpected token type");
2909       case eCSSToken_ID:
2910       case eCSSToken_String:
2911       case eCSSToken_Includes:
2912       case eCSSToken_Beginsmatch:
2913       case eCSSToken_Endsmatch:
2914         UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
2915         break;
2916     }
2917 
2918     lengthBeforeVar = mScanner->RecordingLength();
2919   }
2920 
2921 #undef UPDATE_RECORDING_TOKENS
2922 
2923   aResult.Append(value);
2924   StopRecordingAndAppendTokens(aResult, valueFirstToken, valueLastToken,
2925                                recFirstToken, recLastToken, mScanner);
2926 
2927   // Append any implicitly closed brackets.
2928   if (!stack.IsEmpty()) {
2929     do {
2930       aResult.Append(stack.LastElement());
2931       stack.TruncateLength(stack.Length() - 1);
2932     } while (!stack.IsEmpty());
2933     valueLastToken = eCSSTokenSerialization_Other;
2934   }
2935 
2936   mScanner->StartRecording();
2937   aResultFirstToken = valueFirstToken;
2938   aResultLastToken = valueLastToken;
2939   return true;
2940 }
2941 
2942 bool
2943 CSSParserImpl::ResolveValueWithVariableReferences(
2944                                         const CSSVariableValues* aVariables,
2945                                         nsString& aResult,
2946                                         nsCSSTokenSerializationType& aFirstToken,
2947                                         nsCSSTokenSerializationType& aLastToken)
2948 {
2949   aResult.Truncate(0);
2950 
2951   // Start recording before we read the first token.
2952   mScanner->StartRecording();
2953 
2954   if (!GetToken(false)) {
2955     // Value was empty since we reached EOF.
2956     mScanner->StopRecording();
2957     return false;
2958   }
2959 
2960   UngetToken();
2961 
2962   nsString value;
2963   nsCSSTokenSerializationType firstToken, lastToken;
2964   bool ok = ResolveValueWithVariableReferencesRec(value, firstToken, lastToken, aVariables) &&
2965             !GetToken(true);
2966 
2967   mScanner->StopRecording();
2968 
2969   if (ok) {
2970     aResult = value;
2971     aFirstToken = firstToken;
2972     aLastToken = lastToken;
2973   }
2974   return ok;
2975 }
2976 
2977 bool
2978 CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue,
2979                                     const CSSVariableValues* aVariables,
2980                                     nsString& aResult,
2981                                     nsCSSTokenSerializationType& aFirstToken,
2982                                     nsCSSTokenSerializationType& aLastToken)
2983 {
2984   nsCSSScanner scanner(aPropertyValue, 0);
2985 
2986   // At this point, we know that aPropertyValue is syntactically correct
2987   // for a token stream that has variable references.  We also won't be
2988   // interpreting any of the stream as we parse it, apart from expanding
2989   // var() references, so we don't need a base URL etc. or any useful
2990   // error reporting.
2991   css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
2992   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
2993 
2994   bool valid = ResolveValueWithVariableReferences(aVariables, aResult,
2995                                                   aFirstToken, aLastToken);
2996 
2997   ReleaseScanner();
2998   return valid;
2999 }
3000 
3001 void
3002 CSSParserImpl::ParsePropertyWithVariableReferences(
3003                                             nsCSSPropertyID aPropertyID,
3004                                             nsCSSPropertyID aShorthandPropertyID,
3005                                             const nsAString& aValue,
3006                                             const CSSVariableValues* aVariables,
3007                                             nsRuleData* aRuleData,
3008                                             nsIURI* aDocURL,
3009                                             nsIURI* aBaseURL,
3010                                             nsIPrincipal* aDocPrincipal,
3011                                             CSSStyleSheet* aSheet,
3012                                             uint32_t aLineNumber,
3013                                             uint32_t aLineOffset)
3014 {
3015   mTempData.AssertInitialState();
3016 
3017   bool valid;
3018   nsString expandedValue;
3019 
3020   // Resolve any variable references in the property value.
3021   {
3022     nsCSSScanner scanner(aValue, 0);
3023     css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
3024     InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
3025 
3026     nsCSSTokenSerializationType firstToken, lastToken;
3027     valid = ResolveValueWithVariableReferences(aVariables, expandedValue,
3028                                                firstToken, lastToken);
3029     if (!valid) {
3030       NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropertyID));
3031       REPORT_UNEXPECTED(PEInvalidVariableReference);
3032       REPORT_UNEXPECTED_P(PEValueParsingError, propName);
3033       if (nsCSSProps::IsInherited(aPropertyID)) {
3034         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
3035       } else {
3036         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
3037       }
3038       OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
3039     }
3040     ReleaseScanner();
3041   }
3042 
3043   nsCSSPropertyID propertyToParse =
3044     aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID :
3045                                                    aPropertyID;
3046 
3047   // Parse the property with that resolved value.
3048   if (valid) {
3049     nsCSSScanner scanner(expandedValue, 0);
3050     css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
3051     InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
3052     valid = ParseProperty(propertyToParse);
3053     if (valid && GetToken(true)) {
3054       REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
3055       valid = false;
3056     }
3057     if (!valid) {
3058       NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(
3059                                                               propertyToParse));
3060       REPORT_UNEXPECTED_P_V(PEValueWithVariablesParsingErrorInValue,
3061                             propName, expandedValue);
3062       if (nsCSSProps::IsInherited(aPropertyID)) {
3063         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
3064       } else {
3065         REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
3066       }
3067       OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
3068     }
3069     ReleaseScanner();
3070   }
3071 
3072   // If the property could not be parsed with the resolved value, then we
3073   // treat it as if the value were 'initial' or 'inherit', depending on whether
3074   // the property is an inherited property.
3075   if (!valid) {
3076     nsCSSValue defaultValue;
3077     if (nsCSSProps::IsInherited(aPropertyID)) {
3078       defaultValue.SetInheritValue();
3079     } else {
3080       defaultValue.SetInitialValue();
3081     }
3082     mTempData.AddLonghandProperty(aPropertyID, defaultValue);
3083   }
3084 
3085   // Copy the property value into the rule data.
3086   mTempData.MapRuleInfoInto(aPropertyID, aRuleData);
3087 
3088   mTempData.ClearProperty(propertyToParse);
3089   mTempData.AssertInitialState();
3090 }
3091 
3092 bool
3093 CSSParserImpl::ParseCounterStyleName(const nsAString& aBuffer,
3094                                      nsIURI* aURL,
3095                                      nsAString& aName)
3096 {
3097   nsCSSScanner scanner(aBuffer, 0);
3098   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURL);
3099   InitScanner(scanner, reporter, aURL, aURL, nullptr);
3100 
3101   bool success = ParseCounterStyleName(aName, true) && !GetToken(true);
3102 
3103   OUTPUT_ERROR();
3104   ReleaseScanner();
3105 
3106   return success;
3107 }
3108 
3109 bool
3110 CSSParserImpl::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
3111                                       const nsAString& aBuffer,
3112                                       nsIURI* aSheetURL,
3113                                       nsIURI* aBaseURL,
3114                                       nsIPrincipal* aSheetPrincipal,
3115                                       nsCSSValue& aValue)
3116 {
3117   nsCSSScanner scanner(aBuffer, 0);
3118   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
3119   InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
3120 
3121   bool success = ParseCounterDescriptorValue(aDescID, aValue) &&
3122                  !GetToken(true);
3123 
3124   OUTPUT_ERROR();
3125   ReleaseScanner();
3126 
3127   return success;
3128 }
3129 
3130 bool
3131 CSSParserImpl::ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
3132                                        const nsAString& aBuffer,
3133                                        nsIURI* aSheetURL,
3134                                        nsIURI* aBaseURL,
3135                                        nsIPrincipal* aSheetPrincipal,
3136                                        nsCSSValue& aValue)
3137 {
3138   nsCSSScanner scanner(aBuffer, 0);
3139   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
3140   InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
3141 
3142   bool success = ParseFontDescriptorValue(aDescID, aValue) &&
3143                  !GetToken(true);
3144 
3145   OUTPUT_ERROR();
3146   ReleaseScanner();
3147 
3148   return success;
3149 }
3150 
3151 //----------------------------------------------------------------------
3152 
3153 bool
3154 CSSParserImpl::GetToken(bool aSkipWS)
3155 {
3156   if (mHavePushBack) {
3157     mHavePushBack = false;
3158     if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) {
3159       return true;
3160     }
3161   }
3162   return mScanner->Next(mToken, aSkipWS ?
3163                         eCSSScannerExclude_WhitespaceAndComments :
3164                         eCSSScannerExclude_Comments);
3165 }
3166 
3167 void
3168 CSSParserImpl::UngetToken()
3169 {
3170   NS_PRECONDITION(!mHavePushBack, "double pushback");
3171   mHavePushBack = true;
3172 }
3173 
3174 bool
3175 CSSParserImpl::GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum)
3176 {
3177   // Peek at next token so that mScanner updates line and column vals
3178   if (!GetToken(aSkipWS)) {
3179     return false;
3180   }
3181   UngetToken();
3182   // The scanner uses one-indexing for line numbers but zero-indexing
3183   // for column numbers.
3184   *linenum = mScanner->GetLineNumber();
3185   *colnum = 1 + mScanner->GetColumnNumber();
3186   return true;
3187 }
3188 
3189 bool
3190 CSSParserImpl::ExpectSymbol(char16_t aSymbol,
3191                             bool aSkipWS)
3192 {
3193   if (!GetToken(aSkipWS)) {
3194     // CSS2.1 specifies that all "open constructs" are to be closed at
3195     // EOF.  It simplifies higher layers if we claim to have found an
3196     // ), ], }, or ; if we encounter EOF while looking for one of them.
3197     // Do still issue a diagnostic, to aid debugging.
3198     if (aSymbol == ')' || aSymbol == ']' ||
3199         aSymbol == '}' || aSymbol == ';') {
3200       REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
3201       return true;
3202     }
3203     else
3204       return false;
3205   }
3206   if (mToken.IsSymbol(aSymbol)) {
3207     return true;
3208   }
3209   UngetToken();
3210   return false;
3211 }
3212 
3213 // Checks to see if we're at the end of a property.  If an error occurs during
3214 // the check, does not signal a parse error.
3215 bool
3216 CSSParserImpl::CheckEndProperty()
3217 {
3218   if (!GetToken(true)) {
3219     return true; // properties may end with eof
3220   }
3221   if ((eCSSToken_Symbol == mToken.mType) &&
3222       ((';' == mToken.mSymbol) ||
3223        ('!' == mToken.mSymbol) ||
3224        ('}' == mToken.mSymbol) ||
3225        (')' == mToken.mSymbol))) {
3226     // XXX need to verify that ! is only followed by "important [;|}]
3227     // XXX this requires a multi-token pushback buffer
3228     UngetToken();
3229     return true;
3230   }
3231   UngetToken();
3232   return false;
3233 }
3234 
3235 // Checks if we're at the end of a property, raising an error if we're not.
3236 bool
3237 CSSParserImpl::ExpectEndProperty()
3238 {
3239   if (CheckEndProperty())
3240     return true;
3241 
3242   // If we're here, we read something incorrect, so we should report it.
3243   REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
3244   return false;
3245 }
3246 
3247 // Parses the priority suffix on a property, which at present may be
3248 // either '!important' or nothing.
3249 CSSParserImpl::PriorityParsingStatus
3250 CSSParserImpl::ParsePriority()
3251 {
3252   if (!GetToken(true)) {
3253     return ePriority_None; // properties may end with EOF
3254   }
3255   if (!mToken.IsSymbol('!')) {
3256     UngetToken();
3257     return ePriority_None; // dunno what it is, but it's not a priority
3258   }
3259 
3260   if (!GetToken(true)) {
3261     // EOF is not ok after !
3262     REPORT_UNEXPECTED_EOF(PEImportantEOF);
3263     return ePriority_Error;
3264   }
3265 
3266   if (mToken.mType != eCSSToken_Ident ||
3267       !mToken.mIdent.LowerCaseEqualsLiteral("important")) {
3268     REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
3269     UngetToken();
3270     return ePriority_Error;
3271   }
3272 
3273   return ePriority_Important;
3274 }
3275 
3276 nsSubstring*
3277 CSSParserImpl::NextIdent()
3278 {
3279   // XXX Error reporting?
3280   if (!GetToken(true)) {
3281     return nullptr;
3282   }
3283   if (eCSSToken_Ident != mToken.mType) {
3284     UngetToken();
3285     return nullptr;
3286   }
3287   return &mToken.mIdent;
3288 }
3289 
3290 bool
3291 CSSParserImpl::SkipAtRule(bool aInsideBlock)
3292 {
3293   for (;;) {
3294     if (!GetToken(true)) {
3295       REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2);
3296       return false;
3297     }
3298     if (eCSSToken_Symbol == mToken.mType) {
3299       char16_t symbol = mToken.mSymbol;
3300       if (symbol == ';') {
3301         break;
3302       }
3303       if (aInsideBlock && symbol == '}') {
3304         // The closing } doesn't belong to us.
3305         UngetToken();
3306         break;
3307       }
3308       if (symbol == '{') {
3309         SkipUntil('}');
3310         break;
3311       } else if (symbol == '(') {
3312         SkipUntil(')');
3313       } else if (symbol == '[') {
3314         SkipUntil(']');
3315       }
3316     } else if (eCSSToken_Function == mToken.mType ||
3317                eCSSToken_Bad_URL == mToken.mType) {
3318       SkipUntil(')');
3319     }
3320   }
3321   return true;
3322 }
3323 
3324 bool
3325 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
3326                            void* aData,
3327                            bool aInAtRule)
3328 {
3329 
3330   nsCSSSection newSection;
3331   bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*);
3332 
3333   if ((mSection <= eCSSSection_Charset) &&
3334       (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
3335     parseFunc = &CSSParserImpl::ParseCharsetRule;
3336     newSection = eCSSSection_Import;  // only one charset allowed
3337 
3338   } else if ((mSection <= eCSSSection_Import) &&
3339              mToken.mIdent.LowerCaseEqualsLiteral("import")) {
3340     parseFunc = &CSSParserImpl::ParseImportRule;
3341     newSection = eCSSSection_Import;
3342 
3343   } else if ((mSection <= eCSSSection_NameSpace) &&
3344              mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
3345     parseFunc = &CSSParserImpl::ParseNameSpaceRule;
3346     newSection = eCSSSection_NameSpace;
3347 
3348   } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
3349     parseFunc = &CSSParserImpl::ParseMediaRule;
3350     newSection = eCSSSection_General;
3351 
3352   } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
3353     parseFunc = &CSSParserImpl::ParseMozDocumentRule;
3354     newSection = eCSSSection_General;
3355 
3356   } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
3357     parseFunc = &CSSParserImpl::ParseFontFaceRule;
3358     newSection = eCSSSection_General;
3359 
3360   } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values")) {
3361     parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule;
3362     newSection = eCSSSection_General;
3363 
3364   } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
3365     parseFunc = &CSSParserImpl::ParsePageRule;
3366     newSection = eCSSSection_General;
3367 
3368   } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation,
3369                                     EnabledState()) &&
3370               mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) ||
3371              (nsCSSProps::IsEnabled(eCSSPropertyAlias_WebkitAnimation) &&
3372               mToken.mIdent.LowerCaseEqualsLiteral("-webkit-keyframes")) ||
3373              mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) {
3374     parseFunc = &CSSParserImpl::ParseKeyframesRule;
3375     newSection = eCSSSection_General;
3376 
3377   } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports")) {
3378     parseFunc = &CSSParserImpl::ParseSupportsRule;
3379     newSection = eCSSSection_General;
3380 
3381   } else if (mToken.mIdent.LowerCaseEqualsLiteral("counter-style")) {
3382     parseFunc = &CSSParserImpl::ParseCounterStyleRule;
3383     newSection = eCSSSection_General;
3384 
3385   } else {
3386     if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
3387       REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
3388       OUTPUT_ERROR();
3389     }
3390     // Skip over unsupported at rule, don't advance section
3391     return SkipAtRule(aInAtRule);
3392   }
3393 
3394   // Inside of @-rules, only the rules that can occur anywhere
3395   // are allowed.
3396   bool unnestable = aInAtRule && newSection != eCSSSection_General;
3397   if (unnestable) {
3398     REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule);
3399   }
3400 
3401   if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) {
3402     // Skip over invalid at rule, don't advance section
3403     OUTPUT_ERROR();
3404     return SkipAtRule(aInAtRule);
3405   }
3406 
3407   // Nested @-rules don't affect the top-level rule chain requirement
3408   if (!aInAtRule) {
3409     mSection = newSection;
3410   }
3411 
3412   return true;
3413 }
3414 
3415 bool
3416 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
3417                                 void* aData)
3418 {
3419   uint32_t linenum, colnum;
3420   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3421       !GetToken(true)) {
3422     REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
3423     return false;
3424   }
3425 
3426   if (eCSSToken_String != mToken.mType) {
3427     UngetToken();
3428     REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
3429     return false;
3430   }
3431 
3432   nsAutoString charset = mToken.mIdent;
3433 
3434   if (!ExpectSymbol(';', true)) {
3435     return false;
3436   }
3437 
3438   // It's intentional that we don't create a rule object for @charset rules.
3439 
3440   return true;
3441 }
3442 
3443 bool
3444 CSSParserImpl::ParseURLOrString(nsString& aURL)
3445 {
3446   if (!GetToken(true)) {
3447     return false;
3448   }
3449   if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) {
3450     aURL = mToken.mIdent;
3451     return true;
3452   }
3453   UngetToken();
3454   return false;
3455 }
3456 
3457 bool
3458 CSSParserImpl::ParseMediaQuery(eMediaQueryType aQueryType,
3459                                nsMediaQuery **aQuery,
3460                                bool *aHitStop)
3461 {
3462   *aQuery = nullptr;
3463   *aHitStop = false;
3464   bool inAtRule = aQueryType == eMediaQueryAtRule;
3465   // Attempt to parse a single condition and stop
3466   bool singleCondition = aQueryType == eMediaQuerySingleCondition;
3467 
3468   // "If the comma-separated list is the empty list it is assumed to
3469   // specify the media query 'all'."  (css3-mediaqueries, section
3470   // "Media Queries")
3471   if (!GetToken(true)) {
3472     *aHitStop = true;
3473     // expected termination by EOF
3474     if (!inAtRule)
3475       return true;
3476 
3477     // unexpected termination by EOF
3478     REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
3479     return true;
3480   }
3481 
3482   if (eCSSToken_Symbol == mToken.mType && inAtRule &&
3483       (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) {
3484     *aHitStop = true;
3485     UngetToken();
3486     return true;
3487   }
3488   UngetToken();
3489 
3490   nsMediaQuery* query = new nsMediaQuery;
3491   *aQuery = query;
3492 
3493   if (ExpectSymbol('(', true)) {
3494     // we got an expression without a media type
3495     UngetToken(); // so ParseMediaQueryExpression can handle it
3496     query->SetType(nsGkAtoms::all);
3497     query->SetTypeOmitted();
3498     // Just parse the first expression here.
3499     if (!ParseMediaQueryExpression(query)) {
3500       OUTPUT_ERROR();
3501       query->SetHadUnknownExpression();
3502     }
3503   } else if (singleCondition) {
3504     // Since we are only trying to consume a single condition, which precludes
3505     // media types and not/only, this should be the same as reaching immediate
3506     // EOF (no condition to parse)
3507     *aHitStop = true;
3508     return true;
3509   } else {
3510     nsCOMPtr<nsIAtom> mediaType;
3511     bool gotNotOrOnly = false;
3512     for (;;) {
3513       if (!GetToken(true)) {
3514         REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
3515         return false;
3516       }
3517       if (eCSSToken_Ident != mToken.mType) {
3518         REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
3519         UngetToken();
3520         return false;
3521       }
3522       // case insensitive from CSS - must be lower cased
3523       nsContentUtils::ASCIIToLower(mToken.mIdent);
3524       mediaType = NS_Atomize(mToken.mIdent);
3525       if (!gotNotOrOnly && mediaType == nsGkAtoms::_not) {
3526         gotNotOrOnly = true;
3527         query->SetNegated();
3528       } else if (!gotNotOrOnly && mediaType == nsGkAtoms::only) {
3529         gotNotOrOnly = true;
3530         query->SetHasOnly();
3531       } else if (mediaType == nsGkAtoms::_not ||
3532                  mediaType == nsGkAtoms::only ||
3533                  mediaType == nsGkAtoms::_and ||
3534                  mediaType == nsGkAtoms::_or) {
3535         REPORT_UNEXPECTED_TOKEN(PEGatherMediaReservedMediaType);
3536         UngetToken();
3537         return false;
3538       } else {
3539         // valid media type
3540         break;
3541       }
3542     }
3543     query->SetType(mediaType);
3544   }
3545 
3546   for (;;) {
3547     if (!GetToken(true)) {
3548       *aHitStop = true;
3549       // expected termination by EOF
3550       if (!inAtRule)
3551         break;
3552 
3553       // unexpected termination by EOF
3554       REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
3555       break;
3556     }
3557 
3558     if (eCSSToken_Symbol == mToken.mType && inAtRule &&
3559         (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) {
3560       *aHitStop = true;
3561       UngetToken();
3562       break;
3563     }
3564     if (!singleCondition &&
3565         eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
3566       // Done with the expressions for this query
3567       break;
3568     }
3569     if (eCSSToken_Ident != mToken.mType ||
3570         !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
3571       if (singleCondition) {
3572         // We have a condition at this point -- if we're not chained to other
3573         // conditions with and/or, we're done.
3574         UngetToken();
3575         break;
3576       } else {
3577         REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
3578         UngetToken();
3579         return false;
3580       }
3581     }
3582     if (!ParseMediaQueryExpression(query)) {
3583       OUTPUT_ERROR();
3584       query->SetHadUnknownExpression();
3585     }
3586   }
3587   return true;
3588 }
3589 
3590 // Returns false only when there is a low-level error in the scanner
3591 // (out-of-memory).
3592 bool
3593 CSSParserImpl::GatherMedia(nsMediaList* aMedia,
3594                            bool aInAtRule)
3595 {
3596   eMediaQueryType type = aInAtRule ? eMediaQueryAtRule : eMediaQueryNormal;
3597   for (;;) {
3598     nsAutoPtr<nsMediaQuery> query;
3599     bool hitStop;
3600     if (!ParseMediaQuery(type, getter_Transfers(query), &hitStop)) {
3601       NS_ASSERTION(!hitStop, "should return true when hit stop");
3602       OUTPUT_ERROR();
3603       if (query) {
3604         query->SetHadUnknownExpression();
3605       }
3606       if (aInAtRule) {
3607         const char16_t stopChars[] =
3608           { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) };
3609         SkipUntilOneOf(stopChars);
3610       } else {
3611         SkipUntil(',');
3612       }
3613       // Rely on SkipUntilOneOf leaving mToken around as the last token read.
3614       if (mToken.mType == eCSSToken_Symbol && aInAtRule &&
3615           (mToken.mSymbol == '{' || mToken.mSymbol == ';'  || mToken.mSymbol == '}')) {
3616         UngetToken();
3617         hitStop = true;
3618       }
3619     }
3620     if (query) {
3621       aMedia->AppendQuery(query);
3622     }
3623     if (hitStop) {
3624       break;
3625     }
3626   }
3627   return true;
3628 }
3629 
3630 bool
3631 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
3632 {
3633   if (!ExpectSymbol('(', true)) {
3634     REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
3635     return false;
3636   }
3637   if (! GetToken(true)) {
3638     REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
3639     return false;
3640   }
3641   if (eCSSToken_Ident != mToken.mType) {
3642     REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
3643     UngetToken();
3644     SkipUntil(')');
3645     return false;
3646   }
3647 
3648   nsMediaExpression *expr = aQuery->NewExpression();
3649 
3650   // case insensitive from CSS - must be lower cased
3651   nsContentUtils::ASCIIToLower(mToken.mIdent);
3652   nsDependentString featureString(mToken.mIdent, 0);
3653   uint8_t satisfiedReqFlags = 0;
3654 
3655   // Strip off "-webkit-" prefix from featureString:
3656   if (sWebkitPrefixedAliasesEnabled &&
3657       StringBeginsWith(featureString, NS_LITERAL_STRING("-webkit-"))) {
3658     satisfiedReqFlags |= nsMediaFeature::eHasWebkitPrefix;
3659     featureString.Rebind(featureString, 8);
3660   }
3661   if (sWebkitDevicePixelRatioEnabled) {
3662     satisfiedReqFlags |= nsMediaFeature::eWebkitDevicePixelRatioPrefEnabled;
3663   }
3664 
3665   // Strip off "min-"/"max-" prefix from featureString:
3666   if (StringBeginsWith(featureString, NS_LITERAL_STRING("min-"))) {
3667     expr->mRange = nsMediaExpression::eMin;
3668     featureString.Rebind(featureString, 4);
3669   } else if (StringBeginsWith(featureString, NS_LITERAL_STRING("max-"))) {
3670     expr->mRange = nsMediaExpression::eMax;
3671     featureString.Rebind(featureString, 4);
3672   } else {
3673     expr->mRange = nsMediaExpression::eEqual;
3674   }
3675 
3676   nsCOMPtr<nsIAtom> mediaFeatureAtom = NS_Atomize(featureString);
3677   const nsMediaFeature *feature = nsMediaFeatures::features;
3678   for (; feature->mName; ++feature) {
3679     // See if name matches & all requirement flags are satisfied:
3680     // (We check requirements by turning off all of the flags that have been
3681     // satisfied, and then see if the result is 0.)
3682     if (*(feature->mName) == mediaFeatureAtom &&
3683         !(feature->mReqFlags & ~satisfiedReqFlags)) {
3684       break;
3685     }
3686   }
3687   if (!feature->mName ||
3688       (expr->mRange != nsMediaExpression::eEqual &&
3689        feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
3690     REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
3691     SkipUntil(')');
3692     return false;
3693   }
3694   expr->mFeature = feature;
3695 
3696   if (!GetToken(true) || mToken.IsSymbol(')')) {
3697     // Query expressions for any feature can be given without a value.
3698     // However, min/max prefixes are not allowed.
3699     if (expr->mRange != nsMediaExpression::eEqual) {
3700       REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
3701       return false;
3702     }
3703     expr->mValue.Reset();
3704     return true;
3705   }
3706 
3707   if (!mToken.IsSymbol(':')) {
3708     REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
3709     UngetToken();
3710     SkipUntil(')');
3711     return false;
3712   }
3713 
3714   bool rv = false;
3715   switch (feature->mValueType) {
3716     case nsMediaFeature::eLength:
3717       rv = ParseSingleTokenNonNegativeVariant(expr->mValue, VARIANT_LENGTH,
3718                                               nullptr);
3719       break;
3720     case nsMediaFeature::eInteger:
3721     case nsMediaFeature::eBoolInteger:
3722       rv = ParseNonNegativeInteger(expr->mValue);
3723       // Enforce extra restrictions for eBoolInteger
3724       if (rv &&
3725           feature->mValueType == nsMediaFeature::eBoolInteger &&
3726           expr->mValue.GetIntValue() > 1)
3727         rv = false;
3728       break;
3729     case nsMediaFeature::eFloat:
3730       rv = ParseNonNegativeNumber(expr->mValue);
3731       break;
3732     case nsMediaFeature::eIntRatio:
3733       {
3734         // Two integers separated by '/', with optional whitespace on
3735         // either side of the '/'.
3736         RefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
3737         expr->mValue.SetArrayValue(a, eCSSUnit_Array);
3738         // We don't bother with ParseNonNegativeVariant since we have to
3739         // check for != 0 as well; no need to worry about the UngetToken
3740         // since we're throwing out up to the next ')' anyway.
3741         rv = ParseSingleTokenVariant(a->Item(0), VARIANT_INTEGER, nullptr) &&
3742              a->Item(0).GetIntValue() > 0 &&
3743              ExpectSymbol('/', true) &&
3744              ParseSingleTokenVariant(a->Item(1), VARIANT_INTEGER, nullptr) &&
3745              a->Item(1).GetIntValue() > 0;
3746       }
3747       break;
3748     case nsMediaFeature::eResolution:
3749       rv = GetToken(true);
3750       if (!rv)
3751         break;
3752       rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f;
3753       if (!rv) {
3754         UngetToken();
3755         break;
3756       }
3757       // No worries about whether unitless zero is allowed, since the
3758       // value must be positive (and we checked that above).
3759       NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
3760       if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
3761         expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
3762       } else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) {
3763         expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
3764       } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
3765         expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
3766       } else {
3767         rv = false;
3768       }
3769       break;
3770     case nsMediaFeature::eEnumerated:
3771       rv = ParseSingleTokenVariant(expr->mValue, VARIANT_KEYWORD,
3772                                    feature->mData.mKeywordTable);
3773       break;
3774     case nsMediaFeature::eIdent:
3775       rv = ParseSingleTokenVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr);
3776       break;
3777   }
3778   if (!rv || !ExpectSymbol(')', true)) {
3779     REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
3780     SkipUntil(')');
3781     return false;
3782   }
3783 
3784   return true;
3785 }
3786 
3787 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
3788 bool
3789 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
3790 {
3791   RefPtr<nsMediaList> media = new nsMediaList();
3792 
3793   uint32_t linenum, colnum;
3794   nsAutoString url;
3795   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3796       !ParseURLOrString(url)) {
3797     REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
3798     return false;
3799   }
3800 
3801   if (!ExpectSymbol(';', true)) {
3802     if (!GatherMedia(media, true) ||
3803         !ExpectSymbol(';', true)) {
3804       REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
3805       // don't advance section, simply ignore invalid @import
3806       return false;
3807     }
3808 
3809     // Safe to assert this, since we ensured that there is something
3810     // other than the ';' coming after the @import's url() token.
3811     NS_ASSERTION(media->Length() != 0, "media list must be nonempty");
3812   }
3813 
3814   ProcessImport(url, media, aAppendFunc, aData, linenum, colnum);
3815   return true;
3816 }
3817 
3818 void
3819 CSSParserImpl::ProcessImport(const nsString& aURLSpec,
3820                              nsMediaList* aMedia,
3821                              RuleAppendFunc aAppendFunc,
3822                              void* aData,
3823                              uint32_t aLineNumber,
3824                              uint32_t aColumnNumber)
3825 {
3826   RefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec,
3827                                                        aLineNumber,
3828                                                        aColumnNumber);
3829   (*aAppendFunc)(rule, aData);
3830 
3831   // Diagnose bad URIs even if we don't have a child loader.
3832   nsCOMPtr<nsIURI> url;
3833   // Charset will be deduced from mBaseURI, which is more or less correct.
3834   nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI);
3835 
3836   if (NS_FAILED(rv)) {
3837     if (rv == NS_ERROR_MALFORMED_URI) {
3838       // import url is bad
3839       REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
3840       OUTPUT_ERROR();
3841     }
3842     return;
3843   }
3844 
3845   if (mChildLoader) {
3846     mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule, mReusableSheets);
3847   }
3848 }
3849 
3850 // Parse the {} part of an @media or @-moz-document rule.
3851 bool
3852 CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
3853                               RuleAppendFunc aAppendFunc,
3854                               void* aData)
3855 {
3856   // XXXbz this could use better error reporting throughout the method
3857   if (!ExpectSymbol('{', true)) {
3858     return false;
3859   }
3860 
3861   // push rule on stack, loop over children
3862   PushGroup(aRule);
3863   nsCSSSection holdSection = mSection;
3864   mSection = eCSSSection_General;
3865 
3866   for (;;) {
3867     // Get next non-whitespace token
3868     if (! GetToken(true)) {
3869       REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2);
3870       break;
3871     }
3872     if (mToken.IsSymbol('}')) { // done!
3873       UngetToken();
3874       break;
3875     }
3876     if (eCSSToken_AtKeyword == mToken.mType) {
3877       // Parse for nested rules
3878       ParseAtRule(aAppendFunc, aData, true);
3879       continue;
3880     }
3881     UngetToken();
3882     ParseRuleSet(AppendRuleToSheet, this, true);
3883   }
3884   PopGroup();
3885 
3886   if (!ExpectSymbol('}', true)) {
3887     mSection = holdSection;
3888     return false;
3889   }
3890   (*aAppendFunc)(aRule, aData);
3891   return true;
3892 }
3893 
3894 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
3895 bool
3896 CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
3897 {
3898   RefPtr<nsMediaList> media = new nsMediaList();
3899   uint32_t linenum, colnum;
3900   if (GetNextTokenLocation(true, &linenum, &colnum) &&
3901       GatherMedia(media, true)) {
3902     // XXXbz this could use better error reporting throughout the method
3903     RefPtr<css::MediaRule> rule = new css::MediaRule(linenum, colnum);
3904     // Append first, so when we do SetMedia() the rule
3905     // knows what its stylesheet is.
3906     if (ParseGroupRule(rule, aAppendFunc, aData)) {
3907       rule->SetMedia(media);
3908       return true;
3909     }
3910   }
3911 
3912   return false;
3913 }
3914 
3915 // Parse a @-moz-document rule.  This is like an @media rule, but instead
3916 // of a medium it has a nonempty list of items where each item is either
3917 // url(), url-prefix(), or domain().
3918 bool
3919 CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
3920 {
3921   css::DocumentRule::URL *urls = nullptr;
3922   css::DocumentRule::URL **next = &urls;
3923 
3924   uint32_t linenum, colnum;
3925   if (!GetNextTokenLocation(true, &linenum, &colnum)) {
3926     return false;
3927   }
3928 
3929   do {
3930     if (!GetToken(true)) {
3931       REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF);
3932       delete urls;
3933       return false;
3934     }
3935 
3936     if (!(eCSSToken_URL == mToken.mType ||
3937           (eCSSToken_Function == mToken.mType &&
3938            (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
3939             mToken.mIdent.LowerCaseEqualsLiteral("domain") ||
3940             mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) {
3941       REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc2);
3942       UngetToken();
3943       delete urls;
3944       return false;
3945     }
3946     css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL;
3947     next = &cur->next;
3948     if (mToken.mType == eCSSToken_URL) {
3949       cur->func = css::DocumentRule::eURL;
3950       CopyUTF16toUTF8(mToken.mIdent, cur->url);
3951     } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) {
3952       // regexp() is different from url-prefix() and domain() (but
3953       // probably the way they *should* have been* in that it requires a
3954       // string argument, and doesn't try to behave like url().
3955       cur->func = css::DocumentRule::eRegExp;
3956       GetToken(true);
3957       // copy before we know it's valid (but before ExpectSymbol changes
3958       // mToken.mIdent)
3959       CopyUTF16toUTF8(mToken.mIdent, cur->url);
3960       if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) {
3961         REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString);
3962         SkipUntil(')');
3963         delete urls;
3964         return false;
3965       }
3966     } else {
3967       if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
3968         cur->func = css::DocumentRule::eURLPrefix;
3969       } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
3970         cur->func = css::DocumentRule::eDomain;
3971       }
3972 
3973       NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point");
3974       mScanner->NextURL(mToken);
3975       if (mToken.mType != eCSSToken_URL) {
3976         REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
3977         SkipUntil(')');
3978         delete urls;
3979         return false;
3980       }
3981 
3982       // We could try to make the URL (as long as it's not domain())
3983       // canonical and absolute with NS_NewURI and GetSpec, but I'm
3984       // inclined to think we shouldn't.
3985       CopyUTF16toUTF8(mToken.mIdent, cur->url);
3986     }
3987   } while (ExpectSymbol(',', true));
3988 
3989   RefPtr<css::DocumentRule> rule = new css::DocumentRule(linenum, colnum);
3990   rule->SetURLs(urls);
3991 
3992   return ParseGroupRule(rule, aAppendFunc, aData);
3993 }
3994 
3995 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
3996 bool
3997 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
3998 {
3999   uint32_t linenum, colnum;
4000   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4001       !GetToken(true)) {
4002     REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
4003     return false;
4004   }
4005 
4006   nsAutoString  prefix;
4007   nsAutoString  url;
4008 
4009   if (eCSSToken_Ident == mToken.mType) {
4010     prefix = mToken.mIdent;
4011     // user-specified identifiers are case-sensitive (bug 416106)
4012   } else {
4013     UngetToken();
4014   }
4015 
4016   if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) {
4017     if (mHavePushBack) {
4018       REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
4019     } else {
4020       REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
4021     }
4022     return false;
4023   }
4024 
4025   ProcessNameSpace(prefix, url, aAppendFunc, aData, linenum, colnum);
4026   return true;
4027 }
4028 
4029 void
4030 CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
4031                                 const nsString& aURLSpec,
4032                                 RuleAppendFunc aAppendFunc,
4033                                 void* aData,
4034                                 uint32_t aLineNumber,
4035                                 uint32_t aColumnNumber)
4036 {
4037   nsCOMPtr<nsIAtom> prefix;
4038 
4039   if (!aPrefix.IsEmpty()) {
4040     prefix = NS_Atomize(aPrefix);
4041   }
4042 
4043   RefPtr<css::NameSpaceRule> rule = new css::NameSpaceRule(prefix, aURLSpec,
4044                                                              aLineNumber,
4045                                                              aColumnNumber);
4046   (*aAppendFunc)(rule, aData);
4047 
4048   // If this was the first namespace rule encountered, it will trigger
4049   // creation of a namespace map.
4050   if (!mNameSpaceMap) {
4051     mNameSpaceMap = mSheet->GetNameSpaceMap();
4052   }
4053 }
4054 
4055 // font-face-rule: '@font-face' '{' font-description '}'
4056 // font-description: font-descriptor+
4057 bool
4058 CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
4059 {
4060   uint32_t linenum, colnum;
4061   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4062       !ExpectSymbol('{', true)) {
4063     REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart);
4064     return false;
4065   }
4066 
4067   RefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule(linenum, colnum));
4068 
4069   for (;;) {
4070     if (!GetToken(true)) {
4071       REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
4072       break;
4073     }
4074     if (mToken.IsSymbol('}')) { // done!
4075       UngetToken();
4076       break;
4077     }
4078 
4079     // ignore extra semicolons
4080     if (mToken.IsSymbol(';'))
4081       continue;
4082 
4083     if (!ParseFontDescriptor(rule)) {
4084       REPORT_UNEXPECTED(PEDeclSkipped);
4085       OUTPUT_ERROR();
4086       if (!SkipDeclaration(true))
4087         break;
4088     }
4089   }
4090   if (!ExpectSymbol('}', true)) {
4091     REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd);
4092     return false;
4093   }
4094   (*aAppendFunc)(rule, aData);
4095   return true;
4096 }
4097 
4098 // font-descriptor: font-family-desc
4099 //                | font-style-desc
4100 //                | font-weight-desc
4101 //                | font-stretch-desc
4102 //                | font-src-desc
4103 //                | unicode-range-desc
4104 //
4105 // All font-*-desc productions follow the pattern
4106 //    IDENT ':' value ';'
4107 //
4108 // On entry to this function, mToken is the IDENT.
4109 
4110 bool
4111 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
4112 {
4113   if (eCSSToken_Ident != mToken.mType) {
4114     REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
4115     return false;
4116   }
4117 
4118   nsString descName = mToken.mIdent;
4119   if (!ExpectSymbol(':', true)) {
4120     REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
4121     OUTPUT_ERROR();
4122     return false;
4123   }
4124 
4125   nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
4126   nsCSSValue value;
4127 
4128   if (descID == eCSSFontDesc_UNKNOWN ||
4129       (descID == eCSSFontDesc_Display &&
4130        !Preferences::GetBool("layout.css.font-display.enabled"))) {
4131     if (NonMozillaVendorIdentifier(descName)) {
4132       // silently skip other vendors' extensions
4133       SkipDeclaration(true);
4134       return true;
4135     } else {
4136       REPORT_UNEXPECTED_P(PEUnknownFontDesc, descName);
4137       return false;
4138     }
4139   }
4140 
4141   if (!ParseFontDescriptorValue(descID, value)) {
4142     REPORT_UNEXPECTED_P(PEValueParsingError, descName);
4143     return false;
4144   }
4145 
4146   // Expect termination by ;, }, or EOF.
4147   if (GetToken(true)) {
4148     if (!mToken.IsSymbol(';') &&
4149         !mToken.IsSymbol('}')) {
4150       UngetToken();
4151       REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
4152       return false;
4153     }
4154     UngetToken();
4155   }
4156 
4157   aRule->SetDesc(descID, value);
4158   return true;
4159 }
4160 
4161 // @font-feature-values <font-family># {
4162 //   @<feature-type> {
4163 //     <feature-ident> : <feature-index>+;
4164 //     <feature-ident> : <feature-index>+;
4165 //     ...
4166 //   }
4167 //   ...
4168 // }
4169 
4170 bool
4171 CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
4172                                           void* aData)
4173 {
4174   uint32_t linenum, colnum;
4175   if (!GetNextTokenLocation(true, &linenum, &colnum)) {
4176     return false;
4177   }
4178 
4179   RefPtr<nsCSSFontFeatureValuesRule>
4180                valuesRule(new nsCSSFontFeatureValuesRule(linenum, colnum));
4181 
4182   // parse family list
4183   nsCSSValue fontlistValue;
4184 
4185   if (!ParseFamily(fontlistValue) ||
4186       fontlistValue.GetUnit() != eCSSUnit_FontFamilyList)
4187   {
4188     REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily);
4189     return false;
4190   }
4191 
4192   // add family to rule
4193   const FontFamilyList* fontlist = fontlistValue.GetFontFamilyListValue();
4194 
4195   // family list has generic ==> parse error
4196   if (fontlist->HasGeneric()) {
4197     REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList);
4198     return false;
4199   }
4200 
4201   valuesRule->SetFamilyList(*fontlist);
4202 
4203   // open brace
4204   if (!ExpectSymbol('{', true)) {
4205     REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart);
4206     return false;
4207   }
4208 
4209   // list of sets of feature values, each set bound to a specific
4210   // feature-type (e.g. swash, annotation)
4211   for (;;) {
4212     if (!GetToken(true)) {
4213       REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
4214       break;
4215     }
4216     if (mToken.IsSymbol('}')) { // done!
4217       UngetToken();
4218       break;
4219     }
4220 
4221     if (!ParseFontFeatureValueSet(valuesRule)) {
4222       if (!SkipAtRule(false)) {
4223         break;
4224       }
4225     }
4226   }
4227   if (!ExpectSymbol('}', true)) {
4228     REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd);
4229     SkipUntil('}');
4230     return false;
4231   }
4232 
4233   (*aAppendFunc)(valuesRule, aData);
4234   return true;
4235 }
4236 
4237 #define NUMVALUES_NO_LIMIT  0xFFFF
4238 
4239 // parse a single value set containing name-value pairs for a single feature type
4240 //   @<feature-type> { [ <feature-ident> : <feature-index>+ ; ]* }
4241 //   Ex: @swash { flowing: 1; delicate: 2; }
4242 bool
4243 CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule
4244                                                             *aFeatureValuesRule)
4245 {
4246   // -- @keyword (e.g. swash, styleset)
4247   if (eCSSToken_AtKeyword != mToken.mType) {
4248     REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt);
4249     OUTPUT_ERROR();
4250     UngetToken();
4251     return false;
4252   }
4253 
4254   // which font-specific variant of font-variant-alternates
4255   int32_t whichVariant;
4256   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
4257   if (keyword == eCSSKeyword_UNKNOWN ||
4258       !nsCSSProps::FindKeyword(keyword,
4259                                nsCSSProps::kFontVariantAlternatesFuncsKTable,
4260                                whichVariant))
4261   {
4262     if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
4263       REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue);
4264       OUTPUT_ERROR();
4265     }
4266     UngetToken();
4267     return false;
4268   }
4269 
4270   nsAutoString featureType(mToken.mIdent);
4271 
4272   // open brace
4273   if (!ExpectSymbol('{', true)) {
4274     REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart);
4275     return false;
4276   }
4277 
4278   // styleset and character-variant can be multi-valued, otherwise single value
4279   int32_t limitNumValues;
4280 
4281   switch (keyword) {
4282     case eCSSKeyword_styleset:
4283       limitNumValues = NUMVALUES_NO_LIMIT;
4284       break;
4285     case eCSSKeyword_character_variant:
4286       limitNumValues = 2;
4287       break;
4288     default:
4289       limitNumValues = 1;
4290       break;
4291   }
4292 
4293   // -- ident integer+ [, ident integer+]
4294   AutoTArray<gfxFontFeatureValueSet::ValueList, 5> values;
4295 
4296   // list of font-feature-values-declaration's
4297   for (;;) {
4298     nsAutoString valueId;
4299 
4300     if (!GetToken(true)) {
4301       REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
4302       break;
4303     }
4304 
4305     // ignore extra semicolons
4306     if (mToken.IsSymbol(';')) {
4307       continue;
4308     }
4309 
4310     // close brace ==> done
4311     if (mToken.IsSymbol('}')) {
4312       break;
4313     }
4314 
4315     // ident
4316     if (eCSSToken_Ident != mToken.mType) {
4317       REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent);
4318       if (!SkipDeclaration(true)) {
4319         break;
4320       }
4321       continue;
4322     }
4323 
4324     valueId.Assign(mToken.mIdent);
4325 
4326     // colon
4327     if (!ExpectSymbol(':', true)) {
4328       REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
4329       OUTPUT_ERROR();
4330       if (!SkipDeclaration(true)) {
4331         break;
4332       }
4333       continue;
4334     }
4335 
4336     // value list
4337     AutoTArray<uint32_t,4>   featureSelectors;
4338 
4339     nsCSSValue intValue;
4340     while (ParseNonNegativeInteger(intValue)) {
4341       featureSelectors.AppendElement(uint32_t(intValue.GetIntValue()));
4342     }
4343 
4344     int32_t numValues = featureSelectors.Length();
4345 
4346     if (numValues == 0) {
4347       REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue);
4348       OUTPUT_ERROR();
4349       if (!SkipDeclaration(true)) {
4350         break;
4351       }
4352       continue;
4353     }
4354 
4355     if (numValues > limitNumValues) {
4356       REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType);
4357       OUTPUT_ERROR();
4358       if (!SkipDeclaration(true)) {
4359         break;
4360       }
4361       continue;
4362     }
4363 
4364     if (!GetToken(true)) {
4365       REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
4366       gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
4367       values.AppendElement(v);
4368       break;
4369     }
4370 
4371     // ';' or '}' to end definition
4372     if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) {
4373       REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing);
4374       OUTPUT_ERROR();
4375       if (!SkipDeclaration(true)) {
4376         break;
4377       }
4378       continue;
4379     }
4380 
4381     gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
4382     values.AppendElement(v);
4383 
4384     if (mToken.IsSymbol('}')) {
4385       break;
4386     }
4387  }
4388 
4389   aFeatureValuesRule->AddValueList(whichVariant, values);
4390   return true;
4391 }
4392 
4393 bool
4394 CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData)
4395 {
4396   uint32_t linenum, colnum;
4397   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4398       !GetToken(true)) {
4399     REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF);
4400     return false;
4401   }
4402 
4403   if (mToken.mType != eCSSToken_Ident) {
4404     REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
4405     UngetToken();
4406     return false;
4407   }
4408   nsString name(mToken.mIdent);
4409 
4410   if (!ExpectSymbol('{', true)) {
4411     REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace);
4412     return false;
4413   }
4414 
4415   RefPtr<nsCSSKeyframesRule> rule = new nsCSSKeyframesRule(name,
4416                                                              linenum, colnum);
4417 
4418   while (!ExpectSymbol('}', true)) {
4419     RefPtr<nsCSSKeyframeRule> kid = ParseKeyframeRule();
4420     if (kid) {
4421       rule->AppendStyleRule(kid);
4422     } else {
4423       OUTPUT_ERROR();
4424       SkipRuleSet(true);
4425     }
4426   }
4427 
4428   (*aAppendFunc)(rule, aData);
4429   return true;
4430 }
4431 
4432 bool
4433 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
4434 {
4435   uint32_t linenum, colnum;
4436   if (!GetNextTokenLocation(true, &linenum, &colnum)) {
4437     return false;
4438   }
4439 
4440   // TODO: There can be page selectors after @page such as ":first", ":left".
4441   uint32_t parseFlags = eParseDeclaration_InBraces |
4442                         eParseDeclaration_AllowImportant;
4443 
4444   // Forbid viewport units in @page rules. See bug 811391.
4445   MOZ_ASSERT(mViewportUnitsEnabled,
4446              "Viewport units should be enabled outside of @page rules.");
4447   mViewportUnitsEnabled = false;
4448   RefPtr<css::Declaration> declaration =
4449     ParseDeclarationBlock(parseFlags, eCSSContext_Page);
4450   mViewportUnitsEnabled = true;
4451 
4452   if (!declaration) {
4453     return false;
4454   }
4455 
4456   RefPtr<nsCSSPageRule> rule =
4457     new nsCSSPageRule(declaration, linenum, colnum);
4458 
4459   (*aAppendFunc)(rule, aData);
4460   return true;
4461 }
4462 
4463 already_AddRefed<nsCSSKeyframeRule>
4464 CSSParserImpl::ParseKeyframeRule()
4465 {
4466   InfallibleTArray<float> selectorList;
4467   uint32_t linenum, colnum;
4468   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4469       !ParseKeyframeSelectorList(selectorList)) {
4470     REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
4471     return nullptr;
4472   }
4473 
4474   // Ignore !important in keyframe rules
4475   uint32_t parseFlags = eParseDeclaration_InBraces;
4476   RefPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags));
4477   if (!declaration) {
4478     return nullptr;
4479   }
4480 
4481   // Takes ownership of declaration
4482   RefPtr<nsCSSKeyframeRule> rule =
4483     new nsCSSKeyframeRule(Move(selectorList), declaration.forget(),
4484                           linenum, colnum);
4485   return rule.forget();
4486 }
4487 
4488 bool
4489 CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList)
4490 {
4491   for (;;) {
4492     if (!GetToken(true)) {
4493       // The first time through the loop, this means we got an empty
4494       // list.  Otherwise, it means we have a trailing comma.
4495       return false;
4496     }
4497     float value;
4498     switch (mToken.mType) {
4499       case eCSSToken_Percentage:
4500         value = mToken.mNumber;
4501         break;
4502       case eCSSToken_Ident:
4503         if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
4504           value = 0.0f;
4505           break;
4506         }
4507         if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
4508           value = 1.0f;
4509           break;
4510         }
4511         MOZ_FALLTHROUGH;
4512       default:
4513         UngetToken();
4514         // The first time through the loop, this means we got an empty
4515         // list.  Otherwise, it means we have a trailing comma.
4516         return false;
4517     }
4518     aSelectorList.AppendElement(value);
4519     if (!ExpectSymbol(',', true)) {
4520       return true;
4521     }
4522   }
4523 }
4524 
4525 // supports_rule
4526 //   : "@supports" supports_condition group_rule_body
4527 //   ;
4528 bool
4529 CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData)
4530 {
4531   bool conditionMet = false;
4532   nsString condition;
4533 
4534   mScanner->StartRecording();
4535 
4536   uint32_t linenum, colnum;
4537   if (!GetNextTokenLocation(true, &linenum, &colnum)) {
4538     return false;
4539   }
4540 
4541   bool parsed = ParseSupportsCondition(conditionMet);
4542 
4543   if (!parsed) {
4544     mScanner->StopRecording();
4545     return false;
4546   }
4547 
4548   if (!ExpectSymbol('{', true)) {
4549     REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart);
4550     mScanner->StopRecording();
4551     return false;
4552   }
4553 
4554   UngetToken();
4555   mScanner->StopRecording(condition);
4556 
4557   // Remove the "{" that would follow the condition.
4558   if (condition.Length() != 0) {
4559     condition.Truncate(condition.Length() - 1);
4560   }
4561 
4562   // Remove spaces from the start and end of the recorded supports condition.
4563   condition.Trim(" ", true, true, false);
4564 
4565   // Record whether we are in a failing @supports, so that property parse
4566   // errors don't get reported.
4567   nsAutoFailingSupportsRule failing(this, conditionMet);
4568 
4569   RefPtr<css::GroupRule> rule = new CSSSupportsRule(conditionMet, condition,
4570                                                       linenum, colnum);
4571   return ParseGroupRule(rule, aAppendFunc, aProcessData);
4572 }
4573 
4574 // supports_condition
4575 //   : supports_condition_in_parens supports_condition_terms
4576 //   | supports_condition_negation
4577 //   ;
4578 bool
4579 CSSParserImpl::ParseSupportsCondition(bool& aConditionMet)
4580 {
4581   nsAutoInSupportsCondition aisc(this);
4582 
4583   if (!GetToken(true)) {
4584     REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2);
4585     return false;
4586   }
4587 
4588   UngetToken();
4589 
4590   mScanner->ClearSeenBadToken();
4591 
4592   if (mToken.IsSymbol('(') ||
4593       mToken.mType == eCSSToken_Function ||
4594       mToken.mType == eCSSToken_URL ||
4595       mToken.mType == eCSSToken_Bad_URL) {
4596     bool result = ParseSupportsConditionInParens(aConditionMet) &&
4597                   ParseSupportsConditionTerms(aConditionMet) &&
4598                   !mScanner->SeenBadToken();
4599     return result;
4600   }
4601 
4602   if (mToken.mType == eCSSToken_Ident &&
4603       mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4604     bool result = ParseSupportsConditionNegation(aConditionMet) &&
4605                   !mScanner->SeenBadToken();
4606     return result;
4607   }
4608 
4609   REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart);
4610   return false;
4611 }
4612 
4613 // supports_condition_negation
4614 //   : 'not' S+ supports_condition_in_parens
4615 //   ;
4616 bool
4617 CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet)
4618 {
4619   if (!GetToken(true)) {
4620     REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF);
4621     return false;
4622   }
4623 
4624   if (mToken.mType != eCSSToken_Ident ||
4625       !mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4626     REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot);
4627     return false;
4628   }
4629 
4630   if (!RequireWhitespace()) {
4631     REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
4632     return false;
4633   }
4634 
4635   if (ParseSupportsConditionInParens(aConditionMet)) {
4636     aConditionMet = !aConditionMet;
4637     return true;
4638   }
4639 
4640   return false;
4641 }
4642 
4643 // supports_condition_in_parens
4644 //   : '(' S* supports_condition_in_parens_inside_parens ')' S*
4645 //   | supports_condition_pref
4646 //   | general_enclosed
4647 //   ;
4648 bool
4649 CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet)
4650 {
4651   if (!GetToken(true)) {
4652     REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF);
4653     return false;
4654   }
4655 
4656   if (mToken.mType == eCSSToken_URL) {
4657     aConditionMet = false;
4658     return true;
4659   }
4660 
4661   if (AgentRulesEnabled() &&
4662       mToken.mType == eCSSToken_Function &&
4663       mToken.mIdent.LowerCaseEqualsLiteral("-moz-bool-pref")) {
4664     return ParseSupportsMozBoolPrefName(aConditionMet);
4665   }
4666 
4667   if (mToken.mType == eCSSToken_Function ||
4668       mToken.mType == eCSSToken_Bad_URL) {
4669     if (!SkipUntil(')')) {
4670       REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
4671       return false;
4672     }
4673     aConditionMet = false;
4674     return true;
4675   }
4676 
4677   if (!mToken.IsSymbol('(')) {
4678     REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction);
4679     UngetToken();
4680     return false;
4681   }
4682 
4683   if (!ParseSupportsConditionInParensInsideParens(aConditionMet)) {
4684     if (!SkipUntil(')')) {
4685       REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
4686       return false;
4687     }
4688     aConditionMet = false;
4689     return true;
4690   }
4691 
4692   if (!(ExpectSymbol(')', true))) {
4693     SkipUntil(')');
4694     aConditionMet = false;
4695     return true;
4696   }
4697 
4698   return true;
4699 }
4700 
4701 // supports_condition_pref
4702 //   : '-moz-bool-pref(' bool_pref_name ')'
4703 //   ;
4704 bool
4705 CSSParserImpl::ParseSupportsMozBoolPrefName(bool& aConditionMet)
4706 {
4707   if (!GetToken(true)) {
4708     return false;
4709   }
4710 
4711   if (mToken.mType != eCSSToken_String) {
4712     SkipUntil(')');
4713     return false;
4714   }
4715 
4716   aConditionMet = Preferences::GetBool(
4717     NS_ConvertUTF16toUTF8(mToken.mIdent).get());
4718 
4719   if (!ExpectSymbol(')', true)) {
4720     SkipUntil(')');
4721     return false;
4722   }
4723 
4724   return true;
4725 }
4726 
4727 // supports_condition_in_parens_inside_parens
4728 //   : core_declaration
4729 //   | supports_condition_negation
4730 //   | supports_condition_in_parens supports_condition_terms
4731 //   ;
4732 bool
4733 CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet)
4734 {
4735   if (!GetToken(true)) {
4736     return false;
4737   }
4738 
4739   if (mToken.mType == eCSSToken_Ident) {
4740     if (!mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4741       nsAutoString propertyName = mToken.mIdent;
4742       if (!ExpectSymbol(':', true)) {
4743         return false;
4744       }
4745 
4746       nsCSSPropertyID propID = LookupEnabledProperty(propertyName);
4747       if (propID == eCSSProperty_UNKNOWN) {
4748         if (ExpectSymbol(')', true)) {
4749           UngetToken();
4750           return false;
4751         }
4752         aConditionMet = false;
4753         SkipUntil(')');
4754         UngetToken();
4755       } else if (propID == eCSSPropertyExtra_variable) {
4756         if (ExpectSymbol(')', false)) {
4757           UngetToken();
4758           return false;
4759         }
4760         CSSVariableDeclarations::Type variableType;
4761         nsString variableValue;
4762         aConditionMet =
4763           ParseVariableDeclaration(&variableType, variableValue) &&
4764           ParsePriority() != ePriority_Error;
4765         if (!aConditionMet) {
4766           SkipUntil(')');
4767           UngetToken();
4768         }
4769       } else {
4770         if (ExpectSymbol(')', true)) {
4771           UngetToken();
4772           return false;
4773         }
4774         aConditionMet = ParseProperty(propID) &&
4775                         ParsePriority() != ePriority_Error;
4776         if (!aConditionMet) {
4777           SkipUntil(')');
4778           UngetToken();
4779         }
4780         mTempData.ClearProperty(propID);
4781         mTempData.AssertInitialState();
4782       }
4783       return true;
4784     }
4785 
4786     UngetToken();
4787     return ParseSupportsConditionNegation(aConditionMet);
4788   }
4789 
4790   UngetToken();
4791   return ParseSupportsConditionInParens(aConditionMet) &&
4792          ParseSupportsConditionTerms(aConditionMet);
4793 }
4794 
4795 // supports_condition_terms
4796 //   : S+ 'and' supports_condition_terms_after_operator('and')
4797 //   | S+ 'or' supports_condition_terms_after_operator('or')
4798 //   |
4799 //   ;
4800 bool
4801 CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet)
4802 {
4803   if (!RequireWhitespace() || !GetToken(false)) {
4804     return true;
4805   }
4806 
4807   if (mToken.mType != eCSSToken_Ident) {
4808     UngetToken();
4809     return true;
4810   }
4811 
4812   if (mToken.mIdent.LowerCaseEqualsLiteral("and")) {
4813     return ParseSupportsConditionTermsAfterOperator(aConditionMet, eAnd);
4814   }
4815 
4816   if (mToken.mIdent.LowerCaseEqualsLiteral("or")) {
4817     return ParseSupportsConditionTermsAfterOperator(aConditionMet, eOr);
4818   }
4819 
4820   UngetToken();
4821   return true;
4822 }
4823 
4824 // supports_condition_terms_after_operator(operator)
4825 //   : S+ supports_condition_in_parens ( <operator> supports_condition_in_parens )*
4826 //   ;
4827 bool
4828 CSSParserImpl::ParseSupportsConditionTermsAfterOperator(
4829                          bool& aConditionMet,
4830                          CSSParserImpl::SupportsConditionTermOperator aOperator)
4831 {
4832   if (!RequireWhitespace()) {
4833     REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
4834     return false;
4835   }
4836 
4837   const char* token = aOperator == eAnd ? "and" : "or";
4838   for (;;) {
4839     bool termConditionMet = false;
4840     if (!ParseSupportsConditionInParens(termConditionMet)) {
4841       return false;
4842     }
4843     aConditionMet = aOperator == eAnd ? aConditionMet && termConditionMet :
4844                                         aConditionMet || termConditionMet;
4845 
4846     if (!GetToken(true)) {
4847       return true;
4848     }
4849 
4850     if (mToken.mType != eCSSToken_Ident ||
4851         !mToken.mIdent.LowerCaseEqualsASCII(token)) {
4852       UngetToken();
4853       return true;
4854     }
4855   }
4856 }
4857 
4858 bool
4859 CSSParserImpl::ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aData)
4860 {
4861   nsAutoString name;
4862   uint32_t linenum, colnum;
4863   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4864       !ParseCounterStyleName(name, true)) {
4865     REPORT_UNEXPECTED_TOKEN(PECounterStyleNotIdent);
4866     return false;
4867   }
4868 
4869   if (!ExpectSymbol('{', true)) {
4870     REPORT_UNEXPECTED_TOKEN(PECounterStyleBadBlockStart);
4871     return false;
4872   }
4873 
4874   RefPtr<nsCSSCounterStyleRule> rule = new nsCSSCounterStyleRule(name,
4875                                                                    linenum,
4876                                                                    colnum);
4877   for (;;) {
4878     if (!GetToken(true)) {
4879       REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
4880       break;
4881     }
4882     if (mToken.IsSymbol('}')) {
4883       break;
4884     }
4885     if (mToken.IsSymbol(';')) {
4886       continue;
4887     }
4888 
4889     if (!ParseCounterDescriptor(rule)) {
4890       REPORT_UNEXPECTED(PEDeclSkipped);
4891       OUTPUT_ERROR();
4892       if (!SkipDeclaration(true)) {
4893         REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
4894         break;
4895       }
4896     }
4897   }
4898 
4899   int32_t system = rule->GetSystem();
4900   bool isCorrect = false;
4901   switch (system) {
4902     case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
4903     case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
4904     case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
4905     case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
4906     case NS_STYLE_COUNTER_SYSTEM_FIXED: {
4907       // check whether symbols is set and the length is sufficient
4908       const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
4909       if (symbols.GetUnit() == eCSSUnit_List &&
4910           nsCSSCounterStyleRule::CheckDescValue(
4911               system, eCSSCounterDesc_Symbols, symbols)) {
4912         isCorrect = true;
4913       }
4914       break;
4915     }
4916     case NS_STYLE_COUNTER_SYSTEM_ADDITIVE: {
4917       // for additive system, additive-symbols must be set
4918       const nsCSSValue& symbols =
4919         rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
4920       if (symbols.GetUnit() == eCSSUnit_PairList) {
4921         isCorrect = true;
4922       }
4923       break;
4924     }
4925     case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
4926       // for extends system, symbols & additive-symbols must not be set
4927       const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
4928       const nsCSSValue& additiveSymbols =
4929         rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
4930       if (symbols.GetUnit() == eCSSUnit_Null &&
4931           additiveSymbols.GetUnit() == eCSSUnit_Null) {
4932         isCorrect = true;
4933       }
4934       break;
4935     }
4936     default:
4937       NS_NOTREACHED("unknown system");
4938   }
4939 
4940   if (isCorrect) {
4941     (*aAppendFunc)(rule, aData);
4942   }
4943   return true;
4944 }
4945 
4946 bool
4947 CSSParserImpl::ParseCounterStyleName(nsAString& aName, bool aForDefinition)
4948 {
4949   if (!GetToken(true)) {
4950     return false;
4951   }
4952 
4953   if (mToken.mType != eCSSToken_Ident) {
4954     UngetToken();
4955     return false;
4956   }
4957 
4958   static const nsCSSKeyword kReservedNames[] = {
4959     eCSSKeyword_none,
4960     eCSSKeyword_decimal,
4961     eCSSKeyword_UNKNOWN
4962   };
4963 
4964   nsCSSValue value; // we don't actually care about the value
4965   if (!ParseCustomIdent(value, mToken.mIdent,
4966                         aForDefinition ? kReservedNames : nullptr)) {
4967     REPORT_UNEXPECTED_TOKEN(PECounterStyleBadName);
4968     UngetToken();
4969     return false;
4970   }
4971 
4972   aName = mToken.mIdent;
4973   if (nsCSSProps::IsPredefinedCounterStyle(aName)) {
4974     ToLowerCase(aName);
4975   }
4976   return true;
4977 }
4978 
4979 bool
4980 CSSParserImpl::ParseCounterStyleNameValue(nsCSSValue& aValue)
4981 {
4982   nsString name;
4983   if (ParseCounterStyleName(name, false)) {
4984     aValue.SetStringValue(name, eCSSUnit_Ident);
4985     return true;
4986   }
4987   return false;
4988 }
4989 
4990 bool
4991 CSSParserImpl::ParseCounterDescriptor(nsCSSCounterStyleRule* aRule)
4992 {
4993   if (eCSSToken_Ident != mToken.mType) {
4994     REPORT_UNEXPECTED_TOKEN(PECounterDescExpected);
4995     return false;
4996   }
4997 
4998   nsString descName = mToken.mIdent;
4999   if (!ExpectSymbol(':', true)) {
5000     REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
5001     OUTPUT_ERROR();
5002     return false;
5003   }
5004 
5005   nsCSSCounterDesc descID = nsCSSProps::LookupCounterDesc(descName);
5006   nsCSSValue value;
5007 
5008   if (descID == eCSSCounterDesc_UNKNOWN) {
5009     REPORT_UNEXPECTED_P(PEUnknownCounterDesc, descName);
5010     return false;
5011   }
5012 
5013   if (!ParseCounterDescriptorValue(descID, value)) {
5014     REPORT_UNEXPECTED_P(PEValueParsingError, descName);
5015     return false;
5016   }
5017 
5018   if (!ExpectEndProperty()) {
5019     return false;
5020   }
5021 
5022   aRule->SetDesc(descID, value);
5023   return true;
5024 }
5025 
5026 bool
5027 CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
5028                                            nsCSSValue& aValue)
5029 {
5030   // Should also include VARIANT_IMAGE, but it is not supported currently.
5031   // See bug 1024179.
5032   static const int32_t VARIANT_COUNTER_SYMBOL =
5033     VARIANT_STRING | VARIANT_IDENTIFIER;
5034 
5035   switch (aDescID) {
5036     case eCSSCounterDesc_System: {
5037       nsCSSValue system;
5038       if (!ParseEnum(system, nsCSSProps::kCounterSystemKTable)) {
5039         return false;
5040       }
5041       switch (system.GetIntValue()) {
5042         case NS_STYLE_COUNTER_SYSTEM_FIXED: {
5043           nsCSSValue start;
5044           if (!ParseSingleTokenVariant(start, VARIANT_INTEGER, nullptr)) {
5045             start.SetIntValue(1, eCSSUnit_Integer);
5046           }
5047           aValue.SetPairValue(system, start);
5048           return true;
5049         }
5050         case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
5051           nsCSSValue name;
5052           if (!ParseCounterStyleNameValue(name)) {
5053             REPORT_UNEXPECTED_TOKEN(PECounterExtendsNotIdent);
5054             return false;
5055           }
5056           aValue.SetPairValue(system, name);
5057           return true;
5058         }
5059         default:
5060           aValue = system;
5061           return true;
5062       }
5063     }
5064 
5065     case eCSSCounterDesc_Negative: {
5066       nsCSSValue first, second;
5067       if (!ParseSingleTokenVariant(first, VARIANT_COUNTER_SYMBOL, nullptr)) {
5068         return false;
5069       }
5070       if (!ParseSingleTokenVariant(second, VARIANT_COUNTER_SYMBOL, nullptr)) {
5071         aValue = first;
5072       } else {
5073         aValue.SetPairValue(first, second);
5074       }
5075       return true;
5076     }
5077 
5078     case eCSSCounterDesc_Prefix:
5079     case eCSSCounterDesc_Suffix:
5080       return ParseSingleTokenVariant(aValue, VARIANT_COUNTER_SYMBOL, nullptr);
5081 
5082     case eCSSCounterDesc_Range: {
5083       if (ParseSingleTokenVariant(aValue, VARIANT_AUTO, nullptr)) {
5084         return true;
5085       }
5086       nsCSSValuePairList* item = aValue.SetPairListValue();
5087       for (;;) {
5088         nsCSSValuePair pair;
5089         if (!ParseCounterRange(pair)) {
5090           return false;
5091         }
5092         item->mXValue = pair.mXValue;
5093         item->mYValue = pair.mYValue;
5094         if (!ExpectSymbol(',', true)) {
5095           return true;
5096         }
5097         item->mNext = new nsCSSValuePairList;
5098         item = item->mNext;
5099       }
5100       // should always return in the loop
5101     }
5102 
5103     case eCSSCounterDesc_Pad: {
5104       nsCSSValue width, symbol;
5105       bool hasWidth = ParseNonNegativeInteger(width);
5106       if (!ParseSingleTokenVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
5107           (!hasWidth && !ParseNonNegativeInteger(width))) {
5108         return false;
5109       }
5110       aValue.SetPairValue(width, symbol);
5111       return true;
5112     }
5113 
5114     case eCSSCounterDesc_Fallback:
5115       return ParseCounterStyleNameValue(aValue);
5116 
5117     case eCSSCounterDesc_Symbols: {
5118       nsCSSValueList* item = nullptr;
5119       for (;;) {
5120         nsCSSValue value;
5121         if (!ParseSingleTokenVariant(value, VARIANT_COUNTER_SYMBOL, nullptr)) {
5122           return !!item;
5123         }
5124         if (!item) {
5125           item = aValue.SetListValue();
5126         } else {
5127           item->mNext = new nsCSSValueList;
5128           item = item->mNext;
5129         }
5130         item->mValue = value;
5131       }
5132       // should always return in the loop
5133     }
5134 
5135     case eCSSCounterDesc_AdditiveSymbols: {
5136       nsCSSValuePairList* item = nullptr;
5137       int32_t lastWeight = -1;
5138       for (;;) {
5139         nsCSSValue weight, symbol;
5140         bool hasWeight = ParseNonNegativeInteger(weight);
5141         if (!ParseSingleTokenVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
5142             (!hasWeight && !ParseNonNegativeInteger(weight))) {
5143           return false;
5144         }
5145         if (lastWeight != -1 && weight.GetIntValue() >= lastWeight) {
5146           REPORT_UNEXPECTED(PECounterASWeight);
5147           return false;
5148         }
5149         lastWeight = weight.GetIntValue();
5150         if (!item) {
5151           item = aValue.SetPairListValue();
5152         } else {
5153           item->mNext = new nsCSSValuePairList;
5154           item = item->mNext;
5155         }
5156         item->mXValue = weight;
5157         item->mYValue = symbol;
5158         if (!ExpectSymbol(',', true)) {
5159           return true;
5160         }
5161       }
5162       // should always return in the loop
5163     }
5164 
5165     case eCSSCounterDesc_SpeakAs:
5166       if (ParseSingleTokenVariant(aValue, VARIANT_AUTO | VARIANT_KEYWORD,
5167                                   nsCSSProps::kCounterSpeakAsKTable)) {
5168         if (aValue.GetUnit() == eCSSUnit_Enumerated &&
5169             aValue.GetIntValue() == NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT) {
5170           // Currently spell-out is not supported, so it is explicitly
5171           // rejected here rather than parsed as a custom identifier.
5172           // See bug 1024178.
5173           return false;
5174         }
5175         return true;
5176       }
5177       return ParseCounterStyleNameValue(aValue);
5178 
5179     default:
5180       NS_NOTREACHED("unknown descriptor");
5181       return false;
5182   }
5183 }
5184 
5185 bool
5186 CSSParserImpl::ParseCounterRange(nsCSSValuePair& aPair)
5187 {
5188   static const int32_t VARIANT_BOUND = VARIANT_INTEGER | VARIANT_KEYWORD;
5189   nsCSSValue lower, upper;
5190   if (!ParseSingleTokenVariant(lower, VARIANT_BOUND,
5191                                nsCSSProps::kCounterRangeKTable) ||
5192       !ParseSingleTokenVariant(upper, VARIANT_BOUND,
5193                                nsCSSProps::kCounterRangeKTable)) {
5194     return false;
5195   }
5196   if (lower.GetUnit() != eCSSUnit_Enumerated &&
5197       upper.GetUnit() != eCSSUnit_Enumerated &&
5198       lower.GetIntValue() > upper.GetIntValue()) {
5199     return false;
5200   }
5201   aPair = nsCSSValuePair(lower, upper);
5202   return true;
5203 }
5204 
5205 bool
5206 CSSParserImpl::SkipUntil(char16_t aStopSymbol)
5207 {
5208   nsCSSToken* tk = &mToken;
5209   AutoTArray<char16_t, 16> stack;
5210   stack.AppendElement(aStopSymbol);
5211   for (;;) {
5212     if (!GetToken(true)) {
5213       return false;
5214     }
5215     if (eCSSToken_Symbol == tk->mType) {
5216       char16_t symbol = tk->mSymbol;
5217       uint32_t stackTopIndex = stack.Length() - 1;
5218       if (symbol == stack.ElementAt(stackTopIndex)) {
5219         stack.RemoveElementAt(stackTopIndex);
5220         if (stackTopIndex == 0) {
5221           return true;
5222         }
5223 
5224       // Just handle out-of-memory by parsing incorrectly.  It's
5225       // highly unlikely we're dealing with a legitimate style sheet
5226       // anyway.
5227       } else if ('{' == symbol) {
5228         stack.AppendElement('}');
5229       } else if ('[' == symbol) {
5230         stack.AppendElement(']');
5231       } else if ('(' == symbol) {
5232         stack.AppendElement(')');
5233       }
5234     } else if (eCSSToken_Function == tk->mType ||
5235                eCSSToken_Bad_URL == tk->mType) {
5236       stack.AppendElement(')');
5237     }
5238   }
5239 }
5240 
5241 bool
5242 CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol)
5243 {
5244   nsCSSToken* tk = &mToken;
5245   AutoTArray<char16_t, 16> stack;
5246   stack.AppendElement(aStopSymbol);
5247   for (;;) {
5248     if (!GetToken(true)) {
5249       return true;
5250     }
5251     if (eCSSToken_Symbol == tk->mType) {
5252       char16_t symbol = tk->mSymbol;
5253       uint32_t stackTopIndex = stack.Length() - 1;
5254       if (symbol == stack.ElementAt(stackTopIndex)) {
5255         stack.RemoveElementAt(stackTopIndex);
5256         if (stackTopIndex == 0) {
5257           return true;
5258         }
5259 
5260       // Just handle out-of-memory by parsing incorrectly.  It's
5261       // highly unlikely we're dealing with a legitimate style sheet
5262       // anyway.
5263       } else if ('{' == symbol) {
5264         stack.AppendElement('}');
5265       } else if ('[' == symbol) {
5266         stack.AppendElement(']');
5267       } else if ('(' == symbol) {
5268         stack.AppendElement(')');
5269       } else if (')' == symbol ||
5270                  ']' == symbol ||
5271                  '}' == symbol) {
5272         UngetToken();
5273         return false;
5274       }
5275     } else if (eCSSToken_Function == tk->mType ||
5276                eCSSToken_Bad_URL == tk->mType) {
5277       stack.AppendElement(')');
5278     }
5279   }
5280 }
5281 
5282 void
5283 CSSParserImpl::SkipUntilOneOf(const char16_t* aStopSymbolChars)
5284 {
5285   nsCSSToken* tk = &mToken;
5286   nsDependentString stopSymbolChars(aStopSymbolChars);
5287   for (;;) {
5288     if (!GetToken(true)) {
5289       break;
5290     }
5291     if (eCSSToken_Symbol == tk->mType) {
5292       char16_t symbol = tk->mSymbol;
5293       if (stopSymbolChars.FindChar(symbol) != -1) {
5294         break;
5295       } else if ('{' == symbol) {
5296         SkipUntil('}');
5297       } else if ('[' == symbol) {
5298         SkipUntil(']');
5299       } else if ('(' == symbol) {
5300         SkipUntil(')');
5301       }
5302     } else if (eCSSToken_Function == tk->mType ||
5303                eCSSToken_Bad_URL == tk->mType) {
5304       SkipUntil(')');
5305     }
5306   }
5307 }
5308 
5309 void
5310 CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars)
5311 {
5312   uint32_t i = aStopSymbolChars.Length();
5313   while (i--) {
5314     SkipUntil(aStopSymbolChars[i]);
5315   }
5316 }
5317 
5318 bool
5319 CSSParserImpl::SkipDeclaration(bool aCheckForBraces)
5320 {
5321   nsCSSToken* tk = &mToken;
5322   for (;;) {
5323     if (!GetToken(true)) {
5324       if (aCheckForBraces) {
5325         REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
5326       }
5327       return false;
5328     }
5329     if (eCSSToken_Symbol == tk->mType) {
5330       char16_t symbol = tk->mSymbol;
5331       if (';' == symbol) {
5332         break;
5333       }
5334       if (aCheckForBraces) {
5335         if ('}' == symbol) {
5336           UngetToken();
5337           break;
5338         }
5339       }
5340       if ('{' == symbol) {
5341         SkipUntil('}');
5342       } else if ('(' == symbol) {
5343         SkipUntil(')');
5344       } else if ('[' == symbol) {
5345         SkipUntil(']');
5346       }
5347     } else if (eCSSToken_Function == tk->mType ||
5348                eCSSToken_Bad_URL == tk->mType) {
5349       SkipUntil(')');
5350     }
5351   }
5352   return true;
5353 }
5354 
5355 void
5356 CSSParserImpl::SkipRuleSet(bool aInsideBraces)
5357 {
5358   nsCSSToken* tk = &mToken;
5359   for (;;) {
5360     if (!GetToken(true)) {
5361       REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
5362       break;
5363     }
5364     if (eCSSToken_Symbol == tk->mType) {
5365       char16_t symbol = tk->mSymbol;
5366       if ('}' == symbol && aInsideBraces) {
5367         // leave block closer for higher-level grammar to consume
5368         UngetToken();
5369         break;
5370       } else if ('{' == symbol) {
5371         SkipUntil('}');
5372         break;
5373       } else if ('(' == symbol) {
5374         SkipUntil(')');
5375       } else if ('[' == symbol) {
5376         SkipUntil(']');
5377       }
5378     } else if (eCSSToken_Function == tk->mType ||
5379                eCSSToken_Bad_URL == tk->mType) {
5380       SkipUntil(')');
5381     }
5382   }
5383 }
5384 
5385 void
5386 CSSParserImpl::PushGroup(css::GroupRule* aRule)
5387 {
5388   mGroupStack.AppendElement(aRule);
5389 }
5390 
5391 void
5392 CSSParserImpl::PopGroup()
5393 {
5394   uint32_t count = mGroupStack.Length();
5395   if (0 < count) {
5396     mGroupStack.RemoveElementAt(count - 1);
5397   }
5398 }
5399 
5400 void
5401 CSSParserImpl::AppendRule(css::Rule* aRule)
5402 {
5403   uint32_t count = mGroupStack.Length();
5404   if (0 < count) {
5405     mGroupStack[count - 1]->AppendStyleRule(aRule);
5406   }
5407   else {
5408     mSheet->AppendStyleRule(aRule);
5409   }
5410 }
5411 
5412 bool
5413 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
5414                             bool aInsideBraces)
5415 {
5416   // First get the list of selectors for the rule
5417   nsCSSSelectorList* slist = nullptr;
5418   uint32_t linenum, colnum;
5419   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
5420       !ParseSelectorList(slist, char16_t('{'))) {
5421     REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
5422     OUTPUT_ERROR();
5423     SkipRuleSet(aInsideBraces);
5424     return false;
5425   }
5426   NS_ASSERTION(nullptr != slist, "null selector list");
5427   CLEAR_ERROR();
5428 
5429   // Next parse the declaration block
5430   uint32_t parseFlags = eParseDeclaration_InBraces |
5431                         eParseDeclaration_AllowImportant;
5432   RefPtr<css::Declaration> declaration = ParseDeclarationBlock(parseFlags);
5433   if (nullptr == declaration) {
5434     delete slist;
5435     return false;
5436   }
5437 
5438 #if 0
5439   slist->Dump();
5440   fputs("{\n", stdout);
5441   declaration->List();
5442   fputs("}\n", stdout);
5443 #endif
5444 
5445   // Translate the selector list and declaration block into style data
5446 
5447   RefPtr<css::StyleRule> rule = new css::StyleRule(slist, declaration,
5448                                                      linenum, colnum);
5449   (*aAppendFunc)(rule, aData);
5450 
5451   return true;
5452 }
5453 
5454 bool
5455 CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
5456                                  char16_t aStopChar)
5457 {
5458   nsCSSSelectorList* list = nullptr;
5459   if (! ParseSelectorGroup(list)) {
5460     // must have at least one selector group
5461     aListHead = nullptr;
5462     return false;
5463   }
5464   NS_ASSERTION(nullptr != list, "no selector list");
5465   aListHead = list;
5466 
5467   // After that there must either be a "," or a "{" (the latter if
5468   // StopChar is nonzero)
5469   nsCSSToken* tk = &mToken;
5470   for (;;) {
5471     if (! GetToken(true)) {
5472       if (aStopChar == char16_t(0)) {
5473         return true;
5474       }
5475 
5476       REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
5477       break;
5478     }
5479 
5480     if (eCSSToken_Symbol == tk->mType) {
5481       if (',' == tk->mSymbol) {
5482         nsCSSSelectorList* newList = nullptr;
5483         // Another selector group must follow
5484         if (! ParseSelectorGroup(newList)) {
5485           break;
5486         }
5487         // add new list to the end of the selector list
5488         list->mNext = newList;
5489         list = newList;
5490         continue;
5491       } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) {
5492         UngetToken();
5493         return true;
5494       }
5495     }
5496     REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
5497     UngetToken();
5498     break;
5499   }
5500 
5501   delete aListHead;
5502   aListHead = nullptr;
5503   return false;
5504 }
5505 
5506 static bool IsUniversalSelector(const nsCSSSelector& aSelector)
5507 {
5508   return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
5509                 (aSelector.mLowercaseTag == nullptr) &&
5510                 (aSelector.mIDList == nullptr) &&
5511                 (aSelector.mClassList == nullptr) &&
5512                 (aSelector.mAttrList == nullptr) &&
5513                 (aSelector.mNegations == nullptr) &&
5514                 (aSelector.mPseudoClassList == nullptr));
5515 }
5516 
5517 bool
5518 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
5519 {
5520   char16_t combinator = 0;
5521   nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
5522 
5523   for (;;) {
5524     if (!ParseSelector(list, combinator)) {
5525       return false;
5526     }
5527 
5528     // Look for a combinator.
5529     if (!GetToken(false)) {
5530       break; // EOF ok here
5531     }
5532 
5533     combinator = char16_t(0);
5534     if (mToken.mType == eCSSToken_Whitespace) {
5535       if (!GetToken(true)) {
5536         break; // EOF ok here
5537       }
5538       combinator = char16_t(' ');
5539     }
5540 
5541     if (mToken.mType != eCSSToken_Symbol) {
5542       UngetToken(); // not a combinator
5543     } else {
5544       char16_t symbol = mToken.mSymbol;
5545       if (symbol == '+' || symbol == '>' || symbol == '~') {
5546         combinator = mToken.mSymbol;
5547       } else {
5548         UngetToken(); // not a combinator
5549         if (symbol == ',' || symbol == '{' || symbol == ')') {
5550           break; // end of selector group
5551         }
5552       }
5553     }
5554 
5555     if (!combinator) {
5556       REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
5557       return false;
5558     }
5559   }
5560 
5561   aList = list.forget();
5562   return true;
5563 }
5564 
5565 #define SEL_MASK_NSPACE   0x01
5566 #define SEL_MASK_ELEM     0x02
5567 #define SEL_MASK_ID       0x04
5568 #define SEL_MASK_CLASS    0x08
5569 #define SEL_MASK_ATTRIB   0x10
5570 #define SEL_MASK_PCLASS   0x20
5571 #define SEL_MASK_PELEM    0x40
5572 
5573 //
5574 // Parses an ID selector #name
5575 //
5576 CSSParserImpl::nsSelectorParsingStatus
5577 CSSParserImpl::ParseIDSelector(int32_t&       aDataMask,
5578                                nsCSSSelector& aSelector)
5579 {
5580   NS_ASSERTION(!mToken.mIdent.IsEmpty(),
5581                "Empty mIdent in eCSSToken_ID token?");
5582   aDataMask |= SEL_MASK_ID;
5583   aSelector.AddID(mToken.mIdent);
5584   return eSelectorParsingStatus_Continue;
5585 }
5586 
5587 //
5588 // Parses a class selector .name
5589 //
5590 CSSParserImpl::nsSelectorParsingStatus
5591 CSSParserImpl::ParseClassSelector(int32_t&       aDataMask,
5592                                   nsCSSSelector& aSelector)
5593 {
5594   if (! GetToken(false)) { // get ident
5595     REPORT_UNEXPECTED_EOF(PEClassSelEOF);
5596     return eSelectorParsingStatus_Error;
5597   }
5598   if (eCSSToken_Ident != mToken.mType) {  // malformed selector
5599     REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
5600     UngetToken();
5601     return eSelectorParsingStatus_Error;
5602   }
5603   aDataMask |= SEL_MASK_CLASS;
5604 
5605   aSelector.AddClass(mToken.mIdent);
5606 
5607   return eSelectorParsingStatus_Continue;
5608 }
5609 
5610 //
5611 // Parse a type element selector or a universal selector
5612 // namespace|type or namespace|* or *|* or *
5613 //
5614 CSSParserImpl::nsSelectorParsingStatus
5615 CSSParserImpl::ParseTypeOrUniversalSelector(int32_t&       aDataMask,
5616                                             nsCSSSelector& aSelector,
5617                                             bool           aIsNegated)
5618 {
5619   nsAutoString buffer;
5620   if (mToken.IsSymbol('*')) {  // universal element selector, or universal namespace
5621     if (ExpectSymbol('|', false)) {  // was namespace
5622       aDataMask |= SEL_MASK_NSPACE;
5623       aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
5624 
5625       if (! GetToken(false)) {
5626         REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5627         return eSelectorParsingStatus_Error;
5628       }
5629       if (eCSSToken_Ident == mToken.mType) {  // element name
5630         aDataMask |= SEL_MASK_ELEM;
5631 
5632         aSelector.SetTag(mToken.mIdent);
5633       }
5634       else if (mToken.IsSymbol('*')) {  // universal selector
5635         aDataMask |= SEL_MASK_ELEM;
5636         // don't set tag
5637       }
5638       else {
5639         REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5640         UngetToken();
5641         return eSelectorParsingStatus_Error;
5642       }
5643     }
5644     else {  // was universal element selector
5645       SetDefaultNamespaceOnSelector(aSelector);
5646       aDataMask |= SEL_MASK_ELEM;
5647       // don't set any tag in the selector
5648     }
5649     if (! GetToken(false)) {   // premature eof is ok (here!)
5650       return eSelectorParsingStatus_Done;
5651     }
5652   }
5653   else if (eCSSToken_Ident == mToken.mType) {    // element name or namespace name
5654     buffer = mToken.mIdent; // hang on to ident
5655 
5656     if (ExpectSymbol('|', false)) {  // was namespace
5657       aDataMask |= SEL_MASK_NSPACE;
5658       int32_t nameSpaceID = GetNamespaceIdForPrefix(buffer);
5659       if (nameSpaceID == kNameSpaceID_Unknown) {
5660         return eSelectorParsingStatus_Error;
5661       }
5662       aSelector.SetNameSpace(nameSpaceID);
5663 
5664       if (! GetToken(false)) {
5665         REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5666         return eSelectorParsingStatus_Error;
5667       }
5668       if (eCSSToken_Ident == mToken.mType) {  // element name
5669         aDataMask |= SEL_MASK_ELEM;
5670         aSelector.SetTag(mToken.mIdent);
5671       }
5672       else if (mToken.IsSymbol('*')) {  // universal selector
5673         aDataMask |= SEL_MASK_ELEM;
5674         // don't set tag
5675       }
5676       else {
5677         REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5678         UngetToken();
5679         return eSelectorParsingStatus_Error;
5680       }
5681     }
5682     else {  // was element name
5683       SetDefaultNamespaceOnSelector(aSelector);
5684       aSelector.SetTag(buffer);
5685 
5686       aDataMask |= SEL_MASK_ELEM;
5687     }
5688     if (! GetToken(false)) {   // premature eof is ok (here!)
5689       return eSelectorParsingStatus_Done;
5690     }
5691   }
5692   else if (mToken.IsSymbol('|')) {  // No namespace
5693     aDataMask |= SEL_MASK_NSPACE;
5694     aSelector.SetNameSpace(kNameSpaceID_None);  // explicit NO namespace
5695 
5696     // get mandatory tag
5697     if (! GetToken(false)) {
5698       REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5699       return eSelectorParsingStatus_Error;
5700     }
5701     if (eCSSToken_Ident == mToken.mType) {  // element name
5702       aDataMask |= SEL_MASK_ELEM;
5703       aSelector.SetTag(mToken.mIdent);
5704     }
5705     else if (mToken.IsSymbol('*')) {  // universal selector
5706       aDataMask |= SEL_MASK_ELEM;
5707       // don't set tag
5708     }
5709     else {
5710       REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5711       UngetToken();
5712       return eSelectorParsingStatus_Error;
5713     }
5714     if (! GetToken(false)) {   // premature eof is ok (here!)
5715       return eSelectorParsingStatus_Done;
5716     }
5717   }
5718   else {
5719     SetDefaultNamespaceOnSelector(aSelector);
5720   }
5721 
5722   if (aIsNegated) {
5723     // restore last token read in case of a negated type selector
5724     UngetToken();
5725   }
5726   return eSelectorParsingStatus_Continue;
5727 }
5728 
5729 //
5730 // Parse attribute selectors [attr], [attr=value], [attr|=value],
5731 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
5732 //
5733 CSSParserImpl::nsSelectorParsingStatus
5734 CSSParserImpl::ParseAttributeSelector(int32_t&       aDataMask,
5735                                       nsCSSSelector& aSelector)
5736 {
5737   if (! GetToken(true)) { // premature EOF
5738     REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5739     return eSelectorParsingStatus_Error;
5740   }
5741 
5742   int32_t nameSpaceID = kNameSpaceID_None;
5743   nsAutoString  attr;
5744   if (mToken.IsSymbol('*')) { // wildcard namespace
5745     nameSpaceID = kNameSpaceID_Unknown;
5746     if (ExpectSymbol('|', false)) {
5747       if (! GetToken(false)) { // premature EOF
5748         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5749         return eSelectorParsingStatus_Error;
5750       }
5751       if (eCSSToken_Ident == mToken.mType) { // attr name
5752         attr = mToken.mIdent;
5753       }
5754       else {
5755         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5756         UngetToken();
5757         return eSelectorParsingStatus_Error;
5758        }
5759     }
5760     else {
5761       REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
5762       return eSelectorParsingStatus_Error;
5763     }
5764   }
5765   else if (mToken.IsSymbol('|')) { // NO namespace
5766     if (! GetToken(false)) { // premature EOF
5767       REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5768       return eSelectorParsingStatus_Error;
5769     }
5770     if (eCSSToken_Ident == mToken.mType) { // attr name
5771       attr = mToken.mIdent;
5772     }
5773     else {
5774       REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5775       UngetToken();
5776       return eSelectorParsingStatus_Error;
5777     }
5778   }
5779   else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
5780     attr = mToken.mIdent; // hang on to it
5781     if (ExpectSymbol('|', false)) {  // was a namespace
5782       nameSpaceID = GetNamespaceIdForPrefix(attr);
5783       if (nameSpaceID == kNameSpaceID_Unknown) {
5784         return eSelectorParsingStatus_Error;
5785       }
5786       if (! GetToken(false)) { // premature EOF
5787         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5788         return eSelectorParsingStatus_Error;
5789       }
5790       if (eCSSToken_Ident == mToken.mType) { // attr name
5791         attr = mToken.mIdent;
5792       }
5793       else {
5794         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5795         UngetToken();
5796         return eSelectorParsingStatus_Error;
5797       }
5798     }
5799   }
5800   else {  // malformed
5801     REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
5802     UngetToken();
5803     return eSelectorParsingStatus_Error;
5804   }
5805 
5806   bool gotEOF = false;
5807   if (! GetToken(true)) { // premature EOF
5808     // Treat this just like we saw a ']', but do still output the
5809     // warning, similar to what ExpectSymbol does.
5810     REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
5811     gotEOF = true;
5812   }
5813   if (gotEOF ||
5814       (eCSSToken_Symbol == mToken.mType) ||
5815       (eCSSToken_Includes == mToken.mType) ||
5816       (eCSSToken_Dashmatch == mToken.mType) ||
5817       (eCSSToken_Beginsmatch == mToken.mType) ||
5818       (eCSSToken_Endsmatch == mToken.mType) ||
5819       (eCSSToken_Containsmatch == mToken.mType)) {
5820     uint8_t func;
5821     // Important: Check the EOF/']' case first, since if gotEOF we
5822     // don't want to be examining mToken.
5823     if (gotEOF || ']' == mToken.mSymbol) {
5824       aDataMask |= SEL_MASK_ATTRIB;
5825       aSelector.AddAttribute(nameSpaceID, attr);
5826       func = NS_ATTR_FUNC_SET;
5827     }
5828     else if (eCSSToken_Includes == mToken.mType) {
5829       func = NS_ATTR_FUNC_INCLUDES;
5830     }
5831     else if (eCSSToken_Dashmatch == mToken.mType) {
5832       func = NS_ATTR_FUNC_DASHMATCH;
5833     }
5834     else if (eCSSToken_Beginsmatch == mToken.mType) {
5835       func = NS_ATTR_FUNC_BEGINSMATCH;
5836     }
5837     else if (eCSSToken_Endsmatch == mToken.mType) {
5838       func = NS_ATTR_FUNC_ENDSMATCH;
5839     }
5840     else if (eCSSToken_Containsmatch == mToken.mType) {
5841       func = NS_ATTR_FUNC_CONTAINSMATCH;
5842     }
5843     else if ('=' == mToken.mSymbol) {
5844       func = NS_ATTR_FUNC_EQUALS;
5845     }
5846     else {
5847       REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
5848       UngetToken(); // bad function
5849       return eSelectorParsingStatus_Error;
5850     }
5851     if (NS_ATTR_FUNC_SET != func) { // get value
5852       if (! GetToken(true)) { // premature EOF
5853         REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
5854         return eSelectorParsingStatus_Error;
5855       }
5856       if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
5857         nsAutoString  value(mToken.mIdent);
5858         // Avoid duplicating the eof handling by just not checking for
5859         // the 'i' annotation if we got eof.
5860         typedef nsAttrSelector::ValueCaseSensitivity ValueCaseSensitivity;
5861         ValueCaseSensitivity valueCaseSensitivity =
5862           ValueCaseSensitivity::CaseSensitive;
5863         bool eof = !GetToken(true);
5864         if (!eof) {
5865           if (eCSSToken_Ident == mToken.mType &&
5866               mToken.mIdent.LowerCaseEqualsLiteral("i")) {
5867             valueCaseSensitivity = ValueCaseSensitivity::CaseInsensitive;
5868             eof = !GetToken(true);
5869           }
5870         }
5871         bool gotClosingBracket;
5872         if (eof) { // premature EOF
5873           // Report a warning, but then treat it as a closing bracket.
5874           REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
5875           gotClosingBracket = true;
5876         } else {
5877           gotClosingBracket = mToken.IsSymbol(']');
5878         }
5879         if (gotClosingBracket) {
5880           // For cases when this style sheet is applied to an HTML
5881           // element in an HTML document, and the attribute selector is
5882           // for a non-namespaced attribute, then check to see if it's
5883           // one of the known attributes whose VALUE is
5884           // case-insensitive.
5885           if (valueCaseSensitivity != ValueCaseSensitivity::CaseInsensitive &&
5886               nameSpaceID == kNameSpaceID_None) {
5887             static const char* caseInsensitiveHTMLAttribute[] = {
5888               // list based on http://www.w3.org/TR/html4/
5889               "lang",
5890               "dir",
5891               "http-equiv",
5892               "text",
5893               "link",
5894               "vlink",
5895               "alink",
5896               "compact",
5897               "align",
5898               "frame",
5899               "rules",
5900               "valign",
5901               "scope",
5902               "axis",
5903               "nowrap",
5904               "hreflang",
5905               "rel",
5906               "rev",
5907               "charset",
5908               "codetype",
5909               "declare",
5910               "valuetype",
5911               "shape",
5912               "nohref",
5913               "media",
5914               "bgcolor",
5915               "clear",
5916               "color",
5917               "face",
5918               "noshade",
5919               "noresize",
5920               "scrolling",
5921               "target",
5922               "method",
5923               "enctype",
5924               "accept-charset",
5925               "accept",
5926               "checked",
5927               "multiple",
5928               "selected",
5929               "disabled",
5930               "readonly",
5931               "language",
5932               "defer",
5933               "type",
5934               // additional attributes not in HTML4
5935               "direction", // marquee
5936               nullptr
5937             };
5938             short i = 0;
5939             const char* htmlAttr;
5940             while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
5941               if (attr.LowerCaseEqualsASCII(htmlAttr)) {
5942                 valueCaseSensitivity = ValueCaseSensitivity::CaseInsensitiveInHTML;
5943                 break;
5944               }
5945             }
5946           }
5947           aDataMask |= SEL_MASK_ATTRIB;
5948           aSelector.AddAttribute(nameSpaceID, attr, func, value,
5949                                  valueCaseSensitivity);
5950         }
5951         else {
5952           REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
5953           UngetToken();
5954           return eSelectorParsingStatus_Error;
5955         }
5956       }
5957       else {
5958         REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
5959         UngetToken();
5960         return eSelectorParsingStatus_Error;
5961       }
5962     }
5963   }
5964   else {
5965     REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
5966     UngetToken(); // bad dog, no biscut!
5967     return eSelectorParsingStatus_Error;
5968    }
5969    return eSelectorParsingStatus_Continue;
5970 }
5971 
5972 //
5973 // Parse pseudo-classes and pseudo-elements
5974 //
5975 CSSParserImpl::nsSelectorParsingStatus
5976 CSSParserImpl::ParsePseudoSelector(int32_t&       aDataMask,
5977                                    nsCSSSelector& aSelector,
5978                                    bool           aIsNegated,
5979                                    nsIAtom**      aPseudoElement,
5980                                    nsAtomList**   aPseudoElementArgs,
5981                                    CSSPseudoElementType* aPseudoElementType)
5982 {
5983   NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs),
5984                "expected location to store pseudo element");
5985   NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs),
5986                "negated selectors shouldn't have a place to store "
5987                "pseudo elements");
5988   if (! GetToken(false)) { // premature eof
5989     REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
5990     return eSelectorParsingStatus_Error;
5991   }
5992 
5993   // First, find out whether we are parsing a CSS3 pseudo-element
5994   bool parsingPseudoElement = false;
5995   if (mToken.IsSymbol(':')) {
5996     parsingPseudoElement = true;
5997     if (! GetToken(false)) { // premature eof
5998       REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
5999       return eSelectorParsingStatus_Error;
6000     }
6001   }
6002 
6003   // Do some sanity-checking on the token
6004   if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
6005     // malformed selector
6006     REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
6007     UngetToken();
6008     return eSelectorParsingStatus_Error;
6009   }
6010 
6011   // OK, now we know we have an mIdent.  Atomize it.  All the atoms, for
6012   // pseudo-classes as well as pseudo-elements, start with a single ':'.
6013   nsAutoString buffer;
6014   buffer.Append(char16_t(':'));
6015   buffer.Append(mToken.mIdent);
6016   nsContentUtils::ASCIIToLower(buffer);
6017   nsCOMPtr<nsIAtom> pseudo = NS_Atomize(buffer);
6018 
6019   // stash away some info about this pseudo so we only have to get it once.
6020   bool isTreePseudo = false;
6021   CSSEnabledState enabledState = EnabledState();
6022   CSSPseudoElementType pseudoElementType =
6023     nsCSSPseudoElements::GetPseudoType(pseudo, enabledState);
6024   CSSPseudoClassType pseudoClassType =
6025     nsCSSPseudoClasses::GetPseudoType(pseudo, enabledState);
6026   bool pseudoClassIsUserAction =
6027     nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
6028 
6029   if (nsCSSAnonBoxes::IsNonElement(pseudo)) {
6030     // Non-element anonymous boxes should not match any rule.
6031     REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
6032     UngetToken();
6033     return eSelectorParsingStatus_Error;
6034   }
6035 
6036   // We currently allow :-moz-placeholder and ::-moz-placeholder and
6037   // ::placeholder. We have to be a bit stricter regarding the
6038   // pseudo-element parsing rules.
6039   if (pseudoElementType == CSSPseudoElementType::placeholder &&
6040       pseudoClassType == CSSPseudoClassType::mozPlaceholder) {
6041     if (parsingPseudoElement) {
6042       pseudoClassType = CSSPseudoClassType::NotPseudo;
6043     } else {
6044       pseudoElementType = CSSPseudoElementType::NotPseudo;
6045     }
6046   }
6047 
6048 #ifdef MOZ_XUL
6049   isTreePseudo = (pseudoElementType == CSSPseudoElementType::XULTree);
6050   // If a tree pseudo-element is using the function syntax, it will
6051   // get isTree set here and will pass the check below that only
6052   // allows functions if they are in our list of things allowed to be
6053   // functions.  If it is _not_ using the function syntax, isTree will
6054   // be false, and it will still pass that check.  So the tree
6055   // pseudo-elements are allowed to be either functions or not, as
6056   // desired.
6057   bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
6058 #endif
6059   bool isPseudoElement = (pseudoElementType < CSSPseudoElementType::Count);
6060   // anonymous boxes are only allowed if they're the tree boxes or we have
6061   // enabled agent rules
6062   bool isAnonBox = isTreePseudo ||
6063     (pseudoElementType == CSSPseudoElementType::AnonBox &&
6064      AgentRulesEnabled());
6065   bool isPseudoClass =
6066     (pseudoClassType != CSSPseudoClassType::NotPseudo);
6067 
6068   NS_ASSERTION(!isPseudoClass ||
6069                pseudoElementType == CSSPseudoElementType::NotPseudo,
6070                "Why is this atom both a pseudo-class and a pseudo-element?");
6071   NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1,
6072                "Shouldn't be more than one of these");
6073 
6074   if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
6075     // Not a pseudo-class, not a pseudo-element.... forget it
6076     REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
6077     UngetToken();
6078     return eSelectorParsingStatus_Error;
6079   }
6080 
6081   // If it's a function token, it better be on our "ok" list, and if the name
6082   // is that of a function pseudo it better be a function token
6083   if ((eCSSToken_Function == mToken.mType) !=
6084       (
6085 #ifdef MOZ_XUL
6086        isTree ||
6087 #endif
6088        CSSPseudoClassType::negation == pseudoClassType ||
6089        nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
6090        nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
6091        nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
6092     // There are no other function pseudos
6093     REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
6094     UngetToken();
6095     return eSelectorParsingStatus_Error;
6096   }
6097 
6098   // If it starts with "::", it better be a pseudo-element
6099   if (parsingPseudoElement &&
6100       !isPseudoElement &&
6101       !isAnonBox) {
6102     REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
6103     UngetToken();
6104     return eSelectorParsingStatus_Error;
6105   }
6106 
6107   if (aSelector.IsPseudoElement()) {
6108     CSSPseudoElementType type = aSelector.PseudoType();
6109     if (type >= CSSPseudoElementType::Count ||
6110         !nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
6111       // We only allow user action pseudo-classes on certain pseudo-elements.
6112       REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
6113       UngetToken();
6114       return eSelectorParsingStatus_Error;
6115     }
6116     if (!isPseudoClass || !pseudoClassIsUserAction) {
6117       // CSS 4 Selectors says that pseudo-elements can only be followed by
6118       // a user action pseudo-class.
6119       REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
6120       UngetToken();
6121       return eSelectorParsingStatus_Error;
6122     }
6123   }
6124 
6125   if (!parsingPseudoElement &&
6126       CSSPseudoClassType::negation == pseudoClassType) {
6127     if (aIsNegated) { // :not() can't be itself negated
6128       REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
6129       UngetToken();
6130       return eSelectorParsingStatus_Error;
6131     }
6132     // CSS 3 Negation pseudo-class takes one simple selector as argument
6133     nsSelectorParsingStatus parsingStatus =
6134       ParseNegatedSimpleSelector(aDataMask, aSelector);
6135     if (eSelectorParsingStatus_Continue != parsingStatus) {
6136       return parsingStatus;
6137     }
6138   }
6139   else if (!parsingPseudoElement && isPseudoClass) {
6140     aDataMask |= SEL_MASK_PCLASS;
6141     if (eCSSToken_Function == mToken.mType) {
6142       nsSelectorParsingStatus parsingStatus;
6143       if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
6144         parsingStatus =
6145           ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
6146       }
6147       else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) {
6148         parsingStatus =
6149           ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType);
6150       }
6151       else {
6152         MOZ_ASSERT(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
6153                    "unexpected pseudo with function token");
6154         parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
6155                                                             pseudoClassType);
6156       }
6157       if (eSelectorParsingStatus_Continue != parsingStatus) {
6158         if (eSelectorParsingStatus_Error == parsingStatus) {
6159           SkipUntil(')');
6160         }
6161         return parsingStatus;
6162       }
6163     }
6164     else {
6165       aSelector.AddPseudoClass(pseudoClassType);
6166     }
6167   }
6168   else if (isPseudoElement || isAnonBox) {
6169     // Pseudo-element.  Make some more sanity checks.
6170 
6171     if (aIsNegated) { // pseudo-elements can't be negated
6172       REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
6173       UngetToken();
6174       return eSelectorParsingStatus_Error;
6175     }
6176     // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
6177     // to have a single ':' on them.  Others (CSS3+ pseudo-elements and
6178     // various -moz-* pseudo-elements) must have |parsingPseudoElement|
6179     // set.
6180     if (!parsingPseudoElement &&
6181         !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
6182 #ifdef MOZ_XUL
6183         && !isTreePseudo
6184 #endif
6185         ) {
6186       REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
6187       UngetToken();
6188       return eSelectorParsingStatus_Error;
6189     }
6190 
6191     if (0 == (aDataMask & SEL_MASK_PELEM)) {
6192       aDataMask |= SEL_MASK_PELEM;
6193       NS_ADDREF(*aPseudoElement = pseudo);
6194       *aPseudoElementType = pseudoElementType;
6195 
6196 #ifdef MOZ_XUL
6197       if (isTree) {
6198         // We have encountered a pseudoelement of the form
6199         // -moz-tree-xxxx(a,b,c).  We parse (a,b,c) and add each
6200         // item in the list to the pseudoclass list.  They will be pulled
6201         // from the list later along with the pseudo-element.
6202         if (!ParseTreePseudoElement(aPseudoElementArgs)) {
6203           return eSelectorParsingStatus_Error;
6204         }
6205       }
6206 #endif
6207 
6208       // Pseudo-elements can only be followed by user action pseudo-classes
6209       // or be the end of the selector.  So the next non-whitespace token must
6210       // be ':', '{' or ',' or EOF.
6211       if (!GetToken(true)) { // premature eof is ok (here!)
6212         return eSelectorParsingStatus_Done;
6213       }
6214       if (parsingPseudoElement && mToken.IsSymbol(':')) {
6215         UngetToken();
6216         return eSelectorParsingStatus_Continue;
6217       }
6218       if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
6219         UngetToken();
6220         return eSelectorParsingStatus_Done;
6221       }
6222       REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC);
6223       UngetToken();
6224       return eSelectorParsingStatus_Error;
6225     }
6226     else {  // multiple pseudo elements, not legal
6227       REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
6228       UngetToken();
6229       return eSelectorParsingStatus_Error;
6230     }
6231   }
6232 #ifdef DEBUG
6233   else {
6234     // We should never end up here.  Indeed, if we ended up here, we know (from
6235     // the current if/else cascade) that !isPseudoElement and !isAnonBox.  But
6236     // then due to our earlier check we know that isPseudoClass.  Since we
6237     // didn't fall into the isPseudoClass case in this cascade, we must have
6238     // parsingPseudoElement.  But we've already checked the
6239     // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
6240     // it's happened.
6241     NS_NOTREACHED("How did this happen?");
6242   }
6243 #endif
6244   return eSelectorParsingStatus_Continue;
6245 }
6246 
6247 //
6248 // Parse the argument of a negation pseudo-class :not()
6249 //
6250 CSSParserImpl::nsSelectorParsingStatus
6251 CSSParserImpl::ParseNegatedSimpleSelector(int32_t&       aDataMask,
6252                                           nsCSSSelector& aSelector)
6253 {
6254   if (! GetToken(true)) { // premature eof
6255     REPORT_UNEXPECTED_EOF(PENegationEOF);
6256     return eSelectorParsingStatus_Error;
6257   }
6258 
6259   if (mToken.IsSymbol(')')) {
6260     REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
6261     return eSelectorParsingStatus_Error;
6262   }
6263 
6264   // Create a new nsCSSSelector and add it to the end of
6265   // aSelector.mNegations.
6266   // Given the current parsing rules, every selector in mNegations
6267   // contains only one simple selector (css3 definition) within it.
6268   // This could easily change in future versions of CSS, and the only
6269   // thing we need to change to support that is this parsing code and the
6270   // serialization code for nsCSSSelector.
6271   nsCSSSelector *newSel = new nsCSSSelector();
6272   nsCSSSelector* negations = &aSelector;
6273   while (negations->mNegations) {
6274     negations = negations->mNegations;
6275   }
6276   negations->mNegations = newSel;
6277 
6278   nsSelectorParsingStatus parsingStatus;
6279   if (eCSSToken_ID == mToken.mType) { // #id
6280     parsingStatus = ParseIDSelector(aDataMask, *newSel);
6281   }
6282   else if (mToken.IsSymbol('.')) {    // .class
6283     parsingStatus = ParseClassSelector(aDataMask, *newSel);
6284   }
6285   else if (mToken.IsSymbol(':')) {    // :pseudo
6286     parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true,
6287                                         nullptr, nullptr, nullptr);
6288   }
6289   else if (mToken.IsSymbol('[')) {    // [attribute
6290     parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
6291     if (eSelectorParsingStatus_Error == parsingStatus) {
6292       // Skip forward to the matching ']'
6293       SkipUntil(']');
6294     }
6295   }
6296   else {
6297     // then it should be a type element or universal selector
6298     parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true);
6299   }
6300   if (eSelectorParsingStatus_Error == parsingStatus) {
6301     REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
6302     SkipUntil(')');
6303     return parsingStatus;
6304   }
6305   // close the parenthesis
6306   if (!ExpectSymbol(')', true)) {
6307     REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
6308     SkipUntil(')');
6309     return eSelectorParsingStatus_Error;
6310   }
6311 
6312   NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown ||
6313                (!newSel->mIDList && !newSel->mClassList &&
6314                 !newSel->mPseudoClassList && !newSel->mAttrList),
6315                "Need to fix the serialization code to deal with this");
6316 
6317   return eSelectorParsingStatus_Continue;
6318 }
6319 
6320 //
6321 // Parse the argument of a pseudo-class that has an ident arg
6322 //
6323 CSSParserImpl::nsSelectorParsingStatus
6324 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
6325                                             CSSPseudoClassType aType)
6326 {
6327   if (! GetToken(true)) { // premature eof
6328     REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6329     return eSelectorParsingStatus_Error;
6330   }
6331   // We expect an identifier with a language abbreviation
6332   if (eCSSToken_Ident != mToken.mType) {
6333     REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
6334     UngetToken();
6335     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6336   }
6337 
6338   // -moz-locale-dir and dir take an identifier argument.  While
6339   // only 'ltr' and 'rtl' (case-insensitively) will match anything, any
6340   // other identifier is still valid.
6341   if (aType == CSSPseudoClassType::mozLocaleDir ||
6342       aType == CSSPseudoClassType::mozDir ||
6343       aType == CSSPseudoClassType::dir) {
6344     nsContentUtils::ASCIIToLower(mToken.mIdent); // case insensitive
6345   }
6346 
6347   // Add the pseudo with the language parameter
6348   aSelector.AddPseudoClass(aType, mToken.mIdent.get());
6349 
6350   // close the parenthesis
6351   if (!ExpectSymbol(')', true)) {
6352     REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
6353     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6354   }
6355 
6356   return eSelectorParsingStatus_Continue;
6357 }
6358 
6359 CSSParserImpl::nsSelectorParsingStatus
6360 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
6361                                               CSSPseudoClassType aType)
6362 {
6363   int32_t numbers[2] = { 0, 0 };
6364   int32_t sign[2] = { 1, 1 };
6365   bool hasSign[2] = { false, false };
6366   bool lookForB = true;
6367 
6368   // Follow the whitespace rules as proposed in
6369   // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
6370 
6371   if (! GetToken(true)) {
6372     REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6373     return eSelectorParsingStatus_Error;
6374   }
6375 
6376   if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
6377     hasSign[0] = true;
6378     if (mToken.IsSymbol('-')) {
6379       sign[0] = -1;
6380     }
6381     if (! GetToken(false)) {
6382       REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6383       return eSelectorParsingStatus_Error;
6384     }
6385   }
6386 
6387   // A helper function that checks if the token starts with literal string
6388   // |aStr| using a case-insensitive match.
6389   auto TokenBeginsWith = [this] (const nsLiteralString& aStr) {
6390     return StringBeginsWith(mToken.mIdent, aStr,
6391                             nsASCIICaseInsensitiveStringComparator());
6392   };
6393 
6394   if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
6395     // The CSS tokenization doesn't handle :nth-child() containing - well:
6396     //   2n-1 is a dimension
6397     //   n-1 is an identifier
6398     // The easiest way to deal with that is to push everything from the
6399     // minus on back onto the scanner's pushback buffer.
6400     uint32_t truncAt = 0;
6401     if (TokenBeginsWith(NS_LITERAL_STRING("n-"))) {
6402       truncAt = 1;
6403     } else if (TokenBeginsWith(NS_LITERAL_STRING("-n-")) && !hasSign[0]) {
6404       truncAt = 2;
6405     }
6406     if (truncAt != 0) {
6407       mScanner->Backup(mToken.mIdent.Length() - truncAt);
6408       mToken.mIdent.Truncate(truncAt);
6409     }
6410   }
6411 
6412   if (eCSSToken_Ident == mToken.mType) {
6413     if (mToken.mIdent.LowerCaseEqualsLiteral("odd") && !hasSign[0]) {
6414       numbers[0] = 2;
6415       numbers[1] = 1;
6416       lookForB = false;
6417     }
6418     else if (mToken.mIdent.LowerCaseEqualsLiteral("even") && !hasSign[0]) {
6419       numbers[0] = 2;
6420       numbers[1] = 0;
6421       lookForB = false;
6422     }
6423     else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
6424       numbers[0] = sign[0];
6425     }
6426     else if (mToken.mIdent.LowerCaseEqualsLiteral("-n") && !hasSign[0]) {
6427       numbers[0] = -1;
6428     }
6429     else {
6430       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6431       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6432     }
6433   }
6434   else if (eCSSToken_Number == mToken.mType) {
6435     if (!mToken.mIntegerValid) {
6436       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6437       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6438     }
6439     // for +-an case
6440     if (mToken.mHasSign && hasSign[0]) {
6441       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6442       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6443     }
6444     int32_t intValue = mToken.mInteger * sign[0];
6445     // for -a/**/n case
6446     if (! GetToken(false)) {
6447       numbers[1] = intValue;
6448       lookForB = false;
6449     }
6450     else {
6451       if (eCSSToken_Ident == mToken.mType && mToken.mIdent.LowerCaseEqualsLiteral("n")) {
6452         numbers[0] = intValue;
6453       }
6454       else if (eCSSToken_Ident == mToken.mType && TokenBeginsWith(NS_LITERAL_STRING("n-"))) {
6455         numbers[0] = intValue;
6456         mScanner->Backup(mToken.mIdent.Length() - 1);
6457       }
6458       else {
6459         UngetToken();
6460         numbers[1] = intValue;
6461         lookForB = false;
6462       }
6463     }
6464   }
6465   else if (eCSSToken_Dimension == mToken.mType) {
6466     if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
6467       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6468       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6469     }
6470     // for +-an case
6471     if ( mToken.mHasSign && hasSign[0] ) {
6472       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6473       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6474     }
6475     numbers[0] = mToken.mInteger * sign[0];
6476   }
6477   // XXX If it's a ')', is that valid?  (as 0n+0)
6478   else {
6479     REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6480     UngetToken();
6481     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6482   }
6483 
6484   if (! GetToken(true)) {
6485     REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6486     return eSelectorParsingStatus_Error;
6487   }
6488   if (lookForB && !mToken.IsSymbol(')')) {
6489     // The '+' or '-' sign can optionally be separated by whitespace.
6490     // If it is separated by whitespace from what follows it, it appears
6491     // as a separate token rather than part of the number token.
6492     if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
6493       hasSign[1] = true;
6494       if (mToken.IsSymbol('-')) {
6495         sign[1] = -1;
6496       }
6497       if (! GetToken(true)) {
6498         REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6499         return eSelectorParsingStatus_Error;
6500       }
6501     }
6502     if (eCSSToken_Number != mToken.mType ||
6503         !mToken.mIntegerValid || mToken.mHasSign == hasSign[1]) {
6504       REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6505       UngetToken();
6506       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6507     }
6508     numbers[1] = mToken.mInteger * sign[1];
6509     if (! GetToken(true)) {
6510       REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6511       return eSelectorParsingStatus_Error;
6512     }
6513   }
6514   if (!mToken.IsSymbol(')')) {
6515     REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
6516     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6517   }
6518   aSelector.AddPseudoClass(aType, numbers);
6519   return eSelectorParsingStatus_Continue;
6520 }
6521 
6522 //
6523 // Parse the argument of a pseudo-class that has a selector list argument.
6524 // Such selector lists cannot contain combinators, but can contain
6525 // anything that goes between a pair of combinators.
6526 //
6527 CSSParserImpl::nsSelectorParsingStatus
6528 CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
6529                                                    CSSPseudoClassType aType)
6530 {
6531   nsAutoPtr<nsCSSSelectorList> slist;
6532   if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) {
6533     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6534   }
6535 
6536   // Check that none of the selectors in the list have combinators or
6537   // pseudo-elements.
6538   for (nsCSSSelectorList *l = slist; l; l = l->mNext) {
6539     nsCSSSelector *s = l->mSelectors;
6540     if (s->mNext || s->IsPseudoElement()) {
6541       return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6542     }
6543   }
6544 
6545   // Add the pseudo with the selector list parameter
6546   aSelector.AddPseudoClass(aType, slist.forget());
6547 
6548   // close the parenthesis
6549   if (!ExpectSymbol(')', true)) {
6550     REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
6551     return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6552   }
6553 
6554   return eSelectorParsingStatus_Continue;
6555 }
6556 
6557 
6558 /**
6559  * This is the format for selectors:
6560  * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
6561  */
6562 bool
6563 CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
6564                              char16_t aPrevCombinator)
6565 {
6566   if (! GetToken(true)) {
6567     REPORT_UNEXPECTED_EOF(PESelectorEOF);
6568     return false;
6569   }
6570 
6571   nsCSSSelector* selector = aList->AddSelector(aPrevCombinator);
6572   nsCOMPtr<nsIAtom> pseudoElement;
6573   nsAutoPtr<nsAtomList> pseudoElementArgs;
6574   CSSPseudoElementType pseudoElementType = CSSPseudoElementType::NotPseudo;
6575 
6576   int32_t dataMask = 0;
6577   nsSelectorParsingStatus parsingStatus =
6578     ParseTypeOrUniversalSelector(dataMask, *selector, false);
6579 
6580   while (parsingStatus == eSelectorParsingStatus_Continue) {
6581     if (mToken.IsSymbol(':')) {    // :pseudo
6582       parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
6583                                           getter_AddRefs(pseudoElement),
6584                                           getter_Transfers(pseudoElementArgs),
6585                                           &pseudoElementType);
6586       if (pseudoElement &&
6587           pseudoElementType != CSSPseudoElementType::AnonBox) {
6588         // Pseudo-elements other than anonymous boxes are represented with
6589         // a special ':' combinator.
6590 
6591         aList->mWeight += selector->CalcWeight();
6592 
6593         selector = aList->AddSelector(':');
6594 
6595         selector->mLowercaseTag.swap(pseudoElement);
6596         selector->mClassList = pseudoElementArgs.forget();
6597         selector->SetPseudoType(pseudoElementType);
6598       }
6599     } else if (selector->IsPseudoElement()) {
6600       // Once we parsed a pseudo-element, we can only parse
6601       // pseudo-classes (and only a limited set, which
6602       // ParsePseudoSelector knows how to handle).
6603       parsingStatus = eSelectorParsingStatus_Done;
6604       UngetToken();
6605       break;
6606     } else if (eCSSToken_ID == mToken.mType) { // #id
6607       parsingStatus = ParseIDSelector(dataMask, *selector);
6608     } else if (mToken.IsSymbol('.')) {    // .class
6609       parsingStatus = ParseClassSelector(dataMask, *selector);
6610     }
6611     else if (mToken.IsSymbol('[')) {    // [attribute
6612       parsingStatus = ParseAttributeSelector(dataMask, *selector);
6613       if (eSelectorParsingStatus_Error == parsingStatus) {
6614         SkipUntil(']');
6615       }
6616     }
6617     else {  // not a selector token, we're done
6618       parsingStatus = eSelectorParsingStatus_Done;
6619       UngetToken();
6620       break;
6621     }
6622 
6623     if (parsingStatus != eSelectorParsingStatus_Continue) {
6624       break;
6625     }
6626 
6627     if (! GetToken(false)) { // premature eof is ok (here!)
6628       parsingStatus = eSelectorParsingStatus_Done;
6629       break;
6630     }
6631   }
6632 
6633   if (parsingStatus == eSelectorParsingStatus_Error) {
6634     return false;
6635   }
6636 
6637   if (!dataMask) {
6638     if (selector->mNext) {
6639       REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
6640     } else {
6641       REPORT_UNEXPECTED(PESelectorGroupNoSelector);
6642     }
6643     return false;
6644   }
6645 
6646   if (pseudoElementType == CSSPseudoElementType::AnonBox) {
6647     // We got an anonymous box pseudo-element; it must be the only
6648     // thing in this selector group.
6649     if (selector->mNext || !IsUniversalSelector(*selector)) {
6650       REPORT_UNEXPECTED(PEAnonBoxNotAlone);
6651       return false;
6652     }
6653 
6654     // Rewrite the current selector as this pseudo-element.
6655     // It does not contribute to selector weight.
6656     selector->mLowercaseTag.swap(pseudoElement);
6657     selector->mClassList = pseudoElementArgs.forget();
6658     selector->SetPseudoType(pseudoElementType);
6659     return true;
6660   }
6661 
6662   aList->mWeight += selector->CalcWeight();
6663 
6664   return true;
6665 }
6666 
6667 already_AddRefed<css::Declaration>
6668 CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext)
6669 {
6670   bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
6671 
6672   MOZ_ASSERT(mWebkitBoxUnprefixState == eNotParsingDecls,
6673              "Someone forgot to clear mWebkitBoxUnprefixState!");
6674   AutoRestore<WebkitBoxUnprefixState> autoRestore(mWebkitBoxUnprefixState);
6675   mWebkitBoxUnprefixState = eHaveNotUnprefixed;
6676 
6677   if (checkForBraces) {
6678     if (!ExpectSymbol('{', true)) {
6679       REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
6680       OUTPUT_ERROR();
6681       return nullptr;
6682     }
6683   }
6684   RefPtr<css::Declaration> declaration = new css::Declaration();
6685   mData.AssertInitialState();
6686   for (;;) {
6687     bool changed = false;
6688     if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) {
6689       if (!SkipDeclaration(checkForBraces)) {
6690         break;
6691       }
6692       if (checkForBraces) {
6693         if (ExpectSymbol('}', true)) {
6694           break;
6695         }
6696       }
6697       // Since the skipped declaration didn't end the block we parse
6698       // the next declaration.
6699     }
6700   }
6701   declaration->CompressFrom(&mData);
6702   return declaration.forget();
6703 }
6704 
6705 CSSParseResult
6706 CSSParserImpl::ParseColor(nsCSSValue& aValue)
6707 {
6708   if (!GetToken(true)) {
6709     REPORT_UNEXPECTED_EOF(PEColorEOF);
6710     return CSSParseResult::NotFound;
6711   }
6712 
6713   nsCSSToken* tk = &mToken;
6714   nscolor rgba;
6715   switch (tk->mType) {
6716     case eCSSToken_ID:
6717     case eCSSToken_Hash:
6718       // #rgb, #rrggbb, #rgba, #rrggbbaa
6719       if (NS_HexToRGBA(tk->mIdent, nsHexColorType::AllowAlpha, &rgba)) {
6720         nsCSSUnit unit;
6721         switch (tk->mIdent.Length()) {
6722           case 3:
6723             unit = eCSSUnit_ShortHexColor;
6724             break;
6725           case 4:
6726             unit = eCSSUnit_ShortHexColorAlpha;
6727             break;
6728           case 6:
6729             unit = eCSSUnit_HexColor;
6730             break;
6731           default:
6732             MOZ_FALLTHROUGH_ASSERT("unexpected hex color length");
6733           case 8:
6734             unit = eCSSUnit_HexColorAlpha;
6735             break;
6736         }
6737         aValue.SetIntegerColorValue(rgba, unit);
6738         return CSSParseResult::Ok;
6739       }
6740       break;
6741 
6742     case eCSSToken_Ident:
6743       if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
6744         aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
6745         return CSSParseResult::Ok;
6746       }
6747       else {
6748         nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
6749         if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
6750           int32_t value;
6751           if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
6752             aValue.SetIntValue(value, eCSSUnit_EnumColor);
6753             return CSSParseResult::Ok;
6754           }
6755         }
6756       }
6757       break;
6758     case eCSSToken_Function: {
6759       bool isRGB;
6760       bool isHSL;
6761       if ((isRGB = mToken.mIdent.LowerCaseEqualsLiteral("rgb")) ||
6762           mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
6763         // rgb() = rgb( <percentage>{3} [ / <alpha-value> ]? ) |
6764         //         rgb( <number>{3} [ / <alpha-value> ]? ) |
6765         //         rgb( <percentage>#{3} , <alpha-value>? ) |
6766         //         rgb( <number>#{3} , <alpha-value>? )
6767         // <alpha-value> = <number> | <percentage>
6768         // rgba is an alias of rgb.
6769 
6770         if (GetToken(true)) {
6771           UngetToken();
6772         }
6773         if (mToken.mType == eCSSToken_Number) { // <number>
6774           uint8_t r, g, b, a;
6775 
6776           if (ParseRGBColor(r, g, b, a)) {
6777             aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a),
6778                 isRGB ? eCSSUnit_RGBColor : eCSSUnit_RGBAColor);
6779             return CSSParseResult::Ok;
6780           }
6781         } else {  // <percentage>
6782           float r, g, b, a;
6783 
6784           if (ParseRGBColor(r, g, b, a)) {
6785             aValue.SetFloatColorValue(r, g, b, a,
6786                 isRGB ? eCSSUnit_PercentageRGBColor : eCSSUnit_PercentageRGBAColor);
6787             return CSSParseResult::Ok;
6788           }
6789         }
6790         SkipUntil(')');
6791         return CSSParseResult::Error;
6792       }
6793       else if ((isHSL = mToken.mIdent.LowerCaseEqualsLiteral("hsl")) ||
6794                mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
6795         // hsl() = hsl( <hue> <percentage> <percentage> [ / <alpha-value> ]? ) ||
6796         //         hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )
6797         // <hue> = <number> | <angle>
6798         // hsla is an alias of hsl.
6799 
6800         float h, s, l, a;
6801 
6802         if (ParseHSLColor(h, s, l, a)) {
6803           aValue.SetFloatColorValue(h, s, l, a,
6804               isHSL ? eCSSUnit_HSLColor : eCSSUnit_HSLAColor);
6805           return CSSParseResult::Ok;
6806         }
6807         SkipUntil(')');
6808         return CSSParseResult::Error;
6809       }
6810       break;
6811     }
6812     default:
6813       break;
6814   }
6815 
6816   // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
6817   if (mHashlessColorQuirk) {
6818     // - If the string starts with 'a-f', the nsCSSScanner builds the
6819     //   token as a eCSSToken_Ident and we can parse the string as a
6820     //   'xxyyzz' RGB color.
6821     // - If it only contains '0-9' digits, the token is a
6822     //   eCSSToken_Number and it must be converted back to a 6
6823     //   characters string to be parsed as a RGB color.
6824     // - If it starts with '0-9' and contains any 'a-f', the token is a
6825     //   eCSSToken_Dimension, the mNumber part must be converted back to
6826     //   a string and the mIdent part must be appended to that string so
6827     //   that the resulting string has 6 characters.
6828     // Note: This is a hack for Nav compatibility.  Do not attempt to
6829     // simplify it by hacking into the ncCSSScanner.  This would be very
6830     // bad.
6831     nsAutoString str;
6832     char buffer[20];
6833     switch (tk->mType) {
6834       case eCSSToken_Ident:
6835         str.Assign(tk->mIdent);
6836         break;
6837 
6838       case eCSSToken_Number:
6839         if (tk->mIntegerValid) {
6840           SprintfLiteral(buffer, "%06d", tk->mInteger);
6841           str.AssignWithConversion(buffer);
6842         }
6843         break;
6844 
6845       case eCSSToken_Dimension:
6846         if (tk->mIdent.Length() <= 6) {
6847           SprintfLiteral(buffer, "%06.0f", tk->mNumber);
6848           nsAutoString temp;
6849           temp.AssignWithConversion(buffer);
6850           temp.Right(str, 6 - tk->mIdent.Length());
6851           str.Append(tk->mIdent);
6852         }
6853         break;
6854       default:
6855         // There is a whole bunch of cases that are
6856         // not handled by this switch.  Ignore them.
6857         break;
6858     }
6859     // The hashless color quirk does not support 4 & 8 digit colors with alpha.
6860     if (NS_HexToRGBA(str, nsHexColorType::NoAlpha, &rgba)) {
6861       aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor);
6862       return CSSParseResult::Ok;
6863     }
6864   }
6865 
6866   // It's not a color
6867   REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
6868   UngetToken();
6869   return CSSParseResult::NotFound;
6870 }
6871 
6872 bool
6873 CSSParserImpl::ParseColorComponent(uint8_t& aComponent, Maybe<char> aSeparator)
6874 {
6875   if (!GetToken(true)) {
6876     REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
6877     return false;
6878   }
6879 
6880   if (mToken.mType != eCSSToken_Number) {
6881     REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
6882     UngetToken();
6883     return false;
6884   }
6885 
6886   float value = mToken.mNumber;
6887 
6888   if (aSeparator && !ExpectSymbol(*aSeparator, true)) {
6889     REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator);
6890     return false;
6891   }
6892 
6893   if (value < 0.0f) value = 0.0f;
6894   if (value > 255.0f) value = 255.0f;
6895 
6896   aComponent = NSToIntRound(value);
6897   return true;
6898 }
6899 
6900 bool
6901 CSSParserImpl::ParseColorComponent(float& aComponent, Maybe<char> aSeparator)
6902 {
6903   if (!GetToken(true)) {
6904     REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
6905     return false;
6906   }
6907 
6908   if (mToken.mType != eCSSToken_Percentage) {
6909     REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
6910     UngetToken();
6911     return false;
6912   }
6913 
6914   float value = mToken.mNumber;
6915 
6916   if (aSeparator && !ExpectSymbol(*aSeparator, true)) {
6917     REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator);
6918     return false;
6919   }
6920 
6921   if (value < 0.0f) value = 0.0f;
6922   if (value > 1.0f) value = 1.0f;
6923 
6924   aComponent = value;
6925   return true;
6926 }
6927 
6928 bool
6929 CSSParserImpl::ParseHue(float& aAngle)
6930 {
6931   if (!GetToken(true)) {
6932     REPORT_UNEXPECTED_EOF(PEColorHueEOF);
6933     return false;
6934   }
6935 
6936   // <number>
6937   if (mToken.mType == eCSSToken_Number) {
6938     aAngle = mToken.mNumber;
6939     return true;
6940   }
6941   UngetToken();
6942 
6943   // <angle>
6944   nsCSSValue angleValue;
6945   // The '0' value is handled by <number> parsing, so use VARIANT_ANGLE flag
6946   // instead of VARIANT_ANGLE_OR_ZERO.
6947   if (ParseSingleTokenVariant(angleValue, VARIANT_ANGLE, nullptr)) {
6948     aAngle = angleValue.GetAngleValueInDegrees();
6949     return true;
6950   }
6951 
6952   REPORT_UNEXPECTED_TOKEN(PEExpectedNumberOrAngle);
6953   return false;
6954 }
6955 
6956 bool
6957 CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
6958                              float& aOpacity)
6959 {
6960   // comma-less expression:
6961   // hsl() = hsl( <hue> <saturation> <lightness> [ / <alpha-value> ]? )
6962   // the expression with comma:
6963   // hsl() = hsl( <hue>, <saturation>, <lightness>, <alpha-value>? )
6964 
6965   const char commaSeparator = ',';
6966 
6967   // Parse hue.
6968   // <hue> = <number> | <angle>
6969   float degreeAngle;
6970   if (!ParseHue(degreeAngle)) {
6971     return false;
6972   }
6973   aHue = degreeAngle / 360.0f;
6974   // hue values are wraparound
6975   aHue = aHue - floor(aHue);
6976   // Look for a comma separator after "hue" component to determine if the
6977   // expression is comma-less or not.
6978   bool hasComma = ExpectSymbol(commaSeparator, true);
6979 
6980   // Parse saturation, lightness and opacity.
6981   // The saturation and lightness are <percentage>, so reuse the float version
6982   // of ParseColorComponent function for them. No need to check the separator
6983   // after 'lightness'. It will be checked in opacity value parsing.
6984   const char separatorBeforeAlpha = hasComma ? commaSeparator : '/';
6985   if (ParseColorComponent(aSaturation, hasComma ? Some(commaSeparator) : Nothing()) &&
6986       ParseColorComponent(aLightness, Nothing()) &&
6987       ParseColorOpacityAndCloseParen(aOpacity, separatorBeforeAlpha)) {
6988     return true;
6989   }
6990 
6991   return false;
6992 }
6993 
6994 
6995 bool
6996 CSSParserImpl::ParseColorOpacityAndCloseParen(uint8_t& aOpacity,
6997                                               char aSeparator)
6998 {
6999   float floatOpacity;
7000   if (!ParseColorOpacityAndCloseParen(floatOpacity, aSeparator)) {
7001     return false;
7002   }
7003 
7004   uint8_t value = nsStyleUtil::FloatToColorComponent(floatOpacity);
7005   // Need to compare to something slightly larger
7006   // than 0.5 due to floating point inaccuracies.
7007   NS_ASSERTION(fabs(255.0f * floatOpacity - value) <= 0.51f,
7008                "FloatToColorComponent did something weird");
7009 
7010   aOpacity = value;
7011   return true;
7012 }
7013 
7014 bool
7015 CSSParserImpl::ParseColorOpacityAndCloseParen(float& aOpacity,
7016                                               char aSeparator)
7017 {
7018   if (ExpectSymbol(')', true)) {
7019     // The optional [separator <alpha-value>] was omitted, so set the opacity
7020     // to a fully-opaque value '1.0f' and return success.
7021     aOpacity = 1.0f;
7022     return true;
7023   }
7024 
7025   if (!ExpectSymbol(aSeparator, true)) {
7026     REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aSeparator);
7027     return false;
7028   }
7029 
7030   if (!GetToken(true)) {
7031     REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
7032     return false;
7033   }
7034 
7035   // eCSSToken_Number or eCSSToken_Percentage.
7036   if (mToken.mType != eCSSToken_Number && mToken.mType != eCSSToken_Percentage) {
7037     REPORT_UNEXPECTED_TOKEN(PEExpectedNumberOrPercent);
7038     UngetToken();
7039     return false;
7040   }
7041 
7042   if (!ExpectSymbol(')', true)) {
7043     REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
7044     return false;
7045   }
7046 
7047   if (mToken.mNumber < 0.0f) {
7048     mToken.mNumber = 0.0f;
7049   } else if (mToken.mNumber > 1.0f) {
7050     mToken.mNumber = 1.0f;
7051   }
7052 
7053   aOpacity = mToken.mNumber;
7054   return true;
7055 }
7056 
7057 template<typename ComponentType>
7058 bool
7059 CSSParserImpl::ParseRGBColor(ComponentType& aR,
7060                              ComponentType& aG,
7061                              ComponentType& aB,
7062                              ComponentType& aA)
7063 {
7064   // comma-less expression:
7065   // rgb() = rgb( component{3} [ / <alpha-value> ]? )
7066   // the expression with comma:
7067   // rgb() = rgb( component#{3} , <alpha-value>? )
7068 
7069   const char commaSeparator = ',';
7070 
7071   // Parse R.
7072   if (!ParseColorComponent(aR, Nothing())) {
7073     return false;
7074   }
7075   // Look for a comma separator after "r" component to determine if the
7076   // expression is comma-less or not.
7077   bool hasComma = ExpectSymbol(commaSeparator, true);
7078 
7079   // Parse G, B and A.
7080   // No need to check the separator after 'B'. It will be checked in 'A' value
7081   // parsing.
7082   const char separatorBeforeAlpha = hasComma ? commaSeparator : '/';
7083   if (ParseColorComponent(aG, hasComma ? Some(commaSeparator) : Nothing()) &&
7084       ParseColorComponent(aB, Nothing()) &&
7085       ParseColorOpacityAndCloseParen(aA, separatorBeforeAlpha)) {
7086     return true;
7087   }
7088 
7089   return false;
7090 }
7091 
7092 #ifdef MOZ_XUL
7093 bool
7094 CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs)
7095 {
7096   // The argument to a tree pseudo-element is a sequence of identifiers
7097   // that are either space- or comma-separated.  (Was the intent to
7098   // allow only comma-separated?  That's not what was done.)
7099   nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass
7100 
7101   while (!ExpectSymbol(')', true)) {
7102     if (!GetToken(true)) {
7103       return false;
7104     }
7105     if (eCSSToken_Ident == mToken.mType) {
7106       fakeSelector.AddClass(mToken.mIdent);
7107     }
7108     else if (!mToken.IsSymbol(',')) {
7109       UngetToken();
7110       SkipUntil(')');
7111       return false;
7112     }
7113   }
7114   *aPseudoElementArgs = fakeSelector.mClassList;
7115   fakeSelector.mClassList = nullptr;
7116   return true;
7117 }
7118 #endif
7119 
7120 nsCSSKeyword
7121 CSSParserImpl::LookupKeywordPrefixAware(nsAString& aKeywordStr,
7122                                         const KTableEntry aKeywordTable[])
7123 {
7124   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aKeywordStr);
7125 
7126   if (aKeywordTable == nsCSSProps::kDisplayKTable) {
7127     // NOTE: This code will be considerably simpler once we can do away with
7128     // all Unprefixing Service code, in bug 1259348. But for the time being, we
7129     // have to support two different strategies for handling -webkit-box here:
7130     // (1) "Native support" (sWebkitPrefixedAliasesEnabled): we assume that
7131     //     -webkit-box will parse correctly (via an entry in kDisplayKTable),
7132     //     and we simply make a note that we've parsed it (so that we can we
7133     //     can give later "-moz-box" styling special handling as noted below).
7134     // (2) "Unprefixing Service support" (ShouldUseUnprefixingService): we
7135     //     convert "-webkit-box" directly to modern "flex" (& do the same for
7136     //     any later "-moz-box" styling).
7137     //
7138     // Note that sWebkitPrefixedAliasesEnabled and
7139     // ShouldUseUnprefixingService() are mutually exlusive, because the latter
7140     // explicitly defers to the former.
7141     if ((keyword == eCSSKeyword__webkit_box ||
7142          keyword == eCSSKeyword__webkit_inline_box)) {
7143       const bool usingUnprefixingService = ShouldUseUnprefixingService();
7144       if (sWebkitPrefixedAliasesEnabled || usingUnprefixingService) {
7145         // Make a note that we're accepting some "-webkit-{inline-}box" styling,
7146         // so we can give special treatment to subsequent "-moz-{inline}-box".
7147         // (See special treatment below.)
7148         if (mWebkitBoxUnprefixState == eHaveNotUnprefixed) {
7149           mWebkitBoxUnprefixState = eHaveUnprefixed;
7150         }
7151         if (usingUnprefixingService) {
7152           // When we're using the unprefixing service, we treat
7153           // "display:-webkit-box" as if it were "display:flex"
7154           // (and "-webkit-inline-box" as "inline-flex").
7155           return (keyword == eCSSKeyword__webkit_box) ?
7156             eCSSKeyword_flex : eCSSKeyword_inline_flex;
7157         }
7158       }
7159     }
7160 
7161     // If we've seen "display: -webkit-box" (or "-webkit-inline-box") in an
7162     // earlier declaration and we honored it, then we have to watch out for
7163     // later "display: -moz-box" (and "-moz-inline-box") declarations; they're
7164     // likely just a halfhearted attempt at compatibility, and they actually
7165     // end up stomping on our emulation of the earlier -webkit-box
7166     // display-value, via the CSS cascade. To prevent this problem, we treat
7167     // "display: -moz-box" & "-moz-inline-box" as if they were simply a
7168     // repetition of the webkit equivalent that we already parsed.
7169     if (mWebkitBoxUnprefixState == eHaveUnprefixed &&
7170         (keyword == eCSSKeyword__moz_box ||
7171          keyword == eCSSKeyword__moz_inline_box)) {
7172       MOZ_ASSERT(sWebkitPrefixedAliasesEnabled || ShouldUseUnprefixingService(),
7173                  "mDidUnprefixWebkitBoxInEarlierDecl should only be set if "
7174                  "we're supporting webkit-prefixed aliases, or if we're using "
7175                  "the css unprefixing service on this site");
7176       if (sWebkitPrefixedAliasesEnabled) {
7177         return (keyword == eCSSKeyword__moz_box) ?
7178           eCSSKeyword__webkit_box : eCSSKeyword__webkit_inline_box;
7179       }
7180       // (If we get here, we're using the Unprefixing Service, which means
7181       // we're unprefixing all the way to modern flexbox display values.)
7182       return (keyword == eCSSKeyword__moz_box) ?
7183         eCSSKeyword_flex : eCSSKeyword_inline_flex;
7184     }
7185   }
7186 
7187   return keyword;
7188 }
7189 
7190 bool
7191 CSSParserImpl::ShouldUseUnprefixingService() const
7192 {
7193   if (!sUnprefixingServiceEnabled) {
7194     // Unprefixing is globally disabled.
7195     return false;
7196   }
7197   if (sWebkitPrefixedAliasesEnabled) {
7198     // Native webkit-prefix support is enabled, which trumps the unprefixing
7199     // service for handling prefixed CSS. Don't try to use both at once.
7200     return false;
7201   }
7202 
7203 #ifdef NIGHTLY_BUILD
7204   if (sUnprefixingServiceGloballyWhitelisted) {
7205     // Unprefixing is globally whitelisted,
7206     // so no need to check mSheetPrincipal.
7207     return true;
7208   }
7209 #endif
7210   // Unprefixing enabled; see if our principal is whitelisted for unprefixing.
7211   return mSheetPrincipal && mSheetPrincipal->IsOnCSSUnprefixingWhitelist();
7212 }
7213 
7214 bool
7215 CSSParserImpl::ParsePropertyWithUnprefixingService(
7216   const nsAString& aPropertyName,
7217   css::Declaration* aDeclaration,
7218   uint32_t aFlags,
7219   bool aMustCallValueAppended,
7220   bool* aChanged,
7221   nsCSSContextType aContext)
7222 {
7223   MOZ_ASSERT(ShouldUseUnprefixingService(),
7224              "Caller should've checked ShouldUseUnprefixingService()");
7225 
7226   nsCOMPtr<nsICSSUnprefixingService> unprefixingSvc =
7227     do_GetService(NS_CSSUNPREFIXINGSERVICE_CONTRACTID);
7228   NS_ENSURE_TRUE(unprefixingSvc, false);
7229 
7230   // Save the state so we can jump back to this spot if our unprefixing fails
7231   // (so we can behave as if we didn't even try to unprefix).
7232   nsAutoCSSParserInputStateRestorer parserStateBeforeTryingToUnprefix(this);
7233 
7234   // Caller has already parsed the first half of the declaration --
7235   // aPropertyName and the ":".  Now, we record the rest of the CSS declaration
7236   // (the part after ':') into rightHalfOfDecl.  (This is the property value,
7237   // plus anything else up to the end of the declaration -- maybe "!important",
7238   // maybe trailing junk characters, maybe a semicolon, maybe a trailing "}".)
7239   bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
7240   nsAutoString rightHalfOfDecl;
7241   mScanner->StartRecording();
7242   SkipDeclaration(checkForBraces);
7243   mScanner->StopRecording(rightHalfOfDecl);
7244 
7245   // Try to unprefix:
7246   bool success;
7247   nsAutoString unprefixedDecl;
7248   nsresult rv =
7249     unprefixingSvc->GenerateUnprefixedDeclaration(aPropertyName,
7250                                                   rightHalfOfDecl,
7251                                                   unprefixedDecl, &success);
7252   if (NS_FAILED(rv) || !success) {
7253     return false;
7254   }
7255 
7256   // Attempt to parse the unprefixed declaration:
7257   nsAutoScannerChanger scannerChanger(this, unprefixedDecl);
7258   success = ParseDeclaration(aDeclaration,
7259                              aFlags | eParseDeclaration_FromUnprefixingSvc,
7260                              aMustCallValueAppended, aChanged, aContext);
7261   if (success) {
7262     // We succeeded, so we'll leave the parser pointing at the end of
7263     // the declaration; don't restore it to the pre-recording position.
7264     parserStateBeforeTryingToUnprefix.DoNotRestore();
7265   }
7266 
7267   return success;
7268 }
7269 
7270 bool
7271 CSSParserImpl::ParseWebkitPrefixedGradientWithService(
7272   nsAString& aPrefixedFuncName,
7273   nsCSSValue& aValue)
7274 {
7275   MOZ_ASSERT(ShouldUseUnprefixingService(),
7276              "Should only call if we're allowed to use unprefixing service");
7277 
7278   // Record the body of the "-webkit-*gradient" function into a string.
7279   // Note: we're already just after the opening "(".
7280   nsAutoString prefixedFuncBody;
7281   mScanner->StartRecording();
7282   bool gotCloseParen = SkipUntil(')');
7283   mScanner->StopRecording(prefixedFuncBody);
7284   if (gotCloseParen) {
7285     // Strip off trailing close-paren, so that the value we pass to the
7286     // unprefixing service is *just* the function-body (no parens).
7287     prefixedFuncBody.Truncate(prefixedFuncBody.Length() - 1);
7288   }
7289 
7290   // NOTE: Even if we fail, we'll be leaving the parser's cursor just after
7291   // the close of the "-webkit-*gradient(...)" expression. This is the same
7292   // behavior that the other Parse*Gradient functions have in their failure
7293   // cases -- they call "SkipUntil(')') before returning false. So this is
7294   // probably what we want.
7295   nsCOMPtr<nsICSSUnprefixingService> unprefixingSvc =
7296     do_GetService(NS_CSSUNPREFIXINGSERVICE_CONTRACTID);
7297   NS_ENSURE_TRUE(unprefixingSvc, false);
7298 
7299   bool success;
7300   nsAutoString unprefixedFuncName;
7301   nsAutoString unprefixedFuncBody;
7302   nsresult rv =
7303     unprefixingSvc->GenerateUnprefixedGradientValue(aPrefixedFuncName,
7304                                                     prefixedFuncBody,
7305                                                     unprefixedFuncName,
7306                                                     unprefixedFuncBody,
7307                                                     &success);
7308 
7309   if (NS_FAILED(rv) || !success) {
7310     return false;
7311   }
7312 
7313   // JS service thinks it successfully converted the gradient! Now let's try
7314   // to parse the resulting string.
7315 
7316   // First, add a close-paren if we originally recorded one (so that what we're
7317   // about to put into the CSS parser is a faithful representation of what it
7318   // would've seen if it were just parsing the original input stream):
7319   if (gotCloseParen) {
7320     unprefixedFuncBody.Append(char16_t(')'));
7321   }
7322 
7323   nsAutoScannerChanger scannerChanger(this, unprefixedFuncBody);
7324   if (unprefixedFuncName.EqualsLiteral("linear-gradient")) {
7325     return ParseLinearGradient(aValue, 0);
7326   }
7327   if (unprefixedFuncName.EqualsLiteral("radial-gradient")) {
7328     return ParseRadialGradient(aValue, 0);
7329   }
7330 
7331   NS_ERROR("CSSUnprefixingService returned an unrecognized type of "
7332            "gradient function");
7333 
7334   return false;
7335 }
7336 
7337 //----------------------------------------------------------------------
7338 
7339 bool
7340 CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
7341                                 uint32_t aFlags,
7342                                 bool aMustCallValueAppended,
7343                                 bool* aChanged,
7344                                 nsCSSContextType aContext)
7345 {
7346   NS_PRECONDITION(aContext == eCSSContext_General ||
7347                   aContext == eCSSContext_Page,
7348                   "Must be page or general context");
7349 
7350   bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
7351 
7352   mTempData.AssertInitialState();
7353 
7354   // Get property name
7355   nsCSSToken* tk = &mToken;
7356   nsAutoString propertyName;
7357   for (;;) {
7358     if (!GetToken(true)) {
7359       if (checkForBraces) {
7360         REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
7361       }
7362       return false;
7363     }
7364     if (eCSSToken_Ident == tk->mType) {
7365       propertyName = tk->mIdent;
7366       // grab the ident before the ExpectSymbol trashes the token
7367       if (!ExpectSymbol(':', true)) {
7368         REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
7369         REPORT_UNEXPECTED(PEDeclDropped);
7370         OUTPUT_ERROR();
7371         return false;
7372       }
7373       break;
7374     }
7375     if (tk->IsSymbol(';')) {
7376       // dangling semicolons are skipped
7377       continue;
7378     }
7379 
7380     if (!tk->IsSymbol('}')) {
7381       REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
7382       REPORT_UNEXPECTED(PEDeclSkipped);
7383       OUTPUT_ERROR();
7384 
7385       if (eCSSToken_AtKeyword == tk->mType) {
7386         SkipAtRule(checkForBraces);
7387         return true;  // Not a declaration, but don't skip until ';'
7388       }
7389     }
7390     // Not a declaration...
7391     UngetToken();
7392     return false;
7393   }
7394 
7395   // Don't report property parse errors if we're inside a failing @supports
7396   // rule.
7397   nsAutoSuppressErrors suppressErrors(this, mInFailingSupportsRule);
7398 
7399   // Information about a parsed non-custom property.
7400   nsCSSPropertyID propID;
7401 
7402   // Information about a parsed custom property.
7403   CSSVariableDeclarations::Type variableType;
7404   nsString variableValue;
7405 
7406   // Check if the property name is a custom property.
7407   bool customProperty = nsLayoutUtils::CSSVariablesEnabled() &&
7408                         nsCSSProps::IsCustomPropertyName(propertyName) &&
7409                         aContext == eCSSContext_General;
7410 
7411   if (customProperty) {
7412     if (!ParseVariableDeclaration(&variableType, variableValue)) {
7413       REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
7414       REPORT_UNEXPECTED(PEDeclDropped);
7415       OUTPUT_ERROR();
7416       return false;
7417     }
7418   } else {
7419     // Map property name to its ID.
7420     propID = LookupEnabledProperty(propertyName);
7421     if (eCSSProperty_UNKNOWN == propID ||
7422         eCSSPropertyExtra_variable == propID ||
7423         (aContext == eCSSContext_Page &&
7424          !nsCSSProps::PropHasFlags(propID,
7425                                    CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property
7426       if (NonMozillaVendorIdentifier(propertyName)) {
7427         if (!mInSupportsCondition &&
7428             aContext == eCSSContext_General &&
7429             !(aFlags & eParseDeclaration_FromUnprefixingSvc) && // no recursion
7430             ShouldUseUnprefixingService()) {
7431           if (ParsePropertyWithUnprefixingService(propertyName,
7432                                                   aDeclaration, aFlags,
7433                                                   aMustCallValueAppended,
7434                                                   aChanged, aContext)) {
7435             return true;
7436           }
7437         }
7438       } else {
7439         REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName);
7440         REPORT_UNEXPECTED(PEDeclDropped);
7441         OUTPUT_ERROR();
7442       }
7443       return false;
7444     }
7445     // Then parse the property.
7446     if (!ParseProperty(propID)) {
7447       // XXX Much better to put stuff in the value parsers instead...
7448       REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
7449       REPORT_UNEXPECTED(PEDeclDropped);
7450       OUTPUT_ERROR();
7451       mTempData.ClearProperty(propID);
7452       mTempData.AssertInitialState();
7453       return false;
7454     }
7455   }
7456 
7457   CLEAR_ERROR();
7458 
7459   // Look for "!important".
7460   PriorityParsingStatus status;
7461   if ((aFlags & eParseDeclaration_AllowImportant) != 0) {
7462     status = ParsePriority();
7463   } else {
7464     status = ePriority_None;
7465   }
7466 
7467   // Look for a semicolon or close brace.
7468   if (status != ePriority_Error) {
7469     if (!GetToken(true)) {
7470       // EOF is always ok
7471     } else if (mToken.IsSymbol(';')) {
7472       // semicolon is always ok
7473     } else if (mToken.IsSymbol('}')) {
7474       // brace is ok if checkForBraces, but don't eat it
7475       UngetToken();
7476       if (!checkForBraces) {
7477         status = ePriority_Error;
7478       }
7479     } else {
7480       UngetToken();
7481       status = ePriority_Error;
7482     }
7483   }
7484 
7485   if (status == ePriority_Error) {
7486     if (checkForBraces) {
7487       REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
7488     } else {
7489       REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
7490     }
7491     REPORT_UNEXPECTED(PEDeclDropped);
7492     OUTPUT_ERROR();
7493     if (!customProperty) {
7494       mTempData.ClearProperty(propID);
7495     }
7496     mTempData.AssertInitialState();
7497     return false;
7498   }
7499 
7500   if (customProperty) {
7501     MOZ_ASSERT(Substring(propertyName, 0,
7502                          CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
7503     // remove '--'
7504     nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
7505     aDeclaration->AddVariable(varName, variableType, variableValue,
7506                               status == ePriority_Important, false);
7507   } else {
7508     *aChanged |= mData.TransferFromBlock(mTempData, propID, EnabledState(),
7509                                          status == ePriority_Important,
7510                                          false, aMustCallValueAppended,
7511                                          aDeclaration, GetDocument());
7512   }
7513 
7514   return true;
7515 }
7516 
7517 static const nsCSSPropertyID kBorderTopIDs[] = {
7518   eCSSProperty_border_top_width,
7519   eCSSProperty_border_top_style,
7520   eCSSProperty_border_top_color
7521 };
7522 static const nsCSSPropertyID kBorderRightIDs[] = {
7523   eCSSProperty_border_right_width,
7524   eCSSProperty_border_right_style,
7525   eCSSProperty_border_right_color
7526 };
7527 static const nsCSSPropertyID kBorderBottomIDs[] = {
7528   eCSSProperty_border_bottom_width,
7529   eCSSProperty_border_bottom_style,
7530   eCSSProperty_border_bottom_color
7531 };
7532 static const nsCSSPropertyID kBorderLeftIDs[] = {
7533   eCSSProperty_border_left_width,
7534   eCSSProperty_border_left_style,
7535   eCSSProperty_border_left_color
7536 };
7537 static const nsCSSPropertyID kBorderInlineStartIDs[] = {
7538   eCSSProperty_border_inline_start_width,
7539   eCSSProperty_border_inline_start_style,
7540   eCSSProperty_border_inline_start_color
7541 };
7542 static const nsCSSPropertyID kBorderInlineEndIDs[] = {
7543   eCSSProperty_border_inline_end_width,
7544   eCSSProperty_border_inline_end_style,
7545   eCSSProperty_border_inline_end_color
7546 };
7547 static const nsCSSPropertyID kBorderBlockStartIDs[] = {
7548   eCSSProperty_border_block_start_width,
7549   eCSSProperty_border_block_start_style,
7550   eCSSProperty_border_block_start_color
7551 };
7552 static const nsCSSPropertyID kBorderBlockEndIDs[] = {
7553   eCSSProperty_border_block_end_width,
7554   eCSSProperty_border_block_end_style,
7555   eCSSProperty_border_block_end_color
7556 };
7557 static const nsCSSPropertyID kColumnRuleIDs[] = {
7558   eCSSProperty_column_rule_width,
7559   eCSSProperty_column_rule_style,
7560   eCSSProperty_column_rule_color
7561 };
7562 
7563 bool
7564 CSSParserImpl::ParseEnum(nsCSSValue& aValue,
7565                          const KTableEntry aKeywordTable[])
7566 {
7567   nsSubstring* ident = NextIdent();
7568   if (nullptr == ident) {
7569     return false;
7570   }
7571   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
7572   if (eCSSKeyword_UNKNOWN < keyword) {
7573     int32_t value;
7574     if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
7575       aValue.SetIntValue(value, eCSSUnit_Enumerated);
7576       return true;
7577     }
7578   }
7579 
7580   // Put the unknown identifier back and return
7581   UngetToken();
7582   return false;
7583 }
7584 
7585 bool
7586 CSSParserImpl::ParseAlignEnum(nsCSSValue& aValue,
7587                               const KTableEntry aKeywordTable[])
7588 {
7589   MOZ_ASSERT(nsCSSProps::ValueToKeywordEnum(NS_STYLE_ALIGN_BASELINE,
7590                                             aKeywordTable) !=
7591                eCSSKeyword_UNKNOWN,
7592              "Please use ParseEnum instead");
7593   nsSubstring* ident = NextIdent();
7594   if (!ident) {
7595     return false;
7596   }
7597   nsCSSKeyword baselinePrefix = eCSSKeyword_first;
7598   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
7599   if (keyword == eCSSKeyword_first || keyword == eCSSKeyword_last) {
7600     baselinePrefix = keyword;
7601     ident = NextIdent();
7602     if (!ident) {
7603       return false;
7604     }
7605     keyword = nsCSSKeywords::LookupKeyword(*ident);
7606   }
7607   if (eCSSKeyword_UNKNOWN < keyword) {
7608     int32_t value;
7609     if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
7610       if (baselinePrefix == eCSSKeyword_last &&
7611           keyword == eCSSKeyword_baseline) {
7612         value = NS_STYLE_ALIGN_LAST_BASELINE;
7613       }
7614       aValue.SetIntValue(value, eCSSUnit_Enumerated);
7615       return true;
7616     }
7617   }
7618 
7619   // Put the unknown identifier back and return
7620   UngetToken();
7621   return false;
7622 }
7623 
7624 struct UnitInfo {
7625   char name[6];  // needs to be long enough for the longest unit, with
7626                  // terminating null.
7627   uint32_t length;
7628   nsCSSUnit unit;
7629   int32_t type;
7630 };
7631 
7632 #define STR_WITH_LEN(_str) \
7633   _str, sizeof(_str) - 1
7634 
7635 const UnitInfo UnitData[] = {
7636   { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
7637   { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
7638   { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
7639   { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
7640   { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
7641   { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
7642   { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
7643   { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH },
7644   { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
7645   { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH },
7646   { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH },
7647   { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH },
7648   { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH },
7649   { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH },
7650   { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
7651   { STR_WITH_LEN("q"), eCSSUnit_Quarter, VARIANT_LENGTH },
7652   { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
7653   { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
7654   { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
7655   { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE },
7656   { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
7657   { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
7658   { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
7659   { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
7660 };
7661 
7662 #undef STR_WITH_LEN
7663 
7664 bool
7665 CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
7666                                   uint32_t aVariantMask,
7667                                   float aNumber,
7668                                   const nsString& aUnit)
7669 {
7670   nsCSSUnit units;
7671   int32_t   type = 0;
7672   if (!aUnit.IsEmpty()) {
7673     uint32_t i;
7674     for (i = 0; i < ArrayLength(UnitData); ++i) {
7675       if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
7676                                      UnitData[i].length)) {
7677         units = UnitData[i].unit;
7678         type = UnitData[i].type;
7679         break;
7680       }
7681     }
7682 
7683     if (i == ArrayLength(UnitData)) {
7684       // Unknown unit
7685       return false;
7686     }
7687 
7688     if (!mViewportUnitsEnabled &&
7689         (eCSSUnit_ViewportWidth == units  ||
7690          eCSSUnit_ViewportHeight == units ||
7691          eCSSUnit_ViewportMin == units    ||
7692          eCSSUnit_ViewportMax == units)) {
7693       // Viewport units aren't allowed right now, probably because we're
7694       // inside an @page declaration. Fail.
7695       return false;
7696     }
7697 
7698     if ((VARIANT_ABSOLUTE_DIMENSION & aVariantMask) != 0 &&
7699         !nsCSSValue::IsPixelLengthUnit(units)) {
7700       return false;
7701     }
7702   } else {
7703     // Must be a zero number...
7704     NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
7705     if ((VARIANT_LENGTH & aVariantMask) != 0) {
7706       units = eCSSUnit_Pixel;
7707       type = VARIANT_LENGTH;
7708     }
7709     else if ((VARIANT_ANGLE & aVariantMask) != 0) {
7710       NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE,
7711                    "must have allowed zero angle");
7712       units = eCSSUnit_Degree;
7713       type = VARIANT_ANGLE;
7714     }
7715     else {
7716       NS_ERROR("Variant mask does not include dimension; why were we called?");
7717       return false;
7718     }
7719   }
7720   if ((type & aVariantMask) != 0) {
7721     aValue.SetFloatValue(aNumber, units);
7722     return true;
7723   }
7724   return false;
7725 }
7726 
7727 // Note that this does include VARIANT_CALC, which is numeric.  This is
7728 // because calc() parsing, as proposed, drops range restrictions inside
7729 // the calc() expression and clamps the result of the calculation to the
7730 // range.
7731 #define VARIANT_ALL_NONNUMERIC \
7732   VARIANT_KEYWORD | \
7733   VARIANT_COLOR | \
7734   VARIANT_URL | \
7735   VARIANT_STRING | \
7736   VARIANT_COUNTER | \
7737   VARIANT_ATTR | \
7738   VARIANT_IDENTIFIER | \
7739   VARIANT_IDENTIFIER_NO_INHERIT | \
7740   VARIANT_AUTO | \
7741   VARIANT_INHERIT | \
7742   VARIANT_NONE | \
7743   VARIANT_NORMAL | \
7744   VARIANT_SYSFONT | \
7745   VARIANT_GRADIENT | \
7746   VARIANT_TIMING_FUNCTION | \
7747   VARIANT_ALL | \
7748   VARIANT_CALC | \
7749   VARIANT_OPENTYPE_SVG_KEYWORD
7750 
7751 CSSParseResult
7752 CSSParserImpl::ParseVariantWithRestrictions(nsCSSValue& aValue,
7753                                             int32_t aVariantMask,
7754                                             const KTableEntry aKeywordTable[],
7755                                             uint32_t aRestrictions)
7756 {
7757   switch (aRestrictions) {
7758     default:
7759       MOZ_FALLTHROUGH_ASSERT("should not be reached");
7760     case 0:
7761       return ParseVariant(aValue, aVariantMask, aKeywordTable);
7762     case CSS_PROPERTY_VALUE_NONNEGATIVE:
7763       return ParseNonNegativeVariant(aValue, aVariantMask, aKeywordTable);
7764     case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
7765       return ParseOneOrLargerVariant(aValue, aVariantMask, aKeywordTable);
7766   }
7767 }
7768 
7769 // Note that callers passing VARIANT_CALC in aVariantMask will get
7770 // full-range parsing inside the calc() expression, and the code that
7771 // computes the calc will be required to clamp the resulting value to an
7772 // appropriate range.
7773 CSSParseResult
7774 CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue,
7775                                        int32_t aVariantMask,
7776                                        const KTableEntry aKeywordTable[])
7777 {
7778   // The variant mask must only contain non-numeric variants or the ones
7779   // that we specifically handle.
7780   MOZ_ASSERT((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
7781                                VARIANT_NUMBER |
7782                                VARIANT_LENGTH |
7783                                VARIANT_PERCENT |
7784                                VARIANT_INTEGER)) == 0,
7785              "need to update code below to handle additional variants");
7786 
7787   CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable);
7788   if (result == CSSParseResult::Ok) {
7789     if (eCSSUnit_Number == aValue.GetUnit() ||
7790         aValue.IsLengthUnit()){
7791       if (aValue.GetFloatValue() < 0) {
7792         UngetToken();
7793         return CSSParseResult::NotFound;
7794       }
7795     }
7796     else if (aValue.GetUnit() == eCSSUnit_Percent) {
7797       if (aValue.GetPercentValue() < 0) {
7798         UngetToken();
7799         return CSSParseResult::NotFound;
7800       }
7801     } else if (aValue.GetUnit() == eCSSUnit_Integer) {
7802       if (aValue.GetIntValue() < 0) {
7803         UngetToken();
7804         return CSSParseResult::NotFound;
7805       }
7806     }
7807   }
7808   return result;
7809 }
7810 
7811 // Note that callers passing VARIANT_CALC in aVariantMask will get
7812 // full-range parsing inside the calc() expression, and the code that
7813 // computes the calc will be required to clamp the resulting value to an
7814 // appropriate range.
7815 CSSParseResult
7816 CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue,
7817                                        int32_t aVariantMask,
7818                                        const KTableEntry aKeywordTable[])
7819 {
7820   // The variant mask must only contain non-numeric variants or the ones
7821   // that we specifically handle.
7822   MOZ_ASSERT((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
7823                                VARIANT_NUMBER |
7824                                VARIANT_INTEGER)) == 0,
7825              "need to update code below to handle additional variants");
7826 
7827   CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable);
7828   if (result == CSSParseResult::Ok) {
7829     if (aValue.GetUnit() == eCSSUnit_Integer) {
7830       if (aValue.GetIntValue() < 1) {
7831         UngetToken();
7832         return CSSParseResult::NotFound;
7833       }
7834     } else if (eCSSUnit_Number == aValue.GetUnit()) {
7835       if (aValue.GetFloatValue() < 1.0f) {
7836         UngetToken();
7837         return CSSParseResult::NotFound;
7838       }
7839     }
7840   }
7841   return result;
7842 }
7843 
7844 static bool
7845 IsCSSTokenCalcFunction(const nsCSSToken& aToken)
7846 {
7847   return aToken.mType == eCSSToken_Function &&
7848          (aToken.mIdent.LowerCaseEqualsLiteral("calc") ||
7849           aToken.mIdent.LowerCaseEqualsLiteral("-moz-calc"));
7850 }
7851 
7852 // Assigns to aValue iff it returns CSSParseResult::Ok.
7853 CSSParseResult
7854 CSSParserImpl::ParseVariant(nsCSSValue& aValue,
7855                             uint32_t aVariantMask,
7856                             const KTableEntry aKeywordTable[])
7857 {
7858   NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
7859                !(aVariantMask & VARIANT_NUMBER),
7860                "can't distinguish colors from numbers");
7861   NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
7862                !(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)),
7863                "can't distinguish colors from lengths");
7864   NS_ASSERTION(!(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)) ||
7865                !(aVariantMask & VARIANT_NUMBER),
7866                "can't distinguish lengths from numbers");
7867   MOZ_ASSERT(!(aVariantMask & VARIANT_IDENTIFIER) ||
7868              !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT),
7869              "must not set both VARIANT_IDENTIFIER and "
7870              "VARIANT_IDENTIFIER_NO_INHERIT");
7871 
7872   uint32_t lineBefore, colBefore;
7873   if (!GetNextTokenLocation(true, &lineBefore, &colBefore) ||
7874       !GetToken(true)) {
7875     // Must be at EOF.
7876     return CSSParseResult::NotFound;
7877   }
7878 
7879   nsCSSToken* tk = &mToken;
7880   if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
7881       (eCSSToken_Ident == tk->mType)) {
7882     nsCSSKeyword keyword = LookupKeywordPrefixAware(tk->mIdent,
7883                                                     aKeywordTable);
7884 
7885     if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
7886       if ((aVariantMask & VARIANT_AUTO) != 0) {
7887         if (eCSSKeyword_auto == keyword) {
7888           aValue.SetAutoValue();
7889           return CSSParseResult::Ok;
7890         }
7891       }
7892       if ((aVariantMask & VARIANT_INHERIT) != 0) {
7893         // XXX Should we check IsParsingCompoundProperty, or do all
7894         // callers handle it?  (Not all callers set it, though, since
7895         // they want the quirks that are disabled by setting it.)
7896 
7897         // IMPORTANT: If new keywords are added here,
7898         // they probably need to be added in ParseCustomIdent as well.
7899         if (eCSSKeyword_inherit == keyword) {
7900           aValue.SetInheritValue();
7901           return CSSParseResult::Ok;
7902         }
7903         else if (eCSSKeyword_initial == keyword) {
7904           aValue.SetInitialValue();
7905           return CSSParseResult::Ok;
7906         }
7907         else if (eCSSKeyword_unset == keyword &&
7908                  nsLayoutUtils::UnsetValueEnabled()) {
7909           aValue.SetUnsetValue();
7910           return CSSParseResult::Ok;
7911         }
7912       }
7913       if ((aVariantMask & VARIANT_NONE) != 0) {
7914         if (eCSSKeyword_none == keyword) {
7915           aValue.SetNoneValue();
7916           return CSSParseResult::Ok;
7917         }
7918       }
7919       if ((aVariantMask & VARIANT_ALL) != 0) {
7920         if (eCSSKeyword_all == keyword) {
7921           aValue.SetAllValue();
7922           return CSSParseResult::Ok;
7923         }
7924       }
7925       if ((aVariantMask & VARIANT_NORMAL) != 0) {
7926         if (eCSSKeyword_normal == keyword) {
7927           aValue.SetNormalValue();
7928           return CSSParseResult::Ok;
7929         }
7930       }
7931       if ((aVariantMask & VARIANT_SYSFONT) != 0) {
7932         if (eCSSKeyword__moz_use_system_font == keyword &&
7933             !IsParsingCompoundProperty()) {
7934           aValue.SetSystemFontValue();
7935           return CSSParseResult::Ok;
7936         }
7937       }
7938       if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) {
7939         if (sOpentypeSVGEnabled) {
7940           aVariantMask |= VARIANT_KEYWORD;
7941         }
7942       }
7943       if ((aVariantMask & VARIANT_KEYWORD) != 0) {
7944         int32_t value;
7945         if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
7946           aValue.SetIntValue(value, eCSSUnit_Enumerated);
7947           return CSSParseResult::Ok;
7948         }
7949       }
7950     }
7951   }
7952   // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or
7953   // VARIANT_ZERO_ANGLE.
7954   if (((aVariantMask & VARIANT_NUMBER) != 0) &&
7955       (eCSSToken_Number == tk->mType)) {
7956     aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
7957     return CSSParseResult::Ok;
7958   }
7959   if (((aVariantMask & VARIANT_INTEGER) != 0) &&
7960       (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
7961     aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
7962     return CSSParseResult::Ok;
7963   }
7964   if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
7965                         VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
7966        eCSSToken_Dimension == tk->mType) ||
7967       ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
7968        eCSSToken_Number == tk->mType &&
7969        tk->mNumber == 0.0f)) {
7970     if ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 &&
7971         tk->mNumber < 0.0) {
7972         UngetToken();
7973         AssertNextTokenAt(lineBefore, colBefore);
7974         return CSSParseResult::NotFound;
7975     }
7976     if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
7977       return CSSParseResult::Ok;
7978     }
7979     // Put the token back; we didn't parse it, so we shouldn't consume it
7980     UngetToken();
7981     AssertNextTokenAt(lineBefore, colBefore);
7982     return CSSParseResult::NotFound;
7983   }
7984   if (((aVariantMask & VARIANT_PERCENT) != 0) &&
7985       (eCSSToken_Percentage == tk->mType)) {
7986     aValue.SetPercentValue(tk->mNumber);
7987     return CSSParseResult::Ok;
7988   }
7989   if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px
7990     if (((aVariantMask & VARIANT_LENGTH) != 0) &&
7991         (eCSSToken_Number == tk->mType)) {
7992       aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
7993       return CSSParseResult::Ok;
7994     }
7995   }
7996 
7997   if (IsSVGMode() && !IsParsingCompoundProperty()) {
7998     // STANDARD: SVG Spec states that lengths and coordinates can be unitless
7999     // in which case they default to user-units (1 px = 1 user unit)
8000     if (((aVariantMask & VARIANT_LENGTH) != 0) &&
8001         (eCSSToken_Number == tk->mType)) {
8002       aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
8003       return CSSParseResult::Ok;
8004     }
8005   }
8006 
8007   if (((aVariantMask & VARIANT_URL) != 0) &&
8008       eCSSToken_URL == tk->mType) {
8009     SetValueToURL(aValue, tk->mIdent);
8010     return CSSParseResult::Ok;
8011   }
8012   if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
8013       eCSSToken_Function == tk->mType) {
8014     // a generated gradient
8015     nsDependentString tmp(tk->mIdent, 0);
8016     uint8_t gradientFlags = 0;
8017     if (sMozGradientsEnabled &&
8018         StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) {
8019       tmp.Rebind(tmp, 5);
8020       gradientFlags |= eGradient_MozLegacy;
8021     } else if (sWebkitPrefixedAliasesEnabled &&
8022                StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) {
8023       tmp.Rebind(tmp, 8);
8024       gradientFlags |= eGradient_WebkitLegacy;
8025     }
8026     if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) {
8027       tmp.Rebind(tmp, 10);
8028       gradientFlags |= eGradient_Repeating;
8029     }
8030 
8031     if (tmp.LowerCaseEqualsLiteral("linear-gradient")) {
8032       if (!ParseLinearGradient(aValue, gradientFlags)) {
8033         return CSSParseResult::Error;
8034       }
8035       return CSSParseResult::Ok;
8036     }
8037     if (tmp.LowerCaseEqualsLiteral("radial-gradient")) {
8038       if (!ParseRadialGradient(aValue, gradientFlags)) {
8039         return CSSParseResult::Error;
8040       }
8041       return CSSParseResult::Ok;
8042     }
8043     if ((gradientFlags == eGradient_WebkitLegacy) &&
8044         tmp.LowerCaseEqualsLiteral("gradient")) {
8045       // Note: we check gradientFlags using '==' to select *exactly*
8046       // eGradient_WebkitLegacy -- and exclude eGradient_Repeating -- because
8047       // we don't want to accept -webkit-repeating-gradient() expressions.
8048       // (This is not a recognized syntax.)
8049       if (!ParseWebkitGradient(aValue)) {
8050         return CSSParseResult::Error;
8051       }
8052       return CSSParseResult::Ok;
8053     }
8054 
8055     if (ShouldUseUnprefixingService() &&
8056         !gradientFlags &&
8057         StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) {
8058       // Copy 'tmp' into a string on the stack, since as soon as we
8059       // start parsing, its backing store (in "tk") will be overwritten
8060       nsAutoString prefixedFuncName(tmp);
8061       if (!ParseWebkitPrefixedGradientWithService(prefixedFuncName, aValue)) {
8062         return CSSParseResult::Error;
8063       }
8064       return CSSParseResult::Ok;
8065     }
8066   }
8067   if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
8068       eCSSToken_Function == tk->mType &&
8069       tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
8070     if (!ParseImageRect(aValue)) {
8071       return CSSParseResult::Error;
8072     }
8073     return CSSParseResult::Ok;
8074   }
8075   if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
8076       eCSSToken_Function == tk->mType &&
8077       tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) {
8078     if (!ParseElement(aValue)) {
8079       return CSSParseResult::Error;
8080     }
8081     return CSSParseResult::Ok;
8082   }
8083   if ((aVariantMask & VARIANT_COLOR) != 0) {
8084     if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
8085         (eCSSToken_ID == tk->mType) ||
8086         (eCSSToken_Hash == tk->mType) ||
8087         (eCSSToken_Ident == tk->mType) ||
8088         ((eCSSToken_Function == tk->mType) &&
8089          (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
8090           tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
8091           tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
8092           tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
8093     {
8094       // Put token back so that parse color can get it
8095       UngetToken();
8096       return ParseColor(aValue);
8097     }
8098   }
8099   if (((aVariantMask & VARIANT_STRING) != 0) &&
8100       (eCSSToken_String == tk->mType)) {
8101     nsAutoString  buffer;
8102     buffer.Append(tk->mIdent);
8103     aValue.SetStringValue(buffer, eCSSUnit_String);
8104     return CSSParseResult::Ok;
8105   }
8106   if (((aVariantMask &
8107         (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
8108       (eCSSToken_Ident == tk->mType) &&
8109       ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
8110        !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
8111          tk->mIdent.LowerCaseEqualsLiteral("initial") ||
8112          (tk->mIdent.LowerCaseEqualsLiteral("unset") &&
8113           nsLayoutUtils::UnsetValueEnabled())))) {
8114     aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
8115     return CSSParseResult::Ok;
8116   }
8117   if (((aVariantMask & VARIANT_COUNTER) != 0) &&
8118       (eCSSToken_Function == tk->mType) &&
8119       (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
8120        tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
8121     if (!ParseCounter(aValue)) {
8122       return CSSParseResult::Error;
8123     }
8124     return CSSParseResult::Ok;
8125   }
8126   if (((aVariantMask & VARIANT_ATTR) != 0) &&
8127       (eCSSToken_Function == tk->mType) &&
8128       tk->mIdent.LowerCaseEqualsLiteral("attr")) {
8129     if (!ParseAttr(aValue)) {
8130       SkipUntil(')');
8131       return CSSParseResult::Error;
8132     }
8133     return CSSParseResult::Ok;
8134   }
8135   if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
8136       (eCSSToken_Function == tk->mType)) {
8137     if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
8138       if (!ParseTransitionTimingFunctionValues(aValue)) {
8139         SkipUntil(')');
8140         return CSSParseResult::Error;
8141       }
8142       return CSSParseResult::Ok;
8143     }
8144     if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
8145       if (!ParseTransitionStepTimingFunctionValues(aValue)) {
8146         SkipUntil(')');
8147         return CSSParseResult::Error;
8148       }
8149       return CSSParseResult::Ok;
8150     }
8151   }
8152   if ((aVariantMask & VARIANT_CALC) &&
8153       IsCSSTokenCalcFunction(*tk)) {
8154     // calc() currently allows only lengths and percents and number inside it.
8155     // And note that in current implementation, number cannot be mixed with
8156     // length and percent.
8157     if (!ParseCalc(aValue, aVariantMask & VARIANT_LPN)) {
8158       return CSSParseResult::Error;
8159     }
8160     return CSSParseResult::Ok;
8161   }
8162 
8163   UngetToken();
8164   AssertNextTokenAt(lineBefore, colBefore);
8165   return CSSParseResult::NotFound;
8166 }
8167 
8168 bool
8169 CSSParserImpl::ParseCustomIdent(nsCSSValue& aValue,
8170                                 const nsAutoString& aIdentValue,
8171                                 const nsCSSKeyword aExcludedKeywords[],
8172                                 const nsCSSProps::KTableEntry aPropertyKTable[])
8173 {
8174   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aIdentValue);
8175   if (keyword == eCSSKeyword_UNKNOWN) {
8176     // Fast path for identifiers that are not known CSS keywords:
8177     aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
8178     return true;
8179   }
8180   if (keyword == eCSSKeyword_inherit ||
8181       keyword == eCSSKeyword_initial ||
8182       keyword == eCSSKeyword_unset ||
8183       keyword == eCSSKeyword_default ||
8184       (aPropertyKTable &&
8185         nsCSSProps::FindIndexOfKeyword(keyword, aPropertyKTable) >= 0)) {
8186     return false;
8187   }
8188   if (aExcludedKeywords) {
8189     for (uint32_t i = 0;; i++) {
8190       nsCSSKeyword excludedKeyword = aExcludedKeywords[i];
8191       if (excludedKeyword == eCSSKeyword_UNKNOWN) {
8192         break;
8193       }
8194       if (excludedKeyword == keyword) {
8195         return false;
8196       }
8197     }
8198   }
8199   aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
8200   return true;
8201 }
8202 
8203 bool
8204 CSSParserImpl::ParseCounter(nsCSSValue& aValue)
8205 {
8206   nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
8207                     eCSSUnit_Counter : eCSSUnit_Counters);
8208 
8209   // A non-iterative for loop to break out when an error occurs.
8210   for (;;) {
8211     if (!GetToken(true)) {
8212       break;
8213     }
8214     if (eCSSToken_Ident != mToken.mType) {
8215       UngetToken();
8216       break;
8217     }
8218 
8219     RefPtr<nsCSSValue::Array> val =
8220       nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
8221 
8222     val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
8223 
8224     if (eCSSUnit_Counters == unit) {
8225       // must have a comma and then a separator string
8226       if (!ExpectSymbol(',', true) || !GetToken(true)) {
8227         break;
8228       }
8229       if (eCSSToken_String != mToken.mType) {
8230         UngetToken();
8231         break;
8232       }
8233       val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
8234     }
8235 
8236     // get optional type
8237     int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1;
8238     nsCSSValue& type = val->Item(typeItem);
8239     if (ExpectSymbol(',', true)) {
8240       if (!ParseCounterStyleNameValue(type) && !ParseSymbols(type)) {
8241         break;
8242       }
8243     } else {
8244       type.SetStringValue(NS_LITERAL_STRING("decimal"), eCSSUnit_Ident);
8245     }
8246 
8247     if (!ExpectSymbol(')', true)) {
8248       break;
8249     }
8250 
8251     aValue.SetArrayValue(val, unit);
8252     return true;
8253   }
8254 
8255   SkipUntil(')');
8256   return false;
8257 }
8258 
8259 bool
8260 CSSParserImpl::ParseAttr(nsCSSValue& aValue)
8261 {
8262   if (!GetToken(true)) {
8263     return false;
8264   }
8265 
8266   nsAutoString attr;
8267   if (eCSSToken_Ident == mToken.mType) {  // attr name or namespace
8268     nsAutoString  holdIdent(mToken.mIdent);
8269     if (ExpectSymbol('|', false)) {  // namespace
8270       int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent);
8271       if (nameSpaceID == kNameSpaceID_Unknown) {
8272         return false;
8273       }
8274       attr.AppendInt(nameSpaceID, 10);
8275       attr.Append(char16_t('|'));
8276       if (! GetToken(false)) {
8277         REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
8278         return false;
8279       }
8280       if (eCSSToken_Ident == mToken.mType) {
8281         attr.Append(mToken.mIdent);
8282       }
8283       else {
8284         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
8285         UngetToken();
8286         return false;
8287       }
8288     }
8289     else {  // no namespace
8290       attr = holdIdent;
8291     }
8292   }
8293   else if (mToken.IsSymbol('*')) {  // namespace wildcard
8294     // Wildcard namespace makes no sense here and is not allowed
8295     REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
8296     UngetToken();
8297     return false;
8298   }
8299   else if (mToken.IsSymbol('|')) {  // explicit NO namespace
8300     if (! GetToken(false)) {
8301       REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
8302       return false;
8303     }
8304     if (eCSSToken_Ident == mToken.mType) {
8305       attr.Append(mToken.mIdent);
8306     }
8307     else {
8308       REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
8309       UngetToken();
8310       return false;
8311     }
8312   }
8313   else {
8314     REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
8315     UngetToken();
8316     return false;
8317   }
8318   if (!ExpectSymbol(')', true)) {
8319     return false;
8320   }
8321   aValue.SetStringValue(attr, eCSSUnit_Attr);
8322   return true;
8323 }
8324 
8325 bool
8326 CSSParserImpl::ParseSymbols(nsCSSValue& aValue)
8327 {
8328   if (!GetToken(true)) {
8329     return false;
8330   }
8331   if (mToken.mType != eCSSToken_Function &&
8332       !mToken.mIdent.LowerCaseEqualsLiteral("symbols")) {
8333     UngetToken();
8334     return false;
8335   }
8336 
8337   RefPtr<nsCSSValue::Array> params = nsCSSValue::Array::Create(2);
8338   nsCSSValue& type = params->Item(0);
8339   nsCSSValue& symbols = params->Item(1);
8340 
8341   if (!ParseEnum(type, nsCSSProps::kCounterSymbolsSystemKTable)) {
8342     type.SetIntValue(NS_STYLE_COUNTER_SYSTEM_SYMBOLIC, eCSSUnit_Enumerated);
8343   }
8344 
8345   bool first = true;
8346   nsCSSValueList* item = symbols.SetListValue();
8347   for (;;) {
8348     // FIXME Should also include VARIANT_IMAGE. See bug 1071436.
8349     if (!ParseSingleTokenVariant(item->mValue, VARIANT_STRING, nullptr)) {
8350       break;
8351     }
8352     if (ExpectSymbol(')', true)) {
8353       if (first) {
8354         switch (type.GetIntValue()) {
8355           case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
8356           case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
8357             // require at least two symbols
8358             return false;
8359         }
8360       }
8361       aValue.SetArrayValue(params, eCSSUnit_Symbols);
8362       return true;
8363     }
8364     item->mNext = new nsCSSValueList;
8365     item = item->mNext;
8366     first = false;
8367   }
8368 
8369   SkipUntil(')');
8370   return false;
8371 }
8372 
8373 bool
8374 CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL)
8375 {
8376   if (!mSheetPrincipal) {
8377     if (!mSheetPrincipalRequired) {
8378       /* Pretend to succeed.  */
8379       return true;
8380     }
8381 
8382     NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
8383                   "origin principal");
8384     return false;
8385   }
8386 
8387   RefPtr<nsStringBuffer> buffer(nsCSSValue::BufferFromString(aURL));
8388 
8389   // Note: urlVal retains its own reference to |buffer|.
8390   mozilla::css::URLValue *urlVal =
8391     new mozilla::css::URLValue(buffer, mBaseURI, mSheetURI, mSheetPrincipal);
8392   aValue.SetURLValue(urlVal);
8393   return true;
8394 }
8395 
8396 /**
8397  * Parse the image-orientation property, which has the grammar:
8398  * <angle> flip? | flip | from-image
8399  */
8400 bool
8401 CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue)
8402 {
8403   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
8404     // 'inherit', 'initial' and 'unset' must be alone
8405     return true;
8406   }
8407 
8408   // Check for an angle with optional 'flip'.
8409   nsCSSValue angle;
8410   if (ParseSingleTokenVariant(angle, VARIANT_ANGLE, nullptr)) {
8411     nsCSSValue flip;
8412 
8413     if (ParseSingleTokenVariant(flip, VARIANT_KEYWORD,
8414                                 nsCSSProps::kImageOrientationFlipKTable)) {
8415       RefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(2);
8416       array->Item(0) = angle;
8417       array->Item(1) = flip;
8418       aValue.SetArrayValue(array, eCSSUnit_Array);
8419     } else {
8420       aValue = angle;
8421     }
8422 
8423     return true;
8424   }
8425 
8426   // The remaining possibilities (bare 'flip' and 'from-image') are both
8427   // keywords, so we can handle them at the same time.
8428   nsCSSValue keyword;
8429   if (ParseSingleTokenVariant(keyword, VARIANT_KEYWORD,
8430                               nsCSSProps::kImageOrientationKTable)) {
8431     aValue = keyword;
8432     return true;
8433   }
8434 
8435   // All possibilities failed.
8436   return false;
8437 }
8438 
8439 /**
8440  * Parse the arguments of -moz-image-rect() function.
8441  * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
8442  */
8443 bool
8444 CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
8445 {
8446   // A non-iterative for loop to break out when an error occurs.
8447   for (;;) {
8448     nsCSSValue newFunction;
8449     static const uint32_t kNumArgs = 5;
8450     nsCSSValue::Array* func =
8451       newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs);
8452 
8453     // func->Item(0) is reserved for the function name.
8454     nsCSSValue& url    = func->Item(1);
8455     nsCSSValue& top    = func->Item(2);
8456     nsCSSValue& right  = func->Item(3);
8457     nsCSSValue& bottom = func->Item(4);
8458     nsCSSValue& left   = func->Item(5);
8459 
8460     nsAutoString urlString;
8461     if (!ParseURLOrString(urlString) ||
8462         !SetValueToURL(url, urlString) ||
8463         !ExpectSymbol(',', true)) {
8464       break;
8465     }
8466 
8467     static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT;
8468     if (!ParseSingleTokenNonNegativeVariant(top, VARIANT_SIDE, nullptr) ||
8469         !ExpectSymbol(',', true) ||
8470         !ParseSingleTokenNonNegativeVariant(right, VARIANT_SIDE, nullptr) ||
8471         !ExpectSymbol(',', true) ||
8472         !ParseSingleTokenNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) ||
8473         !ExpectSymbol(',', true) ||
8474         !ParseSingleTokenNonNegativeVariant(left, VARIANT_SIDE, nullptr) ||
8475         !ExpectSymbol(')', true))
8476       break;
8477 
8478     aImage = newFunction;
8479     return true;
8480   }
8481 
8482   SkipUntil(')');
8483   return false;
8484 }
8485 
8486 // <element>: -moz-element(# <element_id> )
8487 bool
8488 CSSParserImpl::ParseElement(nsCSSValue& aValue)
8489 {
8490   // A non-iterative for loop to break out when an error occurs.
8491   for (;;) {
8492     if (!GetToken(true))
8493       break;
8494 
8495     if (mToken.mType == eCSSToken_ID) {
8496       aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element);
8497     } else {
8498       UngetToken();
8499       break;
8500     }
8501 
8502     if (!ExpectSymbol(')', true))
8503       break;
8504 
8505     return true;
8506   }
8507 
8508   // If we detect a syntax error, we must match the opening parenthesis of the
8509   // function with the closing parenthesis and skip all the tokens in between.
8510   SkipUntil(')');
8511   return false;
8512 }
8513 
8514 // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
8515 bool
8516 CSSParserImpl::ParseFlex()
8517 {
8518   // First check for inherit / initial / unset
8519   nsCSSValue tmpVal;
8520   if (ParseSingleTokenVariant(tmpVal, VARIANT_INHERIT, nullptr)) {
8521     AppendValue(eCSSProperty_flex_grow, tmpVal);
8522     AppendValue(eCSSProperty_flex_shrink, tmpVal);
8523     AppendValue(eCSSProperty_flex_basis, tmpVal);
8524     return true;
8525   }
8526 
8527   // Next, check for 'none' == '0 0 auto'
8528   if (ParseSingleTokenVariant(tmpVal, VARIANT_NONE, nullptr)) {
8529     AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number));
8530     AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number));
8531     AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto));
8532     return true;
8533   }
8534 
8535   // OK, try parsing our value as individual per-subproperty components:
8536   //   [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
8537 
8538   // Each subproperty has a default value that it takes when it's omitted in a
8539   // "flex" shorthand value. These default values are *only* for the shorthand
8540   // syntax -- they're distinct from the subproperties' own initial values.  We
8541   // start with each subproperty at its default, as if we had "flex: 1 1 0%".
8542   nsCSSValue flexGrow(1.0f, eCSSUnit_Number);
8543   nsCSSValue flexShrink(1.0f, eCSSUnit_Number);
8544   nsCSSValue flexBasis(0.0f, eCSSUnit_Percent);
8545 
8546   // OVERVIEW OF PARSING STRATEGY:
8547   // =============================
8548   // a) Parse the first component as either flex-basis or flex-grow.
8549   // b) If it wasn't flex-grow, parse the _next_ component as flex-grow.
8550   // c) Now we've just parsed flex-grow -- so try parsing the next thing as
8551   //    flex-shrink.
8552   // d) Finally: If we didn't get flex-basis at the beginning, try to parse
8553   //    it now, at the end.
8554   //
8555   // More details in each section below.
8556 
8557   uint32_t flexBasisVariantMask =
8558     (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT));
8559 
8560   // (a) Parse first component. It can be either be a 'flex-basis' value or a
8561   // 'flex-grow' value, so we use the flex-basis-specific variant mask, along
8562   //  with VARIANT_NUMBER to accept 'flex-grow' values.
8563   //
8564   // NOTE: if we encounter unitless 0 here, we *must* interpret it as a
8565   // 'flex-grow' value (a number), *not* as a 'flex-basis' value (a length).
8566   // Conveniently, that's the behavior this combined variant-mask gives us --
8567   // it'll treat unitless 0 as a number. The flexbox spec requires this:
8568   // "a unitless zero that is not already preceded by two flex factors must be
8569   //  interpreted as a flex factor.
8570   if (ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER,
8571                               nsCSSProps::kWidthKTable) != CSSParseResult::Ok) {
8572     // First component was not a valid flex-basis or flex-grow value. Fail.
8573     return false;
8574   }
8575 
8576   // Record what we just parsed as either flex-basis or flex-grow:
8577   bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number);
8578   (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal;
8579 
8580   // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow.
8581   bool doneParsing = false;
8582   if (wasFirstComponentFlexBasis) {
8583     if (ParseNonNegativeNumber(tmpVal)) {
8584       flexGrow = tmpVal;
8585     } else {
8586       // Failed to parse anything after our flex-basis -- that's fine. We can
8587       // skip the remaining parsing.
8588       doneParsing = true;
8589     }
8590   }
8591 
8592   if (!doneParsing) {
8593     // (c) OK -- the last thing we parsed was flex-grow, so look for a
8594     //     flex-shrink in the next position.
8595     if (ParseNonNegativeNumber(tmpVal)) {
8596       flexShrink = tmpVal;
8597     }
8598 
8599     // d) Finally: If we didn't get flex-basis at the beginning, try to parse
8600     //    it now, at the end.
8601     //
8602     // NOTE: If we encounter unitless 0 in this final position, we'll parse it
8603     // as a 'flex-basis' value.  That's OK, because we know it must have
8604     // been "preceded by 2 flex factors" (justification below), which gets us
8605     // out of the spec's requirement of otherwise having to treat unitless 0
8606     // as a flex factor.
8607     //
8608     // JUSTIFICATION: How do we know that a unitless 0 here must have been
8609     // preceded by 2 flex factors? Well, suppose we had a unitless 0 that
8610     // was preceded by only 1 flex factor.  Then, we would have already
8611     // accepted this unitless 0 as the 'flex-shrink' value, up above (since
8612     // it's a valid flex-shrink value), and we'd have moved on to the next
8613     // token (if any). And of course, if we instead had a unitless 0 preceded
8614     // by *no* flex factors (if it were the first token), we would've already
8615     // parsed it in our very first call to ParseNonNegativeVariant().  So, any
8616     // unitless 0 encountered here *must* have been preceded by 2 flex factors.
8617     if (!wasFirstComponentFlexBasis) {
8618       CSSParseResult result =
8619         ParseNonNegativeVariant(tmpVal, flexBasisVariantMask,
8620                                 nsCSSProps::kWidthKTable);
8621       if (result == CSSParseResult::Error) {
8622         return false;
8623       } else if (result == CSSParseResult::Ok) {
8624         flexBasis = tmpVal;
8625       }
8626     }
8627   }
8628 
8629   AppendValue(eCSSProperty_flex_grow,   flexGrow);
8630   AppendValue(eCSSProperty_flex_shrink, flexShrink);
8631   AppendValue(eCSSProperty_flex_basis,  flexBasis);
8632 
8633   return true;
8634 }
8635 
8636 // flex-flow: <flex-direction> || <flex-wrap>
8637 bool
8638 CSSParserImpl::ParseFlexFlow()
8639 {
8640   static const nsCSSPropertyID kFlexFlowSubprops[] = {
8641     eCSSProperty_flex_direction,
8642     eCSSProperty_flex_wrap
8643   };
8644   const size_t numProps = MOZ_ARRAY_LENGTH(kFlexFlowSubprops);
8645   nsCSSValue values[numProps];
8646 
8647   int32_t found = ParseChoice(values, kFlexFlowSubprops, numProps);
8648 
8649   // Bail if we didn't successfully parse anything
8650   if (found < 1) {
8651     return false;
8652   }
8653 
8654   // If either property didn't get an explicit value, use its initial value.
8655   if ((found & 1) == 0) {
8656     values[0].SetIntValue(NS_STYLE_FLEX_DIRECTION_ROW, eCSSUnit_Enumerated);
8657   }
8658   if ((found & 2) == 0) {
8659     values[1].SetIntValue(NS_STYLE_FLEX_WRAP_NOWRAP, eCSSUnit_Enumerated);
8660   }
8661 
8662   // Store these values and declare success!
8663   for (size_t i = 0; i < numProps; i++) {
8664     AppendValue(kFlexFlowSubprops[i], values[i]);
8665   }
8666   return true;
8667 }
8668 
8669 bool
8670 CSSParserImpl::ParseGridAutoFlow()
8671 {
8672   nsCSSValue value;
8673   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
8674     AppendValue(eCSSProperty_grid_auto_flow, value);
8675     return true;
8676   }
8677 
8678   static const int32_t mask[] = {
8679     NS_STYLE_GRID_AUTO_FLOW_ROW | NS_STYLE_GRID_AUTO_FLOW_COLUMN,
8680     MASK_END_VALUE
8681   };
8682   if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) {
8683     return false;
8684   }
8685   int32_t bitField = value.GetIntValue();
8686 
8687   // If neither row nor column is provided, row is assumed.
8688   if (!(bitField & (NS_STYLE_GRID_AUTO_FLOW_ROW |
8689                     NS_STYLE_GRID_AUTO_FLOW_COLUMN))) {
8690     value.SetIntValue(bitField | NS_STYLE_GRID_AUTO_FLOW_ROW,
8691                       eCSSUnit_Enumerated);
8692   }
8693 
8694   AppendValue(eCSSProperty_grid_auto_flow, value);
8695   return true;
8696 }
8697 
8698 static const nsCSSKeyword kGridLineKeywords[] = {
8699   eCSSKeyword_span,
8700   eCSSKeyword_UNKNOWN  // End-of-array marker
8701 };
8702 
8703 CSSParseResult
8704 CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue)
8705 {
8706   if (!ExpectSymbol('[', true)) {
8707     return CSSParseResult::NotFound;
8708   }
8709   if (!GetToken(true) || mToken.IsSymbol(']')) {
8710     return CSSParseResult::Ok;
8711   }
8712   // 'return' so far leaves aValue untouched, to represent an empty list.
8713 
8714   nsCSSValueList* item;
8715   if (aValue.GetUnit() == eCSSUnit_List) {
8716     // Find the end of an existing list.
8717     // The grid-template shorthand uses this, at most once for a given list.
8718 
8719     // NOTE: we could avoid this traversal by somehow keeping around
8720     // a pointer to the last item from the previous call.
8721     // It's not yet clear if this is worth the additional code complexity.
8722     item = aValue.GetListValue();
8723     while (item->mNext) {
8724       item = item->mNext;
8725     }
8726     item->mNext = new nsCSSValueList;
8727     item = item->mNext;
8728   } else {
8729     MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "Unexpected unit");
8730     item = aValue.SetListValue();
8731   }
8732   for (;;) {
8733     if (!(eCSSToken_Ident == mToken.mType &&
8734           ParseCustomIdent(item->mValue, mToken.mIdent, kGridLineKeywords))) {
8735       UngetToken();
8736       SkipUntil(']');
8737       return CSSParseResult::Error;
8738     }
8739     if (!GetToken(true) || mToken.IsSymbol(']')) {
8740       return CSSParseResult::Ok;
8741     }
8742     item->mNext = new nsCSSValueList;
8743     item = item->mNext;
8744   }
8745 }
8746 
8747 // Assuming the 'repeat(' function token has already been consumed,
8748 // parse the rest of repeat(<positive-integer> | auto-fill, <line-names>+)
8749 // Append to the linked list whose end is given by |aTailPtr|,
8750 // and update |aTailPtr| to point to the new end of the list.
8751 bool
8752 CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr)
8753 {
8754   int32_t repetitions;
8755   Maybe<int32_t> repeatAutoEnum;
8756   if (!ParseGridTrackRepeatIntro(true, &repetitions, &repeatAutoEnum)) {
8757     return false;
8758   }
8759   if (repeatAutoEnum.isSome()) {
8760     // Parse exactly one <line-names>.
8761     nsCSSValue listValue;
8762     nsCSSValueList* list = listValue.SetListValue();
8763     if (ParseGridLineNames(list->mValue) != CSSParseResult::Ok) {
8764       return false;
8765     }
8766     if (!ExpectSymbol(')', true)) {
8767       return false;
8768     }
8769     // Instead of hooking up this list into the flat name list as usual,
8770     // we create a pair(Int, List) where the first value is the auto-fill
8771     // keyword and the second is the name list to repeat.
8772     nsCSSValue kwd;
8773     kwd.SetIntValue(repeatAutoEnum.value(), eCSSUnit_Enumerated);
8774     *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
8775     (*aTailPtr)->mValue.SetPairValue(kwd, listValue);
8776     return true;
8777   }
8778 
8779   // Parse at least one <line-names>
8780   nsCSSValueList* tail = *aTailPtr;
8781   do {
8782     tail->mNext = new nsCSSValueList;
8783     tail = tail->mNext;
8784     if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) {
8785       return false;
8786     }
8787   } while (!ExpectSymbol(')', true));
8788   nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext;
8789   nsCSSValueList* lastRepeatedItem = tail;
8790 
8791   // Our repeated items are already in the target list once,
8792   // so they need to be repeated |repetitions - 1| more times.
8793   MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
8794   while (--repetitions) {
8795     nsCSSValueList* repeatedItem = firstRepeatedItem;
8796     for (;;) {
8797       tail->mNext = new nsCSSValueList;
8798       tail = tail->mNext;
8799       tail->mValue = repeatedItem->mValue;
8800       if (repeatedItem == lastRepeatedItem) {
8801         break;
8802       }
8803       repeatedItem = repeatedItem->mNext;
8804     }
8805   }
8806   *aTailPtr = tail;
8807   return true;
8808 }
8809 
8810 // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>?
8811 bool
8812 CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
8813 {
8814   nsCSSValueList* item = aValue.SetListValue();
8815   // This marker distinguishes the value from a <track-list>.
8816   item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID,
8817                            eCSSUnit_Enumerated);
8818   bool haveRepeatAuto = false;
8819   for (;;) {
8820     // First try to parse <name-repeat>, i.e.
8821     // repeat(<positive-integer> | auto-fill, <line-names>+)
8822     if (!GetToken(true)) {
8823       return true;
8824     }
8825     if (mToken.mType == eCSSToken_Function &&
8826         mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
8827       nsCSSValueList* startOfRepeat = item;
8828       if (!ParseGridLineNameListRepeat(&item)) {
8829         SkipUntil(')');
8830         return false;
8831       }
8832       if (startOfRepeat->mNext->mValue.GetUnit() == eCSSUnit_Pair) {
8833         if (haveRepeatAuto) {
8834           REPORT_UNEXPECTED(PEMoreThanOneGridRepeatAutoFillInNameList);
8835           return false;
8836         }
8837         haveRepeatAuto = true;
8838       }
8839     } else {
8840       UngetToken();
8841 
8842       // This was not a repeat() function. Try to parse <line-names>.
8843       nsCSSValue lineNames;
8844       CSSParseResult result = ParseGridLineNames(lineNames);
8845       if (result == CSSParseResult::NotFound) {
8846         return true;
8847       }
8848       if (result == CSSParseResult::Error) {
8849         return false;
8850       }
8851       item->mNext = new nsCSSValueList;
8852       item = item->mNext;
8853       item->mValue = lineNames;
8854     }
8855   }
8856 }
8857 
8858 CSSParseResult
8859 CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
8860 {
8861   CSSParseResult result = ParseNonNegativeVariant(aValue,
8862                             VARIANT_AUTO | VARIANT_LPCALC | VARIANT_KEYWORD,
8863                             nsCSSProps::kGridTrackBreadthKTable);
8864 
8865   if (result == CSSParseResult::Ok ||
8866       result == CSSParseResult::Error) {
8867     return result;
8868   }
8869 
8870   // Attempt to parse <flex> (a dimension with the "fr" unit).
8871   if (!GetToken(true)) {
8872     return CSSParseResult::NotFound;
8873   }
8874   if (!(eCSSToken_Dimension == mToken.mType &&
8875         mToken.mIdent.LowerCaseEqualsLiteral("fr") &&
8876         mToken.mNumber >= 0)) {
8877     UngetToken();
8878     return CSSParseResult::NotFound;
8879   }
8880   aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction);
8881   return CSSParseResult::Ok;
8882 }
8883 
8884 // Parse a <track-size>, or <fixed-size> when aFlags has eFixedTrackSize.
8885 CSSParseResult
8886 CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue,
8887                                   GridTrackSizeFlags aFlags)
8888 {
8889   const bool requireFixedSize =
8890     !!(aFlags & GridTrackSizeFlags::eFixedTrackSize);
8891   // Attempt to parse a single <track-breadth>.
8892   CSSParseResult result = ParseGridTrackBreadth(aValue);
8893   if (requireFixedSize && result == CSSParseResult::Ok &&
8894       !aValue.IsLengthPercentCalcUnit()) {
8895     result = CSSParseResult::Error;
8896   }
8897   if (result == CSSParseResult::Error) {
8898     return result;
8899   }
8900   if (result == CSSParseResult::Ok) {
8901     if (aValue.GetUnit() == eCSSUnit_FlexFraction) {
8902       // Single value <flex> is represented internally as minmax(auto, <flex>).
8903       nsCSSValue minmax;
8904       nsCSSValue::Array* func = minmax.InitFunction(eCSSKeyword_minmax, 2);
8905       func->Item(1).SetAutoValue();
8906       func->Item(2) = aValue;
8907       aValue = minmax;
8908     }
8909     return result;
8910   }
8911 
8912   // Attempt to parse a minmax() or fit-content() function.
8913   if (!GetToken(true)) {
8914     return CSSParseResult::NotFound;
8915   }
8916   if (eCSSToken_Function != mToken.mType) {
8917     UngetToken();
8918     return CSSParseResult::NotFound;
8919   }
8920   if (mToken.mIdent.LowerCaseEqualsLiteral("fit-content")) {
8921     nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_fit_content, 1);
8922     if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok &&
8923         func->Item(1).IsLengthPercentCalcUnit() &&
8924         ExpectSymbol(')', true)) {
8925       return CSSParseResult::Ok;
8926     }
8927     SkipUntil(')');
8928     return CSSParseResult::Error;
8929   }
8930   if (!mToken.mIdent.LowerCaseEqualsLiteral("minmax")) {
8931     UngetToken();
8932     return CSSParseResult::NotFound;
8933   }
8934   nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2);
8935   if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok &&
8936       ExpectSymbol(',', true) &&
8937       ParseGridTrackBreadth(func->Item(2)) == CSSParseResult::Ok &&
8938       ExpectSymbol(')', true)) {
8939     if (requireFixedSize &&
8940         !func->Item(1).IsLengthPercentCalcUnit() &&
8941         !func->Item(2).IsLengthPercentCalcUnit()) {
8942       return CSSParseResult::Error;
8943     }
8944     // Reject <flex> min-sizing.
8945     if (func->Item(1).GetUnit() == eCSSUnit_FlexFraction) {
8946       return CSSParseResult::Error;
8947     }
8948     return CSSParseResult::Ok;
8949   }
8950   SkipUntil(')');
8951   return CSSParseResult::Error;
8952 }
8953 
8954 bool
8955 CSSParserImpl::ParseGridAutoColumnsRows(nsCSSPropertyID aPropID)
8956 {
8957   nsCSSValue value;
8958   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) ||
8959       ParseGridTrackSize(value) == CSSParseResult::Ok) {
8960     AppendValue(aPropID, value);
8961     return true;
8962   }
8963   return false;
8964 }
8965 
8966 bool
8967 CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
8968                                                     const nsCSSValue& aFirstLineNames,
8969                                                     GridTrackListFlags aFlags)
8970 {
8971   nsCSSValueList* firstLineNamesItem = aValue.SetListValue();
8972   firstLineNamesItem->mValue = aFirstLineNames;
8973 
8974   // This function is trying to parse <track-list>, which is
8975   //   [ <line-names>? [ <track-size> | <repeat()> ] ]+ <line-names>?
8976   // and we're already past the first "<line-names>?".
8977   // If aFlags contains eExplicitTrackList then <repeat()> is disallowed.
8978   //
8979   // Each iteration of the following loop attempts to parse either a
8980   // repeat() or a <track-size> expression, and then an (optional)
8981   // <line-names> expression.
8982   //
8983   // The only successful exit point from this loop is the ::NotFound
8984   // case after ParseGridTrackSize(); i.e. we'll greedily parse
8985   // repeat()/<track-size> until we can't find one.
8986   nsCSSValueList* item = firstLineNamesItem;
8987   bool haveRepeatAuto = false;
8988   for (;;) {
8989     // First try to parse repeat()
8990     if (!GetToken(true)) {
8991       break;
8992     }
8993     if (!(aFlags & GridTrackListFlags::eExplicitTrackList) &&
8994         mToken.mType == eCSSToken_Function &&
8995         mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
8996       nsCSSValueList* startOfRepeat = item;
8997       if (!ParseGridTrackListRepeat(&item)) {
8998         SkipUntil(')');
8999         return false;
9000       }
9001       auto firstRepeat = startOfRepeat->mNext;
9002       if (firstRepeat->mValue.GetUnit() == eCSSUnit_Pair) {
9003         if (haveRepeatAuto) {
9004           REPORT_UNEXPECTED(PEMoreThanOneGridRepeatAutoFillFitInTrackList);
9005           return false;
9006         }
9007         haveRepeatAuto = true;
9008         // We're parsing an <auto-track-list>, which requires that all tracks
9009         // are <fixed-size>, so we need to check the ones we've parsed already.
9010         for (nsCSSValueList* list = firstLineNamesItem->mNext;
9011              list != firstRepeat; list = list->mNext) {
9012           if (list->mValue.GetUnit() == eCSSUnit_Function) {
9013             nsCSSValue::Array* func = list->mValue.GetArrayValue();
9014             auto funcName = func->Item(0).GetKeywordValue();
9015             if (funcName == eCSSKeyword_minmax) {
9016               if (!func->Item(1).IsLengthPercentCalcUnit() &&
9017                   !func->Item(2).IsLengthPercentCalcUnit()) {
9018                 return false;
9019               }
9020             } else {
9021               MOZ_ASSERT(funcName == eCSSKeyword_fit_content,
9022                          "Expected minmax() or fit-content() function");
9023               return false; // fit-content() is not a <fixed-size>
9024             }
9025           } else if (!list->mValue.IsLengthPercentCalcUnit()) {
9026             return false;
9027           }
9028           list = list->mNext; // skip line names
9029         }
9030       }
9031     } else {
9032       UngetToken();
9033 
9034       // Not a repeat() function; try to parse <track-size> | <fixed-size>.
9035       nsCSSValue trackSize;
9036       GridTrackSizeFlags flags = haveRepeatAuto
9037         ? GridTrackSizeFlags::eFixedTrackSize
9038         : GridTrackSizeFlags::eDefaultTrackSize;
9039       CSSParseResult result = ParseGridTrackSize(trackSize, flags);
9040       if (result == CSSParseResult::Error) {
9041         return false;
9042       }
9043       if (result == CSSParseResult::NotFound) {
9044         // What we've parsed so far is a valid <track-list>
9045         // (modulo the "at least one <track-size>" check below.)
9046         // Stop here.
9047         break;
9048       }
9049       item->mNext = new nsCSSValueList;
9050       item = item->mNext;
9051       item->mValue = trackSize;
9052 
9053       item->mNext = new nsCSSValueList;
9054       item = item->mNext;
9055     }
9056     if (ParseGridLineNames(item->mValue) == CSSParseResult::Error) {
9057       return false;
9058     }
9059   }
9060 
9061   // Require at least one <track-size>.
9062   if (item == firstLineNamesItem) {
9063     return false;
9064   }
9065 
9066   MOZ_ASSERT(aValue.GetListValue() &&
9067              aValue.GetListValue()->mNext &&
9068              aValue.GetListValue()->mNext->mNext,
9069              "<track-list> should have a minimum length of 3");
9070   return true;
9071 }
9072 
9073 // Takes ownership of |aSecond|
9074 static void
9075 ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond)
9076 {
9077   if (aSecond.GetUnit() == eCSSUnit_Null) {
9078     // Nothing to do.
9079     return;
9080   }
9081   if (aFirst.GetUnit() == eCSSUnit_Null) {
9082     // Empty or omitted <line-names>. Replace it.
9083     aFirst = aSecond;
9084     return;
9085   }
9086 
9087   // Join the two <line-names> lists.
9088   nsCSSValueList* source = aSecond.GetListValue();
9089   nsCSSValueList* target = aFirst.GetListValue();
9090   // Find the end:
9091   while (target->mNext) {
9092     target = target->mNext;
9093   }
9094   // Copy the first name. We can't take ownership of it
9095   // as it'll be destroyed when |aSecond| goes out of scope.
9096   target->mNext = new nsCSSValueList;
9097   target = target->mNext;
9098   target->mValue = source->mValue;
9099   // Move the rest of the linked list.
9100   target->mNext = source->mNext;
9101   source->mNext = nullptr;
9102 }
9103 
9104 // Assuming the 'repeat(' function token has already been consumed,
9105 // parse "repeat( <positive-integer> | auto-fill | auto-fit ,"
9106 // (or "repeat( <positive-integer> | auto-fill ," when aForSubgrid is true)
9107 // and stop after the comma.  Return true when parsing succeeds,
9108 // with aRepetitions set to the number of repetitions and aRepeatAutoEnum set
9109 // to an enum value for auto-fill | auto-fit (it's not set at all when
9110 // <positive-integer> was parsed).
9111 bool
9112 CSSParserImpl::ParseGridTrackRepeatIntro(bool            aForSubgrid,
9113                                          int32_t*        aRepetitions,
9114                                          Maybe<int32_t>* aRepeatAutoEnum)
9115 {
9116   if (!GetToken(true)) {
9117     return false;
9118   }
9119   if (mToken.mType == eCSSToken_Ident) {
9120     if (mToken.mIdent.LowerCaseEqualsLiteral("auto-fill")) {
9121       aRepeatAutoEnum->emplace(NS_STYLE_GRID_REPEAT_AUTO_FILL);
9122     } else if (!aForSubgrid &&
9123                mToken.mIdent.LowerCaseEqualsLiteral("auto-fit")) {
9124       aRepeatAutoEnum->emplace(NS_STYLE_GRID_REPEAT_AUTO_FIT);
9125     } else {
9126       return false;
9127     }
9128     *aRepetitions = 1;
9129   } else if (mToken.mType == eCSSToken_Number) {
9130     if (!(mToken.mIntegerValid &&
9131           mToken.mInteger > 0)) {
9132       return false;
9133     }
9134     *aRepetitions = std::min(mToken.mInteger, GRID_TEMPLATE_MAX_REPETITIONS);
9135   } else {
9136     return false;
9137   }
9138 
9139   if (!ExpectSymbol(',', true)) {
9140     return false;
9141   }
9142   return true;
9143 }
9144 
9145 // Assuming the 'repeat(' function token has already been consumed,
9146 // parse the rest of
9147 // repeat( <positive-integer> | auto-fill | auto-fit ,
9148 //         [ <line-names>? <track-size> ]+ <line-names>? )
9149 // Append to the linked list whose end is given by |aTailPtr|,
9150 // and update |aTailPtr| to point to the new end of the list.
9151 // Note: only one <track-size> is allowed for auto-fill/fit
9152 bool
9153 CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
9154 {
9155   int32_t repetitions;
9156   Maybe<int32_t> repeatAutoEnum;
9157   if (!ParseGridTrackRepeatIntro(false, &repetitions, &repeatAutoEnum)) {
9158     return false;
9159   }
9160 
9161   // Parse [ <line-names>? <track-size> ]+ <line-names>?
9162   // but keep the first and last <line-names> separate
9163   // because they'll need to be joined.
9164   // http://dev.w3.org/csswg/css-grid/#repeat-notation
9165   nsCSSValue firstLineNames;
9166   nsCSSValue trackSize;
9167   nsCSSValue lastLineNames;
9168   // Optional
9169   if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) {
9170     return false;
9171   }
9172   // Required
9173   GridTrackSizeFlags flags = repeatAutoEnum.isSome()
9174     ? GridTrackSizeFlags::eFixedTrackSize
9175     : GridTrackSizeFlags::eDefaultTrackSize;
9176   if (ParseGridTrackSize(trackSize, flags) != CSSParseResult::Ok) {
9177     return false;
9178   }
9179   // Use nsAutoPtr to free the list in case of early return.
9180   nsAutoPtr<nsCSSValueList> firstTrackSizeItemAuto(new nsCSSValueList);
9181   firstTrackSizeItemAuto->mValue = trackSize;
9182 
9183   nsCSSValueList* item = firstTrackSizeItemAuto;
9184   for (;;) {
9185     // Optional
9186     if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) {
9187       return false;
9188     }
9189 
9190     if (ExpectSymbol(')', true)) {
9191       break;
9192     }
9193 
9194     // <auto-repeat> only accepts a single track size:
9195     // <line-names>? <fixed-size> <line-names>?
9196     if (repeatAutoEnum.isSome()) {
9197       REPORT_UNEXPECTED(PEMoreThanOneGridRepeatTrackSize);
9198       return false;
9199     }
9200 
9201     // Required
9202     if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
9203       return false;
9204     }
9205 
9206     item->mNext = new nsCSSValueList;
9207     item = item->mNext;
9208     item->mValue = lastLineNames;
9209     // Do not append to this list at the next iteration.
9210     lastLineNames.Reset();
9211 
9212     item->mNext = new nsCSSValueList;
9213     item = item->mNext;
9214     item->mValue = trackSize;
9215   }
9216   nsCSSValueList* lastTrackSizeItem = item;
9217 
9218   // [ <line-names>? <track-size> ]+ <line-names>?  is now parsed into:
9219   // * firstLineNames: the first <line-names>
9220   // * a linked list of odd length >= 1, from firstTrackSizeItem
9221   //   (the first <track-size>) to lastTrackSizeItem (the last),
9222   //   with the <line-names> sublists in between
9223   // * lastLineNames: the last <line-names>
9224 
9225   if (repeatAutoEnum.isSome()) {
9226     // Instead of hooking up this list into the flat track/name list as usual,
9227     // we create a pair(Int, List) where the first value is the auto-fill/fit
9228     // keyword and the second is the list to repeat.  There are three items
9229     // in this list, the first is the list of line names before the track size,
9230     // the second item is the track size, and the last item is the list of line
9231     // names after the track size.  Note that the line names are NOT merged
9232     // with any line names before/after the repeat() itself.
9233     nsCSSValue listValue;
9234     nsCSSValueList* list = listValue.SetListValue();
9235     list->mValue = firstLineNames;
9236     list = list->mNext = new nsCSSValueList;
9237     list->mValue = trackSize;
9238     list = list->mNext = new nsCSSValueList;
9239     list->mValue = lastLineNames;
9240     nsCSSValue kwd;
9241     kwd.SetIntValue(repeatAutoEnum.value(), eCSSUnit_Enumerated);
9242     *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
9243     (*aTailPtr)->mValue.SetPairValue(kwd, listValue);
9244     // Append an empty list since the caller expects that to represent the names
9245     // that follows the repeat() function.
9246     *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
9247     return true;
9248   }
9249 
9250   // Join the last and first <line-names> (in that order.)
9251   // For example, repeat(3, (a) 100px (b) 200px (c)) results in
9252   // (a) 100px (b) 200px (c a) 100px (b) 200px (c a) 100px (b) 200px (c)
9253   // This is (c a).
9254   // Make deep copies: the originals will be moved.
9255   nsCSSValue joinerLineNames;
9256   {
9257     nsCSSValueList* target = nullptr;
9258     if (lastLineNames.GetUnit() != eCSSUnit_Null) {
9259       target = joinerLineNames.SetListValue();
9260       nsCSSValueList* source = lastLineNames.GetListValue();
9261       for (;;) {
9262         target->mValue = source->mValue;
9263         source = source->mNext;
9264         if (!source) {
9265           break;
9266         }
9267         target->mNext = new nsCSSValueList;
9268         target = target->mNext;
9269       }
9270     }
9271 
9272     if (firstLineNames.GetUnit() != eCSSUnit_Null) {
9273       if (target) {
9274         target->mNext = new nsCSSValueList;
9275         target = target->mNext;
9276       } else {
9277         target = joinerLineNames.SetListValue();
9278       }
9279       nsCSSValueList* source = firstLineNames.GetListValue();
9280       for (;;) {
9281         target->mValue = source->mValue;
9282         source = source->mNext;
9283         if (!source) {
9284           break;
9285         }
9286         target->mNext = new nsCSSValueList;
9287         target = target->mNext;
9288       }
9289     }
9290   }
9291 
9292   // Join our first <line-names> with the one before repeat().
9293   // (a) repeat(1, (b) 20px) expands to (a b) 20px
9294   nsCSSValueList* previousItemBeforeRepeat = *aTailPtr;
9295   ConcatLineNames(previousItemBeforeRepeat->mValue, firstLineNames);
9296 
9297   // Move our linked list
9298   // (first to last <track-size>, with the <line-names> sublists in between).
9299   // This is the first repetition.
9300   NS_ASSERTION(previousItemBeforeRepeat->mNext == nullptr,
9301                "Expected the end of a linked list");
9302   previousItemBeforeRepeat->mNext = firstTrackSizeItemAuto.forget();
9303   nsCSSValueList* firstTrackSizeItem = previousItemBeforeRepeat->mNext;
9304   nsCSSValueList* tail = lastTrackSizeItem;
9305 
9306   // Repeat |repetitions - 1| more times:
9307   // * the joiner <line-names>
9308   // * the linked list
9309   //   (first to last <track-size>, with the <line-names> sublists in between)
9310   MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
9311   while (--repetitions) {
9312     tail->mNext = new nsCSSValueList;
9313     tail = tail->mNext;
9314     tail->mValue = joinerLineNames;
9315 
9316     nsCSSValueList* repeatedItem = firstTrackSizeItem;
9317     for (;;) {
9318       tail->mNext = new nsCSSValueList;
9319       tail = tail->mNext;
9320       tail->mValue = repeatedItem->mValue;
9321       if (repeatedItem == lastTrackSizeItem) {
9322         break;
9323       }
9324       repeatedItem = repeatedItem->mNext;
9325     }
9326   }
9327 
9328   // Finally, move our last <line-names>.
9329   // Any <line-names> immediately after repeat() will append to it.
9330   tail->mNext = new nsCSSValueList;
9331   tail = tail->mNext;
9332   tail->mValue = lastLineNames;
9333 
9334   *aTailPtr = tail;
9335   return true;
9336 }
9337 
9338 bool
9339 CSSParserImpl::ParseGridTrackList(nsCSSPropertyID aPropID,
9340                                   GridTrackListFlags aFlags)
9341 {
9342   nsCSSValue value;
9343   nsCSSValue firstLineNames;
9344   if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
9345       !ParseGridTrackListWithFirstLineNames(value, firstLineNames, aFlags)) {
9346     return false;
9347   }
9348   AppendValue(aPropID, value);
9349   return true;
9350 }
9351 
9352 bool
9353 CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSPropertyID aPropID)
9354 {
9355   nsCSSValue value;
9356   if (ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
9357     AppendValue(aPropID, value);
9358     return true;
9359   }
9360 
9361   nsSubstring* ident = NextIdent();
9362   if (ident) {
9363     if (ident->LowerCaseEqualsLiteral("subgrid")) {
9364       if (!nsLayoutUtils::IsGridTemplateSubgridValueEnabled()) {
9365         REPORT_UNEXPECTED(PESubgridNotSupported);
9366         return false;
9367       }
9368       if (!ParseOptionalLineNameListAfterSubgrid(value)) {
9369         return false;
9370       }
9371       AppendValue(aPropID, value);
9372       return true;
9373     }
9374     UngetToken();
9375   }
9376 
9377   return ParseGridTrackList(aPropID);
9378 }
9379 
9380 bool
9381 CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput,
9382                                           css::GridTemplateAreasValue* aAreas,
9383                                           nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices)
9384 {
9385   aAreas->mTemplates.AppendElement(mToken.mIdent);
9386 
9387   nsCSSGridTemplateAreaScanner scanner(aInput);
9388   nsCSSGridTemplateAreaToken token;
9389   css::GridNamedArea* currentArea = nullptr;
9390   uint32_t row = aAreas->NRows();
9391   // Column numbers starts at 1, but we might not have any, eg
9392   // grid-template-areas:""; which will result in mNColumns == 0.
9393   uint32_t column = 0;
9394   while (scanner.Next(token)) {
9395     ++column;
9396     if (token.isTrash) {
9397       return false;
9398     }
9399     if (currentArea) {
9400       if (token.mName == currentArea->mName) {
9401         if (currentArea->mRowStart == row) {
9402           // Next column in the first row of this named area.
9403           currentArea->mColumnEnd++;
9404         }
9405         continue;
9406       }
9407       // We're exiting |currentArea|, so currentArea is ending at |column|.
9408       // Make sure that this is consistent with currentArea on previous rows:
9409       if (currentArea->mColumnEnd != column) {
9410         NS_ASSERTION(currentArea->mRowStart != row,
9411                      "Inconsistent column end for the first row of a named area.");
9412         // Not a rectangle
9413         return false;
9414       }
9415       currentArea = nullptr;
9416     }
9417     if (!token.mName.IsEmpty()) {
9418       // Named cell that doesn't have a cell with the same name on its left.
9419 
9420       // Check if this is the continuation of an existing named area:
9421       uint32_t index;
9422       if (aAreaIndices.Get(token.mName, &index)) {
9423         MOZ_ASSERT(index < aAreas->mNamedAreas.Length(),
9424                    "Invalid aAreaIndices hash table");
9425         currentArea = &aAreas->mNamedAreas[index];
9426         if (currentArea->mColumnStart != column ||
9427             currentArea->mRowEnd != row) {
9428           // Existing named area, but not forming a rectangle
9429           return false;
9430         }
9431         // Next row of an existing named area
9432         currentArea->mRowEnd++;
9433       } else {
9434         // New named area
9435         aAreaIndices.Put(token.mName, aAreas->mNamedAreas.Length());
9436         currentArea = aAreas->mNamedAreas.AppendElement();
9437         currentArea->mName = token.mName;
9438         // For column or row N (starting at 1),
9439         // the start line is N, the end line is N + 1
9440         currentArea->mColumnStart = column;
9441         currentArea->mColumnEnd = column + 1;
9442         currentArea->mRowStart = row;
9443         currentArea->mRowEnd = row + 1;
9444       }
9445     }
9446   }
9447   if (currentArea && currentArea->mColumnEnd != column + 1) {
9448     NS_ASSERTION(currentArea->mRowStart != row,
9449                  "Inconsistent column end for the first row of a named area.");
9450     // Not a rectangle
9451     return false;
9452   }
9453 
9454   // On the first row, set the number of columns
9455   // that grid-template-areas contributes to the explicit grid.
9456   // On other rows, check that the number of columns is consistent
9457   // between rows.
9458   if (row == 1) {
9459     aAreas->mNColumns = column;
9460   } else if (aAreas->mNColumns != column) {
9461     return false;
9462   }
9463   return true;
9464 }
9465 
9466 bool
9467 CSSParserImpl::ParseGridTemplateAreas()
9468 {
9469   nsCSSValue value;
9470   if (ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
9471     AppendValue(eCSSProperty_grid_template_areas, value);
9472     return true;
9473   }
9474 
9475   RefPtr<css::GridTemplateAreasValue> areas =
9476     new css::GridTemplateAreasValue();
9477   nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
9478   for (;;) {
9479     if (!GetToken(true)) {
9480       break;
9481     }
9482     if (eCSSToken_String != mToken.mType) {
9483       UngetToken();
9484       break;
9485     }
9486     if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
9487       return false;
9488     }
9489   }
9490 
9491   if (areas->NRows() == 0) {
9492     return false;
9493   }
9494 
9495   AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
9496   return true;
9497 }
9498 
9499 // [ auto-flow && dense? ] <'grid-auto-columns'>? |
9500 // <'grid-template-columns'>
9501 bool
9502 CSSParserImpl::ParseGridTemplateColumnsOrAutoFlow(bool aForGridShorthand)
9503 {
9504   if (aForGridShorthand) {
9505     auto res = ParseGridShorthandAutoProps(NS_STYLE_GRID_AUTO_FLOW_COLUMN);
9506     if (res == CSSParseResult::Error) {
9507       return false;
9508     }
9509     if (res == CSSParseResult::Ok) {
9510       nsCSSValue value(eCSSUnit_None);
9511       AppendValue(eCSSProperty_grid_template_columns, value);
9512       return true;
9513     }
9514   }
9515   return ParseGridTemplateColumnsRows(eCSSProperty_grid_template_columns);
9516 }
9517 
9518 bool
9519 CSSParserImpl::ParseGridTemplate(bool aForGridShorthand)
9520 {
9521   // none |
9522   // subgrid |
9523   // <'grid-template-rows'> / <'grid-template-columns'> |
9524   // [ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list>]?
9525   // or additionally when aForGridShorthand is true:
9526   // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>?
9527   nsCSSValue value;
9528   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
9529     AppendValue(eCSSProperty_grid_template_areas, value);
9530     AppendValue(eCSSProperty_grid_template_rows, value);
9531     AppendValue(eCSSProperty_grid_template_columns, value);
9532     return true;
9533   }
9534 
9535   // 'none' can appear either by itself,
9536   // or as the beginning of <'grid-template-rows'> / <'grid-template-columns'>
9537   if (ParseSingleTokenVariant(value, VARIANT_NONE, nullptr)) {
9538     AppendValue(eCSSProperty_grid_template_rows, value);
9539     AppendValue(eCSSProperty_grid_template_areas, value);
9540     if (ExpectSymbol('/', true)) {
9541       return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand);
9542     }
9543     AppendValue(eCSSProperty_grid_template_columns, value);
9544     return true;
9545   }
9546 
9547   // 'subgrid' can appear either by itself,
9548   // or as the beginning of <'grid-template-rows'> / <'grid-template-columns'>
9549   nsSubstring* ident = NextIdent();
9550   if (ident) {
9551     if (ident->LowerCaseEqualsLiteral("subgrid")) {
9552       if (!nsLayoutUtils::IsGridTemplateSubgridValueEnabled()) {
9553         REPORT_UNEXPECTED(PESubgridNotSupported);
9554         return false;
9555       }
9556       if (!ParseOptionalLineNameListAfterSubgrid(value)) {
9557         return false;
9558       }
9559       AppendValue(eCSSProperty_grid_template_rows, value);
9560       AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(eCSSUnit_None));
9561       if (ExpectSymbol('/', true)) {
9562         return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand);
9563       }
9564       if (value.GetListValue()->mNext) {
9565         // Non-empty <line-name-list> after 'subgrid'.
9566         // This is only valid as part of <'grid-template-rows'>,
9567         // which must be followed by a slash.
9568         return false;
9569       }
9570       // 'subgrid' by itself sets both grid-template-rows/columns.
9571       AppendValue(eCSSProperty_grid_template_columns, value);
9572       return true;
9573     }
9574     UngetToken();
9575   }
9576 
9577   // [ <line-names>? ] here is ambiguous:
9578   // it can be either the start of a <track-list> (in a <'grid-template-rows'>),
9579   // or the start of [ <line-names>? <string> <track-size>? <line-names>? ]+
9580   nsCSSValue firstLineNames;
9581   if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
9582       !GetToken(true)) {
9583     return false;
9584   }
9585   if (mToken.mType == eCSSToken_String) {
9586     // It's the [ <line-names>? <string> <track-size>? <line-names>? ]+ case.
9587     if (!ParseGridTemplateAfterString(firstLineNames)) {
9588       return false;
9589     }
9590     // Parse an optional [ / <explicit-track-list> ] as the columns value.
9591     if (ExpectSymbol('/', true)) {
9592       return ParseGridTrackList(eCSSProperty_grid_template_columns,
9593                                 GridTrackListFlags::eExplicitTrackList);
9594     }
9595     value.SetNoneValue(); // absent means 'none'
9596     AppendValue(eCSSProperty_grid_template_columns, value);
9597     return true;
9598   }
9599   UngetToken();
9600 
9601   // Finish parsing <'grid-template-rows'> with the |firstLineNames| we have,
9602   // and then parse a mandatory [ / <'grid-template-columns'> ].
9603   if (!ParseGridTrackListWithFirstLineNames(value, firstLineNames) ||
9604       !ExpectSymbol('/', true)) {
9605     return false;
9606   }
9607   AppendValue(eCSSProperty_grid_template_rows, value);
9608   value.SetNoneValue();
9609   AppendValue(eCSSProperty_grid_template_areas, value);
9610   return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand);
9611 }
9612 
9613 // Helper for parsing the 'grid-template' shorthand:
9614 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
9615 // with a <line-names>? already consumed, stored in |aFirstLineNames|,
9616 // and the current token a <string>
9617 bool
9618 CSSParserImpl::ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames)
9619 {
9620   MOZ_ASSERT(mToken.mType == eCSSToken_String,
9621              "ParseGridTemplateAfterString called with a non-string token");
9622 
9623   nsCSSValue rowsValue;
9624   RefPtr<css::GridTemplateAreasValue> areas =
9625     new css::GridTemplateAreasValue();
9626   nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
9627   nsCSSValueList* rowsItem = rowsValue.SetListValue();
9628   rowsItem->mValue = aFirstLineNames;
9629 
9630   for (;;) {
9631     if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
9632       return false;
9633     }
9634 
9635     rowsItem->mNext = new nsCSSValueList;
9636     rowsItem = rowsItem->mNext;
9637     CSSParseResult result = ParseGridTrackSize(rowsItem->mValue);
9638     if (result == CSSParseResult::Error) {
9639       return false;
9640     }
9641     if (result == CSSParseResult::NotFound) {
9642       rowsItem->mValue.SetAutoValue();
9643     }
9644 
9645     rowsItem->mNext = new nsCSSValueList;
9646     rowsItem = rowsItem->mNext;
9647     result = ParseGridLineNames(rowsItem->mValue);
9648     if (result == CSSParseResult::Error) {
9649       return false;
9650     }
9651     if (result == CSSParseResult::Ok) {
9652       // Append to the same list as the previous call to ParseGridLineNames.
9653       result = ParseGridLineNames(rowsItem->mValue);
9654       if (result == CSSParseResult::Error) {
9655         return false;
9656       }
9657       if (result == CSSParseResult::Ok) {
9658         // Parsed <line-name> twice.
9659         // The property value can not end here, we expect a string next.
9660         if (!GetToken(true)) {
9661           return false;
9662         }
9663         if (eCSSToken_String != mToken.mType) {
9664           UngetToken();
9665           return false;
9666         }
9667         continue;
9668       }
9669     }
9670 
9671     // Did not find a <line-names>.
9672     // Next, we expect either a string or the end of the property value.
9673     if (!GetToken(true)) {
9674       break;
9675     }
9676     if (eCSSToken_String != mToken.mType) {
9677       UngetToken();
9678       break;
9679     }
9680   }
9681 
9682   AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
9683   AppendValue(eCSSProperty_grid_template_rows, rowsValue);
9684   return true;
9685 }
9686 
9687 // <'grid-template'> |
9688 // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? |
9689 // [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'>
9690 bool
9691 CSSParserImpl::ParseGrid()
9692 {
9693   nsCSSValue value;
9694   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
9695     for (const nsCSSPropertyID* subprops =
9696            nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid);
9697          *subprops != eCSSProperty_UNKNOWN; ++subprops) {
9698       AppendValue(*subprops, value);
9699     }
9700     return true;
9701   }
9702 
9703   // https://drafts.csswg.org/css-grid/#grid-shorthand
9704   // "Also, the gutter properties are reset by this shorthand,
9705   //  even though they can't be set by it."
9706   value.SetFloatValue(0.0f, eCSSUnit_Pixel);
9707   AppendValue(eCSSProperty_grid_row_gap, value);
9708   AppendValue(eCSSProperty_grid_column_gap, value);
9709 
9710   // [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'>
9711   auto res = ParseGridShorthandAutoProps(NS_STYLE_GRID_AUTO_FLOW_ROW);
9712   if (res == CSSParseResult::Error) {
9713     return false;
9714   }
9715   if (res == CSSParseResult::Ok) {
9716     value.SetAutoValue();
9717     AppendValue(eCSSProperty_grid_auto_columns, value);
9718     nsCSSValue none(eCSSUnit_None);
9719     AppendValue(eCSSProperty_grid_template_areas, none);
9720     AppendValue(eCSSProperty_grid_template_rows, none);
9721     if (!ExpectSymbol('/', true)) {
9722       return false;
9723     }
9724     return ParseGridTemplateColumnsRows(eCSSProperty_grid_template_columns);
9725   }
9726 
9727   // Set remaining subproperties that might not be set by ParseGridTemplate to
9728   // their initial values and then parse <'grid-template'> |
9729   // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? .
9730   value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_ROW, eCSSUnit_Enumerated);
9731   AppendValue(eCSSProperty_grid_auto_flow, value);
9732   value.SetAutoValue();
9733   AppendValue(eCSSProperty_grid_auto_rows, value);
9734   AppendValue(eCSSProperty_grid_auto_columns, value);
9735   return ParseGridTemplate(true);
9736 }
9737 
9738 // Parse [ auto-flow && dense? ] <'grid-auto-[rows|columns]'>? for the 'grid'
9739 // shorthand.  If aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW then we're
9740 // parsing row values, otherwise column values.
9741 CSSParseResult
9742 CSSParserImpl::ParseGridShorthandAutoProps(int32_t aAutoFlowAxis)
9743 {
9744   MOZ_ASSERT(aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW ||
9745              aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_COLUMN);
9746   if (!GetToken(true)) {
9747     return CSSParseResult::NotFound;
9748   }
9749   // [ auto-flow && dense? ]
9750   int32_t autoFlowValue = 0;
9751   if (mToken.mType == eCSSToken_Ident) {
9752     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
9753     if (keyword == eCSSKeyword_auto_flow) {
9754       autoFlowValue = aAutoFlowAxis;
9755       if (GetToken(true)) {
9756         if (mToken.mType == eCSSToken_Ident &&
9757             nsCSSKeywords::LookupKeyword(mToken.mIdent) == eCSSKeyword_dense) {
9758           autoFlowValue |= NS_STYLE_GRID_AUTO_FLOW_DENSE;
9759         } else {
9760           UngetToken();
9761         }
9762       }
9763     } else if (keyword == eCSSKeyword_dense) {
9764       if (!GetToken(true)) {
9765         return CSSParseResult::Error;
9766       }
9767       if (mToken.mType != eCSSToken_Ident ||
9768           nsCSSKeywords::LookupKeyword(mToken.mIdent) != eCSSKeyword_auto_flow) {
9769         UngetToken();
9770         return CSSParseResult::Error;
9771       }
9772       autoFlowValue = aAutoFlowAxis | NS_STYLE_GRID_AUTO_FLOW_DENSE;
9773     }
9774   }
9775   if (autoFlowValue) {
9776     nsCSSValue value;
9777     value.SetIntValue(autoFlowValue, eCSSUnit_Enumerated);
9778     AppendValue(eCSSProperty_grid_auto_flow, value);
9779   } else {
9780     UngetToken();
9781     return CSSParseResult::NotFound;
9782   }
9783 
9784   // <'grid-auto-[rows|columns]'>?
9785   nsCSSValue autoTrackValue;
9786   CSSParseResult result = ParseGridTrackSize(autoTrackValue);
9787   if (result == CSSParseResult::Error) {
9788     return result;
9789   }
9790   if (result == CSSParseResult::NotFound) {
9791     autoTrackValue.SetAutoValue();
9792   }
9793   AppendValue(aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW ?
9794                 eCSSProperty_grid_auto_rows : eCSSProperty_grid_auto_columns,
9795               autoTrackValue);
9796   return CSSParseResult::Ok;
9797 }
9798 
9799 // Parse a <grid-line>.
9800 // If successful, set aValue to eCSSUnit_Auto,
9801 // or a eCSSUnit_List containing, in that order:
9802 //
9803 // * An optional eCSSUnit_Enumerated marking a "span" keyword.
9804 // * An optional eCSSUnit_Integer
9805 // * An optional eCSSUnit_Ident
9806 //
9807 // At least one of eCSSUnit_Integer or eCSSUnit_Ident is present.
9808 bool
9809 CSSParserImpl::ParseGridLine(nsCSSValue& aValue)
9810 {
9811   //  <grid-line> =
9812   //    auto |
9813   //    <custom-ident> |
9814   //    [ <integer> && <custom-ident>? ] |
9815   //    [ span && [ <integer> || <custom-ident> ] ]
9816   //
9817   // Syntactically, this simplifies to:
9818   //
9819   //  <grid-line> =
9820   //    auto |
9821   //    [ span? && [ <integer> || <custom-ident> ] ]
9822 
9823   if (ParseSingleTokenVariant(aValue, VARIANT_AUTO, nullptr)) {
9824     return true;
9825   }
9826 
9827   bool hasSpan = false;
9828   bool hasIdent = false;
9829   Maybe<int32_t> integer;
9830   nsCSSValue ident;
9831 
9832 #ifdef MOZ_VALGRIND
9833   // Make the contained value be defined even though we really want a
9834   // Nothing here.  This works around an otherwise difficult to avoid
9835   // Memcheck false positive when this is compiled by gcc-5.3 -O2.
9836   // See bug 1301856.
9837   integer.emplace(0);
9838   integer.reset();
9839 #endif
9840 
9841   if (!GetToken(true)) {
9842     return false;
9843   }
9844   if (mToken.mType == eCSSToken_Ident &&
9845       mToken.mIdent.LowerCaseEqualsLiteral("span")) {
9846     hasSpan = true;
9847     if (!GetToken(true)) {
9848       return false;
9849     }
9850   }
9851 
9852   do {
9853     if (!hasIdent &&
9854         mToken.mType == eCSSToken_Ident &&
9855         ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) {
9856       hasIdent = true;
9857     } else if (integer.isNothing() &&
9858                mToken.mType == eCSSToken_Number &&
9859                mToken.mIntegerValid &&
9860                mToken.mInteger != 0) {
9861       integer.emplace(mToken.mInteger);
9862     } else {
9863       UngetToken();
9864       break;
9865     }
9866   } while (!(integer.isSome() && hasIdent) && GetToken(true));
9867 
9868   // Require at least one of <integer> or <custom-ident>
9869   if (!(integer.isSome() || hasIdent)) {
9870     return false;
9871   }
9872 
9873   if (!hasSpan && GetToken(true)) {
9874     if (mToken.mType == eCSSToken_Ident &&
9875         mToken.mIdent.LowerCaseEqualsLiteral("span")) {
9876       hasSpan = true;
9877     } else {
9878       UngetToken();
9879     }
9880   }
9881 
9882   nsCSSValueList* item = aValue.SetListValue();
9883   if (hasSpan) {
9884     // Given "span", a negative <integer> is invalid.
9885     if (integer.isSome() && integer.ref() < 0) {
9886       return false;
9887     }
9888     // '1' here is a dummy value.
9889     // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword.
9890     item->mValue.SetIntValue(1, eCSSUnit_Enumerated);
9891     item->mNext = new nsCSSValueList;
9892     item = item->mNext;
9893   }
9894   if (integer.isSome()) {
9895     item->mValue.SetIntValue(integer.ref(), eCSSUnit_Integer);
9896     if (hasIdent) {
9897       item->mNext = new nsCSSValueList;
9898       item = item->mNext;
9899     }
9900   }
9901   if (hasIdent) {
9902     item->mValue = ident;
9903   }
9904   return true;
9905 }
9906 
9907 bool
9908 CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSPropertyID aPropID)
9909 {
9910   nsCSSValue value;
9911   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) ||
9912       ParseGridLine(value)) {
9913     AppendValue(aPropID, value);
9914     return true;
9915   }
9916   return false;
9917 }
9918 
9919 // If |aFallback| is a List containing a single Ident, set |aValue| to that.
9920 // Otherwise, set |aValue| to Auto.
9921 // Used with |aFallback| from ParseGridLine()
9922 static void
9923 HandleGridLineFallback(const nsCSSValue& aFallback, nsCSSValue& aValue)
9924 {
9925   if (aFallback.GetUnit() == eCSSUnit_List &&
9926       aFallback.GetListValue()->mValue.GetUnit() == eCSSUnit_Ident &&
9927       !aFallback.GetListValue()->mNext) {
9928     aValue = aFallback;
9929   } else {
9930     aValue.SetAutoValue();
9931   }
9932 }
9933 
9934 bool
9935 CSSParserImpl::ParseGridColumnRow(nsCSSPropertyID aStartPropID,
9936                                   nsCSSPropertyID aEndPropID)
9937 {
9938   nsCSSValue value;
9939   nsCSSValue secondValue;
9940   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
9941     AppendValue(aStartPropID, value);
9942     AppendValue(aEndPropID, value);
9943     return true;
9944   }
9945 
9946   if (!ParseGridLine(value)) {
9947     return false;
9948   }
9949   if (GetToken(true)) {
9950     if (mToken.IsSymbol('/')) {
9951       if (ParseGridLine(secondValue)) {
9952         AppendValue(aStartPropID, value);
9953         AppendValue(aEndPropID, secondValue);
9954         return true;
9955       } else {
9956         return false;
9957       }
9958     }
9959     UngetToken();
9960   }
9961 
9962   // A single <custom-ident> is repeated to both properties,
9963   // anything else sets the grid-{column,row}-end property to 'auto'.
9964   HandleGridLineFallback(value, secondValue);
9965 
9966   AppendValue(aStartPropID, value);
9967   AppendValue(aEndPropID, secondValue);
9968   return true;
9969 }
9970 
9971 bool
9972 CSSParserImpl::ParseGridArea()
9973 {
9974   nsCSSValue values[4];
9975   if (ParseSingleTokenVariant(values[0], VARIANT_INHERIT, nullptr)) {
9976     AppendValue(eCSSProperty_grid_row_start, values[0]);
9977     AppendValue(eCSSProperty_grid_column_start, values[0]);
9978     AppendValue(eCSSProperty_grid_row_end, values[0]);
9979     AppendValue(eCSSProperty_grid_column_end, values[0]);
9980     return true;
9981   }
9982 
9983   int32_t i = 0;
9984   for (;;) {
9985     if (!ParseGridLine(values[i])) {
9986       return false;
9987     }
9988     if (++i == 4 || !GetToken(true)) {
9989       break;
9990     }
9991     if (!mToken.IsSymbol('/')) {
9992       UngetToken();
9993       break;
9994     }
9995   }
9996 
9997   MOZ_ASSERT(i >= 1, "should have parsed at least one grid-line (or returned)");
9998   if (i < 2) {
9999     HandleGridLineFallback(values[0], values[1]);
10000   }
10001   if (i < 3) {
10002     HandleGridLineFallback(values[0], values[2]);
10003   }
10004   if (i < 4) {
10005     HandleGridLineFallback(values[1], values[3]);
10006   }
10007 
10008   AppendValue(eCSSProperty_grid_row_start, values[0]);
10009   AppendValue(eCSSProperty_grid_column_start, values[1]);
10010   AppendValue(eCSSProperty_grid_row_end, values[2]);
10011   AppendValue(eCSSProperty_grid_column_end, values[3]);
10012   return true;
10013 }
10014 
10015 bool
10016 CSSParserImpl::ParseGridGap()
10017 {
10018   nsCSSValue first;
10019   if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
10020     AppendValue(eCSSProperty_grid_row_gap, first);
10021     AppendValue(eCSSProperty_grid_column_gap, first);
10022     return true;
10023   }
10024   if (ParseNonNegativeVariant(first, VARIANT_LPCALC, nullptr) !=
10025         CSSParseResult::Ok) {
10026     return false;
10027   }
10028   nsCSSValue second;
10029   auto result = ParseNonNegativeVariant(second, VARIANT_LPCALC, nullptr);
10030   if (result == CSSParseResult::Error) {
10031     return false;
10032   }
10033   AppendValue(eCSSProperty_grid_row_gap, first);
10034   AppendValue(eCSSProperty_grid_column_gap,
10035               result == CSSParseResult::NotFound ? first : second);
10036   return true;
10037 }
10038 
10039 // normal | [<number> <integer>?]
10040 bool
10041 CSSParserImpl::ParseInitialLetter()
10042 {
10043   nsCSSValue value;
10044   // 'inherit', 'initial', 'unset', 'none', and 'normal' must be alone
10045   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NORMAL,
10046                                nullptr)) {
10047     nsCSSValue first, second;
10048     if (!ParseOneOrLargerNumber(first)) {
10049       return false;
10050     }
10051 
10052     if (!ParseOneOrLargerInteger(second)) {
10053       AppendValue(eCSSProperty_initial_letter, first);
10054       return true;
10055     } else {
10056       RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
10057       val->Item(0) = first;
10058       val->Item(1) = second;
10059       value.SetArrayValue(val, eCSSUnit_Array);
10060     }
10061   }
10062   AppendValue(eCSSProperty_initial_letter, value);
10063   return true;
10064 }
10065 
10066 // [ $aTable && <overflow-position>? ] ?
10067 // $aTable is for <content-position> or <self-position>
10068 bool
10069 CSSParserImpl::ParseAlignJustifyPosition(nsCSSValue& aResult,
10070                                          const KTableEntry aTable[])
10071 {
10072   nsCSSValue pos, overflowPos;
10073   int32_t value = 0;
10074   if (ParseEnum(pos, aTable)) {
10075     value = pos.GetIntValue();
10076     if (ParseEnum(overflowPos, nsCSSProps::kAlignOverflowPosition)) {
10077       value |= overflowPos.GetIntValue();
10078     }
10079     aResult.SetIntValue(value, eCSSUnit_Enumerated);
10080     return true;
10081   }
10082   if (ParseEnum(overflowPos, nsCSSProps::kAlignOverflowPosition)) {
10083     if (ParseEnum(pos, aTable)) {
10084       aResult.SetIntValue(pos.GetIntValue() | overflowPos.GetIntValue(),
10085                           eCSSUnit_Enumerated);
10086       return true;
10087     }
10088     return false; // <overflow-position> must be followed by a value in $table
10089   }
10090   return true;
10091 }
10092 
10093 // auto | normal | stretch | <baseline-position> |
10094 // [ <self-position> && <overflow-position>? ] |
10095 // [ legacy && [ left | right | center ] ]
10096 bool
10097 CSSParserImpl::ParseJustifyItems()
10098 {
10099   nsCSSValue value;
10100   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
10101     if (MOZ_UNLIKELY(ParseEnum(value, nsCSSProps::kAlignLegacy))) {
10102       nsCSSValue legacy;
10103       if (!ParseEnum(legacy, nsCSSProps::kAlignLegacyPosition)) {
10104         return false; // leading 'legacy' not followed by 'left' etc is an error
10105       }
10106       value.SetIntValue(value.GetIntValue() | legacy.GetIntValue(),
10107                         eCSSUnit_Enumerated);
10108     } else {
10109       if (!ParseAlignEnum(value, nsCSSProps::kAlignAutoNormalStretchBaseline)) {
10110         if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) ||
10111             value.GetUnit() == eCSSUnit_Null) {
10112           return false;
10113         }
10114         // check for a trailing 'legacy' after 'left' etc
10115         auto val = value.GetIntValue();
10116         if (val == NS_STYLE_JUSTIFY_CENTER ||
10117             val == NS_STYLE_JUSTIFY_LEFT   ||
10118             val == NS_STYLE_JUSTIFY_RIGHT) {
10119           nsCSSValue legacy;
10120           if (ParseEnum(legacy, nsCSSProps::kAlignLegacy)) {
10121             value.SetIntValue(val | legacy.GetIntValue(), eCSSUnit_Enumerated);
10122           }
10123         }
10124       }
10125     }
10126   }
10127   AppendValue(eCSSProperty_justify_items, value);
10128   return true;
10129 }
10130 
10131 // normal | stretch | <baseline-position> |
10132 // [ <overflow-position>? && <self-position> ]
10133 bool
10134 CSSParserImpl::ParseAlignItems()
10135 {
10136   nsCSSValue value;
10137   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
10138     if (!ParseAlignEnum(value, nsCSSProps::kAlignNormalStretchBaseline)) {
10139       if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) ||
10140           value.GetUnit() == eCSSUnit_Null) {
10141         return false;
10142       }
10143     }
10144   }
10145   AppendValue(eCSSProperty_align_items, value);
10146   return true;
10147 }
10148 
10149 // auto | normal | stretch | <baseline-position> |
10150 // [ <overflow-position>? && <self-position> ]
10151 bool
10152 CSSParserImpl::ParseAlignJustifySelf(nsCSSPropertyID aPropID)
10153 {
10154   nsCSSValue value;
10155   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
10156     if (!ParseAlignEnum(value, nsCSSProps::kAlignAutoNormalStretchBaseline)) {
10157       if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) ||
10158           value.GetUnit() == eCSSUnit_Null) {
10159         return false;
10160       }
10161     }
10162   }
10163   AppendValue(aPropID, value);
10164   return true;
10165 }
10166 
10167 // normal | <baseline-position> | [ <content-distribution> ||
10168 //   [ <overflow-position>? && <content-position> ] ]
10169 // (the part after the || is called <*-position> below)
10170 bool
10171 CSSParserImpl::ParseAlignJustifyContent(nsCSSPropertyID aPropID)
10172 {
10173   nsCSSValue value;
10174   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
10175     if (!ParseAlignEnum(value, nsCSSProps::kAlignNormalBaseline)) {
10176       nsCSSValue fallbackValue;
10177       if (!ParseEnum(value, nsCSSProps::kAlignContentDistribution)) {
10178         if (!ParseAlignJustifyPosition(fallbackValue,
10179                                        nsCSSProps::kAlignContentPosition) ||
10180             fallbackValue.GetUnit() == eCSSUnit_Null) {
10181           return false;
10182         }
10183         // optional <content-distribution> after <*-position> ...
10184         if (!ParseEnum(value, nsCSSProps::kAlignContentDistribution)) {
10185           // ... is missing so the <*-position> is the value, not the fallback
10186           value = fallbackValue;
10187           fallbackValue.Reset();
10188         }
10189       } else {
10190         // any optional <*-position> is a fallback value
10191         if (!ParseAlignJustifyPosition(fallbackValue,
10192                                        nsCSSProps::kAlignContentPosition)) {
10193           return false;
10194         }
10195       }
10196       if (fallbackValue.GetUnit() != eCSSUnit_Null) {
10197         auto fallback = fallbackValue.GetIntValue();
10198         value.SetIntValue(value.GetIntValue() |
10199                             (fallback << NS_STYLE_ALIGN_ALL_SHIFT),
10200                           eCSSUnit_Enumerated);
10201       }
10202     }
10203   }
10204   AppendValue(aPropID, value);
10205   return true;
10206 }
10207 
10208 // place-content: [ normal | <baseline-position> | <content-distribution> |
10209 //                  <content-position> ]{1,2}
10210 bool
10211 CSSParserImpl::ParsePlaceContent()
10212 {
10213   nsCSSValue first;
10214   if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
10215     AppendValue(eCSSProperty_align_content, first);
10216     AppendValue(eCSSProperty_justify_content, first);
10217     return true;
10218   }
10219   if (!ParseAlignEnum(first, nsCSSProps::kAlignNormalBaseline) &&
10220       !ParseEnum(first, nsCSSProps::kAlignContentDistribution) &&
10221       !ParseEnum(first, nsCSSProps::kAlignContentPosition)) {
10222     return false;
10223   }
10224   AppendValue(eCSSProperty_align_content, first);
10225   nsCSSValue second;
10226   if (!ParseAlignEnum(second, nsCSSProps::kAlignNormalBaseline) &&
10227       !ParseEnum(second, nsCSSProps::kAlignContentDistribution) &&
10228       !ParseEnum(second, nsCSSProps::kAlignContentPosition)) {
10229     AppendValue(eCSSProperty_justify_content, first);
10230   } else {
10231     AppendValue(eCSSProperty_justify_content, second);
10232   }
10233   return true;
10234 }
10235 
10236 // place-items:  <x> [ auto | <x> ]?
10237 // <x> = [ normal | stretch | <baseline-position> | <self-position> ]
10238 bool
10239 CSSParserImpl::ParsePlaceItems()
10240 {
10241   nsCSSValue first;
10242   if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
10243     AppendValue(eCSSProperty_align_items, first);
10244     AppendValue(eCSSProperty_justify_items, first);
10245     return true;
10246   }
10247   if (!ParseAlignEnum(first, nsCSSProps::kAlignNormalStretchBaseline) &&
10248       !ParseEnum(first, nsCSSProps::kAlignSelfPosition)) {
10249     return false;
10250   }
10251   AppendValue(eCSSProperty_align_items, first);
10252   nsCSSValue second;
10253   // Note: 'auto' is valid for justify-items, but not align-items.
10254   if (!ParseAlignEnum(second, nsCSSProps::kAlignAutoNormalStretchBaseline) &&
10255       !ParseEnum(second, nsCSSProps::kAlignSelfPosition)) {
10256     AppendValue(eCSSProperty_justify_items, first);
10257   } else {
10258     AppendValue(eCSSProperty_justify_items, second);
10259   }
10260   return true;
10261 }
10262 
10263 // place-self: [ auto | normal | stretch | <baseline-position> |
10264 //               <self-position> ]{1,2}
10265 bool
10266 CSSParserImpl::ParsePlaceSelf()
10267 {
10268   nsCSSValue first;
10269   if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
10270     AppendValue(eCSSProperty_align_self, first);
10271     AppendValue(eCSSProperty_justify_self, first);
10272     return true;
10273   }
10274   if (!ParseAlignEnum(first, nsCSSProps::kAlignAutoNormalStretchBaseline) &&
10275       !ParseEnum(first, nsCSSProps::kAlignSelfPosition)) {
10276     return false;
10277   }
10278   AppendValue(eCSSProperty_align_self, first);
10279   nsCSSValue second;
10280   if (!ParseAlignEnum(second, nsCSSProps::kAlignAutoNormalStretchBaseline) &&
10281       !ParseEnum(second, nsCSSProps::kAlignSelfPosition)) {
10282     AppendValue(eCSSProperty_justify_self, first);
10283   } else {
10284     AppendValue(eCSSProperty_justify_self, second);
10285   }
10286   return true;
10287 }
10288 
10289 // <color-stop> : <color> [ <percentage> | <length> ]?
10290 bool
10291 CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
10292 {
10293   nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
10294   CSSParseResult result = ParseVariant(stop->mColor, VARIANT_COLOR, nullptr);
10295   if (result == CSSParseResult::Error) {
10296     return false;
10297   } else if (result == CSSParseResult::NotFound) {
10298     stop->mIsInterpolationHint = true;
10299   }
10300 
10301   // Stop positions do not have to fall between the starting-point and
10302   // ending-point, so we don't use ParseNonNegativeVariant.
10303   result = ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr);
10304   if (result == CSSParseResult::Error) {
10305     return false;
10306   } else if (result == CSSParseResult::NotFound) {
10307     if (stop->mIsInterpolationHint) {
10308       return false;
10309     }
10310     stop->mLocation.SetNoneValue();
10311   }
10312   return true;
10313 }
10314 
10315 // Helper for ParseLinearGradient -- returns true iff aPosition represents a
10316 // box-position value which was parsed with only edge keywords.
10317 // e.g. "left top", or "bottom", but not "left 10px"
10318 //
10319 // (NOTE: Even though callers may want to exclude explicit "center", we still
10320 // need to allow for _CENTER here, because omitted position-values (e.g. the
10321 // x-component of a value like "top") will have been parsed as being *implicit*
10322 // center. The correct way to disallow *explicit* center is to pass "false" for
10323 // ParseBoxPositionValues()'s "aAllowExplicitCenter" parameter, before you
10324 // call this function.)
10325 static bool
10326 IsBoxPositionStrictlyEdgeKeywords(nsCSSValuePair& aPosition)
10327 {
10328   const nsCSSValue& xValue = aPosition.mXValue;
10329   const nsCSSValue& yValue = aPosition.mYValue;
10330   return (xValue.GetUnit() == eCSSUnit_Enumerated &&
10331           (xValue.GetIntValue() & (NS_STYLE_IMAGELAYER_POSITION_LEFT |
10332                                    NS_STYLE_IMAGELAYER_POSITION_CENTER |
10333                                    NS_STYLE_IMAGELAYER_POSITION_RIGHT)) &&
10334           yValue.GetUnit() == eCSSUnit_Enumerated &&
10335           (yValue.GetIntValue() & (NS_STYLE_IMAGELAYER_POSITION_TOP |
10336                                    NS_STYLE_IMAGELAYER_POSITION_CENTER |
10337                                    NS_STYLE_IMAGELAYER_POSITION_BOTTOM)));
10338 }
10339 
10340 // <gradient>
10341 //    : linear-gradient( <linear-gradient-line>? <color-stops> ')'
10342 //    | radial-gradient( <radial-gradient-line>? <color-stops> ')'
10343 //
10344 // <linear-gradient-line> : [ to [left | right] || [top | bottom] ] ,
10345 //                        | <legacy-gradient-line>
10346 // <radial-gradient-line> : [ <shape> || <size> ] [ at <position> ]? ,
10347 //                        | [ at <position> ] ,
10348 //                        | <legacy-gradient-line>? <legacy-shape-size>?
10349 // <shape> : circle | ellipse
10350 // <size> : closest-side | closest-corner | farthest-side | farthest-corner
10351 //        | <length> | [<length> | <percentage>]{2}
10352 //
10353 // <legacy-gradient-line> : [ <position> || <angle>] ,
10354 //
10355 // <legacy-shape-size> : [ <shape> || <legacy-size> ] ,
10356 // <legacy-size> : closest-side | closest-corner | farthest-side
10357 //               | farthest-corner | contain | cover
10358 //
10359 // <color-stops> : <color-stop> , <color-stop> [, <color-stop>]*
10360 bool
10361 CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue,
10362                                    uint8_t aFlags)
10363 {
10364   RefPtr<nsCSSValueGradient> cssGradient
10365     = new nsCSSValueGradient(false, aFlags & eGradient_Repeating);
10366 
10367   if (!GetToken(true)) {
10368     return false;
10369   }
10370 
10371   // Check for "to" syntax (but not if parsing a -webkit-linear-gradient)
10372   if (!(aFlags & eGradient_WebkitLegacy) &&
10373       mToken.mType == eCSSToken_Ident &&
10374       mToken.mIdent.LowerCaseEqualsLiteral("to")) {
10375 
10376     // "to" syntax doesn't allow explicit "center"
10377     if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) {
10378       SkipUntil(')');
10379       return false;
10380     }
10381 
10382     // [ to [left | right] || [top | bottom] ] ,
10383     if (!IsBoxPositionStrictlyEdgeKeywords(cssGradient->mBgPos)) {
10384       SkipUntil(')');
10385       return false;
10386     }
10387 
10388     if (!ExpectSymbol(',', true)) {
10389       SkipUntil(')');
10390       return false;
10391     }
10392 
10393     return ParseGradientColorStops(cssGradient, aValue);
10394   }
10395 
10396   if (!(aFlags & eGradient_AnyLegacy)) {
10397     // We're parsing an unprefixed linear-gradient, and we tried & failed to
10398     // parse a 'to' token above. Put the token back & try to re-parse our
10399     // expression as <angle>? <color-stop-list>
10400     UngetToken();
10401 
10402     // <angle> ,
10403     if (ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) &&
10404         !ExpectSymbol(',', true)) {
10405       SkipUntil(')');
10406       return false;
10407     }
10408 
10409     return ParseGradientColorStops(cssGradient, aValue);
10410   }
10411 
10412   // If we get here, we're parsing a prefixed linear-gradient expression.  Put
10413   // back the first token (which we may have checked for "to" above) and try to
10414   // parse expression as <legacy-gradient-line>? <color-stop-list>
10415   bool haveGradientLine = IsLegacyGradientLine(mToken.mType, mToken.mIdent);
10416   UngetToken();
10417 
10418   if (haveGradientLine) {
10419     // Parse a <legacy-gradient-line>
10420     cssGradient->mIsLegacySyntax = true;
10421     // In -webkit-linear-gradient expressions (handled below), we need to accept
10422     // unitless 0 for angles, to match WebKit/Blink.
10423     int32_t angleFlags = (aFlags & eGradient_WebkitLegacy) ?
10424       VARIANT_ANGLE | VARIANT_ZERO_ANGLE :
10425       VARIANT_ANGLE;
10426 
10427     bool haveAngle =
10428       ParseSingleTokenVariant(cssGradient->mAngle, angleFlags, nullptr);
10429 
10430     // If we got an angle, we might now have a comma, ending the gradient-line.
10431     bool haveAngleComma = haveAngle && ExpectSymbol(',', true);
10432 
10433     // If we're webkit-prefixed & didn't get an angle,
10434     // OR if we're moz-prefixed & didn't get an angle+comma,
10435     // then proceed to parse a box-position.
10436     if (((aFlags & eGradient_WebkitLegacy) && !haveAngle) ||
10437         ((aFlags & eGradient_MozLegacy) && !haveAngleComma)) {
10438       // (Note: 3rd arg controls whether the "center" keyword is allowed.
10439       // -moz-linear-gradient allows it; -webkit-linear-gradient does not.)
10440       if (!ParseBoxPositionValues(cssGradient->mBgPos, false,
10441                                   (aFlags & eGradient_MozLegacy))) {
10442         SkipUntil(')');
10443         return false;
10444       }
10445 
10446       // -webkit-linear-gradient only supports edge keywords here.
10447       if ((aFlags & eGradient_WebkitLegacy) &&
10448           !IsBoxPositionStrictlyEdgeKeywords(cssGradient->mBgPos)) {
10449         SkipUntil(')');
10450         return false;
10451       }
10452 
10453       if (!ExpectSymbol(',', true) &&
10454           // If we didn't already get an angle, and we're not -webkit prefixed,
10455           // we can parse an angle+comma now.  Otherwise it's an error.
10456           (haveAngle ||
10457            (aFlags & eGradient_WebkitLegacy) ||
10458            !ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE,
10459                                     nullptr) ||
10460            // now we better have a comma
10461            !ExpectSymbol(',', true))) {
10462         SkipUntil(')');
10463         return false;
10464       }
10465     }
10466   }
10467 
10468   return ParseGradientColorStops(cssGradient, aValue);
10469 }
10470 
10471 bool
10472 CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue,
10473                                    uint8_t aFlags)
10474 {
10475   RefPtr<nsCSSValueGradient> cssGradient
10476     = new nsCSSValueGradient(true, aFlags & eGradient_Repeating);
10477 
10478   // [ <shape> || <size> ]
10479   bool haveShape =
10480     ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
10481                             nsCSSProps::kRadialGradientShapeKTable);
10482 
10483   bool haveSize =
10484     ParseSingleTokenVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
10485                             (aFlags & eGradient_AnyLegacy) ?
10486                             nsCSSProps::kRadialGradientLegacySizeKTable :
10487                             nsCSSProps::kRadialGradientSizeKTable);
10488   if (haveSize) {
10489     if (!haveShape) {
10490       // <size> <shape>
10491       haveShape =
10492         ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
10493                                 nsCSSProps::kRadialGradientShapeKTable);
10494     }
10495   } else if (!(aFlags & eGradient_AnyLegacy)) {
10496     // Save RadialShape before parsing RadiusX because RadialShape and
10497     // RadiusX share the storage.
10498     int32_t shape =
10499       cssGradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated ?
10500       cssGradient->GetRadialShape().GetIntValue() : -1;
10501     // <length> | [<length> | <percentage>]{2}
10502     cssGradient->mIsExplicitSize = true;
10503     haveSize =
10504       ParseSingleTokenNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP,
10505                                          nullptr);
10506     if (!haveSize) {
10507       // It was not an explicit size after all.
10508       // Note that ParseNonNegativeVariant may have put something
10509       // invalid into our storage, but only in the case where it was
10510       // rejected only for being negative.  Since this means the token
10511       // was a length or a percentage, we know it's not valid syntax
10512       // (which must be a comma, the 'at' keyword, or a color), so we
10513       // know this value will be dropped.  This means it doesn't matter
10514       // that we have something invalid in our storage.
10515       cssGradient->mIsExplicitSize = false;
10516     } else {
10517       // vertical extent is optional
10518       bool haveYSize =
10519         ParseSingleTokenNonNegativeVariant(cssGradient->GetRadiusY(),
10520                                            VARIANT_LP, nullptr);
10521       if (!haveShape) {
10522         nsCSSValue shapeValue;
10523         haveShape =
10524           ParseSingleTokenVariant(shapeValue, VARIANT_KEYWORD,
10525                                   nsCSSProps::kRadialGradientShapeKTable);
10526         if (haveShape) {
10527           shape = shapeValue.GetIntValue();
10528         }
10529       }
10530       if (haveYSize
10531             ? shape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR
10532             : cssGradient->GetRadiusX().GetUnit() == eCSSUnit_Percent ||
10533               shape == NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL) {
10534         SkipUntil(')');
10535         return false;
10536       }
10537     }
10538   }
10539 
10540   if ((haveShape || haveSize) && ExpectSymbol(',', true)) {
10541     // [ <shape> || <size> ] ,
10542     return ParseGradientColorStops(cssGradient, aValue);
10543   }
10544 
10545   if (!GetToken(true)) {
10546     return false;
10547   }
10548 
10549   if (!(aFlags & eGradient_AnyLegacy)) {
10550     if (mToken.mType == eCSSToken_Ident &&
10551         mToken.mIdent.LowerCaseEqualsLiteral("at")) {
10552       // [ <shape> || <size> ]? at <position> ,
10553       if (!ParseBoxPositionValues(cssGradient->mBgPos, false) ||
10554           !ExpectSymbol(',', true)) {
10555         SkipUntil(')');
10556         return false;
10557       }
10558 
10559       return ParseGradientColorStops(cssGradient, aValue);
10560     }
10561 
10562     // <color-stops> only
10563     UngetToken();
10564     return ParseGradientColorStops(cssGradient, aValue);
10565   }
10566   MOZ_ASSERT(!cssGradient->mIsExplicitSize);
10567 
10568   nsCSSTokenType ty = mToken.mType;
10569   nsString id = mToken.mIdent;
10570   UngetToken();
10571 
10572   // <legacy-gradient-line>
10573   bool haveGradientLine = false;
10574   // if we already encountered a shape or size,
10575   // we can not have a gradient-line in legacy syntax
10576   if (!haveShape && !haveSize) {
10577       haveGradientLine = IsLegacyGradientLine(ty, id);
10578   }
10579   if (haveGradientLine) {
10580     // Note: -webkit-radial-gradient() doesn't accept angles.
10581     bool haveAngle = (aFlags & eGradient_WebkitLegacy)
10582       ? false
10583       : ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
10584 
10585     // If we got an angle, we might now have a comma, ending the gradient-line
10586     if (!haveAngle || !ExpectSymbol(',', true)) {
10587       if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
10588         SkipUntil(')');
10589         return false;
10590       }
10591 
10592       if (!ExpectSymbol(',', true) &&
10593           // If we didn't already get an angle, and we're not -webkit prefixed,
10594           // can parse an angle+comma now.  Otherwise it's an error.
10595           (haveAngle ||
10596            (aFlags & eGradient_WebkitLegacy) ||
10597            !ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE,
10598                                     nullptr) ||
10599            // now we better have a comma
10600            !ExpectSymbol(',', true))) {
10601         SkipUntil(')');
10602         return false;
10603       }
10604     }
10605 
10606     if (cssGradient->mAngle.GetUnit() != eCSSUnit_None) {
10607       cssGradient->mIsLegacySyntax = true;
10608     }
10609   }
10610 
10611   // radial gradients might have a shape and size here for legacy syntax
10612   if (!haveShape && !haveSize) {
10613     haveShape =
10614       ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
10615                               nsCSSProps::kRadialGradientShapeKTable);
10616     haveSize =
10617       ParseSingleTokenVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
10618                               nsCSSProps::kRadialGradientLegacySizeKTable);
10619 
10620     // could be in either order
10621     if (!haveShape) {
10622       haveShape =
10623         ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
10624                                 nsCSSProps::kRadialGradientShapeKTable);
10625     }
10626   }
10627 
10628   if ((haveShape || haveSize) && !ExpectSymbol(',', true)) {
10629     SkipUntil(')');
10630     return false;
10631   }
10632 
10633   return ParseGradientColorStops(cssGradient, aValue);
10634 }
10635 
10636 bool
10637 CSSParserImpl::IsLegacyGradientLine(const nsCSSTokenType& aType,
10638                                     const nsString& aId)
10639 {
10640   // N.B. ParseBoxPositionValues is not guaranteed to put back
10641   // everything it scanned if it fails, so we must only call it
10642   // if there is no alternative to consuming a <box-position>.
10643   // ParseVariant, as used here, will either succeed and consume
10644   // a single token, or fail and consume none, so we can be more
10645   // cavalier about calling it.
10646 
10647   bool haveGradientLine = false;
10648   switch (aType) {
10649   case eCSSToken_Percentage:
10650   case eCSSToken_Number:
10651   case eCSSToken_Dimension:
10652     haveGradientLine = true;
10653     break;
10654 
10655   case eCSSToken_Function:
10656     if (aId.LowerCaseEqualsLiteral("calc") ||
10657         aId.LowerCaseEqualsLiteral("-moz-calc")) {
10658       haveGradientLine = true;
10659       break;
10660     }
10661     MOZ_FALLTHROUGH;
10662   case eCSSToken_ID:
10663   case eCSSToken_Hash:
10664     // this is a color
10665     break;
10666 
10667   case eCSSToken_Ident: {
10668     // This is only a gradient line if it's a box position keyword.
10669     nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(aId);
10670     int32_t junk;
10671     if (kw != eCSSKeyword_UNKNOWN &&
10672         nsCSSProps::FindKeyword(kw, nsCSSProps::kImageLayerPositionKTable,
10673                                 junk)) {
10674       haveGradientLine = true;
10675     }
10676     break;
10677   }
10678 
10679   default:
10680     // error
10681     break;
10682   }
10683 
10684   return haveGradientLine;
10685 }
10686 
10687 bool
10688 CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient,
10689                                        nsCSSValue& aValue)
10690 {
10691   // At least two color stops are required
10692   if (!ParseColorStop(aGradient) ||
10693       !ExpectSymbol(',', true) ||
10694       !ParseColorStop(aGradient)) {
10695     SkipUntil(')');
10696     return false;
10697   }
10698 
10699   // Additional color stops
10700   while (ExpectSymbol(',', true)) {
10701     if (!ParseColorStop(aGradient)) {
10702       SkipUntil(')');
10703       return false;
10704     }
10705   }
10706 
10707   if (!ExpectSymbol(')', true)) {
10708     SkipUntil(')');
10709     return false;
10710   }
10711 
10712   // Check if interpolation hints are in the correct location
10713   bool previousPointWasInterpolationHint = true;
10714   for (size_t x = 0; x < aGradient->mStops.Length(); x++) {
10715     bool isInterpolationHint = aGradient->mStops[x].mIsInterpolationHint;
10716     if (isInterpolationHint && previousPointWasInterpolationHint) {
10717       return false;
10718     }
10719     previousPointWasInterpolationHint = isInterpolationHint;
10720   }
10721 
10722   if (previousPointWasInterpolationHint) {
10723     return false;
10724   }
10725 
10726   aValue.SetGradientValue(aGradient);
10727   return true;
10728 }
10729 
10730 // Parses the x or y component of a -webkit-gradient() <point> expression.
10731 // See ParseWebkitGradientPoint() documentation for more.
10732 bool
10733 CSSParserImpl::ParseWebkitGradientPointComponent(nsCSSValue& aComponent,
10734                                                  bool aIsHorizontal)
10735 {
10736   if (!GetToken(true)) {
10737     return false;
10738   }
10739 
10740   // Keyword tables to use for keyword-matching
10741   // (Keyword order is important; we assume the index can be multiplied by 50%
10742   // to convert to a percent-valued component.)
10743   static const nsCSSKeyword kHorizKeywords[] = {
10744     eCSSKeyword_left,   //   0%
10745     eCSSKeyword_center, //  50%
10746     eCSSKeyword_right   // 100%
10747   };
10748   static const nsCSSKeyword kVertKeywords[] = {
10749     eCSSKeyword_top,     //   0%
10750     eCSSKeyword_center,  //  50%
10751     eCSSKeyword_bottom   // 100%
10752   };
10753   static const size_t kNumKeywords = MOZ_ARRAY_LENGTH(kHorizKeywords);
10754   static_assert(kNumKeywords == MOZ_ARRAY_LENGTH(kVertKeywords),
10755                 "Horizontal & vertical keyword tables must have same count");
10756 
10757   // Try to parse the component as a number, or a percent, or a
10758   // keyword-converted-to-percent.
10759   if (mToken.mType == eCSSToken_Number) {
10760     aComponent.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
10761   } else if (mToken.mType == eCSSToken_Percentage) {
10762     aComponent.SetPercentValue(mToken.mNumber);
10763   } else if (mToken.mType == eCSSToken_Ident) {
10764     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
10765     if (keyword == eCSSKeyword_UNKNOWN) {
10766       return false;
10767     }
10768     // Choose our keyword table:
10769     const nsCSSKeyword* kwTable = aIsHorizontal ? kHorizKeywords : kVertKeywords;
10770     // Convert keyword to percent value (0%, 50%, or 100%)
10771     bool didAcceptKeyword = false;
10772     for (size_t i = 0; i < kNumKeywords; i++) {
10773       if (keyword == kwTable[i]) {
10774         // 0%, 50%, or 100%:
10775         aComponent.SetPercentValue(i * 0.5);
10776         didAcceptKeyword = true;
10777         break;
10778       }
10779     }
10780     if (!didAcceptKeyword) {
10781       return false;
10782     }
10783   } else {
10784     // Unrecognized token type. Put it back. (It might be a closing-paren of an
10785     // invalid -webkit-gradient(...) expression, and we need to be sure caller
10786     // can see it & stops parsing at that point.)
10787     UngetToken();
10788     return false;
10789   }
10790 
10791   MOZ_ASSERT(aComponent.GetUnit() == eCSSUnit_Pixel ||
10792              aComponent.GetUnit() == eCSSUnit_Percent,
10793              "If we get here, we should've successfully parsed a number (as a "
10794              "pixel length), a percent, or a keyword (converted to percent)");
10795   return true;
10796 }
10797 
10798 // This function parses a "<point>" expression for -webkit-gradient(...)
10799 // Quoting https://www.webkit.org/blog/175/introducing-css-gradients/ :
10800 //   "A point is a pair of space-separated values.
10801 //    The syntax supports numbers, percentages or
10802 //    the keywords top, bottom, left and right
10803 //    for point values."
10804 //
10805 // Two additional notes:
10806 //  - WebKit also accepts the "center" keyword (not listed in the text above).
10807 //  - WebKit only accepts horizontal-flavored keywords (left/center/right) in
10808 //    the first ("x") component, and vertical-flavored keywords
10809 //    (top/center/bottom) in the second ("y") component. (This is different
10810 //    from the standard gradient syntax, which accepts both orderings, e.g.
10811 //    "top left" as well as "left top".)
10812 bool
10813 CSSParserImpl::ParseWebkitGradientPoint(nsCSSValuePair& aPoint)
10814 {
10815   return ParseWebkitGradientPointComponent(aPoint.mXValue, true) &&
10816     ParseWebkitGradientPointComponent(aPoint.mYValue, false);
10817 }
10818 
10819 // Parse the next token as a <number> (for a <radius> in a -webkit-gradient
10820 // expresison).  Returns true on success; returns false & puts back
10821 // whatever it parsed on failure.
10822 bool
10823 CSSParserImpl::ParseWebkitGradientRadius(float& aRadius)
10824 {
10825   if (!GetToken(true)) {
10826     return false;
10827   }
10828 
10829   if (mToken.mType != eCSSToken_Number) {
10830     UngetToken();
10831     return false;
10832   }
10833 
10834   aRadius = mToken.mNumber;
10835   return true;
10836 }
10837 
10838 // Parse one of:
10839 //  color-stop(number|percent, color)
10840 //  from(color)
10841 //  to(color)
10842 //
10843 // Quoting https://www.webkit.org/blog/175/introducing-css-gradients/ :
10844 //   A stop is a function, color-stop, that takes two arguments, the stop value
10845 //   (either a percentage or a number between 0 and 1.0), and a color (any
10846 //   valid CSS color). In addition the shorthand functions from and to are
10847 //   supported. These functions only require a color argument and are
10848 //   equivalent to color-stop(0, ...) and color-stop(1.0, …) respectively.
10849 bool
10850 CSSParserImpl::ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient)
10851 {
10852   MOZ_ASSERT(aGradient, "null gradient");
10853 
10854   if (!GetToken(true)) {
10855     return false;
10856   }
10857 
10858   // We're expecting color-stop(...), from(...), or to(...) which are all
10859   // functions. Bail if we got anything else.
10860   if (mToken.mType != eCSSToken_Function) {
10861     UngetToken();
10862     return false;
10863   }
10864 
10865   nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
10866 
10867   // Parse color-stop location (or infer it, for shorthands "from"/"to"):
10868   if (mToken.mIdent.LowerCaseEqualsLiteral("color-stop")) {
10869     // Parse stop location, followed by comma.
10870     if (!ParseSingleTokenVariant(stop->mLocation,
10871                                  VARIANT_NUMBER | VARIANT_PERCENT,
10872                                  nullptr) ||
10873         !ExpectSymbol(',', true)) {
10874       SkipUntil(')'); // Skip to end of color-stop(...) expression.
10875       return false;
10876     }
10877 
10878     // If we got a <number>, convert it to percentage for consistency:
10879     if (stop->mLocation.GetUnit() == eCSSUnit_Number) {
10880       stop->mLocation.SetPercentValue(stop->mLocation.GetFloatValue());
10881     }
10882   } else if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
10883     // Shorthand for color-stop(0%, ...)
10884     stop->mLocation.SetPercentValue(0.0f);
10885   } else if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
10886     // Shorthand for color-stop(100%, ...)
10887     stop->mLocation.SetPercentValue(1.0f);
10888   } else {
10889     // Unrecognized function name (invalid for a -webkit-gradient color stop).
10890     UngetToken();
10891     return false;
10892   }
10893 
10894   CSSParseResult result = ParseVariant(stop->mColor, VARIANT_COLOR, nullptr);
10895   if (result != CSSParseResult::Ok ||
10896       (stop->mColor.GetUnit() == eCSSUnit_EnumColor &&
10897        stop->mColor.GetIntValue() == NS_COLOR_CURRENTCOLOR)) {
10898     // Parse failure, or parsed "currentColor" which is forbidden in
10899     // -webkit-gradient for some reason.
10900     SkipUntil(')');
10901     return false;
10902   }
10903 
10904   // Parse color-stop function close-paren
10905   if (!ExpectSymbol(')', true)) {
10906     SkipUntil(')');
10907     return false;
10908   }
10909 
10910   MOZ_ASSERT(stop->mLocation.GetUnit() == eCSSUnit_Percent,
10911              "Should produce only percent-valued stop-locations. "
10912              "(Caller depends on this when sorting color stops.)");
10913 
10914   return true;
10915 }
10916 
10917 // Comparatison function to use for sorting -webkit-gradient() stops by
10918 // location. This function assumes stops have percent-valued locations (and
10919 // CSSParserImpl::ParseWebkitGradientColorStop should enforce this).
10920 static bool
10921 IsColorStopPctLocationLessThan(const nsCSSValueGradientStop& aStop1,
10922                                const nsCSSValueGradientStop& aStop2) {
10923   return (aStop1.mLocation.GetPercentValue() <
10924           aStop2.mLocation.GetPercentValue());
10925 }
10926 
10927 // This function parses a list of comma-separated color-stops for a
10928 // -webkit-gradient(...) expression, and then pads & sorts the list as-needed.
10929 bool
10930 CSSParserImpl::ParseWebkitGradientColorStops(nsCSSValueGradient* aGradient)
10931 {
10932   MOZ_ASSERT(aGradient, "null gradient");
10933 
10934   // Parse any number of ", <color-stop>" expressions. (0 or more)
10935   // Note: This is different from unprefixed gradient syntax, which
10936   // requires at least 2 stops.
10937   while (ExpectSymbol(',', true)) {
10938     if (!ParseWebkitGradientColorStop(aGradient)) {
10939       return false;
10940     }
10941   }
10942 
10943   // Pad up to 2 stops as-needed:
10944   // (Modern gradient expressions are required to have at least 2 stops, so we
10945   // depend on this internally -- e.g. we have an assertion about this in
10946   // nsCSSRendering.cpp. -webkit-gradient syntax allows 0 stops or 1 stop,
10947   // though, so we just pad up to 2 stops in this case).
10948 
10949   // If we have no stops, pad with transparent-black:
10950   if (aGradient->mStops.IsEmpty()) {
10951     nsCSSValueGradientStop* stop1 = aGradient->mStops.AppendElement();
10952     stop1->mColor.SetIntegerColorValue(NS_RGBA(0, 0, 0, 0),
10953                                        eCSSUnit_RGBAColor);
10954     stop1->mLocation.SetPercentValue(0.0f);
10955 
10956     nsCSSValueGradientStop* stop2 = aGradient->mStops.AppendElement();
10957     stop2->mColor.SetIntegerColorValue(NS_RGBA(0, 0, 0, 0),
10958                                        eCSSUnit_RGBAColor);
10959     stop2->mLocation.SetPercentValue(1.0f);
10960   } else if (aGradient->mStops.Length() == 1) {
10961     // Copy whatever the author provided in the first stop:
10962     nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
10963     *stop = aGradient->mStops[0];
10964   } else {
10965     // We have >2 stops. Sort them in order of increasing location.
10966     std::stable_sort(aGradient->mStops.begin(),
10967                      aGradient->mStops.end(),
10968                      IsColorStopPctLocationLessThan);
10969   }
10970   return true;
10971 }
10972 
10973 // Compares aStartCoord to aEndCoord, and returns true iff they share the same
10974 // unit (both pixel, or both percent) and aStartCoord is larger.
10975 static bool
10976 IsWebkitGradientCoordLarger(const nsCSSValue& aStartCoord,
10977                             const nsCSSValue& aEndCoord)
10978 {
10979   if (aStartCoord.GetUnit() == eCSSUnit_Percent &&
10980       aEndCoord.GetUnit() == eCSSUnit_Percent) {
10981     return aStartCoord.GetPercentValue() > aEndCoord.GetPercentValue();
10982   }
10983 
10984   if (aStartCoord.GetUnit() == eCSSUnit_Pixel &&
10985       aEndCoord.GetUnit() == eCSSUnit_Pixel) {
10986     return aStartCoord.GetFloatValue() > aEndCoord.GetFloatValue();
10987   }
10988 
10989   // We can't compare them, since their units differ. Returning false suggests
10990   // that aEndCoord is larger, which is probably a decent guess anyway.
10991   return false;
10992 }
10993 
10994 // Finalize our internal representation of a -webkit-gradient(linear, ...)
10995 // expression, given the parsed points.  (The parsed color stops
10996 // should already be hanging off of the passed-in nsCSSValueGradient.)
10997 //
10998 // Note: linear gradients progress along a line between two points.  The
10999 // -webkit-gradient(linear, ...) syntax lets the author precisely specify the
11000 // starting and ending point. However, our internal gradient structures
11001 // only store one point, and the other point is implicitly its reflection
11002 // across the painted area's center. (The legacy -moz-linear-gradient syntax
11003 // also lets us store an angle.)
11004 //
11005 // In this function, we try to go from the two-point representation to an
11006 // equivalent or approximately-equivalent one-point representation.
11007 void
11008 CSSParserImpl::FinalizeLinearWebkitGradient(nsCSSValueGradient* aGradient,
11009                                             const nsCSSValuePair& aStartPoint,
11010                                             const nsCSSValuePair& aEndPoint)
11011 {
11012   MOZ_ASSERT(!aGradient->mIsRadial, "passed-in gradient must be linear");
11013 
11014   // If the start & end points have the same Y-coordinate, then we can treat
11015   // this as a horizontal gradient progressing towards the center of the left
11016   // or right side.
11017   if (aStartPoint.mYValue == aEndPoint.mYValue) {
11018     aGradient->mBgPos.mYValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_CENTER,
11019                                           eCSSUnit_Enumerated);
11020     if (IsWebkitGradientCoordLarger(aStartPoint.mXValue, aEndPoint.mXValue)) {
11021       aGradient->mBgPos.mXValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_LEFT,
11022                                             eCSSUnit_Enumerated);
11023     } else {
11024       aGradient->mBgPos.mXValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_RIGHT,
11025                                             eCSSUnit_Enumerated);
11026     }
11027     return;
11028   }
11029 
11030   // If the start & end points have the same X-coordinate, then we can treat
11031   // this as a horizontal gradient progressing towards the center of the top
11032   // or bottom side.
11033   if (aStartPoint.mXValue == aEndPoint.mXValue) {
11034     aGradient->mBgPos.mXValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_CENTER,
11035                                           eCSSUnit_Enumerated);
11036     if (IsWebkitGradientCoordLarger(aStartPoint.mYValue, aEndPoint.mYValue)) {
11037       aGradient->mBgPos.mYValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_TOP,
11038                                             eCSSUnit_Enumerated);
11039     } else {
11040       aGradient->mBgPos.mYValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_BOTTOM,
11041                                             eCSSUnit_Enumerated);
11042     }
11043     return;
11044   }
11045 
11046   // OK, the gradient is angled, which means we likely can't represent it
11047   // exactly in |aGradient|, without doing analysis on the two points to
11048   // extract an angle (which we might not be able to do depending on the units
11049   // used).  For now, we'll just do something really basic -- just use the
11050   // first point as if it were the starting point in a legacy
11051   // -moz-linear-gradient() expression. That way, the rendered gradient will
11052   // progress from this first point, towards the center of the covered element,
11053   // to a reflected end point on the far side. Note that we have to use
11054   // mIsLegacySyntax=true for this to work, because standardized (non-legacy)
11055   // gradients place some restrictions on the reference point [namely, that it
11056   // use percent units & be on the border of the element].
11057   aGradient->mIsLegacySyntax = true;
11058   aGradient->mBgPos = aStartPoint;
11059 }
11060 
11061 // Finalize our internal representation of a -webkit-gradient(radial, ...)
11062 // expression, given the parsed points & radii.  (The parsed color-stops
11063 // should already be hanging off of the passed-in nsCSSValueGradient).
11064 void
11065 CSSParserImpl::FinalizeRadialWebkitGradient(nsCSSValueGradient* aGradient,
11066                                             const nsCSSValuePair& aFirstCenter,
11067                                             const nsCSSValuePair& aSecondCenter,
11068                                             const float aFirstRadius,
11069                                             const float aSecondRadius)
11070 {
11071   MOZ_ASSERT(aGradient->mIsRadial, "passed-in gradient must be radial");
11072 
11073   // NOTE: -webkit-gradient(radial, ...) has *two arbitrary circles*, with the
11074   // gradient stretching between the circles' edges.  In contrast, the standard
11075   // syntax (and hence our data structures) can only represent *one* circle,
11076   // with the gradient going from its center to its edge.  To bridge this gap
11077   // in expressiveness, we'll just see which of our two circles is smaller, and
11078   // we'll treat that circle as if it were zero-sized and located at the center
11079   // of the larger circle. Then, we'll be able to use the same data structures
11080   // that we use for the standard radial-gradient syntax.
11081   if (aSecondRadius >= aFirstRadius) {
11082     // Second circle is larger.
11083     aGradient->mBgPos = aSecondCenter;
11084     aGradient->mIsExplicitSize = true;
11085     aGradient->GetRadiusX().SetFloatValue(aSecondRadius, eCSSUnit_Pixel);
11086     return;
11087   }
11088 
11089   // First circle is larger, so we'll have it be the outer circle.
11090   aGradient->mBgPos = aFirstCenter;
11091   aGradient->mIsExplicitSize = true;
11092   aGradient->GetRadiusX().SetFloatValue(aFirstRadius, eCSSUnit_Pixel);
11093 
11094   // For this to work properly (with the earlier color stops attached to the
11095   // first circle), we need to also reverse the color-stop list, so that
11096   // e.g. the author's "from" color is attached to the outer edge (the first
11097   // circle), rather than attached to the center (the collapsed second circle).
11098   std::reverse(aGradient->mStops.begin(), aGradient->mStops.end());
11099 
11100   // And now invert the stop locations:
11101   for (nsCSSValueGradientStop& colorStop : aGradient->mStops) {
11102     float origLocation = colorStop.mLocation.GetPercentValue();
11103     colorStop.mLocation.SetPercentValue(1.0f - origLocation);
11104   }
11105 }
11106 
11107 bool
11108 CSSParserImpl::ParseWebkitGradient(nsCSSValue& aValue)
11109 {
11110   // Parse type of gradient
11111   if (!GetToken(true)) {
11112     return false;
11113   }
11114 
11115   if (mToken.mType != eCSSToken_Ident) {
11116     UngetToken(); // Important; the token might be ")", which we're about to
11117                   // seek to.
11118     SkipUntil(')');
11119     return false;
11120   }
11121 
11122   bool isRadial;
11123   if (mToken.mIdent.LowerCaseEqualsLiteral("radial")) {
11124     isRadial = true;
11125   } else if (mToken.mIdent.LowerCaseEqualsLiteral("linear")) {
11126     isRadial = false;
11127   } else {
11128     // Unrecognized gradient type.
11129     SkipUntil(')');
11130     return false;
11131   }
11132 
11133   // Parse a comma + first point:
11134   nsCSSValuePair firstPoint;
11135   if (!ExpectSymbol(',', true) ||
11136       !ParseWebkitGradientPoint(firstPoint)) {
11137     SkipUntil(')');
11138     return false;
11139   }
11140 
11141   // If radial, parse comma + first radius:
11142   float firstRadius;
11143   if (isRadial) {
11144     if (!ExpectSymbol(',', true) ||
11145         !ParseWebkitGradientRadius(firstRadius)) {
11146       SkipUntil(')');
11147       return false;
11148     }
11149   }
11150 
11151   // Parse a comma + second point:
11152   nsCSSValuePair secondPoint;
11153   if (!ExpectSymbol(',', true) ||
11154       !ParseWebkitGradientPoint(secondPoint)) {
11155     SkipUntil(')');
11156     return false;
11157   }
11158 
11159   // If radial, parse comma + second radius:
11160   float secondRadius;
11161   if (isRadial) {
11162     if (!ExpectSymbol(',', true) ||
11163         !ParseWebkitGradientRadius(secondRadius)) {
11164       SkipUntil(')');
11165       return false;
11166     }
11167   }
11168 
11169   // Construct a nsCSSValueGradient object, and parse color stops into it:
11170   RefPtr<nsCSSValueGradient> cssGradient =
11171     new nsCSSValueGradient(isRadial, false /* aIsRepeating */);
11172 
11173   if (!ParseWebkitGradientColorStops(cssGradient) ||
11174       !ExpectSymbol(')', true)) {
11175     // Failed to parse color-stops, or found trailing junk between them & ')'.
11176     SkipUntil(')');
11177     return false;
11178   }
11179 
11180   // Finish building cssGradient, based on our parsed positioning/sizing info:
11181   if (isRadial) {
11182     FinalizeRadialWebkitGradient(cssGradient, firstPoint, secondPoint,
11183                            firstRadius, secondRadius);
11184   } else {
11185     FinalizeLinearWebkitGradient(cssGradient, firstPoint, secondPoint);
11186   }
11187 
11188   aValue.SetGradientValue(cssGradient);
11189   return true;
11190 }
11191 
11192 bool
11193 CSSParserImpl::ParseWebkitTextStroke()
11194 {
11195   static const nsCSSPropertyID kWebkitTextStrokeIDs[] = {
11196     eCSSProperty__webkit_text_stroke_width,
11197     eCSSProperty__webkit_text_stroke_color
11198   };
11199 
11200   const size_t numProps = MOZ_ARRAY_LENGTH(kWebkitTextStrokeIDs);
11201   nsCSSValue values[numProps];
11202 
11203   int32_t found = ParseChoice(values, kWebkitTextStrokeIDs, numProps);
11204   if (found < 1) {
11205     return false;
11206   }
11207 
11208   if (!(found & 1)) { // Provide default -webkit-text-stroke-width
11209     values[0].SetFloatValue(0, eCSSUnit_Pixel);
11210   }
11211 
11212   if (!(found & 2)) { // Provide default -webkit-text-stroke-color
11213     values[1].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
11214   }
11215 
11216   for (size_t index = 0; index < numProps; ++index) {
11217     AppendValue(kWebkitTextStrokeIDs[index], values[index]);
11218   }
11219 
11220   return true;
11221 }
11222 
11223   int32_t
11224 CSSParserImpl::ParseChoice(nsCSSValue aValues[],
11225                            const nsCSSPropertyID aPropIDs[], int32_t aNumIDs)
11226 {
11227   int32_t found = 0;
11228   nsAutoParseCompoundProperty compound(this);
11229 
11230   int32_t loop;
11231   for (loop = 0; loop < aNumIDs; loop++) {
11232     // Try each property parser in order
11233     int32_t hadFound = found;
11234     int32_t index;
11235     for (index = 0; index < aNumIDs; index++) {
11236       int32_t bit = 1 << index;
11237       if ((found & bit) == 0) {
11238         CSSParseResult result =
11239           ParseSingleValueProperty(aValues[index], aPropIDs[index]);
11240         if (result == CSSParseResult::Error) {
11241           return -1;
11242         }
11243         if (result == CSSParseResult::Ok) {
11244           found |= bit;
11245           // It's more efficient to break since it will reset |hadFound|
11246           // to |found|.  Furthermore, ParseListStyle depends on our going
11247           // through the properties in order for each value..
11248           break;
11249         }
11250       }
11251     }
11252     if (found == hadFound) {  // found nothing new
11253       break;
11254     }
11255   }
11256   if (0 < found) {
11257     if (1 == found) { // only first property
11258       if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
11259         for (loop = 1; loop < aNumIDs; loop++) {
11260           aValues[loop].SetInheritValue();
11261         }
11262         found = ((1 << aNumIDs) - 1);
11263       }
11264       else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
11265         for (loop = 1; loop < aNumIDs; loop++) {
11266           aValues[loop].SetInitialValue();
11267         }
11268         found = ((1 << aNumIDs) - 1);
11269       }
11270       else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset
11271         for (loop = 1; loop < aNumIDs; loop++) {
11272           aValues[loop].SetUnsetValue();
11273         }
11274         found = ((1 << aNumIDs) - 1);
11275       }
11276     }
11277     else {  // more than one value, verify no inherits, initials or unsets
11278       for (loop = 0; loop < aNumIDs; loop++) {
11279         if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
11280           found = -1;
11281           break;
11282         }
11283         else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
11284           found = -1;
11285           break;
11286         }
11287         else if (eCSSUnit_Unset == aValues[loop].GetUnit()) {
11288           found = -1;
11289           break;
11290         }
11291       }
11292     }
11293   }
11294   return found;
11295 }
11296 
11297 void
11298 CSSParserImpl::AppendValue(nsCSSPropertyID aPropID, const nsCSSValue& aValue)
11299 {
11300   mTempData.AddLonghandProperty(aPropID, aValue);
11301 }
11302 
11303 /**
11304  * Parse a "box" property. Box properties have 1 to 4 values. When less
11305  * than 4 values are provided a standard mapping is used to replicate
11306  * existing values.
11307  */
11308 bool
11309 CSSParserImpl::ParseBoxProperties(const nsCSSPropertyID aPropIDs[])
11310 {
11311   // Get up to four values for the property
11312   int32_t count = 0;
11313   nsCSSRect result;
11314   NS_FOR_CSS_SIDES (index) {
11315     CSSParseResult parseResult =
11316       ParseBoxProperty(result.*(nsCSSRect::sides[index]), aPropIDs[index]);
11317     if (parseResult == CSSParseResult::NotFound) {
11318       break;
11319     }
11320     if (parseResult == CSSParseResult::Error) {
11321       return false;
11322     }
11323     count++;
11324   }
11325   if (count == 0) {
11326     return false;
11327   }
11328 
11329   if (1 < count) { // verify no more than single inherit, initial or unset
11330     NS_FOR_CSS_SIDES (index) {
11331       nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
11332       if (eCSSUnit_Inherit == unit ||
11333           eCSSUnit_Initial == unit ||
11334           eCSSUnit_Unset == unit) {
11335         return false;
11336       }
11337     }
11338   }
11339 
11340   // Provide missing values by replicating some of the values found
11341   switch (count) {
11342     case 1: // Make right == top
11343       result.mRight = result.mTop;
11344       MOZ_FALLTHROUGH;
11345     case 2: // Make bottom == top
11346       result.mBottom = result.mTop;
11347       MOZ_FALLTHROUGH;
11348     case 3: // Make left == right
11349       result.mLeft = result.mRight;
11350   }
11351 
11352   NS_FOR_CSS_SIDES (index) {
11353     AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index]));
11354   }
11355   return true;
11356 }
11357 
11358 // Similar to ParseBoxProperties, except there is only one property
11359 // with the result as its value, not four.
11360 bool
11361 CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask,
11362                                        /** outparam */ nsCSSValue& aValue,
11363                                        uint32_t aRestrictions)
11364 {
11365   nsCSSRect& result = aValue.SetRectValue();
11366 
11367   int32_t count = 0;
11368   NS_FOR_CSS_SIDES (index) {
11369     CSSParseResult parseResult =
11370       ParseVariantWithRestrictions(result.*(nsCSSRect::sides[index]),
11371                                    aVariantMask, nullptr,
11372                                    aRestrictions);
11373     if (parseResult == CSSParseResult::NotFound) {
11374       break;
11375     }
11376     if (parseResult == CSSParseResult::Error) {
11377       return false;
11378     }
11379     count++;
11380   }
11381 
11382   if (count == 0) {
11383     return false;
11384   }
11385 
11386   // Provide missing values by replicating some of the values found
11387   switch (count) {
11388     case 1: // Make right == top
11389       result.mRight = result.mTop;
11390       MOZ_FALLTHROUGH;
11391     case 2: // Make bottom == top
11392       result.mBottom = result.mTop;
11393       MOZ_FALLTHROUGH;
11394     case 3: // Make left == right
11395       result.mLeft = result.mRight;
11396   }
11397 
11398   return true;
11399 }
11400 
11401 bool
11402 CSSParserImpl::ParseBoxCornerRadius(nsCSSPropertyID aPropID)
11403 {
11404   nsCSSValue dimenX, dimenY;
11405   // required first value
11406   if (ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr) !=
11407       CSSParseResult::Ok) {
11408     return false;
11409   }
11410 
11411   // optional second value (forbidden if first value is inherit/initial/unset)
11412   if (dimenX.GetUnit() != eCSSUnit_Inherit &&
11413       dimenX.GetUnit() != eCSSUnit_Initial &&
11414       dimenX.GetUnit() != eCSSUnit_Unset) {
11415     if (ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr) ==
11416         CSSParseResult::Error) {
11417       return false;
11418     }
11419   }
11420 
11421   if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) {
11422     AppendValue(aPropID, dimenX);
11423   } else {
11424     nsCSSValue value;
11425     value.SetPairValue(dimenX, dimenY);
11426     AppendValue(aPropID, value);
11427   }
11428   return true;
11429 }
11430 
11431 bool
11432 CSSParserImpl::ParseBoxCornerRadiiInternals(nsCSSValue array[])
11433 {
11434   // Rectangles are used as scratch storage.
11435   // top => top-left, right => top-right,
11436   // bottom => bottom-right, left => bottom-left.
11437   nsCSSRect dimenX, dimenY;
11438   int32_t countX = 0, countY = 0;
11439 
11440   NS_FOR_CSS_SIDES (side) {
11441     CSSParseResult result =
11442       ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side],
11443                               (side > 0 ? 0 : VARIANT_INHERIT) |
11444                                 VARIANT_LP | VARIANT_CALC,
11445                               nullptr);
11446     if (result == CSSParseResult::Error) {
11447       return false;
11448     } else if (result == CSSParseResult::NotFound) {
11449       break;
11450     }
11451     countX++;
11452   }
11453   if (countX == 0)
11454     return false;
11455 
11456   if (ExpectSymbol('/', true)) {
11457     NS_FOR_CSS_SIDES (side) {
11458       CSSParseResult result =
11459         ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side],
11460                                 VARIANT_LP | VARIANT_CALC, nullptr);
11461       if (result == CSSParseResult::Error) {
11462         return false;
11463       } else if (result == CSSParseResult::NotFound) {
11464         break;
11465       }
11466       countY++;
11467     }
11468     if (countY == 0)
11469       return false;
11470   }
11471 
11472   // if 'initial', 'inherit' or 'unset' was used, it must be the only value
11473   if (countX > 1 || countY > 0) {
11474     nsCSSUnit unit = dimenX.mTop.GetUnit();
11475     if (eCSSUnit_Inherit == unit ||
11476         eCSSUnit_Initial == unit ||
11477         eCSSUnit_Unset == unit)
11478       return false;
11479   }
11480 
11481   // if we have no Y-values, use the X-values
11482   if (countY == 0) {
11483     dimenY = dimenX;
11484     countY = countX;
11485   }
11486 
11487   // Provide missing values by replicating some of the values found
11488   switch (countX) {
11489     case 1: // Make top-right same as top-left
11490       dimenX.mRight = dimenX.mTop;
11491       MOZ_FALLTHROUGH;
11492     case 2: // Make bottom-right same as top-left
11493       dimenX.mBottom = dimenX.mTop;
11494       MOZ_FALLTHROUGH;
11495     case 3: // Make bottom-left same as top-right
11496       dimenX.mLeft = dimenX.mRight;
11497   }
11498 
11499   switch (countY) {
11500     case 1: // Make top-right same as top-left
11501       dimenY.mRight = dimenY.mTop;
11502       MOZ_FALLTHROUGH;
11503     case 2: // Make bottom-right same as top-left
11504       dimenY.mBottom = dimenY.mTop;
11505       MOZ_FALLTHROUGH;
11506     case 3: // Make bottom-left same as top-right
11507       dimenY.mLeft = dimenY.mRight;
11508   }
11509 
11510   NS_FOR_CSS_SIDES(side) {
11511     nsCSSValue& x = dimenX.*nsCSSRect::sides[side];
11512     nsCSSValue& y = dimenY.*nsCSSRect::sides[side];
11513 
11514     if (x == y) {
11515       array[side] = x;
11516     } else {
11517       nsCSSValue pair;
11518       pair.SetPairValue(x, y);
11519       array[side] = pair;
11520     }
11521   }
11522   return true;
11523 }
11524 
11525 bool
11526 CSSParserImpl::ParseBoxCornerRadii(const nsCSSPropertyID aPropIDs[])
11527 {
11528   nsCSSValue value[4];
11529   if (!ParseBoxCornerRadiiInternals(value)) {
11530     return false;
11531   }
11532 
11533   NS_FOR_CSS_SIDES(side) {
11534     AppendValue(aPropIDs[side], value[side]);
11535   }
11536   return true;
11537 }
11538 
11539 // These must be in CSS order (top,right,bottom,left) for indexing to work
11540 static const nsCSSPropertyID kBorderStyleIDs[] = {
11541   eCSSProperty_border_top_style,
11542   eCSSProperty_border_right_style,
11543   eCSSProperty_border_bottom_style,
11544   eCSSProperty_border_left_style
11545 };
11546 static const nsCSSPropertyID kBorderWidthIDs[] = {
11547   eCSSProperty_border_top_width,
11548   eCSSProperty_border_right_width,
11549   eCSSProperty_border_bottom_width,
11550   eCSSProperty_border_left_width
11551 };
11552 static const nsCSSPropertyID kBorderColorIDs[] = {
11553   eCSSProperty_border_top_color,
11554   eCSSProperty_border_right_color,
11555   eCSSProperty_border_bottom_color,
11556   eCSSProperty_border_left_color
11557 };
11558 static const nsCSSPropertyID kBorderRadiusIDs[] = {
11559   eCSSProperty_border_top_left_radius,
11560   eCSSProperty_border_top_right_radius,
11561   eCSSProperty_border_bottom_right_radius,
11562   eCSSProperty_border_bottom_left_radius
11563 };
11564 static const nsCSSPropertyID kOutlineRadiusIDs[] = {
11565   eCSSProperty__moz_outline_radius_topLeft,
11566   eCSSProperty__moz_outline_radius_topRight,
11567   eCSSProperty__moz_outline_radius_bottomRight,
11568   eCSSProperty__moz_outline_radius_bottomLeft
11569 };
11570 
11571 void
11572 CSSParserImpl::SaveInputState(CSSParserInputState& aState)
11573 {
11574   aState.mToken = mToken;
11575   aState.mHavePushBack = mHavePushBack;
11576   mScanner->SavePosition(aState.mPosition);
11577 }
11578 
11579 void
11580 CSSParserImpl::RestoreSavedInputState(const CSSParserInputState& aState)
11581 {
11582   mToken = aState.mToken;
11583   mHavePushBack = aState.mHavePushBack;
11584   mScanner->RestoreSavedPosition(aState.mPosition);
11585 }
11586 
11587 bool
11588 CSSParserImpl::ParseProperty(nsCSSPropertyID aPropID)
11589 {
11590   // Can't use AutoRestore<bool> because it's a bitfield.
11591   MOZ_ASSERT(!mHashlessColorQuirk,
11592              "hashless color quirk should not be set");
11593   MOZ_ASSERT(!mUnitlessLengthQuirk,
11594              "unitless length quirk should not be set");
11595   MOZ_ASSERT(aPropID != eCSSPropertyExtra_variable);
11596 
11597   if (mNavQuirkMode) {
11598     mHashlessColorQuirk =
11599       nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_HASHLESS_COLOR_QUIRK);
11600     mUnitlessLengthQuirk =
11601       nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_UNITLESS_LENGTH_QUIRK);
11602   }
11603 
11604   // Save the current input state so that we can restore it later if we
11605   // have to re-parse the property value as a variable-reference-containing
11606   // token stream.
11607   CSSParserInputState stateBeforeProperty;
11608   SaveInputState(stateBeforeProperty);
11609   mScanner->ClearSeenVariableReference();
11610 
11611   NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
11612   bool allowVariables = true;
11613   bool result;
11614   switch (nsCSSProps::PropertyParseType(aPropID)) {
11615     case CSS_PROPERTY_PARSE_INACCESSIBLE: {
11616       // The user can't use these
11617       REPORT_UNEXPECTED(PEInaccessibleProperty2);
11618       allowVariables = false;
11619       result = false;
11620       break;
11621     }
11622     case CSS_PROPERTY_PARSE_FUNCTION: {
11623       result = ParsePropertyByFunction(aPropID);
11624       break;
11625     }
11626     case CSS_PROPERTY_PARSE_VALUE: {
11627       result = false;
11628       nsCSSValue value;
11629       if (ParseSingleValueProperty(value, aPropID) == CSSParseResult::Ok) {
11630         AppendValue(aPropID, value);
11631         result = true;
11632       }
11633       // XXX Report errors?
11634       break;
11635     }
11636     case CSS_PROPERTY_PARSE_VALUE_LIST: {
11637       result = ParseValueList(aPropID);
11638       break;
11639     }
11640     default: {
11641       result = false;
11642       allowVariables = false;
11643       MOZ_ASSERT(false,
11644                  "Property's flags field in nsCSSPropList.h is missing "
11645                  "one of the CSS_PROPERTY_PARSE_* constants");
11646       break;
11647     }
11648   }
11649 
11650   if (result) {
11651     // We need to call ExpectEndProperty() to decide whether to reparse
11652     // with variables.  This is needed because the property parsing may
11653     // have stopped upon finding a variable (e.g., 'margin: 1px var(a)')
11654     // in a way that future variable substitutions will be valid, or
11655     // because it parsed everything that's possible but we still want to
11656     // act as though the property contains variables even though we know
11657     // the substitution will never work (e.g., for 'margin: 1px 2px 3px
11658     // 4px 5px var(a)').
11659     //
11660     // It would be nice to find a better solution here
11661     // (and for the SkipUntilOneOf below), though, that doesn't depend
11662     // on using what we don't accept for doing parsing correctly.
11663     if (!ExpectEndProperty()) {
11664       result = false;
11665     }
11666   }
11667 
11668   bool seenVariable = mScanner->SeenVariableReference() ||
11669     (stateBeforeProperty.mHavePushBack &&
11670      stateBeforeProperty.mToken.mType == eCSSToken_Function &&
11671      stateBeforeProperty.mToken.mIdent.LowerCaseEqualsLiteral("var"));
11672   bool parseAsTokenStream;
11673 
11674   if (!result && allowVariables) {
11675     parseAsTokenStream = true;
11676     if (!seenVariable) {
11677       // We might have stopped parsing the property before its end and before
11678       // finding a variable reference.  Keep checking until the end of the
11679       // property.
11680       CSSParserInputState stateAtError;
11681       SaveInputState(stateAtError);
11682 
11683       const char16_t stopChars[] = { ';', '!', '}', ')', 0 };
11684       SkipUntilOneOf(stopChars);
11685       UngetToken();
11686       parseAsTokenStream = mScanner->SeenVariableReference();
11687 
11688       if (!parseAsTokenStream) {
11689         // If we parsed to the end of the propery and didn't find any variable
11690         // references, then the real position we want to report the error at
11691         // is |stateAtError|.
11692         RestoreSavedInputState(stateAtError);
11693       }
11694     }
11695   } else {
11696     parseAsTokenStream = false;
11697   }
11698 
11699   if (parseAsTokenStream) {
11700     // Go back to the start of the property value and parse it to make sure
11701     // its variable references are syntactically valid and is otherwise
11702     // balanced.
11703     RestoreSavedInputState(stateBeforeProperty);
11704 
11705     if (!mInSupportsCondition) {
11706       mScanner->StartRecording();
11707     }
11708 
11709     CSSVariableDeclarations::Type type;
11710     bool dropBackslash;
11711     nsString impliedCharacters;
11712     nsCSSValue value;
11713     if (ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
11714                                 nullptr, nullptr)) {
11715       MOZ_ASSERT(type == CSSVariableDeclarations::eTokenStream,
11716                  "a non-custom property reparsed since it contained variable "
11717                  "references should not have been 'initial' or 'inherit'");
11718 
11719       nsString propertyValue;
11720 
11721       if (!mInSupportsCondition) {
11722         // If we are in an @supports condition, we don't need to store the
11723         // actual token stream on the nsCSSValue.
11724         mScanner->StopRecording(propertyValue);
11725         if (dropBackslash) {
11726           MOZ_ASSERT(!propertyValue.IsEmpty() &&
11727                      propertyValue[propertyValue.Length() - 1] == '\\');
11728           propertyValue.Truncate(propertyValue.Length() - 1);
11729         }
11730         propertyValue.Append(impliedCharacters);
11731       }
11732 
11733       if (mHavePushBack) {
11734         // If we came to the end of a property value that had a variable
11735         // reference and a token was pushed back, then it would have been
11736         // ended by '!', ')', ';', ']' or '}'.  We should remove it from the
11737         // recorded property value.
11738         MOZ_ASSERT(mToken.IsSymbol('!') ||
11739                    mToken.IsSymbol(')') ||
11740                    mToken.IsSymbol(';') ||
11741                    mToken.IsSymbol(']') ||
11742                    mToken.IsSymbol('}'));
11743         if (!mInSupportsCondition) {
11744           MOZ_ASSERT(!propertyValue.IsEmpty());
11745           MOZ_ASSERT(propertyValue[propertyValue.Length() - 1] ==
11746                      mToken.mSymbol);
11747           propertyValue.Truncate(propertyValue.Length() - 1);
11748         }
11749       }
11750 
11751       if (!mInSupportsCondition) {
11752         if (nsCSSProps::IsShorthand(aPropID)) {
11753           // If this is a shorthand property, we store the token stream on each
11754           // of its corresponding longhand properties.
11755           CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID, EnabledState()) {
11756             nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
11757             tokenStream->mPropertyID = *p;
11758             tokenStream->mShorthandPropertyID = aPropID;
11759             tokenStream->mTokenStream = propertyValue;
11760             tokenStream->mBaseURI = mBaseURI;
11761             tokenStream->mSheetURI = mSheetURI;
11762             tokenStream->mSheetPrincipal = mSheetPrincipal;
11763             // XXX Should store sheet here (see bug 952338).
11764             // tokenStream->mSheet = mSheet;
11765             tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
11766             tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
11767             value.SetTokenStreamValue(tokenStream);
11768             AppendValue(*p, value);
11769           }
11770         } else {
11771           nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
11772           tokenStream->mPropertyID = aPropID;
11773           tokenStream->mTokenStream = propertyValue;
11774           tokenStream->mBaseURI = mBaseURI;
11775           tokenStream->mSheetURI = mSheetURI;
11776           tokenStream->mSheetPrincipal = mSheetPrincipal;
11777           // XXX Should store sheet here (see bug 952338).
11778           // tokenStream->mSheet = mSheet;
11779           tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
11780           tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
11781           value.SetTokenStreamValue(tokenStream);
11782           AppendValue(aPropID, value);
11783         }
11784       }
11785       result = true;
11786     } else {
11787       if (!mInSupportsCondition) {
11788         mScanner->StopRecording();
11789       }
11790     }
11791   }
11792 
11793   if (mNavQuirkMode) {
11794     mHashlessColorQuirk = false;
11795     mUnitlessLengthQuirk = false;
11796   }
11797 
11798   return result;
11799 }
11800 
11801 bool
11802 CSSParserImpl::ParsePropertyByFunction(nsCSSPropertyID aPropID)
11803 {
11804   switch (aPropID) {  // handle shorthand or multiple properties
11805   case eCSSProperty_place_content:
11806     return ParsePlaceContent();
11807   case eCSSProperty_place_items:
11808     return ParsePlaceItems();
11809   case eCSSProperty_place_self:
11810     return ParsePlaceSelf();
11811   case eCSSProperty_background:
11812     return ParseImageLayers(nsStyleImageLayers::kBackgroundLayerTable);
11813   case eCSSProperty_background_repeat:
11814     return ParseImageLayerRepeat(eCSSProperty_background_repeat);
11815   case eCSSProperty_background_position:
11816     return ParseImageLayerPosition(nsStyleImageLayers::kBackgroundLayerTable);
11817   case eCSSProperty_background_position_x:
11818   case eCSSProperty_background_position_y:
11819     return ParseImageLayerPositionCoord(aPropID,
11820                aPropID == eCSSProperty_background_position_x);
11821   case eCSSProperty_background_size:
11822     return ParseImageLayerSize(eCSSProperty_background_size);
11823   case eCSSProperty_border:
11824     return ParseBorderSide(kBorderTopIDs, true);
11825   case eCSSProperty_border_color:
11826     return ParseBorderColor();
11827   case eCSSProperty_border_spacing:
11828     return ParseBorderSpacing();
11829   case eCSSProperty_border_style:
11830     return ParseBorderStyle();
11831   case eCSSProperty_border_block_end:
11832     return ParseBorderSide(kBorderBlockEndIDs, false);
11833   case eCSSProperty_border_block_start:
11834     return ParseBorderSide(kBorderBlockStartIDs, false);
11835   case eCSSProperty_border_bottom:
11836     return ParseBorderSide(kBorderBottomIDs, false);
11837   case eCSSProperty_border_inline_end:
11838     return ParseBorderSide(kBorderInlineEndIDs, false);
11839   case eCSSProperty_border_inline_start:
11840     return ParseBorderSide(kBorderInlineStartIDs, false);
11841   case eCSSProperty_border_left:
11842     return ParseBorderSide(kBorderLeftIDs, false);
11843   case eCSSProperty_border_right:
11844     return ParseBorderSide(kBorderRightIDs, false);
11845   case eCSSProperty_border_top:
11846     return ParseBorderSide(kBorderTopIDs, false);
11847   case eCSSProperty_border_bottom_colors:
11848   case eCSSProperty_border_left_colors:
11849   case eCSSProperty_border_right_colors:
11850   case eCSSProperty_border_top_colors:
11851     return ParseBorderColors(aPropID);
11852   case eCSSProperty_border_image_slice:
11853     return ParseBorderImageSlice(true, nullptr);
11854   case eCSSProperty_border_image_width:
11855     return ParseBorderImageWidth(true);
11856   case eCSSProperty_border_image_outset:
11857     return ParseBorderImageOutset(true);
11858   case eCSSProperty_border_image_repeat:
11859     return ParseBorderImageRepeat(true);
11860   case eCSSProperty_border_image:
11861     return ParseBorderImage();
11862   case eCSSProperty_border_width:
11863     return ParseBorderWidth();
11864   case eCSSProperty_border_radius:
11865     return ParseBoxCornerRadii(kBorderRadiusIDs);
11866   case eCSSProperty__moz_outline_radius:
11867     return ParseBoxCornerRadii(kOutlineRadiusIDs);
11868 
11869   case eCSSProperty_border_top_left_radius:
11870   case eCSSProperty_border_top_right_radius:
11871   case eCSSProperty_border_bottom_right_radius:
11872   case eCSSProperty_border_bottom_left_radius:
11873   case eCSSProperty__moz_outline_radius_topLeft:
11874   case eCSSProperty__moz_outline_radius_topRight:
11875   case eCSSProperty__moz_outline_radius_bottomRight:
11876   case eCSSProperty__moz_outline_radius_bottomLeft:
11877     return ParseBoxCornerRadius(aPropID);
11878 
11879   case eCSSProperty_box_shadow:
11880   case eCSSProperty_text_shadow:
11881     return ParseShadowList(aPropID);
11882 
11883   case eCSSProperty_clip:
11884     return ParseRect(eCSSProperty_clip);
11885   case eCSSProperty_columns:
11886     return ParseColumns();
11887   case eCSSProperty_column_rule:
11888     return ParseBorderSide(kColumnRuleIDs, false);
11889   case eCSSProperty_content:
11890     return ParseContent();
11891   case eCSSProperty_counter_increment:
11892   case eCSSProperty_counter_reset:
11893     return ParseCounterData(aPropID);
11894   case eCSSProperty_cursor:
11895     return ParseCursor();
11896   case eCSSProperty_filter:
11897     return ParseFilter();
11898   case eCSSProperty_flex:
11899     return ParseFlex();
11900   case eCSSProperty_flex_flow:
11901     return ParseFlexFlow();
11902   case eCSSProperty_font:
11903     return ParseFont();
11904   case eCSSProperty_font_variant:
11905     return ParseFontVariant();
11906   case eCSSProperty_grid_auto_flow:
11907     return ParseGridAutoFlow();
11908   case eCSSProperty_grid_auto_columns:
11909   case eCSSProperty_grid_auto_rows:
11910     return ParseGridAutoColumnsRows(aPropID);
11911   case eCSSProperty_grid_template_areas:
11912     return ParseGridTemplateAreas();
11913   case eCSSProperty_grid_template_columns:
11914   case eCSSProperty_grid_template_rows:
11915     return ParseGridTemplateColumnsRows(aPropID);
11916   case eCSSProperty_grid_template:
11917     return ParseGridTemplate();
11918   case eCSSProperty_grid:
11919     return ParseGrid();
11920   case eCSSProperty_grid_column_start:
11921   case eCSSProperty_grid_column_end:
11922   case eCSSProperty_grid_row_start:
11923   case eCSSProperty_grid_row_end:
11924     return ParseGridColumnRowStartEnd(aPropID);
11925   case eCSSProperty_grid_column:
11926     return ParseGridColumnRow(eCSSProperty_grid_column_start,
11927                               eCSSProperty_grid_column_end);
11928   case eCSSProperty_grid_row:
11929     return ParseGridColumnRow(eCSSProperty_grid_row_start,
11930                               eCSSProperty_grid_row_end);
11931   case eCSSProperty_grid_area:
11932     return ParseGridArea();
11933   case eCSSProperty_grid_gap:
11934     return ParseGridGap();
11935   case eCSSProperty_image_region:
11936     return ParseRect(eCSSProperty_image_region);
11937   case eCSSProperty_align_content:
11938   case eCSSProperty_justify_content:
11939     return ParseAlignJustifyContent(aPropID);
11940   case eCSSProperty_align_items:
11941     return ParseAlignItems();
11942   case eCSSProperty_align_self:
11943   case eCSSProperty_justify_self:
11944     return ParseAlignJustifySelf(aPropID);
11945   case eCSSProperty_initial_letter:
11946     return ParseInitialLetter();
11947   case eCSSProperty_justify_items:
11948     return ParseJustifyItems();
11949   case eCSSProperty_list_style:
11950     return ParseListStyle();
11951   case eCSSProperty_margin:
11952     return ParseMargin();
11953   case eCSSProperty_object_position:
11954     return ParseObjectPosition();
11955   case eCSSProperty_outline:
11956     return ParseOutline();
11957   case eCSSProperty_overflow:
11958     return ParseOverflow();
11959   case eCSSProperty_padding:
11960     return ParsePadding();
11961   case eCSSProperty_quotes:
11962     return ParseQuotes();
11963   case eCSSProperty_text_decoration:
11964     return ParseTextDecoration();
11965   case eCSSProperty_text_emphasis:
11966     return ParseTextEmphasis();
11967   case eCSSProperty_will_change:
11968     return ParseWillChange();
11969   case eCSSProperty_transform:
11970     return ParseTransform(false);
11971   case eCSSProperty__moz_transform:
11972     return ParseTransform(true);
11973   case eCSSProperty_transform_origin:
11974     return ParseTransformOrigin(false);
11975   case eCSSProperty_perspective_origin:
11976     return ParseTransformOrigin(true);
11977   case eCSSProperty_transition:
11978     return ParseTransition();
11979   case eCSSProperty_animation:
11980     return ParseAnimation();
11981   case eCSSProperty_transition_property:
11982     return ParseTransitionProperty();
11983   case eCSSProperty_fill:
11984   case eCSSProperty_stroke:
11985     return ParsePaint(aPropID);
11986   case eCSSProperty_stroke_dasharray:
11987     return ParseDasharray();
11988   case eCSSProperty_marker:
11989     return ParseMarker();
11990   case eCSSProperty_paint_order:
11991     return ParsePaintOrder();
11992   case eCSSProperty_scroll_snap_type:
11993     return ParseScrollSnapType();
11994 #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
11995   case eCSSProperty_mask:
11996     return ParseImageLayers(nsStyleImageLayers::kMaskLayerTable);
11997   case eCSSProperty_mask_repeat:
11998     return ParseImageLayerRepeat(eCSSProperty_mask_repeat);
11999   case eCSSProperty_mask_position:
12000     return ParseImageLayerPosition(nsStyleImageLayers::kMaskLayerTable);
12001   case eCSSProperty_mask_position_x:
12002   case eCSSProperty_mask_position_y:
12003     return ParseImageLayerPositionCoord(aPropID,
12004                aPropID == eCSSProperty_mask_position_x);
12005   case eCSSProperty_mask_size:
12006     return ParseImageLayerSize(eCSSProperty_mask_size);
12007 #endif
12008   case eCSSProperty__webkit_text_stroke:
12009     return ParseWebkitTextStroke();
12010   case eCSSProperty_all:
12011     return ParseAll();
12012   default:
12013     MOZ_ASSERT(false, "should not be called");
12014     return false;
12015   }
12016 }
12017 
12018 // Bits used in determining which background position info we have
12019 #define BG_CENTER  NS_STYLE_IMAGELAYER_POSITION_CENTER
12020 #define BG_TOP     NS_STYLE_IMAGELAYER_POSITION_TOP
12021 #define BG_BOTTOM  NS_STYLE_IMAGELAYER_POSITION_BOTTOM
12022 #define BG_LEFT    NS_STYLE_IMAGELAYER_POSITION_LEFT
12023 #define BG_RIGHT   NS_STYLE_IMAGELAYER_POSITION_RIGHT
12024 #define BG_CTB    (BG_CENTER | BG_TOP | BG_BOTTOM)
12025 #define BG_TB     (BG_TOP | BG_BOTTOM)
12026 #define BG_CLR    (BG_CENTER | BG_LEFT | BG_RIGHT)
12027 #define BG_LR     (BG_LEFT | BG_RIGHT)
12028 
12029 CSSParseResult
12030 CSSParserImpl::ParseBoxProperty(nsCSSValue& aValue,
12031                                 nsCSSPropertyID aPropID)
12032 {
12033   if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
12034     MOZ_ASSERT(false, "must only be called for longhand properties");
12035     return CSSParseResult::NotFound;
12036   }
12037 
12038   MOZ_ASSERT(!nsCSSProps::PropHasFlags(aPropID,
12039                                        CSS_PROPERTY_VALUE_PARSER_FUNCTION),
12040              "must only be called for non-function-parsed properties");
12041 
12042   uint32_t variant = nsCSSProps::ParserVariant(aPropID);
12043   if (variant == 0) {
12044     MOZ_ASSERT(false, "must only be called for variant-parsed properties");
12045     return CSSParseResult::NotFound;
12046   }
12047 
12048   if (variant & ~(VARIANT_AHKLP | VARIANT_COLOR | VARIANT_CALC)) {
12049     MOZ_ASSERT(false, "must only be called for properties that take certain "
12050                       "variants");
12051     return CSSParseResult::NotFound;
12052   }
12053 
12054   const KTableEntry* kwtable = nsCSSProps::kKeywordTableTable[aPropID];
12055   uint32_t restrictions = nsCSSProps::ValueRestrictions(aPropID);
12056 
12057   return ParseVariantWithRestrictions(aValue, variant, kwtable, restrictions);
12058 }
12059 
12060 bool
12061 CSSParserImpl::ParseSingleValuePropertyByFunction(nsCSSValue& aValue,
12062                                                   nsCSSPropertyID aPropID)
12063 {
12064   switch (aPropID) {
12065     case eCSSProperty_clip_path:
12066       return ParseClipPath(aValue);
12067     case eCSSProperty_contain:
12068       return ParseContain(aValue);
12069     case eCSSProperty_font_family:
12070       return ParseFamily(aValue);
12071     case eCSSProperty_font_synthesis:
12072       return ParseFontSynthesis(aValue);
12073     case eCSSProperty_font_variant_alternates:
12074       return ParseFontVariantAlternates(aValue);
12075     case eCSSProperty_font_variant_east_asian:
12076       return ParseFontVariantEastAsian(aValue);
12077     case eCSSProperty_font_variant_ligatures:
12078       return ParseFontVariantLigatures(aValue);
12079     case eCSSProperty_font_variant_numeric:
12080       return ParseFontVariantNumeric(aValue);
12081     case eCSSProperty_font_feature_settings:
12082       return ParseFontFeatureSettings(aValue);
12083     case eCSSProperty_font_weight:
12084       return ParseFontWeight(aValue);
12085     case eCSSProperty_image_orientation:
12086       return ParseImageOrientation(aValue);
12087     case eCSSProperty_list_style_type:
12088       return ParseListStyleType(aValue);
12089     case eCSSProperty_scroll_snap_points_x:
12090       return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_x);
12091     case eCSSProperty_scroll_snap_points_y:
12092       return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_y);
12093     case eCSSProperty_scroll_snap_destination:
12094       return ParseScrollSnapDestination(aValue);
12095     case eCSSProperty_scroll_snap_coordinate:
12096       return ParseScrollSnapCoordinate(aValue);
12097     case eCSSProperty_shape_outside:
12098       return ParseShapeOutside(aValue);
12099     case eCSSProperty_text_align:
12100       return ParseTextAlign(aValue);
12101     case eCSSProperty_text_align_last:
12102       return ParseTextAlignLast(aValue);
12103     case eCSSProperty_text_decoration_line:
12104       return ParseTextDecorationLine(aValue);
12105     case eCSSProperty_text_combine_upright:
12106       return ParseTextCombineUpright(aValue);
12107     case eCSSProperty_text_emphasis_position:
12108       return ParseTextEmphasisPosition(aValue);
12109     case eCSSProperty_text_emphasis_style:
12110       return ParseTextEmphasisStyle(aValue);
12111     case eCSSProperty_text_overflow:
12112       return ParseTextOverflow(aValue);
12113     case eCSSProperty_touch_action:
12114       return ParseTouchAction(aValue);
12115     default:
12116       MOZ_ASSERT(false, "should not reach here");
12117       return false;
12118   }
12119 }
12120 
12121 CSSParseResult
12122 CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
12123                                         nsCSSPropertyID aPropID)
12124 {
12125   if (aPropID == eCSSPropertyExtra_x_none_value) {
12126     return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nullptr);
12127   }
12128 
12129   if (aPropID == eCSSPropertyExtra_x_auto_value) {
12130     return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nullptr);
12131   }
12132 
12133   if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
12134     MOZ_ASSERT(false, "not a single value property");
12135     return CSSParseResult::NotFound;
12136   }
12137 
12138   if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) {
12139     uint32_t lineBefore, colBefore;
12140     if (!GetNextTokenLocation(true, &lineBefore, &colBefore)) {
12141       // We're at EOF before parsing.
12142       return CSSParseResult::NotFound;
12143     }
12144 
12145     if (ParseSingleValuePropertyByFunction(aValue, aPropID)) {
12146       return CSSParseResult::Ok;
12147     }
12148 
12149     uint32_t lineAfter, colAfter;
12150     if (!GetNextTokenLocation(true, &lineAfter, &colAfter) ||
12151         lineAfter != lineBefore ||
12152         colAfter != colBefore) {
12153       // Any single token value that was invalid will have been pushed back,
12154       // so GetNextTokenLocation encountering EOF means we failed while
12155       // parsing a multi-token value.
12156       return CSSParseResult::Error;
12157     }
12158 
12159     return CSSParseResult::NotFound;
12160   }
12161 
12162   uint32_t variant = nsCSSProps::ParserVariant(aPropID);
12163   if (variant == 0) {
12164     MOZ_ASSERT(false, "not a single value property");
12165     return CSSParseResult::NotFound;
12166   }
12167 
12168   const KTableEntry* kwtable = nsCSSProps::kKeywordTableTable[aPropID];
12169   uint32_t restrictions = nsCSSProps::ValueRestrictions(aPropID);
12170   return ParseVariantWithRestrictions(aValue, variant, kwtable, restrictions);
12171 }
12172 
12173 // font-descriptor: descriptor ':' value ';'
12174 // caller has advanced mToken to point at the descriptor
12175 bool
12176 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
12177                                         nsCSSValue& aValue)
12178 {
12179   switch (aDescID) {
12180     // These four are similar to the properties of the same name,
12181     // possibly with more restrictions on the values they can take.
12182   case eCSSFontDesc_Family: {
12183     nsCSSValue value;
12184     if (!ParseFamily(value) ||
12185         value.GetUnit() != eCSSUnit_FontFamilyList)
12186       return false;
12187 
12188     // name can only be a single, non-generic name
12189     const FontFamilyList* f = value.GetFontFamilyListValue();
12190     const nsTArray<FontFamilyName>& fontlist = f->GetFontlist();
12191 
12192     if (fontlist.Length() != 1 || !fontlist[0].IsNamed()) {
12193       return false;
12194     }
12195 
12196     aValue.SetStringValue(fontlist[0].mName, eCSSUnit_String);
12197     return true;
12198   }
12199 
12200   case eCSSFontDesc_Style:
12201     // property is VARIANT_HMK|VARIANT_SYSFONT
12202     return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
12203                                    nsCSSProps::kFontStyleKTable);
12204 
12205   case eCSSFontDesc_Display:
12206     return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD,
12207                                    nsCSSProps::kFontDisplayKTable);
12208 
12209   case eCSSFontDesc_Weight:
12210     return (ParseFontWeight(aValue) &&
12211             aValue.GetUnit() != eCSSUnit_Inherit &&
12212             aValue.GetUnit() != eCSSUnit_Initial &&
12213             aValue.GetUnit() != eCSSUnit_Unset &&
12214             (aValue.GetUnit() != eCSSUnit_Enumerated ||
12215              (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
12216               aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
12217 
12218   case eCSSFontDesc_Stretch:
12219     // property is VARIANT_HK|VARIANT_SYSFONT
12220     return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD,
12221                                    nsCSSProps::kFontStretchKTable);
12222 
12223     // These two are unique to @font-face and have their own special grammar.
12224   case eCSSFontDesc_Src:
12225     return ParseFontSrc(aValue);
12226 
12227   case eCSSFontDesc_UnicodeRange:
12228     return ParseFontRanges(aValue);
12229 
12230   case eCSSFontDesc_FontFeatureSettings:
12231     return ParseFontFeatureSettings(aValue);
12232 
12233   case eCSSFontDesc_FontLanguageOverride:
12234     return ParseSingleTokenVariant(aValue, VARIANT_NORMAL | VARIANT_STRING,
12235                                    nullptr);
12236 
12237   case eCSSFontDesc_UNKNOWN:
12238   case eCSSFontDesc_COUNT:
12239     NS_NOTREACHED("bad nsCSSFontDesc code");
12240   }
12241   // explicitly do NOT have a default case to let the compiler
12242   // help find missing descriptors
12243   return false;
12244 }
12245 
12246 static nsCSSValue
12247 BoxPositionMaskToCSSValue(int32_t aMask, bool isX)
12248 {
12249   int32_t val = NS_STYLE_IMAGELAYER_POSITION_CENTER;
12250   if (isX) {
12251     if (aMask & BG_LEFT) {
12252       val = NS_STYLE_IMAGELAYER_POSITION_LEFT;
12253     }
12254     else if (aMask & BG_RIGHT) {
12255       val = NS_STYLE_IMAGELAYER_POSITION_RIGHT;
12256     }
12257   }
12258   else {
12259     if (aMask & BG_TOP) {
12260       val = NS_STYLE_IMAGELAYER_POSITION_TOP;
12261     }
12262     else if (aMask & BG_BOTTOM) {
12263       val = NS_STYLE_IMAGELAYER_POSITION_BOTTOM;
12264     }
12265   }
12266 
12267   return nsCSSValue(val, eCSSUnit_Enumerated);
12268 }
12269 
12270 bool
12271 CSSParserImpl::ParseImageLayers(const nsCSSPropertyID aTable[])
12272 {
12273   nsAutoParseCompoundProperty compound(this);
12274 
12275   // background-color can only be set once, so it's not a list.
12276   nsCSSValue color;
12277 
12278   // Check first for inherit/initial/unset.
12279   if (ParseSingleTokenVariant(color, VARIANT_INHERIT, nullptr)) {
12280     // must be alone
12281     for (const nsCSSPropertyID* subprops =
12282            nsCSSProps::SubpropertyEntryFor(aTable[nsStyleImageLayers::shorthand]);
12283          *subprops != eCSSProperty_UNKNOWN; ++subprops) {
12284       AppendValue(*subprops, color);
12285     }
12286     return true;
12287   }
12288 
12289   nsCSSValue image, repeat, attachment, clip, origin, positionX, positionY, size,
12290              composite, maskMode;
12291   ImageLayersShorthandParseState state(color, image.SetListValue(),
12292                                        repeat.SetPairListValue(),
12293                                        attachment.SetListValue(), clip.SetListValue(),
12294                                        origin.SetListValue(),
12295                                        positionX.SetListValue(), positionY.SetListValue(),
12296                                        size.SetPairListValue(), composite.SetListValue(),
12297                                        maskMode.SetListValue());
12298 
12299   for (;;) {
12300     if (!ParseImageLayersItem(state, aTable)) {
12301       return false;
12302     }
12303 
12304     // If we saw a color, this must be the last item.
12305     if (color.GetUnit() != eCSSUnit_Null) {
12306       MOZ_ASSERT(aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN);
12307       break;
12308     }
12309 
12310     // If there's a comma, expect another item.
12311     if (!ExpectSymbol(',', true)) {
12312       break;
12313     }
12314 
12315 #define APPENDNEXT(propID_, propMember_, propType_) \
12316   if (aTable[propID_] != eCSSProperty_UNKNOWN) { \
12317     propMember_->mNext = new propType_; \
12318     propMember_ = propMember_->mNext; \
12319   }
12320     // Chain another entry on all the lists.
12321     APPENDNEXT(nsStyleImageLayers::image, state.mImage,
12322                nsCSSValueList);
12323     APPENDNEXT(nsStyleImageLayers::repeat, state.mRepeat,
12324                nsCSSValuePairList);
12325     APPENDNEXT(nsStyleImageLayers::clip, state.mClip,
12326                nsCSSValueList);
12327     APPENDNEXT(nsStyleImageLayers::origin, state.mOrigin,
12328                nsCSSValueList);
12329     APPENDNEXT(nsStyleImageLayers::positionX, state.mPositionX,
12330                nsCSSValueList);
12331     APPENDNEXT(nsStyleImageLayers::positionY, state.mPositionY,
12332                nsCSSValueList);
12333     APPENDNEXT(nsStyleImageLayers::size, state.mSize,
12334                nsCSSValuePairList);
12335     APPENDNEXT(nsStyleImageLayers::attachment, state.mAttachment,
12336                nsCSSValueList);
12337     APPENDNEXT(nsStyleImageLayers::maskMode, state.mMode,
12338                nsCSSValueList);
12339     APPENDNEXT(nsStyleImageLayers::composite, state.mComposite,
12340                nsCSSValueList);
12341 #undef APPENDNEXT
12342   }
12343 
12344   // If we get to this point without seeing a color, provide a default.
12345  if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
12346     if (color.GetUnit() == eCSSUnit_Null) {
12347       color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor);
12348     }
12349   }
12350 
12351 #define APPENDVALUE(propID_, propValue_) \
12352   if (propID_ != eCSSProperty_UNKNOWN) { \
12353     AppendValue(propID_,  propValue_); \
12354   }
12355 
12356   APPENDVALUE(aTable[nsStyleImageLayers::image],      image);
12357   APPENDVALUE(aTable[nsStyleImageLayers::repeat],     repeat);
12358   APPENDVALUE(aTable[nsStyleImageLayers::clip],       clip);
12359   APPENDVALUE(aTable[nsStyleImageLayers::origin],     origin);
12360   APPENDVALUE(aTable[nsStyleImageLayers::positionX],  positionX);
12361   APPENDVALUE(aTable[nsStyleImageLayers::positionY],  positionY);
12362   APPENDVALUE(aTable[nsStyleImageLayers::size],       size);
12363   APPENDVALUE(aTable[nsStyleImageLayers::color],      color);
12364   APPENDVALUE(aTable[nsStyleImageLayers::attachment], attachment);
12365   APPENDVALUE(aTable[nsStyleImageLayers::maskMode],   maskMode);
12366   APPENDVALUE(aTable[nsStyleImageLayers::composite],  composite);
12367 
12368 #undef APPENDVALUE
12369 
12370   return true;
12371 }
12372 
12373 // Helper for ParseImageLayersItem. Returns true if the passed-in nsCSSToken is
12374 // a function which is accepted for background-image.
12375 bool
12376 CSSParserImpl::IsFunctionTokenValidForImageLayerImage(
12377   const nsCSSToken& aToken) const
12378 {
12379   MOZ_ASSERT(aToken.mType == eCSSToken_Function,
12380              "Should only be called for function-typed tokens");
12381 
12382   const nsAString& funcName = aToken.mIdent;
12383 
12384   return funcName.LowerCaseEqualsLiteral("linear-gradient") ||
12385     funcName.LowerCaseEqualsLiteral("radial-gradient") ||
12386     funcName.LowerCaseEqualsLiteral("repeating-linear-gradient") ||
12387     funcName.LowerCaseEqualsLiteral("repeating-radial-gradient") ||
12388     funcName.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
12389     funcName.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
12390     funcName.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
12391     funcName.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
12392     funcName.LowerCaseEqualsLiteral("-moz-image-rect") ||
12393     funcName.LowerCaseEqualsLiteral("-moz-element") ||
12394     ((sWebkitPrefixedAliasesEnabled || ShouldUseUnprefixingService()) &&
12395      (funcName.LowerCaseEqualsLiteral("-webkit-gradient") ||
12396       funcName.LowerCaseEqualsLiteral("-webkit-linear-gradient") ||
12397       funcName.LowerCaseEqualsLiteral("-webkit-radial-gradient") ||
12398       funcName.LowerCaseEqualsLiteral("-webkit-repeating-linear-gradient") ||
12399       funcName.LowerCaseEqualsLiteral("-webkit-repeating-radial-gradient")));
12400 }
12401 
12402 // Parse one item of the background shorthand property.
12403 bool
12404 CSSParserImpl::ParseImageLayersItem(
12405   CSSParserImpl::ImageLayersShorthandParseState& aState,
12406   const nsCSSPropertyID aTable[])
12407 {
12408   // Fill in the values that the shorthand will set if we don't find
12409   // other values.
12410   aState.mImage->mValue.SetNoneValue();
12411   aState.mAttachment->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL,
12412                                          eCSSUnit_Enumerated);
12413   aState.mClip->mValue.SetIntValue(NS_STYLE_IMAGELAYER_CLIP_BORDER,
12414                                    eCSSUnit_Enumerated);
12415 
12416   aState.mRepeat->mXValue.SetIntValue(NS_STYLE_IMAGELAYER_REPEAT_REPEAT,
12417                                       eCSSUnit_Enumerated);
12418   aState.mRepeat->mYValue.Reset();
12419 
12420   RefPtr<nsCSSValue::Array> positionXArr = nsCSSValue::Array::Create(2);
12421   RefPtr<nsCSSValue::Array> positionYArr = nsCSSValue::Array::Create(2);
12422   aState.mPositionX->mValue.SetArrayValue(positionXArr, eCSSUnit_Array);
12423   aState.mPositionY->mValue.SetArrayValue(positionYArr, eCSSUnit_Array);
12424 
12425   if (eCSSProperty_mask == aTable[nsStyleImageLayers::shorthand]) {
12426     aState.mOrigin->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ORIGIN_BORDER,
12427                                        eCSSUnit_Enumerated);
12428   } else {
12429     aState.mOrigin->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ORIGIN_PADDING,
12430                                        eCSSUnit_Enumerated);
12431   }
12432   positionXArr->Item(1).SetPercentValue(0.0f);
12433   positionYArr->Item(1).SetPercentValue(0.0f);
12434 
12435   aState.mSize->mXValue.SetAutoValue();
12436   aState.mSize->mYValue.SetAutoValue();
12437   aState.mComposite->mValue.SetIntValue(NS_STYLE_MASK_COMPOSITE_ADD,
12438                                         eCSSUnit_Enumerated);
12439   aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_MATCH_SOURCE,
12440                                    eCSSUnit_Enumerated);
12441   bool haveColor = false,
12442        haveImage = false,
12443        haveRepeat = false,
12444        haveAttach = false,
12445        havePositionAndSize = false,
12446        haveOrigin = false,
12447        haveComposite = false,
12448        haveMode = false,
12449        haveSomething = false;
12450 
12451   while (GetToken(true)) {
12452     nsCSSTokenType tt = mToken.mType;
12453     UngetToken(); // ...but we'll still cheat and use mToken
12454     if (tt == eCSSToken_Symbol) {
12455       // ExpectEndProperty only looks for symbols, and nothing else will
12456       // show up as one.
12457       break;
12458     }
12459 
12460     if (tt == eCSSToken_Ident) {
12461       nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
12462       int32_t dummy;
12463       if (keyword == eCSSKeyword_inherit ||
12464           keyword == eCSSKeyword_initial ||
12465           keyword == eCSSKeyword_unset) {
12466         return false;
12467       } else if (keyword == eCSSKeyword_none) {
12468         if (haveImage)
12469           return false;
12470         haveImage = true;
12471         if (ParseSingleValueProperty(aState.mImage->mValue,
12472                                      aTable[nsStyleImageLayers::image]) !=
12473             CSSParseResult::Ok) {
12474           NS_NOTREACHED("should be able to parse");
12475           return false;
12476         }
12477       } else if (aTable[nsStyleImageLayers::attachment] !=
12478                    eCSSProperty_UNKNOWN &&
12479                  nsCSSProps::FindKeyword(keyword,
12480                    nsCSSProps::kImageLayerAttachmentKTable, dummy)) {
12481         if (haveAttach)
12482           return false;
12483         haveAttach = true;
12484         if (ParseSingleValueProperty(aState.mAttachment->mValue,
12485                                      aTable[nsStyleImageLayers::attachment]) !=
12486             CSSParseResult::Ok) {
12487           NS_NOTREACHED("should be able to parse");
12488           return false;
12489         }
12490       } else if (nsCSSProps::FindKeyword(keyword,
12491                    nsCSSProps::kImageLayerRepeatKTable, dummy)) {
12492         if (haveRepeat)
12493           return false;
12494         haveRepeat = true;
12495         nsCSSValuePair scratch;
12496         if (!ParseImageLayerRepeatValues(scratch)) {
12497           NS_NOTREACHED("should be able to parse");
12498           return false;
12499         }
12500         aState.mRepeat->mXValue = scratch.mXValue;
12501         aState.mRepeat->mYValue = scratch.mYValue;
12502       } else if (nsCSSProps::FindKeyword(keyword,
12503                    nsCSSProps::kImageLayerPositionKTable, dummy)) {
12504         if (havePositionAndSize)
12505           return false;
12506         havePositionAndSize = true;
12507 
12508         if (!ParsePositionValueSeparateCoords(aState.mPositionX->mValue,
12509                                               aState.mPositionY->mValue)) {
12510           return false;
12511         }
12512         if (ExpectSymbol('/', true)) {
12513           nsCSSValuePair scratch;
12514           if (!ParseImageLayerSizeValues(scratch)) {
12515             return false;
12516           }
12517           aState.mSize->mXValue = scratch.mXValue;
12518           aState.mSize->mYValue = scratch.mYValue;
12519         }
12520       } else if (nsCSSProps::FindKeyword(keyword,
12521                    nsCSSProps::kImageLayerOriginKTable, dummy)) {
12522         if (haveOrigin)
12523           return false;
12524         haveOrigin = true;
12525         if (ParseSingleValueProperty(aState.mOrigin->mValue,
12526                                      aTable[nsStyleImageLayers::origin]) !=
12527             CSSParseResult::Ok) {
12528           NS_NOTREACHED("should be able to parse");
12529           return false;
12530         }
12531 
12532         // The spec allows a second box value (for background-clip),
12533         // immediately following the first one (for background-origin).
12534 
12535 #ifdef DEBUG
12536         for (size_t i = 0; nsCSSProps::kImageLayerOriginKTable[i].mValue != -1; i++) {
12537           // For each keyword & value in kOriginKTable, ensure that
12538           // kBackgroundKTable has a matching entry at the same position.
12539           MOZ_ASSERT(nsCSSProps::kImageLayerOriginKTable[i].mKeyword ==
12540                      nsCSSProps::kBackgroundClipKTable[i].mKeyword);
12541           MOZ_ASSERT(nsCSSProps::kImageLayerOriginKTable[i].mValue ==
12542                      nsCSSProps::kBackgroundClipKTable[i].mValue);
12543         }
12544 #endif
12545         static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER ==
12546                       NS_STYLE_IMAGELAYER_ORIGIN_BORDER &&
12547                       NS_STYLE_IMAGELAYER_CLIP_PADDING ==
12548                       NS_STYLE_IMAGELAYER_ORIGIN_PADDING &&
12549                       NS_STYLE_IMAGELAYER_CLIP_CONTENT ==
12550                       NS_STYLE_IMAGELAYER_ORIGIN_CONTENT,
12551                       "bg-clip and bg-origin style constants must agree");
12552 
12553         CSSParseResult result =
12554           ParseSingleValueProperty(aState.mClip->mValue,
12555                                    aTable[nsStyleImageLayers::clip]);
12556         MOZ_ASSERT(result != CSSParseResult::Error,
12557                    "how can failing to parse a single background-clip value "
12558                    "consume tokens?");
12559         if (result == CSSParseResult::NotFound) {
12560           // When exactly one <box> value is set, it is used for both
12561           // 'background-origin' and 'background-clip'.
12562           // See assertions above showing these values are compatible.
12563           aState.mClip->mValue = aState.mOrigin->mValue;
12564         }
12565       } else if (aTable[nsStyleImageLayers::composite] != eCSSProperty_UNKNOWN &&
12566                  nsCSSProps::FindKeyword(keyword,
12567                    nsCSSProps::kImageLayerCompositeKTable, dummy)) {
12568         if (haveComposite)
12569           return false;
12570         haveComposite = true;
12571         if (ParseSingleValueProperty(aState.mComposite->mValue,
12572                                      aTable[nsStyleImageLayers::composite]) !=
12573             CSSParseResult::Ok) {
12574           NS_NOTREACHED("should be able to parse");
12575           return false;
12576         }
12577       } else if (aTable[nsStyleImageLayers::maskMode] != eCSSProperty_UNKNOWN &&
12578                  nsCSSProps::FindKeyword(keyword,
12579                    nsCSSProps::kImageLayerModeKTable, dummy)) {
12580         if (haveMode)
12581           return false;
12582         haveMode = true;
12583         if (ParseSingleValueProperty(aState.mMode->mValue,
12584                                      aTable[nsStyleImageLayers::maskMode]) !=
12585             CSSParseResult::Ok) {
12586           NS_NOTREACHED("should be able to parse");
12587           return false;
12588         }
12589       } else if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
12590         if (haveColor)
12591           return false;
12592         haveColor = true;
12593         if (ParseSingleValueProperty(aState.mColor,
12594                                      aTable[nsStyleImageLayers::color]) !=
12595                                        CSSParseResult::Ok) {
12596           return false;
12597         }
12598       } else {
12599         return false;
12600       }
12601     } else if (tt == eCSSToken_URL ||
12602                (tt == eCSSToken_Function &&
12603                 IsFunctionTokenValidForImageLayerImage(mToken))) {
12604       if (haveImage)
12605         return false;
12606       haveImage = true;
12607       if (ParseSingleValueProperty(aState.mImage->mValue,
12608                                    aTable[nsStyleImageLayers::image]) !=
12609           CSSParseResult::Ok) {
12610         return false;
12611       }
12612     } else if (tt == eCSSToken_Dimension ||
12613                tt == eCSSToken_Number ||
12614                tt == eCSSToken_Percentage ||
12615                (tt == eCSSToken_Function &&
12616                 (mToken.mIdent.LowerCaseEqualsLiteral("calc") ||
12617                  mToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")))) {
12618       if (havePositionAndSize)
12619         return false;
12620       havePositionAndSize = true;
12621       if (!ParsePositionValueSeparateCoords(aState.mPositionX->mValue,
12622                                             aState.mPositionY->mValue)) {
12623         return false;
12624       }
12625       if (ExpectSymbol('/', true)) {
12626         nsCSSValuePair scratch;
12627         if (!ParseImageLayerSizeValues(scratch)) {
12628           return false;
12629         }
12630         aState.mSize->mXValue = scratch.mXValue;
12631         aState.mSize->mYValue = scratch.mYValue;
12632       }
12633     } else if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
12634       if (haveColor)
12635         return false;
12636       haveColor = true;
12637       // Note: This parses 'inherit', 'initial' and 'unset', but
12638       // we've already checked for them, so it's ok.
12639       if (ParseSingleValueProperty(aState.mColor,
12640                                    aTable[nsStyleImageLayers::color]) !=
12641                                      CSSParseResult::Ok) {
12642         return false;
12643       }
12644     } else {
12645       return false;
12646     }
12647 
12648     haveSomething = true;
12649   }
12650 
12651   return haveSomething;
12652 }
12653 
12654 // This function is very similar to ParseScrollSnapCoordinate,
12655 // ParseImageLayerPosition, and ParseImageLayersSize.
12656 bool
12657 CSSParserImpl::ParseValueList(nsCSSPropertyID aPropID)
12658 {
12659   // aPropID is a single value prop-id
12660   nsCSSValue value;
12661   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
12662   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
12663     nsCSSValueList* item = value.SetListValue();
12664     for (;;) {
12665       if (ParseSingleValueProperty(item->mValue, aPropID) !=
12666           CSSParseResult::Ok) {
12667         return false;
12668       }
12669       if (!ExpectSymbol(',', true)) {
12670         break;
12671       }
12672       item->mNext = new nsCSSValueList;
12673       item = item->mNext;
12674     }
12675   }
12676   AppendValue(aPropID, value);
12677   return true;
12678 }
12679 
12680 bool
12681 CSSParserImpl::ParseImageLayerRepeat(nsCSSPropertyID aPropID)
12682 {
12683   nsCSSValue value;
12684   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
12685   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
12686     nsCSSValuePair valuePair;
12687     if (!ParseImageLayerRepeatValues(valuePair)) {
12688       return false;
12689     }
12690     nsCSSValuePairList* item = value.SetPairListValue();
12691     for (;;) {
12692       item->mXValue = valuePair.mXValue;
12693       item->mYValue = valuePair.mYValue;
12694       if (!ExpectSymbol(',', true)) {
12695         break;
12696       }
12697       if (!ParseImageLayerRepeatValues(valuePair)) {
12698         return false;
12699       }
12700       item->mNext = new nsCSSValuePairList;
12701       item = item->mNext;
12702     }
12703   }
12704 
12705   AppendValue(aPropID, value);
12706   return true;
12707 }
12708 
12709 bool
12710 CSSParserImpl::ParseImageLayerRepeatValues(nsCSSValuePair& aValue)
12711 {
12712   nsCSSValue& xValue = aValue.mXValue;
12713   nsCSSValue& yValue = aValue.mYValue;
12714 
12715   if (ParseEnum(xValue, nsCSSProps::kImageLayerRepeatKTable)) {
12716     int32_t value = xValue.GetIntValue();
12717     // For single values set yValue as eCSSUnit_Null.
12718     if (value == NS_STYLE_IMAGELAYER_REPEAT_REPEAT_X ||
12719         value == NS_STYLE_IMAGELAYER_REPEAT_REPEAT_Y ||
12720         !ParseEnum(yValue, nsCSSProps::kImageLayerRepeatPartKTable)) {
12721       // the caller will fail cases like "repeat-x no-repeat"
12722       // by expecting a list separator or an end property.
12723       yValue.Reset();
12724     }
12725     return true;
12726   }
12727 
12728   return false;
12729 }
12730 
12731 bool
12732 CSSParserImpl::ParseImageLayerPosition(const nsCSSPropertyID aTable[])
12733 {
12734   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
12735   nsCSSValue position;
12736   if (ParseSingleTokenVariant(position, VARIANT_INHERIT, nullptr)) {
12737     AppendValue(aTable[nsStyleImageLayers::positionX], position);
12738     AppendValue(aTable[nsStyleImageLayers::positionY], position);
12739     return true;
12740   }
12741 
12742   nsCSSValue itemValueX;
12743   nsCSSValue itemValueY;
12744   if (!ParsePositionValueSeparateCoords(itemValueX, itemValueY)) {
12745     return false;
12746   }
12747 
12748   nsCSSValue valueX;
12749   nsCSSValue valueY;
12750   nsCSSValueList* itemX = valueX.SetListValue();
12751   nsCSSValueList* itemY = valueY.SetListValue();
12752   for (;;) {
12753     itemX->mValue = itemValueX;
12754     itemY->mValue = itemValueY;
12755     if (!ExpectSymbol(',', true)) {
12756       break;
12757     }
12758     if (!ParsePositionValueSeparateCoords(itemValueX, itemValueY)) {
12759       return false;
12760     }
12761     itemX->mNext = new nsCSSValueList;
12762     itemY->mNext = new nsCSSValueList;
12763     itemX = itemX->mNext;
12764     itemY = itemY->mNext;
12765   }
12766   AppendValue(aTable[nsStyleImageLayers::positionX], valueX);
12767   AppendValue(aTable[nsStyleImageLayers::positionY], valueY);
12768   return true;
12769 }
12770 
12771 bool
12772 CSSParserImpl::ParseImageLayerPositionCoord(nsCSSPropertyID aPropID, bool aIsHorizontal)
12773 {
12774   nsCSSValue value;
12775   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
12776   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
12777     nsCSSValue itemValue;
12778     if (!ParseImageLayerPositionCoordItem(itemValue, aIsHorizontal)) {
12779       return false;
12780     }
12781     nsCSSValueList* item = value.SetListValue();
12782     for (;;) {
12783       item->mValue = itemValue;
12784       if (!ExpectSymbol(',', true)) {
12785         break;
12786       }
12787       if (!ParseImageLayerPositionCoordItem(itemValue, aIsHorizontal)) {
12788         return false;
12789       }
12790       item->mNext = new nsCSSValueList;
12791       item = item->mNext;
12792     }
12793   }
12794   AppendValue(aPropID, value);
12795   return true;
12796 }
12797 
12798 /**
12799  * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used
12800  * for parsing the CSS 2.1 background-position syntax (which has at
12801  * most two values).  (Compare to the css3-background syntax which
12802  * takes up to four values.)  Some current CSS specifications that
12803  * use background-position-like syntax still use this old syntax.
12804  **
12805  * Parses two values that correspond to positions in a box.  These can be
12806  * values corresponding to percentages of the box, raw offsets, or keywords
12807  * like "top," "left center," etc.
12808  *
12809  * @param aOut The nsCSSValuePair in which to place the result.
12810  * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are
12811  *   legal values
12812  * @param aAllowExplicitCenter If true, 'center' is a legal value
12813  * @return Whether or not the operation succeeded.
12814  */
12815 bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
12816                                            bool aAcceptsInherit,
12817                                            bool aAllowExplicitCenter)
12818 {
12819   // First try a percentage or a length value
12820   nsCSSValue &xValue = aOut.mXValue,
12821              &yValue = aOut.mYValue;
12822   int32_t variantMask =
12823     (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC;
12824   CSSParseResult result = ParseVariant(xValue, variantMask, nullptr);
12825   if (result == CSSParseResult::Error) {
12826     return false;
12827   } else if (result == CSSParseResult::Ok) {
12828     if (eCSSUnit_Inherit == xValue.GetUnit() ||
12829         eCSSUnit_Initial == xValue.GetUnit() ||
12830         eCSSUnit_Unset == xValue.GetUnit()) {  // both are inherit, initial or unset
12831       yValue = xValue;
12832       return true;
12833     }
12834     // We have one percentage/length/calc. Get the optional second
12835     // percentage/length/calc/keyword.
12836     result = ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr);
12837     if (result == CSSParseResult::Error) {
12838       return false;
12839     } else if (result == CSSParseResult::Ok) {
12840       // We have two numbers
12841       return true;
12842     }
12843 
12844     if (ParseEnum(yValue, nsCSSProps::kImageLayerPositionKTable)) {
12845       int32_t yVal = yValue.GetIntValue();
12846       if (!(yVal & BG_CTB)) {
12847         // The second keyword can only be 'center', 'top', or 'bottom'
12848         return false;
12849       }
12850       yValue = BoxPositionMaskToCSSValue(yVal, false);
12851       return true;
12852     }
12853 
12854     // If only one percentage or length value is given, it sets the
12855     // horizontal position only, and the vertical position will be 50%.
12856     yValue.SetPercentValue(0.5f);
12857     return true;
12858   }
12859 
12860   // Now try keywords. We do this manually to allow for the first
12861   // appearance of "center" to apply to the either the x or y
12862   // position (it's ambiguous so we have to disambiguate). Each
12863   // allowed keyword value is assigned it's own bit. We don't allow
12864   // any duplicate keywords other than center. We try to get two
12865   // keywords but it's okay if there is only one.
12866   int32_t mask = 0;
12867   if (ParseEnum(xValue, nsCSSProps::kImageLayerPositionKTable)) {
12868     int32_t bit = xValue.GetIntValue();
12869     mask |= bit;
12870     if (ParseEnum(xValue, nsCSSProps::kImageLayerPositionKTable)) {
12871       bit = xValue.GetIntValue();
12872       if (mask & (bit & ~BG_CENTER)) {
12873         // Only the 'center' keyword can be duplicated.
12874         return false;
12875       }
12876       mask |= bit;
12877     }
12878     else {
12879       // Only one keyword.  See if we have a length, percentage, or calc.
12880       result = ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr);
12881       if (result == CSSParseResult::Error) {
12882         return false;
12883       } else if (result == CSSParseResult::Ok) {
12884         if (!(mask & BG_CLR)) {
12885           // The first keyword can only be 'center', 'left', or 'right'
12886           return false;
12887         }
12888 
12889         xValue = BoxPositionMaskToCSSValue(mask, true);
12890         return true;
12891       }
12892     }
12893   }
12894 
12895   // Check for bad input. Bad input consists of no matching keywords,
12896   // or pairs of x keywords or pairs of y keywords.
12897   if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
12898       (mask == (BG_LEFT | BG_RIGHT)) ||
12899       (!aAllowExplicitCenter && (mask & BG_CENTER))) {
12900     return false;
12901   }
12902 
12903   // Create style values
12904   xValue = BoxPositionMaskToCSSValue(mask, true);
12905   yValue = BoxPositionMaskToCSSValue(mask, false);
12906   return true;
12907 }
12908 
12909 // Parses a CSS <position> value, for e.g. the 'background-position' property.
12910 // Spec reference: http://www.w3.org/TR/css3-background/#ltpositiongt
12911 // Invariants:
12912 //  - Always produces a four-value array on a successful parse.
12913 //  - The values are: X edge, X offset, Y edge, Y offset.
12914 //  - Edges are always keywords or null.
12915 //  - A |center| edge will not have an offset.
12916 bool
12917 CSSParserImpl::ParsePositionValue(nsCSSValue& aOut)
12918 {
12919   RefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(4);
12920   aOut.SetArrayValue(value, eCSSUnit_Array);
12921 
12922   // The following clarifies organisation of the array.
12923   nsCSSValue &xEdge   = value->Item(0),
12924              &xOffset = value->Item(1),
12925              &yEdge   = value->Item(2),
12926              &yOffset = value->Item(3);
12927 
12928   // Parse all the values into the array.
12929   uint32_t valueCount = 0;
12930   for (int32_t i = 0; i < 4; i++) {
12931     CSSParseResult result =
12932       ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD,
12933                    nsCSSProps::kImageLayerPositionKTable);
12934     if (result == CSSParseResult::Error) {
12935       return false;
12936     } else if (result == CSSParseResult::NotFound) {
12937       break;
12938     }
12939     ++valueCount;
12940   }
12941 
12942   switch (valueCount) {
12943     case 4:
12944       // "If three or four values are given, then each <percentage> or <length>
12945       // represents an offset and must be preceded by a keyword, which specifies
12946       // from which edge the offset is given."
12947       if (eCSSUnit_Enumerated != xEdge.GetUnit() ||
12948           BG_CENTER == xEdge.GetIntValue() ||
12949           eCSSUnit_Enumerated == xOffset.GetUnit() ||
12950           eCSSUnit_Enumerated != yEdge.GetUnit() ||
12951           BG_CENTER == yEdge.GetIntValue() ||
12952           eCSSUnit_Enumerated == yOffset.GetUnit()) {
12953         return false;
12954       }
12955       break;
12956     case 3:
12957       // "If three or four values are given, then each <percentage> or<length>
12958       // represents an offset and must be preceded by a keyword, which specifies
12959       // from which edge the offset is given." ... "If three values are given,
12960       // the missing offset is assumed to be zero."
12961       if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) {
12962         // keyword offset keyword
12963         // Second value is non-keyword, thus first value must be a non-center
12964         // keyword.
12965         if (eCSSUnit_Enumerated != value->Item(0).GetUnit() ||
12966             BG_CENTER == value->Item(0).GetIntValue()) {
12967           return false;
12968         }
12969 
12970         // Remaining value must be a keyword.
12971         if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
12972           return false;
12973         }
12974 
12975         yOffset.Reset(); // Everything else is in the correct position.
12976       } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
12977         // keyword keyword offset
12978         // Third value is non-keyword, thus second value must be non-center
12979         // keyword.
12980         if (BG_CENTER == value->Item(1).GetIntValue()) {
12981           return false;
12982         }
12983 
12984         // Remaining value must be a keyword.
12985         if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) {
12986           return false;
12987         }
12988 
12989         // Move the values to the correct position in the array.
12990         value->Item(3) = value->Item(2); // yOffset
12991         value->Item(2) = value->Item(1); // yEdge
12992         value->Item(1).Reset(); // xOffset
12993       } else {
12994         return false;
12995       }
12996       break;
12997     case 2:
12998       // "If two values are given and at least one value is not a keyword, then
12999       // the first value represents the horizontal position (or offset) and the
13000       // second represents the vertical position (or offset)"
13001       if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
13002         if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
13003           // keyword keyword
13004           value->Item(2) = value->Item(1); // move yEdge to correct position
13005           xOffset.Reset();
13006           yOffset.Reset();
13007         } else {
13008           // keyword offset
13009           // First value must represent horizontal position.
13010           if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) {
13011             return false;
13012           }
13013           value->Item(3) = value->Item(1); // move yOffset to correct position
13014           xOffset.Reset();
13015           yEdge.Reset();
13016         }
13017       } else {
13018         if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
13019           // offset keyword
13020           // Second value must represent vertical position.
13021           if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) {
13022             return false;
13023           }
13024           value->Item(2) = value->Item(1); // move yEdge to correct position
13025           value->Item(1) = value->Item(0); // move xOffset to correct position
13026           xEdge.Reset();
13027           yOffset.Reset();
13028         } else {
13029           // offset offset
13030           value->Item(3) = value->Item(1); // move yOffset to correct position
13031           value->Item(1) = value->Item(0); // move xOffset to correct position
13032           xEdge.Reset();
13033           yEdge.Reset();
13034         }
13035       }
13036       break;
13037     case 1:
13038       // "If only one value is specified, the second value is assumed to be
13039       // center."
13040       if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
13041         xOffset.Reset();
13042       } else {
13043         value->Item(1) = value->Item(0); // move xOffset to correct position
13044         xEdge.Reset();
13045       }
13046       yEdge.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_CENTER, eCSSUnit_Enumerated);
13047       yOffset.Reset();
13048       break;
13049     default:
13050       return false;
13051   }
13052 
13053   // For compatibility with CSS2.1 code the edges can be unspecified.
13054   // Unspecified edges are recorded as nullptr.
13055   NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit()  ||
13056                 eCSSUnit_Null       == xEdge.GetUnit()) &&
13057                (eCSSUnit_Enumerated == yEdge.GetUnit()  ||
13058                 eCSSUnit_Null       == yEdge.GetUnit()) &&
13059                 eCSSUnit_Enumerated != xOffset.GetUnit()  &&
13060                 eCSSUnit_Enumerated != yOffset.GetUnit(),
13061                 "Unexpected units");
13062 
13063   // Keywords in first and second pairs can not both be vertical or
13064   // horizontal keywords. (eg. left right, bottom top). Additionally,
13065   // non-center keyword can not be duplicated (eg. left left).
13066   int32_t xEdgeEnum =
13067           xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0;
13068   int32_t yEdgeEnum =
13069           yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0;
13070   if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) ||
13071       (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) ||
13072       (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) {
13073     return false;
13074   }
13075 
13076   // The values could be in an order that is different than expected.
13077   // eg. x contains vertical information, y contains horizontal information.
13078   // Swap if incorrect order.
13079   if (xEdgeEnum & (BG_TOP | BG_BOTTOM) ||
13080       yEdgeEnum & (BG_LEFT | BG_RIGHT)) {
13081     nsCSSValue swapEdge = xEdge;
13082     nsCSSValue swapOffset = xOffset;
13083     xEdge = yEdge;
13084     xOffset = yOffset;
13085     yEdge = swapEdge;
13086     yOffset = swapOffset;
13087   }
13088 
13089   return true;
13090 }
13091 
13092 static void
13093 AdjustEdgeOffsetPairForBasicShape(nsCSSValue& aEdge,
13094                                   nsCSSValue& aOffset,
13095                                   uint8_t aDefaultEdge)
13096 {
13097   // 0 length offsets are 0%
13098   if (aOffset.IsLengthUnit() && aOffset.GetFloatValue() == 0.0) {
13099     aOffset.SetPercentValue(0);
13100   }
13101 
13102   // Default edge is top/left in the 4-value case
13103   // In case of 1 or 0 values, the default is center,
13104   // but ParsePositionValue already handles this case
13105   if (eCSSUnit_Null == aEdge.GetUnit()) {
13106     aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
13107   }
13108   // Default offset is 0%
13109   if (eCSSUnit_Null == aOffset.GetUnit()) {
13110     aOffset.SetPercentValue(0.0);
13111   }
13112   if (eCSSUnit_Enumerated == aEdge.GetUnit() &&
13113       eCSSUnit_Percent == aOffset.GetUnit()) {
13114     switch (aEdge.GetIntValue()) {
13115       case NS_STYLE_IMAGELAYER_POSITION_CENTER:
13116         aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
13117         MOZ_ASSERT(aOffset.GetPercentValue() == 0.0,
13118                    "center cannot be used with an offset");
13119         aOffset.SetPercentValue(0.5);
13120         break;
13121       case NS_STYLE_IMAGELAYER_POSITION_BOTTOM:
13122         MOZ_ASSERT(aDefaultEdge == NS_STYLE_IMAGELAYER_POSITION_TOP);
13123         aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
13124         aOffset.SetPercentValue(1 - aOffset.GetPercentValue());
13125         break;
13126       case NS_STYLE_IMAGELAYER_POSITION_RIGHT:
13127         MOZ_ASSERT(aDefaultEdge == NS_STYLE_IMAGELAYER_POSITION_LEFT);
13128         aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
13129         aOffset.SetPercentValue(1 - aOffset.GetPercentValue());
13130     }
13131   }
13132 }
13133 
13134 // https://drafts.csswg.org/css-shapes/#basic-shape-serialization
13135 // We set values to defaults while parsing for basic shapes
13136 // Invariants:
13137 //  - Always produces a four-value array on a successful parse.
13138 //  - The values are: X edge, X offset, Y edge, Y offset
13139 //  - Edges are always keywords (not including center)
13140 //  - Offsets are nonnull
13141 //  - Percentage offsets have keywords folded into them,
13142 //    so "bottom 40%" or "right 20%" will not exist.
13143 bool
13144 CSSParserImpl::ParsePositionValueForBasicShape(nsCSSValue& aOut)
13145 {
13146   if (!ParsePositionValue(aOut)) {
13147     return false;
13148   }
13149   nsCSSValue::Array* value = aOut.GetArrayValue();
13150   nsCSSValue& xEdge   = value->Item(0);
13151   nsCSSValue& xOffset = value->Item(1);
13152   nsCSSValue& yEdge   = value->Item(2);
13153   nsCSSValue& yOffset = value->Item(3);
13154   // A keyword edge + percent offset pair can be contracted
13155   // into the percentage with the default value in the edge.
13156   // Offset lengths which are 0 can also be rewritten as 0%
13157   AdjustEdgeOffsetPairForBasicShape(xEdge, xOffset,
13158                                     NS_STYLE_IMAGELAYER_POSITION_LEFT);
13159   AdjustEdgeOffsetPairForBasicShape(yEdge, yOffset,
13160                                     NS_STYLE_IMAGELAYER_POSITION_TOP);
13161   return true;
13162 }
13163 
13164 bool
13165 CSSParserImpl::ParsePositionValueSeparateCoords(nsCSSValue& aOutX, nsCSSValue& aOutY)
13166 {
13167   nsCSSValue scratch;
13168   if (!ParsePositionValue(scratch)) {
13169     return false;
13170   }
13171 
13172   // Separate the four values into two pairs of two values for X and Y.
13173   RefPtr<nsCSSValue::Array> valueX = nsCSSValue::Array::Create(2);
13174   RefPtr<nsCSSValue::Array> valueY = nsCSSValue::Array::Create(2);
13175   aOutX.SetArrayValue(valueX, eCSSUnit_Array);
13176   aOutY.SetArrayValue(valueY, eCSSUnit_Array);
13177 
13178   RefPtr<nsCSSValue::Array> value = scratch.GetArrayValue();
13179   valueX->Item(0) = value->Item(0);
13180   valueX->Item(1) = value->Item(1);
13181   valueY->Item(0) = value->Item(2);
13182   valueY->Item(1) = value->Item(3);
13183   return true;
13184 }
13185 
13186 // Parses one item in a list of values for the 'background-position-x' or
13187 // 'background-position-y' property. Does not support the start/end keywords.
13188 // Spec reference: https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-x
13189 bool
13190 CSSParserImpl::ParseImageLayerPositionCoordItem(nsCSSValue& aOut, bool aIsHorizontal)
13191 {
13192   RefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(2);
13193   aOut.SetArrayValue(value, eCSSUnit_Array);
13194 
13195   nsCSSValue &edge   = value->Item(0),
13196              &offset = value->Item(1);
13197 
13198   nsCSSValue edgeOrOffset;
13199   CSSParseResult result =
13200     ParseVariant(edgeOrOffset, VARIANT_LPCALC | VARIANT_KEYWORD,
13201                  nsCSSProps::kImageLayerPositionKTable);
13202   if (result != CSSParseResult::Ok) {
13203     return false;
13204   }
13205 
13206   if (edgeOrOffset.GetUnit() == eCSSUnit_Enumerated) {
13207     edge = edgeOrOffset;
13208 
13209     // The edge can be followed by an optional offset.
13210     result = ParseVariant(offset, VARIANT_LPCALC, nullptr);
13211     if (result == CSSParseResult::Error) {
13212       return false;
13213     }
13214   } else {
13215     offset = edgeOrOffset;
13216   }
13217 
13218   // Keywords for horizontal properties cannot be vertical keywords, and
13219   // keywords for vertical properties cannot be horizontal keywords.
13220   // Also, if an offset is specified, the edge cannot be center.
13221   int32_t edgeEnum =
13222           edge.GetUnit() == eCSSUnit_Enumerated ? edge.GetIntValue() : 0;
13223   int32_t allowedKeywords =
13224     (aIsHorizontal ? (BG_LEFT | BG_RIGHT) : (BG_TOP | BG_BOTTOM)) |
13225     (offset.GetUnit() == eCSSUnit_Null ? BG_CENTER : 0);
13226   if (edgeEnum & ~allowedKeywords) {
13227     return false;
13228   }
13229 
13230   NS_ASSERTION((eCSSUnit_Enumerated == edge.GetUnit() ||
13231                 eCSSUnit_Null       == edge.GetUnit()) &&
13232                eCSSUnit_Enumerated != offset.GetUnit(),
13233                "Unexpected units");
13234 
13235   return true;
13236 }
13237 
13238 // This function is very similar to ParseScrollSnapCoordinate,
13239 // ParseImageLayers, and ParseImageLayerPosition.
13240 bool
13241 CSSParserImpl::ParseImageLayerSize(nsCSSPropertyID aPropID)
13242 {
13243   nsCSSValue value;
13244   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
13245   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13246     nsCSSValuePair valuePair;
13247     if (!ParseImageLayerSizeValues(valuePair)) {
13248       return false;
13249     }
13250     nsCSSValuePairList* item = value.SetPairListValue();
13251     for (;;) {
13252       item->mXValue = valuePair.mXValue;
13253       item->mYValue = valuePair.mYValue;
13254       if (!ExpectSymbol(',', true)) {
13255         break;
13256       }
13257       if (!ParseImageLayerSizeValues(valuePair)) {
13258         return false;
13259       }
13260       item->mNext = new nsCSSValuePairList;
13261       item = item->mNext;
13262     }
13263   }
13264   AppendValue(aPropID, value);
13265   return true;
13266 }
13267 
13268 /**
13269  * Parses two values that correspond to lengths for the background-size
13270  * property.  These can be one or two lengths (or the 'auto' keyword) or
13271  * percentages corresponding to the element's dimensions or the single keywords
13272  * 'contain' or 'cover'.  'initial', 'inherit' and 'unset' must be handled by
13273  * the caller if desired.
13274  *
13275  * @param aOut The nsCSSValuePair in which to place the result.
13276  * @return Whether or not the operation succeeded.
13277  */
13278 #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC)
13279 bool CSSParserImpl::ParseImageLayerSizeValues(nsCSSValuePair &aOut)
13280 {
13281   // First try a percentage or a length value
13282   nsCSSValue &xValue = aOut.mXValue,
13283              &yValue = aOut.mYValue;
13284   CSSParseResult result =
13285     ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr);
13286   if (result == CSSParseResult::Error) {
13287     return false;
13288   } else if (result == CSSParseResult::Ok) {
13289     // We have one percentage/length/calc/auto. Get the optional second
13290     // percentage/length/calc/keyword.
13291     result = ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr);
13292     if (result == CSSParseResult::Error) {
13293       return false;
13294     } else if (result == CSSParseResult::Ok) {
13295       // We have a second percentage/length/calc/auto.
13296       return true;
13297     }
13298 
13299     // If only one percentage or length value is given, it sets the
13300     // horizontal size only, and the vertical size will be as if by 'auto'.
13301     yValue.SetAutoValue();
13302     return true;
13303   }
13304 
13305   // Now address 'contain' and 'cover'.
13306   if (!ParseEnum(xValue, nsCSSProps::kImageLayerSizeKTable))
13307     return false;
13308   yValue.Reset();
13309   return true;
13310 }
13311 
13312 #undef BG_SIZE_VARIANT
13313 
13314 bool
13315 CSSParserImpl::ParseBorderColor()
13316 {
13317   return ParseBoxProperties(kBorderColorIDs);
13318 }
13319 
13320 void
13321 CSSParserImpl::SetBorderImageInitialValues()
13322 {
13323   // border-image-source: none
13324   nsCSSValue source;
13325   source.SetNoneValue();
13326   AppendValue(eCSSProperty_border_image_source, source);
13327 
13328   // border-image-slice: 100%
13329   nsCSSValue sliceBoxValue;
13330   nsCSSRect& sliceBox = sliceBoxValue.SetRectValue();
13331   sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent));
13332   nsCSSValue slice;
13333   nsCSSValueList* sliceList = slice.SetListValue();
13334   sliceList->mValue = sliceBoxValue;
13335   AppendValue(eCSSProperty_border_image_slice, slice);
13336 
13337   // border-image-width: 1
13338   nsCSSValue width;
13339   nsCSSRect& widthBox = width.SetRectValue();
13340   widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number));
13341   AppendValue(eCSSProperty_border_image_width, width);
13342 
13343   // border-image-outset: 0
13344   nsCSSValue outset;
13345   nsCSSRect& outsetBox = outset.SetRectValue();
13346   outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number));
13347   AppendValue(eCSSProperty_border_image_outset, outset);
13348 
13349   // border-image-repeat: repeat
13350   nsCSSValue repeat;
13351   nsCSSValuePair repeatPair;
13352   repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
13353                                         eCSSUnit_Enumerated));
13354   repeat.SetPairValue(&repeatPair);
13355   AppendValue(eCSSProperty_border_image_repeat, repeat);
13356 }
13357 
13358 bool
13359 CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
13360                                      bool* aConsumedTokens)
13361 {
13362   // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
13363   nsCSSValue value;
13364 
13365   if (aConsumedTokens) {
13366     *aConsumedTokens = true;
13367   }
13368 
13369   if (aAcceptsInherit &&
13370       ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13371     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
13372     // are done.
13373     AppendValue(eCSSProperty_border_image_slice, value);
13374     return true;
13375   }
13376 
13377   // Try parsing "fill" value.
13378   nsCSSValue imageSliceFillValue;
13379   bool hasFill = ParseEnum(imageSliceFillValue,
13380                            nsCSSProps::kBorderImageSliceKTable);
13381 
13382   // Parse the box dimensions.
13383   nsCSSValue imageSliceBoxValue;
13384   if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue,
13385                                CSS_PROPERTY_VALUE_NONNEGATIVE)) {
13386     if (!hasFill && aConsumedTokens) {
13387       *aConsumedTokens = false;
13388     }
13389 
13390     return false;
13391   }
13392 
13393   // Try parsing "fill" keyword again if the first time failed because keyword
13394   // and slice dimensions can be in any order.
13395   if (!hasFill) {
13396     hasFill = ParseEnum(imageSliceFillValue,
13397                         nsCSSProps::kBorderImageSliceKTable);
13398   }
13399 
13400   nsCSSValueList* borderImageSlice = value.SetListValue();
13401   // Put the box value into the list.
13402   borderImageSlice->mValue = imageSliceBoxValue;
13403 
13404   if (hasFill) {
13405     // Put the "fill" value into the list.
13406     borderImageSlice->mNext = new nsCSSValueList;
13407     borderImageSlice->mNext->mValue = imageSliceFillValue;
13408   }
13409 
13410   AppendValue(eCSSProperty_border_image_slice, value);
13411   return true;
13412 }
13413 
13414 bool
13415 CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
13416 {
13417   // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
13418   nsCSSValue value;
13419 
13420   if (aAcceptsInherit &&
13421       ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13422     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
13423     // are done.
13424     AppendValue(eCSSProperty_border_image_width, value);
13425     return true;
13426   }
13427 
13428   // Parse the box dimensions.
13429   if (!ParseGroupedBoxProperty(VARIANT_ALPN, value, CSS_PROPERTY_VALUE_NONNEGATIVE)) {
13430     return false;
13431   }
13432 
13433   AppendValue(eCSSProperty_border_image_width, value);
13434   return true;
13435 }
13436 
13437 bool
13438 CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
13439 {
13440   // border-image-outset: initial | [<length>|<number>]{1,4}
13441   nsCSSValue value;
13442 
13443   if (aAcceptsInherit &&
13444       ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13445     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
13446     // are done.
13447     AppendValue(eCSSProperty_border_image_outset, value);
13448     return true;
13449   }
13450 
13451   // Parse the box dimensions.
13452   if (!ParseGroupedBoxProperty(VARIANT_LN, value, CSS_PROPERTY_VALUE_NONNEGATIVE)) {
13453     return false;
13454   }
13455 
13456   AppendValue(eCSSProperty_border_image_outset, value);
13457   return true;
13458 }
13459 
13460 bool
13461 CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit)
13462 {
13463   nsCSSValue value;
13464   if (aAcceptsInherit &&
13465       ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13466     // Keywords "inherit", "initial" and "unset" can not be mixed, so we
13467     // are done.
13468     AppendValue(eCSSProperty_border_image_repeat, value);
13469     return true;
13470   }
13471 
13472   nsCSSValuePair result;
13473   if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) {
13474     return false;
13475   }
13476 
13477   // optional second keyword, defaults to first
13478   if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) {
13479     result.mYValue = result.mXValue;
13480   }
13481 
13482   value.SetPairValue(&result);
13483   AppendValue(eCSSProperty_border_image_repeat, value);
13484   return true;
13485 }
13486 
13487 bool
13488 CSSParserImpl::ParseBorderImage()
13489 {
13490   nsAutoParseCompoundProperty compound(this);
13491 
13492   // border-image: inherit | initial |
13493   // <border-image-source> ||
13494   // <border-image-slice>
13495   //   [ / <border-image-width> |
13496   //     / <border-image-width>? / <border-image-outset> ]? ||
13497   // <border-image-repeat>
13498 
13499   nsCSSValue value;
13500   if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13501     AppendValue(eCSSProperty_border_image_source, value);
13502     AppendValue(eCSSProperty_border_image_slice, value);
13503     AppendValue(eCSSProperty_border_image_width, value);
13504     AppendValue(eCSSProperty_border_image_outset, value);
13505     AppendValue(eCSSProperty_border_image_repeat, value);
13506     // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done.
13507     return true;
13508   }
13509 
13510   // No empty property.
13511   if (CheckEndProperty()) {
13512     return false;
13513   }
13514 
13515   // Shorthand properties are required to set everything they can.
13516   SetBorderImageInitialValues();
13517 
13518   bool foundSource = false;
13519   bool foundSliceWidthOutset = false;
13520   bool foundRepeat = false;
13521 
13522   // This loop is used to handle the parsing of border-image properties which
13523   // can appear in any order.
13524   nsCSSValue imageSourceValue;
13525   while (!CheckEndProperty()) {
13526     // <border-image-source>
13527     if (!foundSource) {
13528       CSSParseResult result =
13529         ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr);
13530       if (result == CSSParseResult::Error) {
13531         return false;
13532       } else if (result == CSSParseResult::Ok) {
13533         AppendValue(eCSSProperty_border_image_source, imageSourceValue);
13534         foundSource = true;
13535         continue;
13536       }
13537     }
13538 
13539     // <border-image-slice>
13540     // ParseBorderImageSlice is weird.  It may consume tokens and then return
13541     // false, because it parses a property with two required components that
13542     // can appear in either order.  Since the tokens that were consumed cannot
13543     // parse as anything else we care about, this isn't a problem.
13544     if (!foundSliceWidthOutset) {
13545       bool sliceConsumedTokens = false;
13546       if (ParseBorderImageSlice(false, &sliceConsumedTokens)) {
13547         foundSliceWidthOutset = true;
13548 
13549         // [ / <border-image-width>?
13550         if (ExpectSymbol('/', true)) {
13551           bool foundBorderImageWidth = ParseBorderImageWidth(false);
13552 
13553           // [ / <border-image-outset>
13554           if (ExpectSymbol('/', true)) {
13555             if (!ParseBorderImageOutset(false)) {
13556               return false;
13557             }
13558           } else if (!foundBorderImageWidth) {
13559             // If this part has an trailing slash, the whole declaration is
13560             // invalid.
13561             return false;
13562           }
13563         }
13564 
13565         continue;
13566       } else {
13567         // If we consumed some tokens for <border-image-slice> but did not
13568         // successfully parse it, we have an error.
13569         if (sliceConsumedTokens) {
13570           return false;
13571         }
13572       }
13573     }
13574 
13575     // <border-image-repeat>
13576     if (!foundRepeat && ParseBorderImageRepeat(false)) {
13577       foundRepeat = true;
13578       continue;
13579     }
13580 
13581     return false;
13582   }
13583 
13584   return true;
13585 }
13586 
13587 bool
13588 CSSParserImpl::ParseBorderSpacing()
13589 {
13590   nsCSSValue xValue, yValue;
13591   if (ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr) !=
13592       CSSParseResult::Ok) {
13593     return false;
13594   }
13595 
13596   // If we have one length, get the optional second length.
13597   // set the second value equal to the first.
13598   if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) {
13599     if (ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC,
13600                                 nullptr) == CSSParseResult::Error) {
13601       return false;
13602     }
13603   }
13604 
13605   if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) {
13606     AppendValue(eCSSProperty_border_spacing, xValue);
13607   } else {
13608     nsCSSValue pair;
13609     pair.SetPairValue(xValue, yValue);
13610     AppendValue(eCSSProperty_border_spacing, pair);
13611   }
13612   return true;
13613 }
13614 
13615 bool
13616 CSSParserImpl::ParseBorderSide(const nsCSSPropertyID aPropIDs[],
13617                                bool aSetAllSides)
13618 {
13619   const int32_t numProps = 3;
13620   nsCSSValue  values[numProps];
13621 
13622   int32_t found = ParseChoice(values, aPropIDs, numProps);
13623   if (found < 1) {
13624     return false;
13625   }
13626 
13627   if ((found & 1) == 0) { // Provide default border-width
13628     values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
13629   }
13630   if ((found & 2) == 0) { // Provide default border-style
13631     values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
13632   }
13633   if ((found & 4) == 0) { // text color will be used
13634     values[2].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
13635   }
13636 
13637   if (aSetAllSides) {
13638     // Parsing "border" shorthand; set all four sides to the same thing
13639     for (int32_t index = 0; index < 4; index++) {
13640       NS_ASSERTION(numProps == 3, "This code needs updating");
13641       AppendValue(kBorderWidthIDs[index], values[0]);
13642       AppendValue(kBorderStyleIDs[index], values[1]);
13643       AppendValue(kBorderColorIDs[index], values[2]);
13644     }
13645 
13646     static const nsCSSPropertyID kBorderColorsProps[] = {
13647       eCSSProperty_border_top_colors,
13648       eCSSProperty_border_right_colors,
13649       eCSSProperty_border_bottom_colors,
13650       eCSSProperty_border_left_colors
13651     };
13652 
13653     // Set the other properties that the border shorthand sets to their
13654     // initial values.
13655     nsCSSValue extraValue;
13656     switch (values[0].GetUnit()) {
13657     case eCSSUnit_Inherit:
13658     case eCSSUnit_Initial:
13659     case eCSSUnit_Unset:
13660       extraValue = values[0];
13661       // Set value of border-image properties to initial/inherit/unset
13662       AppendValue(eCSSProperty_border_image_source, extraValue);
13663       AppendValue(eCSSProperty_border_image_slice, extraValue);
13664       AppendValue(eCSSProperty_border_image_width, extraValue);
13665       AppendValue(eCSSProperty_border_image_outset, extraValue);
13666       AppendValue(eCSSProperty_border_image_repeat, extraValue);
13667       break;
13668     default:
13669       extraValue.SetNoneValue();
13670       SetBorderImageInitialValues();
13671       break;
13672     }
13673     NS_FOR_CSS_SIDES(side) {
13674       AppendValue(kBorderColorsProps[side], extraValue);
13675     }
13676   }
13677   else {
13678     // Just set our one side
13679     for (int32_t index = 0; index < numProps; index++) {
13680       AppendValue(aPropIDs[index], values[index]);
13681     }
13682   }
13683   return true;
13684 }
13685 
13686 bool
13687 CSSParserImpl::ParseBorderStyle()
13688 {
13689   return ParseBoxProperties(kBorderStyleIDs);
13690 }
13691 
13692 bool
13693 CSSParserImpl::ParseBorderWidth()
13694 {
13695   return ParseBoxProperties(kBorderWidthIDs);
13696 }
13697 
13698 bool
13699 CSSParserImpl::ParseBorderColors(nsCSSPropertyID aProperty)
13700 {
13701   nsCSSValue value;
13702   // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
13703   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
13704                                nullptr)) {
13705     nsCSSValueList *cur = value.SetListValue();
13706     for (;;) {
13707       if (ParseVariant(cur->mValue, VARIANT_COLOR, nullptr) !=
13708           CSSParseResult::Ok) {
13709         return false;
13710       }
13711       if (CheckEndProperty()) {
13712         break;
13713       }
13714       cur->mNext = new nsCSSValueList;
13715       cur = cur->mNext;
13716     }
13717   }
13718   AppendValue(aProperty, value);
13719   return true;
13720 }
13721 
13722 // Parse the top level of a calc() expression.
13723 bool
13724 CSSParserImpl::ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask)
13725 {
13726   // Parsing calc expressions requires, in a number of cases, looking
13727   // for a token that is *either* a value of the property or a number.
13728   // This can be done without lookahead when we assume that the property
13729   // values cannot themselves be numbers.
13730   MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
13731 
13732   bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk;
13733   mUnitlessLengthQuirk = false;
13734 
13735   // One-iteration loop so we can break to the error-handling case.
13736   do {
13737     // The toplevel of a calc() is always an nsCSSValue::Array of length 1.
13738     RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
13739 
13740     if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask))
13741       break;
13742 
13743     if (!ExpectSymbol(')', true))
13744       break;
13745 
13746     aValue.SetArrayValue(arr, eCSSUnit_Calc);
13747     mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
13748     return true;
13749   } while (false);
13750 
13751   SkipUntil(')');
13752   mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
13753   return false;
13754 }
13755 
13756 // We optimize away the <value-expression> production given that
13757 // ParseVariant consumes initial whitespace and we call
13758 // ExpectSymbol(')') with true for aSkipWS.
13759 //  * If aVariantMask is VARIANT_NUMBER, this function parses the
13760 //    <number-additive-expression> production.
13761 //  * If aVariantMask does not contain VARIANT_NUMBER, this function
13762 //    parses the <value-additive-expression> production.
13763 //  * Otherwise (VARIANT_NUMBER and other bits) this function parses
13764 //    whichever one of the productions matches ***and modifies
13765 //    aVariantMask*** to reflect which one it has parsed by either
13766 //    removing VARIANT_NUMBER or removing all other bits.
13767 // It does so iteratively, but builds the correct recursive
13768 // data structure.
13769 bool
13770 CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue,
13771                                            uint32_t& aVariantMask)
13772 {
13773   MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
13774   nsCSSValue *storage = &aValue;
13775   for (;;) {
13776     bool haveWS;
13777     if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS))
13778       return false;
13779 
13780     if (!haveWS || !GetToken(false))
13781       return true;
13782     nsCSSUnit unit;
13783     if (mToken.IsSymbol('+')) {
13784       unit = eCSSUnit_Calc_Plus;
13785     } else if (mToken.IsSymbol('-')) {
13786       unit = eCSSUnit_Calc_Minus;
13787     } else {
13788       UngetToken();
13789       return true;
13790     }
13791     if (!RequireWhitespace())
13792       return false;
13793 
13794     RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
13795     arr->Item(0) = aValue;
13796     storage = &arr->Item(1);
13797     aValue.SetArrayValue(arr, unit);
13798   }
13799 }
13800 
13801 struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
13802                              public mozilla::css::CSSValueInputCalcOps
13803 {
13804   result_type ComputeLeafValue(const nsCSSValue& aValue)
13805   {
13806     MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
13807     return aValue.GetFloatValue();
13808   }
13809 
13810   float ComputeNumber(const nsCSSValue& aValue)
13811   {
13812     return mozilla::css::ComputeCalc(aValue, *this);
13813   }
13814 };
13815 
13816 //  * If aVariantMask is VARIANT_NUMBER, this function parses the
13817 //    <number-multiplicative-expression> production.
13818 //  * If aVariantMask does not contain VARIANT_NUMBER, this function
13819 //    parses the <value-multiplicative-expression> production.
13820 //  * Otherwise (VARIANT_NUMBER and other bits) this function parses
13821 //    whichever one of the productions matches ***and modifies
13822 //    aVariantMask*** to reflect which one it has parsed by either
13823 //    removing VARIANT_NUMBER or removing all other bits.
13824 // It does so iteratively, but builds the correct recursive data
13825 // structure.
13826 // This function always consumes *trailing* whitespace when it returns
13827 // true; whether there was any such whitespace is returned in the
13828 // aHadFinalWS parameter.
13829 bool
13830 CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
13831                                                  uint32_t& aVariantMask,
13832                                                  bool *aHadFinalWS)
13833 {
13834   MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
13835   bool gotValue = false; // already got the part with the unit
13836   bool afterDivision = false;
13837 
13838   nsCSSValue *storage = &aValue;
13839   for (;;) {
13840     uint32_t variantMask;
13841     if (afterDivision || gotValue) {
13842       variantMask = VARIANT_NUMBER;
13843     } else {
13844       variantMask = aVariantMask | VARIANT_NUMBER;
13845     }
13846     if (!ParseCalcTerm(*storage, variantMask))
13847       return false;
13848     MOZ_ASSERT(variantMask != 0,
13849                "ParseCalcTerm did not set variantMask appropriately");
13850     MOZ_ASSERT(!(variantMask & VARIANT_NUMBER) ||
13851                !(variantMask & ~int32_t(VARIANT_NUMBER)),
13852                "ParseCalcTerm did not set variantMask appropriately");
13853 
13854     if (variantMask & VARIANT_NUMBER) {
13855       // Simplify the value immediately so we can check for division by
13856       // zero.
13857       ReduceNumberCalcOps ops;
13858       float number = mozilla::css::ComputeCalc(*storage, ops);
13859       if (number == 0.0 && afterDivision)
13860         return false;
13861       storage->SetFloatValue(number, eCSSUnit_Number);
13862     } else {
13863       gotValue = true;
13864 
13865       if (storage != &aValue) {
13866         // Simplify any numbers in the Times_L position (which are
13867         // not simplified by the check above).
13868         MOZ_ASSERT(storage == &aValue.GetArrayValue()->Item(1),
13869                    "unexpected relationship to current storage");
13870         nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
13871         ReduceNumberCalcOps ops;
13872         float number = mozilla::css::ComputeCalc(leftValue, ops);
13873         leftValue.SetFloatValue(number, eCSSUnit_Number);
13874       }
13875     }
13876 
13877     bool hadWS = RequireWhitespace();
13878     if (!GetToken(false)) {
13879       *aHadFinalWS = hadWS;
13880       break;
13881     }
13882     nsCSSUnit unit;
13883     if (mToken.IsSymbol('*')) {
13884       unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
13885       afterDivision = false;
13886     } else if (mToken.IsSymbol('/')) {
13887       unit = eCSSUnit_Calc_Divided;
13888       afterDivision = true;
13889     } else {
13890       UngetToken();
13891       *aHadFinalWS = hadWS;
13892       break;
13893     }
13894 
13895     RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
13896     arr->Item(0) = aValue;
13897     storage = &arr->Item(1);
13898     aValue.SetArrayValue(arr, unit);
13899   }
13900 
13901   // Adjust aVariantMask (see comments above function) to reflect which
13902   // option we took.
13903   if (aVariantMask & VARIANT_NUMBER) {
13904     if (gotValue) {
13905       aVariantMask &= ~int32_t(VARIANT_NUMBER);
13906     } else {
13907       aVariantMask = VARIANT_NUMBER;
13908     }
13909   } else {
13910     if (!gotValue) {
13911       // We had to find a value, but we didn't.
13912       return false;
13913     }
13914   }
13915 
13916   return true;
13917 }
13918 
13919 //  * If aVariantMask is VARIANT_NUMBER, this function parses the
13920 //    <number-term> production.
13921 //  * If aVariantMask does not contain VARIANT_NUMBER, this function
13922 //    parses the <value-term> production.
13923 //  * Otherwise (VARIANT_NUMBER and other bits) this function parses
13924 //    whichever one of the productions matches ***and modifies
13925 //    aVariantMask*** to reflect which one it has parsed by either
13926 //    removing VARIANT_NUMBER or removing all other bits.
13927 bool
13928 CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask)
13929 {
13930   MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
13931   if (!GetToken(true))
13932     return false;
13933   // Either an additive expression in parentheses...
13934   if (mToken.IsSymbol('(') ||
13935       // Treat nested calc() as plain parenthesis.
13936       IsCSSTokenCalcFunction(mToken)) {
13937     if (!ParseCalcAdditiveExpression(aValue, aVariantMask) ||
13938         !ExpectSymbol(')', true)) {
13939       SkipUntil(')');
13940       return false;
13941     }
13942     return true;
13943   }
13944   // ... or just a value
13945   UngetToken();
13946   // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
13947   // always gets picked up
13948   if (ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr) !=
13949       CSSParseResult::Ok) {
13950     return false;
13951   }
13952   // ...and do the VARIANT_NUMBER check ourselves.
13953   if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
13954     return false;
13955   }
13956   // If we did the value parsing, we need to adjust aVariantMask to
13957   // reflect which option we took (see above).
13958   if (aVariantMask & VARIANT_NUMBER) {
13959     if (aValue.GetUnit() == eCSSUnit_Number) {
13960       aVariantMask = VARIANT_NUMBER;
13961     } else {
13962       aVariantMask &= ~int32_t(VARIANT_NUMBER);
13963     }
13964   }
13965   return true;
13966 }
13967 
13968 // This function consumes all consecutive whitespace and returns whether
13969 // there was any.
13970 bool
13971 CSSParserImpl::RequireWhitespace()
13972 {
13973   if (!GetToken(false))
13974     return false;
13975   if (mToken.mType != eCSSToken_Whitespace) {
13976     UngetToken();
13977     return false;
13978   }
13979   // Skip any additional whitespace tokens.
13980   if (GetToken(true)) {
13981     UngetToken();
13982   }
13983   return true;
13984 }
13985 
13986 bool
13987 CSSParserImpl::ParseRect(nsCSSPropertyID aPropID)
13988 {
13989   nsCSSValue val;
13990   if (ParseSingleTokenVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) {
13991     AppendValue(aPropID, val);
13992     return true;
13993   }
13994 
13995   if (! GetToken(true)) {
13996     return false;
13997   }
13998 
13999   if (mToken.mType == eCSSToken_Function &&
14000       mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
14001     nsCSSRect& rect = val.SetRectValue();
14002     bool useCommas;
14003     NS_FOR_CSS_SIDES(side) {
14004       if (!ParseSingleTokenVariant(rect.*(nsCSSRect::sides[side]),
14005                                    VARIANT_AL, nullptr)) {
14006         return false;
14007       }
14008       if (side == 0) {
14009         useCommas = ExpectSymbol(',', true);
14010       } else if (useCommas && side < 3) {
14011         // Skip optional commas between elements, but only if the first
14012         // separator was a comma.
14013         if (!ExpectSymbol(',', true)) {
14014           return false;
14015         }
14016       }
14017     }
14018     if (!ExpectSymbol(')', true)) {
14019       return false;
14020     }
14021   } else {
14022     UngetToken();
14023     return false;
14024   }
14025 
14026   AppendValue(aPropID, val);
14027   return true;
14028 }
14029 
14030 bool
14031 CSSParserImpl::ParseColumns()
14032 {
14033   // We use a similar "fake value" hack to ParseListStyle, because
14034   // "auto" is acceptable for both column-count and column-width.
14035   // If the fake "auto" value is found, and one of the real values isn't,
14036   // that means the fake auto value is meant for the real value we didn't
14037   // find.
14038   static const nsCSSPropertyID columnIDs[] = {
14039     eCSSPropertyExtra_x_auto_value,
14040     eCSSProperty_column_count,
14041     eCSSProperty_column_width
14042   };
14043   const int32_t numProps = MOZ_ARRAY_LENGTH(columnIDs);
14044 
14045   nsCSSValue values[numProps];
14046   int32_t found = ParseChoice(values, columnIDs, numProps);
14047   if (found < 1) {
14048     return false;
14049   }
14050   if ((found & (1|2|4)) == (1|2|4) &&
14051       values[0].GetUnit() ==  eCSSUnit_Auto) {
14052     // We filled all 3 values, which is invalid
14053     return false;
14054   }
14055 
14056   if ((found & 2) == 0) {
14057     // Provide auto column-count
14058     values[1].SetAutoValue();
14059   }
14060   if ((found & 4) == 0) {
14061     // Provide auto column-width
14062     values[2].SetAutoValue();
14063   }
14064 
14065   // Start at index 1 to skip the fake auto value.
14066   for (int32_t index = 1; index < numProps; index++) {
14067     AppendValue(columnIDs[index], values[index]);
14068   }
14069   return true;
14070 }
14071 
14072 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
14073                          VARIANT_KEYWORD)
14074 bool
14075 CSSParserImpl::ParseContent()
14076 {
14077   // We need to divide the 'content' keywords into two classes for
14078   // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable.
14079   static const KTableEntry kContentListKWs[] = {
14080     { eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE },
14081     { eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE },
14082     { eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE },
14083     { eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE },
14084     { eCSSKeyword_UNKNOWN, -1 }
14085   };
14086 
14087   static const KTableEntry kContentSolitaryKWs[] = {
14088     { eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT },
14089     { eCSSKeyword_UNKNOWN, -1 }
14090   };
14091 
14092   // Verify that these two lists add up to the size of
14093   // nsCSSProps::kContentKTable.
14094   MOZ_ASSERT(nsCSSProps::kContentKTable[
14095                ArrayLength(kContentListKWs) +
14096                ArrayLength(kContentSolitaryKWs) - 2].mKeyword ==
14097                  eCSSKeyword_UNKNOWN &&
14098              nsCSSProps::kContentKTable[
14099                ArrayLength(kContentListKWs) +
14100                ArrayLength(kContentSolitaryKWs) - 2].mValue == -1,
14101              "content keyword tables out of sync");
14102 
14103   nsCSSValue value;
14104   // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must
14105   // be alone
14106   if (!ParseSingleTokenVariant(value, VARIANT_HMK | VARIANT_NONE,
14107                                kContentSolitaryKWs)) {
14108     nsCSSValueList* cur = value.SetListValue();
14109     for (;;) {
14110       if (ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs) !=
14111           CSSParseResult::Ok) {
14112         return false;
14113       }
14114       if (CheckEndProperty()) {
14115         break;
14116       }
14117       cur->mNext = new nsCSSValueList;
14118       cur = cur->mNext;
14119     }
14120   }
14121   AppendValue(eCSSProperty_content, value);
14122   return true;
14123 }
14124 
14125 bool
14126 CSSParserImpl::ParseCounterData(nsCSSPropertyID aPropID)
14127 {
14128   static const nsCSSKeyword kCounterDataKTable[] = {
14129     eCSSKeyword_none,
14130     eCSSKeyword_UNKNOWN
14131   };
14132   nsCSSValue value;
14133   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
14134                                nullptr)) {
14135     if (!GetToken(true)) {
14136       return false;
14137     }
14138     if (mToken.mType != eCSSToken_Ident) {
14139       UngetToken();
14140       return false;
14141     }
14142 
14143     nsCSSValuePairList *cur = value.SetPairListValue();
14144     for (;;) {
14145       if (!ParseCustomIdent(cur->mXValue, mToken.mIdent, kCounterDataKTable)) {
14146         return false;
14147       }
14148       if (!GetToken(true)) {
14149         break;
14150       }
14151       if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
14152         cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
14153       } else {
14154         UngetToken();
14155       }
14156       if (!GetToken(true)) {
14157         break;
14158       }
14159       if (mToken.mType != eCSSToken_Ident) {
14160         UngetToken();
14161         break;
14162       }
14163       cur->mNext = new nsCSSValuePairList;
14164       cur = cur->mNext;
14165     }
14166   }
14167   AppendValue(aPropID, value);
14168   return true;
14169 }
14170 
14171 bool
14172 CSSParserImpl::ParseCursor()
14173 {
14174   nsCSSValue value;
14175   // 'inherit', 'initial' and 'unset' must be alone
14176   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
14177     nsCSSValueList* cur = value.SetListValue();
14178     for (;;) {
14179       if (!ParseSingleTokenVariant(cur->mValue, VARIANT_UK,
14180                                    nsCSSProps::kCursorKTable)) {
14181         return false;
14182       }
14183       if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last
14184         break;
14185       }
14186 
14187       // We have a URL, so make a value array with three values.
14188       RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
14189       val->Item(0) = cur->mValue;
14190 
14191       // Parse optional x and y position of cursor hotspot (css3-ui).
14192       if (ParseSingleTokenVariant(val->Item(1), VARIANT_NUMBER, nullptr)) {
14193         // If we have one number, we must have two.
14194         if (!ParseSingleTokenVariant(val->Item(2), VARIANT_NUMBER, nullptr)) {
14195           return false;
14196         }
14197       }
14198       cur->mValue.SetArrayValue(val, eCSSUnit_Array);
14199 
14200       if (!ExpectSymbol(',', true)) { // url must not be last
14201         return false;
14202       }
14203       cur->mNext = new nsCSSValueList;
14204       cur = cur->mNext;
14205     }
14206   }
14207   AppendValue(eCSSProperty_cursor, value);
14208   return true;
14209 }
14210 
14211 
14212 bool
14213 CSSParserImpl::ParseFont()
14214 {
14215   nsCSSValue  family;
14216   if (ParseSingleTokenVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
14217     if (eCSSUnit_Inherit == family.GetUnit() ||
14218         eCSSUnit_Initial == family.GetUnit() ||
14219         eCSSUnit_Unset == family.GetUnit()) {
14220       AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
14221       AppendValue(eCSSProperty_font_family, family);
14222       AppendValue(eCSSProperty_font_style, family);
14223       AppendValue(eCSSProperty_font_weight, family);
14224       AppendValue(eCSSProperty_font_size, family);
14225       AppendValue(eCSSProperty_line_height, family);
14226       AppendValue(eCSSProperty_font_stretch, family);
14227       AppendValue(eCSSProperty_font_size_adjust, family);
14228       AppendValue(eCSSProperty_font_feature_settings, family);
14229       AppendValue(eCSSProperty_font_language_override, family);
14230       AppendValue(eCSSProperty_font_kerning, family);
14231       AppendValue(eCSSProperty_font_synthesis, family);
14232       AppendValue(eCSSProperty_font_variant_alternates, family);
14233       AppendValue(eCSSProperty_font_variant_caps, family);
14234       AppendValue(eCSSProperty_font_variant_east_asian, family);
14235       AppendValue(eCSSProperty_font_variant_ligatures, family);
14236       AppendValue(eCSSProperty_font_variant_numeric, family);
14237       AppendValue(eCSSProperty_font_variant_position, family);
14238     }
14239     else {
14240       AppendValue(eCSSProperty__x_system_font, family);
14241       nsCSSValue systemFont(eCSSUnit_System_Font);
14242       AppendValue(eCSSProperty_font_family, systemFont);
14243       AppendValue(eCSSProperty_font_style, systemFont);
14244       AppendValue(eCSSProperty_font_weight, systemFont);
14245       AppendValue(eCSSProperty_font_size, systemFont);
14246       AppendValue(eCSSProperty_line_height, systemFont);
14247       AppendValue(eCSSProperty_font_stretch, systemFont);
14248       AppendValue(eCSSProperty_font_size_adjust, systemFont);
14249       AppendValue(eCSSProperty_font_feature_settings, systemFont);
14250       AppendValue(eCSSProperty_font_language_override, systemFont);
14251       AppendValue(eCSSProperty_font_kerning, systemFont);
14252       AppendValue(eCSSProperty_font_synthesis, systemFont);
14253       AppendValue(eCSSProperty_font_variant_alternates, systemFont);
14254       AppendValue(eCSSProperty_font_variant_caps, systemFont);
14255       AppendValue(eCSSProperty_font_variant_east_asian, systemFont);
14256       AppendValue(eCSSProperty_font_variant_ligatures, systemFont);
14257       AppendValue(eCSSProperty_font_variant_numeric, systemFont);
14258       AppendValue(eCSSProperty_font_variant_position, systemFont);
14259     }
14260     return true;
14261   }
14262 
14263   // Get optional font-style, font-variant, font-weight, font-stretch
14264   // (in any order)
14265 
14266   // Indexes into fontIDs[] and values[] arrays.
14267   const int kFontStyleIndex = 0;
14268   const int kFontVariantIndex = 1;
14269   const int kFontWeightIndex = 2;
14270   const int kFontStretchIndex = 3;
14271 
14272   // The order of the initializers here must match the order of the indexes
14273   // defined above!
14274   static const nsCSSPropertyID fontIDs[] = {
14275     eCSSProperty_font_style,
14276     eCSSProperty_font_variant_caps,
14277     eCSSProperty_font_weight,
14278     eCSSProperty_font_stretch
14279   };
14280 
14281   const int32_t numProps = MOZ_ARRAY_LENGTH(fontIDs);
14282   nsCSSValue  values[numProps];
14283   int32_t found = ParseChoice(values, fontIDs, numProps);
14284   if (found < 0 ||
14285       eCSSUnit_Inherit == values[kFontStyleIndex].GetUnit() ||
14286       eCSSUnit_Initial == values[kFontStyleIndex].GetUnit() ||
14287       eCSSUnit_Unset == values[kFontStyleIndex].GetUnit()) { // illegal data
14288     return false;
14289   }
14290   if ((found & (1 << kFontStyleIndex)) == 0) {
14291     // Provide default font-style
14292     values[kFontStyleIndex].SetIntValue(NS_FONT_STYLE_NORMAL,
14293                                         eCSSUnit_Enumerated);
14294   }
14295   if ((found & (1 << kFontVariantIndex)) == 0) {
14296     // Provide default font-variant
14297     values[kFontVariantIndex].SetNormalValue();
14298   } else {
14299     if (values[kFontVariantIndex].GetUnit() == eCSSUnit_Enumerated &&
14300         values[kFontVariantIndex].GetIntValue() !=
14301         NS_FONT_VARIANT_CAPS_SMALLCAPS) {
14302       // only normal or small-caps is allowed in font shorthand
14303       // this also assumes other values for font-variant-caps never overlap
14304       // possible values for style or weight
14305       return false;
14306     }
14307   }
14308   if ((found & (1 << kFontWeightIndex)) == 0) {
14309     // Provide default font-weight
14310     values[kFontWeightIndex].SetIntValue(NS_FONT_WEIGHT_NORMAL,
14311                                          eCSSUnit_Enumerated);
14312   }
14313   if ((found & (1 << kFontStretchIndex)) == 0) {
14314     // Provide default font-stretch
14315     values[kFontStretchIndex].SetIntValue(NS_FONT_STRETCH_NORMAL,
14316                                           eCSSUnit_Enumerated);
14317   }
14318 
14319   // Get mandatory font-size
14320   nsCSSValue  size;
14321   if (!ParseSingleTokenNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP,
14322                                           nsCSSProps::kFontSizeKTable)) {
14323     return false;
14324   }
14325 
14326   // Get optional "/" line-height
14327   nsCSSValue  lineHeight;
14328   if (ExpectSymbol('/', true)) {
14329     if (ParseNonNegativeVariant(lineHeight,
14330                                 VARIANT_NUMBER | VARIANT_LP |
14331                                   VARIANT_NORMAL | VARIANT_CALC,
14332                                 nullptr) != CSSParseResult::Ok) {
14333       return false;
14334     }
14335   }
14336   else {
14337     lineHeight.SetNormalValue();
14338   }
14339 
14340   // Get final mandatory font-family
14341   nsAutoParseCompoundProperty compound(this);
14342   if (ParseFamily(family)) {
14343     if (eCSSUnit_Inherit != family.GetUnit() &&
14344         eCSSUnit_Initial != family.GetUnit() &&
14345         eCSSUnit_Unset != family.GetUnit()) {
14346       AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
14347       AppendValue(eCSSProperty_font_family, family);
14348       AppendValue(eCSSProperty_font_style, values[kFontStyleIndex]);
14349       AppendValue(eCSSProperty_font_variant_caps, values[kFontVariantIndex]);
14350       AppendValue(eCSSProperty_font_weight, values[kFontWeightIndex]);
14351       AppendValue(eCSSProperty_font_size, size);
14352       AppendValue(eCSSProperty_line_height, lineHeight);
14353       AppendValue(eCSSProperty_font_stretch, values[kFontStretchIndex]);
14354       AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
14355       AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal));
14356       AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal));
14357       AppendValue(eCSSProperty_font_kerning,
14358                   nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated));
14359       AppendValue(eCSSProperty_font_synthesis,
14360                   nsCSSValue(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE,
14361                              eCSSUnit_Enumerated));
14362       AppendValue(eCSSProperty_font_variant_alternates,
14363                   nsCSSValue(eCSSUnit_Normal));
14364       AppendValue(eCSSProperty_font_variant_east_asian,
14365                   nsCSSValue(eCSSUnit_Normal));
14366       AppendValue(eCSSProperty_font_variant_ligatures,
14367                   nsCSSValue(eCSSUnit_Normal));
14368       AppendValue(eCSSProperty_font_variant_numeric,
14369                   nsCSSValue(eCSSUnit_Normal));
14370       AppendValue(eCSSProperty_font_variant_position,
14371                   nsCSSValue(eCSSUnit_Normal));
14372       return true;
14373     }
14374   }
14375   return false;
14376 }
14377 
14378 bool
14379 CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue)
14380 {
14381   if (!ParseSingleTokenVariant(aValue, VARIANT_HK | VARIANT_NONE,
14382                                nsCSSProps::kFontSynthesisKTable)) {
14383     return false;
14384   }
14385 
14386   // first value 'none' ==> done
14387   if (eCSSUnit_None == aValue.GetUnit() ||
14388       eCSSUnit_Initial == aValue.GetUnit() ||
14389       eCSSUnit_Inherit == aValue.GetUnit() ||
14390       eCSSUnit_Unset == aValue.GetUnit())
14391   {
14392     return true;
14393   }
14394 
14395   // look for a second value
14396   int32_t intValue = aValue.GetIntValue();
14397   nsCSSValue nextValue;
14398 
14399   if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) {
14400     int32_t nextIntValue = nextValue.GetIntValue();
14401     if (nextIntValue & intValue) {
14402       return false;
14403     }
14404     aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
14405   }
14406 
14407   return true;
14408 }
14409 
14410 // font-variant-alternates allows for a combination of multiple
14411 // simple enumerated values and functional values.  Functional values have
14412 // parameter lists with one or more idents which are later resolved
14413 // based on values defined in @font-feature-value rules.
14414 //
14415 // font-variant-alternates: swash(flowing) historical-forms styleset(alt-g, alt-m);
14416 //
14417 // So for this the nsCSSValue is set to a pair value, with one
14418 // value for a bitmask of both simple and functional property values
14419 // and another value containing a ValuePairList with lists of idents
14420 // for each functional property value.
14421 //
14422 // pairValue
14423 //   o intValue
14424 //       NS_FONT_VARIANT_ALTERNATES_SWASH |
14425 //       NS_FONT_VARIANT_ALTERNATES_STYLESET
14426 //   o valuePairList, each element with
14427 //     - intValue - indicates which alternate
14428 //     - string or valueList of strings
14429 //
14430 // Note: when only 'historical-forms' is specified, there are no
14431 // functional values to store, in which case the valuePairList is a
14432 // single element dummy list.  In all other cases, the length of the
14433 // list will match the number of functional values.
14434 
14435 #define MAX_ALLOWED_FEATURES 512
14436 
14437 static uint16_t
14438 MaxElementsForAlternateType(nsCSSKeyword keyword)
14439 {
14440   uint16_t maxElems = 1;
14441   if (keyword == eCSSKeyword_styleset ||
14442       keyword == eCSSKeyword_character_variant) {
14443     maxElems = MAX_ALLOWED_FEATURES;
14444   }
14445   return maxElems;
14446 }
14447 
14448 bool
14449 CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature,
14450                                     nsCSSValue& aValue)
14451 {
14452   if (!GetToken(true)) {
14453     return false;
14454   }
14455 
14456   bool isIdent = (mToken.mType == eCSSToken_Ident);
14457   if (mToken.mType != eCSSToken_Function && !isIdent) {
14458     UngetToken();
14459     return false;
14460   }
14461 
14462   // ident ==> simple enumerated prop val (e.g. historical-forms)
14463   // function ==> e.g. swash(flowing) styleset(alt-g, alt-m)
14464 
14465   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
14466   if (!(eCSSKeyword_UNKNOWN < keyword &&
14467         nsCSSProps::FindKeyword(keyword,
14468                                 (isIdent ?
14469                                  nsCSSProps::kFontVariantAlternatesKTable :
14470                                  nsCSSProps::kFontVariantAlternatesFuncsKTable),
14471                                 aWhichFeature)))
14472   {
14473     // failed, pop token
14474     UngetToken();
14475     return false;
14476   }
14477 
14478   if (isIdent) {
14479     aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated);
14480     return true;
14481   }
14482 
14483   return ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER,
14484                        1, MaxElementsForAlternateType(keyword), aValue);
14485 }
14486 
14487 bool
14488 CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue)
14489 {
14490   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
14491                               nullptr)) {
14492     return true;
14493   }
14494 
14495   // iterate through parameters
14496   nsCSSValue listValue;
14497   int32_t feature, featureFlags = 0;
14498 
14499   // if no functional values, this may be a list with a single, unused element
14500   listValue.SetListValue();
14501 
14502   nsCSSValueList* list = nullptr;
14503   nsCSSValue value;
14504   while (ParseSingleAlternate(feature, value)) {
14505 
14506     // check to make sure value not already set
14507     if (feature == 0 ||
14508         feature & featureFlags) {
14509       return false;
14510     }
14511 
14512     featureFlags |= feature;
14513 
14514     // if function, need to add to the list of functions
14515     if (value.GetUnit() == eCSSUnit_Function) {
14516       if (!list) {
14517         list = listValue.GetListValue();
14518       } else {
14519         list->mNext = new nsCSSValueList;
14520         list = list->mNext;
14521       }
14522       list->mValue = value;
14523     }
14524   }
14525 
14526   if (featureFlags == 0) {
14527     // ParseSingleAlternate failed the first time through the loop.
14528     return false;
14529   }
14530 
14531   nsCSSValue featureValue;
14532   featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated);
14533   aValue.SetPairValue(featureValue, listValue);
14534 
14535   return true;
14536 }
14537 
14538 bool
14539 CSSParserImpl::MergeBitmaskValue(int32_t aNewValue,
14540                                  const int32_t aMasks[],
14541                                  int32_t& aMergedValue)
14542 {
14543   // check to make sure value not already set
14544   if (aNewValue & aMergedValue) {
14545     return false;
14546   }
14547 
14548   const int32_t *m = aMasks;
14549   int32_t c = 0;
14550 
14551   while (*m != MASK_END_VALUE) {
14552     if (*m & aNewValue) {
14553       c = aMergedValue & *m;
14554       break;
14555     }
14556     m++;
14557   }
14558 
14559   if (c) {
14560     return false;
14561   }
14562 
14563   aMergedValue |= aNewValue;
14564   return true;
14565 }
14566 
14567 // aMasks - array of masks for mutually-exclusive property values,
14568 //          e.g. proportial-nums, tabular-nums
14569 
14570 bool
14571 CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue,
14572                                   const KTableEntry aKeywordTable[],
14573                                   const int32_t aMasks[])
14574 {
14575   // Parse at least one keyword
14576   if (!ParseEnum(aValue, aKeywordTable)) {
14577     return false;
14578   }
14579 
14580   // look for more values
14581   nsCSSValue nextValue;
14582   int32_t mergedValue = aValue.GetIntValue();
14583 
14584   while (ParseEnum(nextValue, aKeywordTable))
14585   {
14586     if (!MergeBitmaskValue(nextValue.GetIntValue(), aMasks, mergedValue)) {
14587       return false;
14588     }
14589   }
14590 
14591   aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated);
14592 
14593   return true;
14594 }
14595 
14596 static const int32_t maskEastAsian[] = {
14597   NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK,
14598   NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK,
14599   MASK_END_VALUE
14600 };
14601 
14602 bool
14603 CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue)
14604 {
14605   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
14606                               nullptr)) {
14607     return true;
14608   }
14609 
14610   NS_ASSERTION(maskEastAsian[ArrayLength(maskEastAsian) - 1] ==
14611                  MASK_END_VALUE,
14612                "incorrectly terminated array");
14613 
14614   return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable,
14615                             maskEastAsian);
14616 }
14617 
14618 bool
14619 CSSParserImpl::ParseContain(nsCSSValue& aValue)
14620 {
14621   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE,
14622                               nullptr)) {
14623     return true;
14624   }
14625   static const int32_t maskContain[] = { MASK_END_VALUE };
14626   if (!ParseBitmaskValues(aValue, nsCSSProps::kContainKTable, maskContain)) {
14627     return false;
14628   }
14629   if (aValue.GetIntValue() & NS_STYLE_CONTAIN_STRICT) {
14630     if (aValue.GetIntValue() != NS_STYLE_CONTAIN_STRICT) {
14631       // Disallow any other keywords in combination with 'strict'.
14632       return false;
14633     }
14634     // Strict implies layout, style, and paint.
14635     // However, for serialization purposes, we keep the strict bit around.
14636     aValue.SetIntValue(NS_STYLE_CONTAIN_STRICT |
14637         NS_STYLE_CONTAIN_ALL_BITS, eCSSUnit_Enumerated);
14638   }
14639   return true;
14640 }
14641 
14642 static const int32_t maskLigatures[] = {
14643   NS_FONT_VARIANT_LIGATURES_COMMON_MASK,
14644   NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK,
14645   NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK,
14646   NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK,
14647   MASK_END_VALUE
14648 };
14649 
14650 bool
14651 CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue)
14652 {
14653   if (ParseSingleTokenVariant(aValue,
14654                               VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE,
14655                               nullptr)) {
14656     return true;
14657   }
14658 
14659   NS_ASSERTION(maskLigatures[ArrayLength(maskLigatures) - 1] ==
14660                  MASK_END_VALUE,
14661                "incorrectly terminated array");
14662 
14663   return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable,
14664                             maskLigatures);
14665 }
14666 
14667 static const int32_t maskNumeric[] = {
14668   NS_FONT_VARIANT_NUMERIC_FIGURE_MASK,
14669   NS_FONT_VARIANT_NUMERIC_SPACING_MASK,
14670   NS_FONT_VARIANT_NUMERIC_FRACTION_MASK,
14671   MASK_END_VALUE
14672 };
14673 
14674 bool
14675 CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue)
14676 {
14677   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
14678                               nullptr)) {
14679     return true;
14680   }
14681 
14682   NS_ASSERTION(maskNumeric[ArrayLength(maskNumeric) - 1] ==
14683                  MASK_END_VALUE,
14684                "incorrectly terminated array");
14685 
14686   return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable,
14687                             maskNumeric);
14688 }
14689 
14690 bool
14691 CSSParserImpl::ParseFontVariant()
14692 {
14693   // parse single values - normal/inherit/none
14694   nsCSSValue value;
14695   nsCSSValue normal(eCSSUnit_Normal);
14696 
14697   if (ParseSingleTokenVariant(value,
14698                               VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE,
14699                               nullptr)) {
14700     AppendValue(eCSSProperty_font_variant_ligatures, value);
14701     if (eCSSUnit_None == value.GetUnit()) {
14702       // 'none' applies the value 'normal' to all properties other
14703       // than 'font-variant-ligatures'
14704       value.SetNormalValue();
14705     }
14706     AppendValue(eCSSProperty_font_variant_alternates, value);
14707     AppendValue(eCSSProperty_font_variant_caps, value);
14708     AppendValue(eCSSProperty_font_variant_east_asian, value);
14709     AppendValue(eCSSProperty_font_variant_numeric, value);
14710     AppendValue(eCSSProperty_font_variant_position, value);
14711     return true;
14712   }
14713 
14714   // set each of the individual subproperties
14715   int32_t altFeatures = 0, capsFeatures = 0, eastAsianFeatures = 0,
14716           ligFeatures = 0, numericFeatures = 0, posFeatures = 0;
14717   nsCSSValue altListValue;
14718   nsCSSValueList* altList = nullptr;
14719 
14720   // if no functional values, this may be a list with a single, unused element
14721   altListValue.SetListValue();
14722 
14723   bool foundValid = false; // found at least one proper value
14724   while (GetToken(true)) {
14725     // only an ident or a function at this point
14726     bool isFunction = (mToken.mType == eCSSToken_Function);
14727     if (mToken.mType != eCSSToken_Ident && !isFunction) {
14728       UngetToken();
14729       break;
14730     }
14731 
14732     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
14733     if (keyword == eCSSKeyword_UNKNOWN) {
14734       UngetToken();
14735       return false;
14736     }
14737 
14738     int32_t feature;
14739 
14740     // function? ==> font-variant-alternates
14741     if (isFunction) {
14742       if (!nsCSSProps::FindKeyword(keyword,
14743                                    nsCSSProps::kFontVariantAlternatesFuncsKTable,
14744                                    feature) ||
14745           (feature & altFeatures)) {
14746         UngetToken();
14747         return false;
14748       }
14749 
14750       altFeatures |= feature;
14751       nsCSSValue funcValue;
14752       if (!ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER, 1,
14753                          MaxElementsForAlternateType(keyword), funcValue) ||
14754           funcValue.GetUnit() != eCSSUnit_Function) {
14755         UngetToken();
14756         return false;
14757       }
14758 
14759       if (!altList) {
14760         altList = altListValue.GetListValue();
14761       } else {
14762         altList->mNext = new nsCSSValueList;
14763         altList = altList->mNext;
14764       }
14765       altList->mValue = funcValue;
14766     } else if (nsCSSProps::FindKeyword(keyword,
14767                                        nsCSSProps::kFontVariantCapsKTable,
14768                                        feature)) {
14769       if (capsFeatures != 0) {
14770         // multiple values for font-variant-caps
14771         UngetToken();
14772         return false;
14773       }
14774       capsFeatures = feature;
14775     } else if (nsCSSProps::FindKeyword(keyword,
14776                                        nsCSSProps::kFontVariantAlternatesKTable,
14777                                        feature)) {
14778       if (feature & altFeatures) {
14779         // same value repeated
14780         UngetToken();
14781         return false;
14782       }
14783       altFeatures |= feature;
14784     } else if (nsCSSProps::FindKeyword(keyword,
14785                                        nsCSSProps::kFontVariantEastAsianKTable,
14786                                        feature)) {
14787       if (!MergeBitmaskValue(feature, maskEastAsian, eastAsianFeatures)) {
14788         // multiple mutually exclusive values
14789         UngetToken();
14790         return false;
14791       }
14792     } else if (nsCSSProps::FindKeyword(keyword,
14793                                        nsCSSProps::kFontVariantLigaturesKTable,
14794                                        feature)) {
14795       if (keyword == eCSSKeyword_none ||
14796           !MergeBitmaskValue(feature, maskLigatures, ligFeatures)) {
14797         // none or multiple mutually exclusive values
14798         UngetToken();
14799         return false;
14800       }
14801     } else if (nsCSSProps::FindKeyword(keyword,
14802                                        nsCSSProps::kFontVariantNumericKTable,
14803                                        feature)) {
14804       if (!MergeBitmaskValue(feature, maskNumeric, numericFeatures)) {
14805         // multiple mutually exclusive values
14806         UngetToken();
14807         return false;
14808       }
14809     } else if (nsCSSProps::FindKeyword(keyword,
14810                                        nsCSSProps::kFontVariantPositionKTable,
14811                                        feature)) {
14812       if (posFeatures != 0) {
14813         // multiple values for font-variant-caps
14814         UngetToken();
14815         return false;
14816       }
14817       posFeatures = feature;
14818     } else {
14819       // bogus keyword, bail...
14820       UngetToken();
14821       return false;
14822     }
14823 
14824     foundValid = true;
14825   }
14826 
14827   if (!foundValid) {
14828     return false;
14829   }
14830 
14831   if (altFeatures) {
14832     nsCSSValue featureValue;
14833     featureValue.SetIntValue(altFeatures, eCSSUnit_Enumerated);
14834     value.SetPairValue(featureValue, altListValue);
14835     AppendValue(eCSSProperty_font_variant_alternates, value);
14836   } else {
14837     AppendValue(eCSSProperty_font_variant_alternates, normal);
14838   }
14839 
14840   if (capsFeatures) {
14841     value.SetIntValue(capsFeatures, eCSSUnit_Enumerated);
14842     AppendValue(eCSSProperty_font_variant_caps, value);
14843   } else {
14844     AppendValue(eCSSProperty_font_variant_caps, normal);
14845   }
14846 
14847   if (eastAsianFeatures) {
14848     value.SetIntValue(eastAsianFeatures, eCSSUnit_Enumerated);
14849     AppendValue(eCSSProperty_font_variant_east_asian, value);
14850   } else {
14851     AppendValue(eCSSProperty_font_variant_east_asian, normal);
14852   }
14853 
14854   if (ligFeatures) {
14855     value.SetIntValue(ligFeatures, eCSSUnit_Enumerated);
14856     AppendValue(eCSSProperty_font_variant_ligatures, value);
14857   } else {
14858     AppendValue(eCSSProperty_font_variant_ligatures, normal);
14859   }
14860 
14861   if (numericFeatures) {
14862     value.SetIntValue(numericFeatures, eCSSUnit_Enumerated);
14863     AppendValue(eCSSProperty_font_variant_numeric, value);
14864   } else {
14865     AppendValue(eCSSProperty_font_variant_numeric, normal);
14866   }
14867 
14868   if (posFeatures) {
14869     value.SetIntValue(posFeatures, eCSSUnit_Enumerated);
14870     AppendValue(eCSSProperty_font_variant_position, value);
14871   } else {
14872     AppendValue(eCSSProperty_font_variant_position, normal);
14873   }
14874 
14875   return true;
14876 }
14877 
14878 bool
14879 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
14880 {
14881   if (ParseSingleTokenVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT,
14882                               nsCSSProps::kFontWeightKTable)) {
14883     if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
14884       int32_t intValue = aValue.GetIntValue();
14885       if ((100 <= intValue) &&
14886           (intValue <= 900) &&
14887           (0 == (intValue % 100))) {
14888         return true;
14889       } else {
14890         UngetToken();
14891         return false;
14892       }
14893     }
14894     return true;
14895   }
14896   return false;
14897 }
14898 
14899 bool
14900 CSSParserImpl::ParseOneFamily(nsAString& aFamily,
14901                               bool& aOneKeyword,
14902                               bool& aQuoted)
14903 {
14904   if (!GetToken(true))
14905     return false;
14906 
14907   nsCSSToken* tk = &mToken;
14908 
14909   aOneKeyword = false;
14910   aQuoted = false;
14911   if (eCSSToken_Ident == tk->mType) {
14912     aOneKeyword = true;
14913     aFamily.Append(tk->mIdent);
14914     for (;;) {
14915       if (!GetToken(false))
14916         break;
14917 
14918       if (eCSSToken_Ident == tk->mType) {
14919         aOneKeyword = false;
14920         // We had at least another keyword before.
14921         // "If a sequence of identifiers is given as a font family name,
14922         //  the computed value is the name converted to a string by joining
14923         //  all the identifiers in the sequence by single spaces."
14924         // -- CSS 2.1, section 15.3
14925         // Whitespace tokens do not actually matter,
14926         // identifier tokens can be separated by comments.
14927         aFamily.Append(char16_t(' '));
14928         aFamily.Append(tk->mIdent);
14929       } else if (eCSSToken_Whitespace != tk->mType) {
14930         UngetToken();
14931         break;
14932       }
14933     }
14934     return true;
14935 
14936   } else if (eCSSToken_String == tk->mType) {
14937     aQuoted = true;
14938     aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
14939     return true;
14940 
14941   } else {
14942     UngetToken();
14943     return false;
14944   }
14945 }
14946 
14947 
14948 static bool
14949 AppendGeneric(nsCSSKeyword aKeyword, FontFamilyList *aFamilyList)
14950 {
14951   switch (aKeyword) {
14952     case eCSSKeyword_serif:
14953       aFamilyList->Append(FontFamilyName(eFamily_serif));
14954       return true;
14955     case eCSSKeyword_sans_serif:
14956       aFamilyList->Append(FontFamilyName(eFamily_sans_serif));
14957       return true;
14958     case eCSSKeyword_monospace:
14959       aFamilyList->Append(FontFamilyName(eFamily_monospace));
14960       return true;
14961     case eCSSKeyword_cursive:
14962       aFamilyList->Append(FontFamilyName(eFamily_cursive));
14963       return true;
14964     case eCSSKeyword_fantasy:
14965       aFamilyList->Append(FontFamilyName(eFamily_fantasy));
14966       return true;
14967     case eCSSKeyword__moz_fixed:
14968       aFamilyList->Append(FontFamilyName(eFamily_moz_fixed));
14969       return true;
14970     default:
14971       break;
14972   }
14973 
14974   return false;
14975 }
14976 
14977 bool
14978 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
14979 {
14980   RefPtr<css::FontFamilyListRefCnt> familyList =
14981     new css::FontFamilyListRefCnt();
14982   nsAutoString family;
14983   bool single, quoted;
14984 
14985   // keywords only have meaning in the first position
14986   if (!ParseOneFamily(family, single, quoted))
14987     return false;
14988 
14989   // check for keywords, but only when keywords appear by themselves
14990   // i.e. not in compounds such as font-family: default blah;
14991   bool foundGeneric = false;
14992   if (single) {
14993     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
14994     switch (keyword) {
14995       case eCSSKeyword_inherit:
14996         aValue.SetInheritValue();
14997         return true;
14998       case eCSSKeyword_default:
14999         // 605231 - don't parse unquoted 'default' reserved keyword
15000         return false;
15001       case eCSSKeyword_initial:
15002         aValue.SetInitialValue();
15003         return true;
15004       case eCSSKeyword_unset:
15005         if (nsLayoutUtils::UnsetValueEnabled()) {
15006           aValue.SetUnsetValue();
15007           return true;
15008         }
15009         break;
15010       case eCSSKeyword__moz_use_system_font:
15011         if (!IsParsingCompoundProperty()) {
15012           aValue.SetSystemFontValue();
15013           return true;
15014         }
15015         break;
15016       default:
15017         foundGeneric = AppendGeneric(keyword, familyList);
15018     }
15019   }
15020 
15021   if (!foundGeneric) {
15022     familyList->Append(
15023       FontFamilyName(family, (quoted ? eQuotedName : eUnquotedName)));
15024   }
15025 
15026   for (;;) {
15027     if (!ExpectSymbol(',', true))
15028       break;
15029 
15030     nsAutoString nextFamily;
15031     if (!ParseOneFamily(nextFamily, single, quoted))
15032       return false;
15033 
15034     // at this point unquoted keywords are not allowed
15035     // as font family names but can appear within names
15036     foundGeneric = false;
15037     if (single) {
15038       nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily);
15039       switch (keyword) {
15040         case eCSSKeyword_inherit:
15041         case eCSSKeyword_initial:
15042         case eCSSKeyword_default:
15043         case eCSSKeyword__moz_use_system_font:
15044           return false;
15045         case eCSSKeyword_unset:
15046           if (nsLayoutUtils::UnsetValueEnabled()) {
15047             return false;
15048           }
15049           break;
15050         default:
15051           foundGeneric = AppendGeneric(keyword, familyList);
15052           break;
15053       }
15054     }
15055 
15056     if (!foundGeneric) {
15057       familyList->Append(
15058         FontFamilyName(nextFamily, (quoted ? eQuotedName : eUnquotedName)));
15059     }
15060   }
15061 
15062   if (familyList->IsEmpty()) {
15063     return false;
15064   }
15065 
15066   aValue.SetFontFamilyListValue(familyList);
15067   return true;
15068 }
15069 
15070 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
15071 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
15072 // local-src: 'local(' ( string | ident ) ')'
15073 
15074 bool
15075 CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
15076 {
15077   // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>?
15078   InfallibleTArray<nsCSSValue> values;
15079   nsCSSValue cur;
15080   for (;;) {
15081     if (!GetToken(true))
15082       break;
15083 
15084     if (mToken.mType == eCSSToken_URL) {
15085       SetValueToURL(cur, mToken.mIdent);
15086       values.AppendElement(cur);
15087       if (!ParseFontSrcFormat(values))
15088         return false;
15089 
15090     } else if (mToken.mType == eCSSToken_Function &&
15091                mToken.mIdent.LowerCaseEqualsLiteral("local")) {
15092       // css3-fonts does not specify a formal grammar for local().
15093       // The text permits both unquoted identifiers and quoted
15094       // strings.  We resolve this ambiguity in the spec by
15095       // assuming that the appropriate production is a single
15096       // <family-name>, possibly surrounded by whitespace.
15097 
15098       nsAutoString family;
15099       bool single, quoted;
15100       if (!ParseOneFamily(family, single, quoted)) {
15101         SkipUntil(')');
15102         return false;
15103       }
15104       if (!ExpectSymbol(')', true)) {
15105         SkipUntil(')');
15106         return false;
15107       }
15108 
15109       // reject generics
15110       if (single) {
15111         nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
15112         switch (keyword) {
15113           case eCSSKeyword_serif:
15114           case eCSSKeyword_sans_serif:
15115           case eCSSKeyword_monospace:
15116           case eCSSKeyword_cursive:
15117           case eCSSKeyword_fantasy:
15118           case eCSSKeyword__moz_fixed:
15119             return false;
15120           default:
15121             break;
15122         }
15123       }
15124 
15125       cur.SetStringValue(family, eCSSUnit_Local_Font);
15126       values.AppendElement(cur);
15127     } else {
15128       // We don't know what to do with this token; unget it and error out
15129       UngetToken();
15130       return false;
15131     }
15132 
15133     if (!ExpectSymbol(',', true))
15134       break;
15135   }
15136 
15137   if (values.Length() == 0)
15138     return false;
15139 
15140   RefPtr<nsCSSValue::Array> srcVals
15141     = nsCSSValue::Array::Create(values.Length());
15142 
15143   uint32_t i;
15144   for (i = 0; i < values.Length(); i++)
15145     srcVals->Item(i) = values[i];
15146   aValue.SetArrayValue(srcVals, eCSSUnit_Array);
15147   return true;
15148 }
15149 
15150 bool
15151 CSSParserImpl::ParseFontSrcFormat(InfallibleTArray<nsCSSValue> & values)
15152 {
15153   if (!GetToken(true))
15154     return true; // EOF harmless here
15155   if (mToken.mType != eCSSToken_Function ||
15156       !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
15157     UngetToken();
15158     return true;
15159   }
15160 
15161   do {
15162     if (!GetToken(true))
15163       return false; // EOF - no need for SkipUntil
15164 
15165     if (mToken.mType != eCSSToken_String) {
15166       UngetToken();
15167       SkipUntil(')');
15168       return false;
15169     }
15170 
15171     nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
15172     values.AppendElement(cur);
15173   } while (ExpectSymbol(',', true));
15174 
15175   if (!ExpectSymbol(')', true)) {
15176     SkipUntil(')');
15177     return false;
15178   }
15179 
15180   return true;
15181 }
15182 
15183 // font-ranges: urange ( ',' urange )*
15184 bool
15185 CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
15186 {
15187   InfallibleTArray<uint32_t> ranges;
15188   for (;;) {
15189     if (!GetToken(true))
15190       break;
15191 
15192     if (mToken.mType != eCSSToken_URange) {
15193       UngetToken();
15194       break;
15195     }
15196 
15197     // An invalid range token is a parsing error, causing the entire
15198     // descriptor to be ignored.
15199     if (!mToken.mIntegerValid)
15200       return false;
15201 
15202     uint32_t low = mToken.mInteger;
15203     uint32_t high = mToken.mInteger2;
15204 
15205     // A range that descends, or a range that is entirely outside the
15206     // current range of Unicode (U+0-10FFFF) is ignored, but does not
15207     // invalidate the descriptor.  A range that straddles the high end
15208     // is clipped.
15209     if (low <= 0x10FFFF && low <= high) {
15210       if (high > 0x10FFFF)
15211         high = 0x10FFFF;
15212 
15213       ranges.AppendElement(low);
15214       ranges.AppendElement(high);
15215     }
15216     if (!ExpectSymbol(',', true))
15217       break;
15218   }
15219 
15220   if (ranges.Length() == 0)
15221     return false;
15222 
15223   RefPtr<nsCSSValue::Array> srcVals
15224     = nsCSSValue::Array::Create(ranges.Length());
15225 
15226   for (uint32_t i = 0; i < ranges.Length(); i++)
15227     srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer);
15228   aValue.SetArrayValue(srcVals, eCSSUnit_Array);
15229   return true;
15230 }
15231 
15232 // font-feature-settings: normal | <feature-tag-value> [, <feature-tag-value>]*
15233 // <feature-tag-value> = <string> [ <integer> | on | off ]?
15234 
15235 // minimum - "tagx", "tagy", "tagz"
15236 // edge error case - "tagx" on 1, "tagx" "tagy", "tagx" -1, "tagx" big
15237 
15238 // pair value is always x = string, y = int
15239 
15240 // font feature tags must be four ASCII characters
15241 #define FEATURE_TAG_LENGTH   4
15242 
15243 static bool
15244 ValidFontFeatureTag(const nsString& aTag)
15245 {
15246   if (aTag.Length() != FEATURE_TAG_LENGTH) {
15247     return false;
15248   }
15249   uint32_t i;
15250   for (i = 0; i < FEATURE_TAG_LENGTH; i++) {
15251     uint32_t ch = aTag[i];
15252     if (ch < 0x20 || ch > 0x7e) {
15253       return false;
15254     }
15255   }
15256   return true;
15257 }
15258 
15259 bool
15260 CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue)
15261 {
15262   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
15263                               nullptr)) {
15264     return true;
15265   }
15266 
15267   nsCSSValuePairList *cur = aValue.SetPairListValue();
15268   for (;;) {
15269     // feature tag
15270     if (!GetToken(true)) {
15271       return false;
15272     }
15273 
15274     if (mToken.mType != eCSSToken_String ||
15275         !ValidFontFeatureTag(mToken.mIdent)) {
15276       UngetToken();
15277       return false;
15278     }
15279     cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
15280 
15281     if (!GetToken(true)) {
15282       cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
15283       break;
15284     }
15285 
15286     // optional value or on/off keyword
15287     if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid &&
15288         mToken.mInteger >= 0) {
15289       cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
15290     } else if (mToken.mType == eCSSToken_Ident &&
15291                mToken.mIdent.LowerCaseEqualsLiteral("on")) {
15292       cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
15293     } else if (mToken.mType == eCSSToken_Ident &&
15294                mToken.mIdent.LowerCaseEqualsLiteral("off")) {
15295       cur->mYValue.SetIntValue(0, eCSSUnit_Integer);
15296     } else {
15297       // something other than value/on/off, set default value
15298       cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
15299       UngetToken();
15300     }
15301 
15302     if (!ExpectSymbol(',', true)) {
15303       break;
15304     }
15305 
15306     cur->mNext = new nsCSSValuePairList;
15307     cur = cur->mNext;
15308   }
15309 
15310   return true;
15311 }
15312 
15313 bool
15314 CSSParserImpl::ParseListStyle()
15315 {
15316   // 'list-style' can accept 'none' for two different subproperties,
15317   // 'list-style-type' and 'list-style-image'.  In order to accept
15318   // 'none' as the value of either but still allow another value for
15319   // either, we need to ensure that the first 'none' we find gets
15320   // allocated to a dummy property instead. Since parse function for
15321   // 'list-style-type' could accept values for 'list-style-position',
15322   // we put position in front of type.
15323   static const nsCSSPropertyID listStyleIDs[] = {
15324     eCSSPropertyExtra_x_none_value,
15325     eCSSProperty_list_style_position,
15326     eCSSProperty_list_style_type,
15327     eCSSProperty_list_style_image
15328   };
15329 
15330   nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)];
15331   int32_t found =
15332     ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs));
15333   if (found < 1) {
15334     return false;
15335   }
15336 
15337   if ((found & (1|4|8)) == (1|4|8)) {
15338     if (values[0].GetUnit() == eCSSUnit_None) {
15339       // We found a 'none' plus another value for both of
15340       // 'list-style-type' and 'list-style-image'.  This is a parse
15341       // error, since the 'none' has to count for at least one of them.
15342       return false;
15343     } else {
15344       NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] &&
15345                    values[0] == values[2] && values[0] == values[3],
15346                    "should be a special value");
15347     }
15348   }
15349 
15350   if ((found & 2) == 0) {
15351     values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
15352                           eCSSUnit_Enumerated);
15353   }
15354   if ((found & 4) == 0) {
15355     // Provide default values
15356     nsString type = (found & 1) ?
15357       NS_LITERAL_STRING("none") : NS_LITERAL_STRING("disc");
15358     values[2].SetStringValue(type, eCSSUnit_Ident);
15359   }
15360   if ((found & 8) == 0) {
15361     values[3].SetNoneValue();
15362   }
15363 
15364   // Start at 1 to avoid appending fake value.
15365   for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) {
15366     AppendValue(listStyleIDs[index], values[index]);
15367   }
15368   return true;
15369 }
15370 
15371 bool
15372 CSSParserImpl::ParseListStyleType(nsCSSValue& aValue)
15373 {
15374   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_STRING,
15375                               nullptr)) {
15376     return true;
15377   }
15378 
15379   if (ParseCounterStyleNameValue(aValue) || ParseSymbols(aValue)) {
15380     return true;
15381   }
15382 
15383   return false;
15384 }
15385 
15386 bool
15387 CSSParserImpl::ParseMargin()
15388 {
15389   static const nsCSSPropertyID kMarginSideIDs[] = {
15390     eCSSProperty_margin_top,
15391     eCSSProperty_margin_right,
15392     eCSSProperty_margin_bottom,
15393     eCSSProperty_margin_left
15394   };
15395 
15396   return ParseBoxProperties(kMarginSideIDs);
15397 }
15398 
15399 bool
15400 CSSParserImpl::ParseObjectPosition()
15401 {
15402   nsCSSValue value;
15403   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) &&
15404       !ParsePositionValue(value)) {
15405     return false;
15406   }
15407   AppendValue(eCSSProperty_object_position, value);
15408   return true;
15409 }
15410 
15411 bool
15412 CSSParserImpl::ParseOutline()
15413 {
15414   const int32_t numProps = 3;
15415   static const nsCSSPropertyID kOutlineIDs[] = {
15416     eCSSProperty_outline_color,
15417     eCSSProperty_outline_style,
15418     eCSSProperty_outline_width
15419   };
15420 
15421   nsCSSValue  values[numProps];
15422   int32_t found = ParseChoice(values, kOutlineIDs, numProps);
15423   if (found < 1) {
15424     return false;
15425   }
15426 
15427   // Provide default values
15428   if ((found & 1) == 0) { // Provide default outline-color
15429     values[0].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
15430   }
15431   if ((found & 2) == 0) { // Provide default outline-style
15432     values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
15433   }
15434   if ((found & 4) == 0) { // Provide default outline-width
15435     values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
15436   }
15437 
15438   int32_t index;
15439   for (index = 0; index < numProps; index++) {
15440     AppendValue(kOutlineIDs[index], values[index]);
15441   }
15442   return true;
15443 }
15444 
15445 bool
15446 CSSParserImpl::ParseOverflow()
15447 {
15448   nsCSSValue overflow;
15449   if (!ParseSingleTokenVariant(overflow, VARIANT_HK,
15450                                nsCSSProps::kOverflowKTable)) {
15451     return false;
15452   }
15453 
15454   nsCSSValue overflowX(overflow);
15455   nsCSSValue overflowY(overflow);
15456   if (eCSSUnit_Enumerated == overflow.GetUnit())
15457     switch(overflow.GetIntValue()) {
15458       case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
15459         overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
15460         overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
15461         break;
15462       case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
15463         overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
15464         overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
15465         break;
15466     }
15467   AppendValue(eCSSProperty_overflow_x, overflowX);
15468   AppendValue(eCSSProperty_overflow_y, overflowY);
15469   return true;
15470 }
15471 
15472 bool
15473 CSSParserImpl::ParsePadding()
15474 {
15475   static const nsCSSPropertyID kPaddingSideIDs[] = {
15476     eCSSProperty_padding_top,
15477     eCSSProperty_padding_right,
15478     eCSSProperty_padding_bottom,
15479     eCSSProperty_padding_left
15480   };
15481 
15482   return ParseBoxProperties(kPaddingSideIDs);
15483 }
15484 
15485 bool
15486 CSSParserImpl::ParseQuotes()
15487 {
15488   nsCSSValue value;
15489   if (!ParseSingleTokenVariant(value, VARIANT_HOS, nullptr)) {
15490     return false;
15491   }
15492   if (value.GetUnit() == eCSSUnit_String) {
15493     nsCSSValue open = value;
15494     nsCSSValuePairList* quotes = value.SetPairListValue();
15495     for (;;) {
15496       quotes->mXValue = open;
15497       // get mandatory close
15498       if (!ParseSingleTokenVariant(quotes->mYValue, VARIANT_STRING, nullptr)) {
15499         return false;
15500       }
15501       // look for another open
15502       if (!ParseSingleTokenVariant(open, VARIANT_STRING, nullptr)) {
15503         break;
15504       }
15505       quotes->mNext = new nsCSSValuePairList;
15506       quotes = quotes->mNext;
15507     }
15508   }
15509   AppendValue(eCSSProperty_quotes, value);
15510   return true;
15511 }
15512 
15513 bool
15514 CSSParserImpl::ParseTextDecoration()
15515 {
15516   static const nsCSSPropertyID kTextDecorationIDs[] = {
15517     eCSSProperty_text_decoration_line,
15518     eCSSProperty_text_decoration_style,
15519     eCSSProperty_text_decoration_color
15520   };
15521   const int32_t numProps = MOZ_ARRAY_LENGTH(kTextDecorationIDs);
15522   nsCSSValue values[numProps];
15523 
15524   int32_t found = ParseChoice(values, kTextDecorationIDs, numProps);
15525   if (found < 1) {
15526     return false;
15527   }
15528 
15529   // Provide default values
15530   if ((found & 1) == 0) { // Provide default text-decoration-line
15531     values[0].SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE,
15532                           eCSSUnit_Enumerated);
15533   }
15534   if ((found & 2) == 0) { // Provide default text-decoration-style
15535     values[1].SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
15536                           eCSSUnit_Enumerated);
15537   }
15538   if ((found & 4) == 0) { // Provide default text-decoration-color
15539     values[2].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
15540   }
15541 
15542   for (int32_t index = 0; index < numProps; index++) {
15543     AppendValue(kTextDecorationIDs[index], values[index]);
15544   }
15545   return true;
15546 }
15547 
15548 bool
15549 CSSParserImpl::ParseTextEmphasis()
15550 {
15551   static constexpr nsCSSPropertyID kTextEmphasisIDs[] = {
15552     eCSSProperty_text_emphasis_style,
15553     eCSSProperty_text_emphasis_color
15554   };
15555   constexpr int32_t numProps = MOZ_ARRAY_LENGTH(kTextEmphasisIDs);
15556   nsCSSValue values[numProps];
15557 
15558   int32_t found = ParseChoice(values, kTextEmphasisIDs, numProps);
15559   if (found < 1) {
15560     return false;
15561   }
15562 
15563   if (!(found & 1)) { // Provide default text-emphasis-style
15564     values[0].SetNoneValue();
15565   }
15566   if (!(found & 2)) { // Provide default text-emphasis-color
15567     values[1].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
15568   }
15569 
15570   for (int32_t index = 0; index < numProps; index++) {
15571     AppendValue(kTextEmphasisIDs[index], values[index]);
15572   }
15573   return true;
15574 }
15575 
15576 bool
15577 CSSParserImpl::ParseTextEmphasisPosition(nsCSSValue& aValue)
15578 {
15579   static_assert((NS_STYLE_TEXT_EMPHASIS_POSITION_OVER ^
15580                  NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER ^
15581                  NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT ^
15582                  NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT) ==
15583                 (NS_STYLE_TEXT_EMPHASIS_POSITION_OVER |
15584                  NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER |
15585                  NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT |
15586                  NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT),
15587                 "text-emphasis-position constants should be bitmasks");
15588 
15589   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
15590     return true;
15591   }
15592 
15593   nsCSSValue first, second;
15594   const auto& kTable = nsCSSProps::kTextEmphasisPositionKTable;
15595   if (!ParseSingleTokenVariant(first, VARIANT_KEYWORD, kTable) ||
15596       !ParseSingleTokenVariant(second, VARIANT_KEYWORD, kTable)) {
15597     return false;
15598   }
15599 
15600   auto firstValue = first.GetIntValue();
15601   auto secondValue = second.GetIntValue();
15602   if ((firstValue == NS_STYLE_TEXT_EMPHASIS_POSITION_OVER ||
15603       firstValue == NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER) ==
15604       (secondValue == NS_STYLE_TEXT_EMPHASIS_POSITION_OVER ||
15605       secondValue == NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER)) {
15606     return false;
15607   }
15608 
15609   aValue.SetIntValue(firstValue | secondValue, eCSSUnit_Enumerated);
15610   return true;
15611 }
15612 
15613 bool
15614 CSSParserImpl::ParseTextEmphasisStyle(nsCSSValue& aValue)
15615 {
15616   static_assert((NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK ^
15617                  NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK) ==
15618                 (NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK |
15619                  NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK),
15620                 "text-emphasis-style shape and fill constants "
15621                 "should not intersect");
15622   static_assert(NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED == 0,
15623                 "Making 'filled' zero ensures that if neither 'filled' nor "
15624                 "'open' is specified, we compute it to 'filled' per spec");
15625 
15626   if (ParseSingleTokenVariant(aValue, VARIANT_HOS, nullptr)) {
15627     return true;
15628   }
15629 
15630   nsCSSValue first, second;
15631   const auto& fillKTable = nsCSSProps::kTextEmphasisStyleFillKTable;
15632   const auto& shapeKTable = nsCSSProps::kTextEmphasisStyleShapeKTable;
15633   if (ParseSingleTokenVariant(first, VARIANT_KEYWORD, fillKTable)) {
15634     ParseSingleTokenVariant(second, VARIANT_KEYWORD, shapeKTable);
15635   } else if (ParseSingleTokenVariant(first, VARIANT_KEYWORD, shapeKTable)) {
15636     ParseSingleTokenVariant(second, VARIANT_KEYWORD, fillKTable);
15637   } else {
15638     return false;
15639   }
15640 
15641   auto value = first.GetIntValue();
15642   if (second.GetUnit() == eCSSUnit_Enumerated) {
15643     value |= second.GetIntValue();
15644   }
15645   aValue.SetIntValue(value, eCSSUnit_Enumerated);
15646   return true;
15647 }
15648 
15649 bool
15650 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableEntry aTable[])
15651 {
15652   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
15653     // 'inherit', 'initial' and 'unset' must be alone
15654     return true;
15655   }
15656 
15657   nsCSSValue left;
15658   if (!ParseSingleTokenVariant(left, VARIANT_KEYWORD, aTable)) {
15659     return false;
15660   }
15661 
15662   if (!nsLayoutUtils::IsTextAlignUnsafeValueEnabled()) {
15663     aValue = left;
15664     return true;
15665   }
15666 
15667   nsCSSValue right;
15668   if (ParseSingleTokenVariant(right, VARIANT_KEYWORD, aTable)) {
15669     // 'true' must be combined with some other value than 'true'.
15670     if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE &&
15671         right.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) {
15672       return false;
15673     }
15674     aValue.SetPairValue(left, right);
15675   } else {
15676     // Single value 'true' is not allowed.
15677     if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) {
15678       return false;
15679     }
15680     aValue = left;
15681   }
15682   return true;
15683 }
15684 
15685 bool
15686 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue)
15687 {
15688   return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable);
15689 }
15690 
15691 bool
15692 CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue)
15693 {
15694   return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable);
15695 }
15696 
15697 bool
15698 CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
15699 {
15700   static_assert((NS_STYLE_TEXT_DECORATION_LINE_NONE ^
15701                  NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE ^
15702                  NS_STYLE_TEXT_DECORATION_LINE_OVERLINE ^
15703                  NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH ^
15704                  NS_STYLE_TEXT_DECORATION_LINE_BLINK ^
15705                  NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS) ==
15706                 (NS_STYLE_TEXT_DECORATION_LINE_NONE |
15707                  NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE |
15708                  NS_STYLE_TEXT_DECORATION_LINE_OVERLINE |
15709                  NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH |
15710                  NS_STYLE_TEXT_DECORATION_LINE_BLINK |
15711                  NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS),
15712                 "text decoration constants need to be bitmasks");
15713   if (ParseSingleTokenVariant(aValue, VARIANT_HK,
15714                               nsCSSProps::kTextDecorationLineKTable)) {
15715     if (eCSSUnit_Enumerated == aValue.GetUnit()) {
15716       int32_t intValue = aValue.GetIntValue();
15717       if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
15718         // look for more keywords
15719         nsCSSValue  keyword;
15720         int32_t index;
15721         for (index = 0; index < 3; index++) {
15722           if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) {
15723             int32_t newValue = keyword.GetIntValue();
15724             if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE ||
15725                 newValue & intValue) {
15726               // 'none' keyword in conjuction with others is not allowed, and
15727               // duplicate keyword is not allowed.
15728               return false;
15729             }
15730             intValue |= newValue;
15731           }
15732           else {
15733             break;
15734           }
15735         }
15736         aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
15737       }
15738     }
15739     return true;
15740   }
15741   return false;
15742 }
15743 
15744 bool
15745 CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
15746 {
15747   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
15748     // 'inherit', 'initial' and 'unset' must be alone
15749     return true;
15750   }
15751 
15752   nsCSSValue left;
15753   if (!ParseSingleTokenVariant(left, VARIANT_KEYWORD | VARIANT_STRING,
15754                                nsCSSProps::kTextOverflowKTable))
15755     return false;
15756 
15757   nsCSSValue right;
15758   if (ParseSingleTokenVariant(right, VARIANT_KEYWORD | VARIANT_STRING,
15759                               nsCSSProps::kTextOverflowKTable))
15760     aValue.SetPairValue(left, right);
15761   else {
15762     aValue = left;
15763   }
15764   return true;
15765 }
15766 
15767 bool
15768 CSSParserImpl::ParseTouchAction(nsCSSValue& aValue)
15769 {
15770   // Avaliable values of property touch-action:
15771   // auto | none | [pan-x || pan-y] | manipulation
15772 
15773   if (!ParseSingleTokenVariant(aValue, VARIANT_HK,
15774                                nsCSSProps::kTouchActionKTable)) {
15775     return false;
15776   }
15777 
15778   // Auto and None keywords aren't allowed in conjunction with others.
15779   // Also inherit, initial and unset values are available.
15780   if (eCSSUnit_Enumerated != aValue.GetUnit()) {
15781     return true;
15782   }
15783 
15784   int32_t intValue = aValue.GetIntValue();
15785   nsCSSValue nextValue;
15786   if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) {
15787     int32_t nextIntValue = nextValue.GetIntValue();
15788 
15789     // duplicates aren't allowed.
15790     if (nextIntValue & intValue) {
15791       return false;
15792     }
15793 
15794     // Auto and None and Manipulation is not allowed in conjunction with others.
15795     if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE |
15796                                      NS_STYLE_TOUCH_ACTION_AUTO |
15797                                      NS_STYLE_TOUCH_ACTION_MANIPULATION)) {
15798       return false;
15799     }
15800 
15801     aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
15802   }
15803 
15804   return true;
15805 }
15806 
15807 bool
15808 CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue)
15809 {
15810   if (!ParseSingleTokenVariant(aValue, VARIANT_HK,
15811                                nsCSSProps::kTextCombineUprightKTable)) {
15812     return false;
15813   }
15814 
15815   // if 'digits', need to check for an explicit number [2, 3, 4]
15816   if (eCSSUnit_Enumerated == aValue.GetUnit() &&
15817       aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) {
15818     if (!nsLayoutUtils::TextCombineUprightDigitsEnabled()) {
15819       return false;
15820     }
15821     if (!GetToken(true)) {
15822       return true;
15823     }
15824     if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
15825       switch (mToken.mInteger) {
15826         case 2:  // already set, nothing to do
15827           break;
15828         case 3:
15829           aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3,
15830                              eCSSUnit_Enumerated);
15831           break;
15832         case 4:
15833           aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4,
15834                              eCSSUnit_Enumerated);
15835           break;
15836         default:
15837           // invalid digits value
15838           return false;
15839       }
15840     } else {
15841       UngetToken();
15842     }
15843   }
15844   return true;
15845 }
15846 
15847 ///////////////////////////////////////////////////////
15848 // transform Parsing Implementation
15849 
15850 /* Reads a function list of arguments and consumes the closing parenthesis.
15851  * Do not call this function directly; it's meant to be called from
15852  * ParseFunction.
15853  */
15854 bool
15855 CSSParserImpl::ParseFunctionInternals(const uint32_t aVariantMask[],
15856                                       uint32_t aVariantMaskAll,
15857                                       uint16_t aMinElems,
15858                                       uint16_t aMaxElems,
15859                                       InfallibleTArray<nsCSSValue> &aOutput)
15860 {
15861   NS_ASSERTION((aVariantMask && !aVariantMaskAll) ||
15862                (!aVariantMask && aVariantMaskAll),
15863                "only one of the two variant mask parameters can be set");
15864 
15865   for (uint16_t index = 0; index < aMaxElems; ++index) {
15866     nsCSSValue newValue;
15867     uint32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index];
15868     if (ParseVariant(newValue, m, nullptr) != CSSParseResult::Ok) {
15869       break;
15870     }
15871 
15872     if (nsCSSValue::IsFloatUnit(newValue.GetUnit())) {
15873       // Clamp infinity or -infinity values to max float or -max float to avoid
15874       // calculations with infinity.
15875       newValue.SetFloatValue(
15876         mozilla::clamped(newValue.GetFloatValue(),
15877                          -std::numeric_limits<float>::max(),
15878                           std::numeric_limits<float>::max()),
15879         newValue.GetUnit());
15880     }
15881 
15882     aOutput.AppendElement(newValue);
15883 
15884     if (ExpectSymbol(',', true)) {
15885       // Move on to the next argument if we see a comma.
15886       continue;
15887     }
15888 
15889     if (ExpectSymbol(')', true)) {
15890       // Make sure we've read enough symbols if we see a closing parenthesis.
15891       return (index + 1) >= aMinElems;
15892     }
15893 
15894     // Only a comma or a closing parenthesis is valid after an argument.
15895     break;
15896   }
15897 
15898   // If we're here, we've hit an error without seeing a closing parenthesis or
15899   // we've read too many elements without seeing a closing parenthesis.
15900   SkipUntil(')');
15901   return false;
15902 }
15903 
15904 /* Parses a function [ input of the form (a [, b]*) ] and stores it
15905  * as an nsCSSValue that holds a function of the form
15906  * function-name arg1 arg2 ... argN
15907  *
15908  * On error, the return value is false.
15909  *
15910  * @param aFunction The name of the function that we're reading.
15911  * @param aAllowedTypes An array of values corresponding to the legal
15912  *        types for each element in the function.  The zeroth element in the
15913  *        array corresponds to the first function parameter, etc.  The length
15914  *        of this array _must_ be greater than or equal to aMaxElems or the
15915  *        behavior is undefined.  If not null, aAllowTypesAll must be 0.
15916  * @param aAllowedTypesAll If set, every element tested for these types
15917  * @param aMinElems Minimum number of elements to read.  Reading fewer than
15918  *        this many elements will result in the function failing.
15919  * @param aMaxElems Maximum number of elements to read.  Reading more than
15920  *        this many elements will result in the function failing.
15921  * @param aValue (out) The value that was parsed.
15922  */
15923 bool
15924 CSSParserImpl::ParseFunction(nsCSSKeyword aFunction,
15925                              const uint32_t aAllowedTypes[],
15926                              uint32_t aAllowedTypesAll,
15927                              uint16_t aMinElems, uint16_t aMaxElems,
15928                              nsCSSValue &aValue)
15929 {
15930   NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) ||
15931                (!aAllowedTypes && aAllowedTypesAll),
15932                "only one of the two allowed type parameter can be set");
15933   typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
15934 
15935   /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
15936    * elements stored in the the nsCSSValue::Array.
15937    */
15938   static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
15939 
15940   /* Read in a list of values as an array, failing if we can't or if
15941    * it's out of bounds.
15942    *
15943    * We reserve 16 entries in the foundValues array in order to avoid
15944    * having to resize the array dynamically when parsing some well-formed
15945    * functions.  The number 16 is coming from the number of arguments that
15946    * matrix3d() accepts.
15947    */
15948   AutoTArray<nsCSSValue, 16> foundValues;
15949   if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems,
15950                               aMaxElems, foundValues)) {
15951     return false;
15952   }
15953 
15954   /*
15955    * In case the user has given us more than 2^16 - 2 arguments,
15956    * we'll truncate them at 2^16 - 2 arguments.
15957    */
15958   uint16_t numArgs = std::min(foundValues.Length(), MAX_ALLOWED_ELEMS);
15959   RefPtr<nsCSSValue::Array> convertedArray =
15960     aValue.InitFunction(aFunction, numArgs);
15961 
15962   /* Copy things over. */
15963   for (uint16_t index = 0; index < numArgs; ++index)
15964     convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
15965 
15966   /* Return it! */
15967   return true;
15968 }
15969 
15970 /**
15971  * Given a token, determines the minimum and maximum number of function
15972  * parameters to read, along with the mask that should be used to read
15973  * those function parameters.  If the token isn't a transform function,
15974  * returns an error.
15975  *
15976  * @param aToken The token identifying the function.
15977  * @param aIsPrefixed If true, parse matrices using the matrix syntax
15978  *   for -moz-transform.
15979  * @param aDisallowRelativeValues If true, only allow variants that are
15980  *   numbers or have non-relative dimensions.
15981  * @param aMinElems [out] The minimum number of elements to read.
15982  * @param aMaxElems [out] The maximum number of elements to read
15983  * @param aVariantMask [out] The variant mask to use during parsing
15984  * @return Whether the information was loaded successfully.
15985  */
15986 static bool GetFunctionParseInformation(nsCSSKeyword aToken,
15987                                         bool aIsPrefixed,
15988                                         bool aDisallowRelativeValues,
15989                                         uint16_t &aMinElems,
15990                                         uint16_t &aMaxElems,
15991                                         const uint32_t *& aVariantMask)
15992 {
15993 /* These types represent the common variant masks that will be used to
15994    * parse out the individual functions.  The order in the enumeration
15995    * must match the order in which the masks are declared.
15996    */
15997   enum { eLengthPercentCalc,
15998          eLengthCalc,
15999          eAbsoluteLengthCalc,
16000          eTwoLengthPercentCalcs,
16001          eTwoAbsoluteLengthCalcs,
16002          eTwoLengthPercentCalcsOneLengthCalc,
16003          eThreeAbsoluteLengthCalc,
16004          eAngle,
16005          eTwoAngles,
16006          eNumber,
16007          eNonNegativeLength,
16008          eNonNegativeAbsoluteLength,
16009          eTwoNumbers,
16010          eThreeNumbers,
16011          eThreeNumbersOneAngle,
16012          eMatrix,
16013          eMatrixPrefixed,
16014          eMatrix3d,
16015          eMatrix3dPrefixed,
16016          eNumVariantMasks };
16017   static const int32_t kMaxElemsPerFunction = 16;
16018   static const uint32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
16019     {VARIANT_LPCALC},
16020     {VARIANT_LCALC},
16021     {VARIANT_LB},
16022     {VARIANT_LPCALC, VARIANT_LPCALC},
16023     {VARIANT_LBCALC, VARIANT_LBCALC},
16024     {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LCALC},
16025     {VARIANT_LBCALC, VARIANT_LBCALC, VARIANT_LBCALC},
16026     {VARIANT_ANGLE_OR_ZERO},
16027     {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
16028     {VARIANT_NUMBER},
16029     {VARIANT_LENGTH|VARIANT_NONNEGATIVE_DIMENSION},
16030     {VARIANT_LB|VARIANT_NONNEGATIVE_DIMENSION},
16031     {VARIANT_NUMBER, VARIANT_NUMBER},
16032     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
16033     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
16034     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
16035      VARIANT_NUMBER, VARIANT_NUMBER},
16036     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
16037      VARIANT_LPNCALC, VARIANT_LPNCALC},
16038     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
16039      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
16040      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
16041      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
16042     {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
16043      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
16044      VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
16045      VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
16046   // Map from a mask to a congruent mask that excludes relative variants.
16047   static const int32_t kNonRelativeVariantMap[eNumVariantMasks] = {
16048     eAbsoluteLengthCalc,
16049     eAbsoluteLengthCalc,
16050     eAbsoluteLengthCalc,
16051     eTwoAbsoluteLengthCalcs,
16052     eTwoAbsoluteLengthCalcs,
16053     eThreeAbsoluteLengthCalc,
16054     eThreeAbsoluteLengthCalc,
16055     eAngle,
16056     eTwoAngles,
16057     eNumber,
16058     eNonNegativeAbsoluteLength,
16059     eNonNegativeAbsoluteLength,
16060     eTwoNumbers,
16061     eThreeNumbers,
16062     eThreeNumbersOneAngle,
16063     eMatrix,
16064     eMatrix,
16065     eMatrix3d,
16066     eMatrix3d };
16067 
16068 #ifdef DEBUG
16069   static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
16070     {1, 1, 1, 2, 2, 3, 3, 1, 2, 1, 1, 1, 2, 3, 4, 6, 6, 16, 16};
16071 #endif
16072 
16073   int32_t variantIndex = eNumVariantMasks;
16074 
16075   switch (aToken) {
16076   case eCSSKeyword_translatex:
16077   case eCSSKeyword_translatey:
16078     /* Exactly one length or percent. */
16079     variantIndex = eLengthPercentCalc;
16080     aMinElems = 1U;
16081     aMaxElems = 1U;
16082     break;
16083   case eCSSKeyword_translatez:
16084     /* Exactly one length */
16085     variantIndex = eLengthCalc;
16086     aMinElems = 1U;
16087     aMaxElems = 1U;
16088     break;
16089   case eCSSKeyword_translate3d:
16090     /* Exactly two lengthds or percents and a number */
16091     variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
16092     aMinElems = 3U;
16093     aMaxElems = 3U;
16094     break;
16095   case eCSSKeyword_scalez:
16096   case eCSSKeyword_scalex:
16097   case eCSSKeyword_scaley:
16098     /* Exactly one scale factor. */
16099     variantIndex = eNumber;
16100     aMinElems = 1U;
16101     aMaxElems = 1U;
16102     break;
16103   case eCSSKeyword_scale3d:
16104     /* Exactly three scale factors. */
16105     variantIndex = eThreeNumbers;
16106     aMinElems = 3U;
16107     aMaxElems = 3U;
16108     break;
16109   case eCSSKeyword_rotatex:
16110   case eCSSKeyword_rotatey:
16111   case eCSSKeyword_rotate:
16112   case eCSSKeyword_rotatez:
16113     /* Exactly one angle. */
16114     variantIndex = eAngle;
16115     aMinElems = 1U;
16116     aMaxElems = 1U;
16117     break;
16118   case eCSSKeyword_rotate3d:
16119     variantIndex = eThreeNumbersOneAngle;
16120     aMinElems = 4U;
16121     aMaxElems = 4U;
16122     break;
16123   case eCSSKeyword_translate:
16124     /* One or two lengths or percents. */
16125     variantIndex = eTwoLengthPercentCalcs;
16126     aMinElems = 1U;
16127     aMaxElems = 2U;
16128     break;
16129   case eCSSKeyword_skew:
16130     /* Exactly one or two angles. */
16131     variantIndex = eTwoAngles;
16132     aMinElems = 1U;
16133     aMaxElems = 2U;
16134     break;
16135   case eCSSKeyword_scale:
16136     /* One or two scale factors. */
16137     variantIndex = eTwoNumbers;
16138     aMinElems = 1U;
16139     aMaxElems = 2U;
16140     break;
16141   case eCSSKeyword_skewx:
16142     /* Exactly one angle. */
16143     variantIndex = eAngle;
16144     aMinElems = 1U;
16145     aMaxElems = 1U;
16146     break;
16147   case eCSSKeyword_skewy:
16148     /* Exactly one angle. */
16149     variantIndex = eAngle;
16150     aMinElems = 1U;
16151     aMaxElems = 1U;
16152     break;
16153   case eCSSKeyword_matrix:
16154     /* Six values, all numbers. */
16155     variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
16156     aMinElems = 6U;
16157     aMaxElems = 6U;
16158     break;
16159   case eCSSKeyword_matrix3d:
16160     /* 16 matrix values, all numbers */
16161     variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
16162     aMinElems = 16U;
16163     aMaxElems = 16U;
16164     break;
16165   case eCSSKeyword_perspective:
16166     /* Exactly one scale number. */
16167     variantIndex = eNonNegativeLength;
16168     aMinElems = 1U;
16169     aMaxElems = 1U;
16170     break;
16171   default:
16172     /* Oh dear, we didn't match.  Report an error. */
16173     return false;
16174   }
16175 
16176   if (aDisallowRelativeValues) {
16177     variantIndex = kNonRelativeVariantMap[variantIndex];
16178   }
16179 
16180   NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
16181   NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
16182   NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
16183   NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
16184   NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
16185 #ifdef DEBUG
16186   NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
16187                "Invalid aMaxElems for this variant mask.");
16188 #endif
16189 
16190   // Convert the index into a mask.
16191   aVariantMask = kVariantMasks[variantIndex];
16192 
16193   return true;
16194 }
16195 
16196 bool CSSParserImpl::ParseWillChange()
16197 {
16198   nsCSSValue listValue;
16199   nsCSSValueList* currentListValue = listValue.SetListValue();
16200   bool first = true;
16201   for (;;) {
16202     const uint32_t variantMask = VARIANT_IDENTIFIER |
16203                                  VARIANT_INHERIT |
16204                                  VARIANT_NONE |
16205                                  VARIANT_ALL |
16206                                  VARIANT_AUTO;
16207     nsCSSValue value;
16208     if (!ParseSingleTokenVariant(value, variantMask, nullptr)) {
16209       return false;
16210     }
16211 
16212     if (value.GetUnit() == eCSSUnit_None ||
16213         value.GetUnit() == eCSSUnit_All)
16214     {
16215       return false;
16216     }
16217 
16218     if (value.GetUnit() != eCSSUnit_Ident) {
16219       if (first) {
16220         AppendValue(eCSSProperty_will_change, value);
16221         return true;
16222       } else {
16223         return false;
16224       }
16225     }
16226 
16227     nsString str;
16228     value.GetStringValue(str);
16229     if (str.LowerCaseEqualsLiteral("default") ||
16230         str.LowerCaseEqualsLiteral("will-change")) {
16231       return false;
16232     }
16233 
16234     currentListValue->mValue = value;
16235 
16236     if (!ExpectSymbol(',', true)) {
16237       break;
16238     }
16239     currentListValue->mNext = new nsCSSValueList;
16240     currentListValue = currentListValue->mNext;
16241     first = false;
16242   }
16243 
16244   AppendValue(eCSSProperty_will_change, listValue);
16245   return true;
16246 }
16247 
16248 /* Reads a single transform function from the tokenizer stream, reporting an
16249  * error if something goes wrong.
16250  */
16251 bool
16252 CSSParserImpl::ParseSingleTransform(bool aIsPrefixed,
16253                                     bool aDisallowRelativeValues,
16254                                     nsCSSValue& aValue)
16255 {
16256   if (!GetToken(true))
16257     return false;
16258 
16259   if (mToken.mType != eCSSToken_Function) {
16260     UngetToken();
16261     return false;
16262   }
16263 
16264   const uint32_t* variantMask;
16265   uint16_t minElems, maxElems;
16266   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
16267 
16268   if (!GetFunctionParseInformation(keyword, aIsPrefixed,
16269                                    aDisallowRelativeValues,
16270                                    minElems, maxElems,
16271                                    variantMask))
16272     return false;
16273 
16274   return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue);
16275 }
16276 
16277 /* Parses a transform property list by continuously reading in properties
16278  * and constructing a matrix from it.
16279  */
16280 bool
16281 CSSParserImpl::ParseTransform(bool aIsPrefixed, bool aDisallowRelativeValues)
16282 {
16283   nsCSSValue value;
16284   // 'inherit', 'initial', 'unset' and 'none' must be alone
16285   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
16286                                nullptr)) {
16287     nsCSSValueSharedList* list = new nsCSSValueSharedList;
16288     value.SetSharedListValue(list);
16289     list->mHead = new nsCSSValueList;
16290     nsCSSValueList* cur = list->mHead;
16291     for (;;) {
16292       if (!ParseSingleTransform(aIsPrefixed, aDisallowRelativeValues,
16293                                 cur->mValue)) {
16294         return false;
16295       }
16296       if (CheckEndProperty()) {
16297         break;
16298       }
16299       cur->mNext = new nsCSSValueList;
16300       cur = cur->mNext;
16301     }
16302   }
16303   AppendValue(eCSSProperty_transform, value);
16304   return true;
16305 }
16306 
16307 /* Reads a polygon function's argument list.
16308  */
16309 bool
16310 CSSParserImpl::ParsePolygonFunction(nsCSSValue& aValue)
16311 {
16312   uint16_t numArgs = 1;
16313 
16314   nsCSSValue fillRuleValue;
16315   if (ParseEnum(fillRuleValue, nsCSSProps::kFillRuleKTable)) {
16316     numArgs++;
16317 
16318     // The fill-rule must be comma separated from the polygon points.
16319     if (!ExpectSymbol(',', true)) {
16320       REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
16321       SkipUntil(')');
16322       return false;
16323     }
16324   }
16325 
16326   nsCSSValue coordinates;
16327   nsCSSValuePairList* item = coordinates.SetPairListValue();
16328   for (;;) {
16329     nsCSSValue xValue, yValue;
16330     if (ParseVariant(xValue, VARIANT_LPCALC, nullptr) != CSSParseResult::Ok ||
16331         ParseVariant(yValue, VARIANT_LPCALC, nullptr) != CSSParseResult::Ok) {
16332       REPORT_UNEXPECTED_TOKEN(PECoordinatePair);
16333       SkipUntil(')');
16334       return false;
16335     }
16336     item->mXValue = xValue;
16337     item->mYValue = yValue;
16338 
16339     // See whether to continue or whether to look for end of function.
16340     if (!ExpectSymbol(',', true)) {
16341       // We need to read the closing parenthesis.
16342       if (!ExpectSymbol(')', true)) {
16343         REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
16344         SkipUntil(')');
16345         return false;
16346       }
16347       break;
16348     }
16349     item->mNext = new nsCSSValuePairList;
16350     item = item->mNext;
16351   }
16352 
16353   RefPtr<nsCSSValue::Array> functionArray =
16354     aValue.InitFunction(eCSSKeyword_polygon, numArgs);
16355   functionArray->Item(numArgs) = coordinates;
16356   if (numArgs > 1) {
16357     functionArray->Item(1) = fillRuleValue;
16358   }
16359 
16360   return true;
16361 }
16362 
16363 bool
16364 CSSParserImpl::ParseCircleOrEllipseFunction(nsCSSKeyword aKeyword,
16365                                             nsCSSValue& aValue)
16366 {
16367   nsCSSValue radiusX, radiusY, position;
16368   bool hasRadius = false, hasPosition = false;
16369 
16370   int32_t mask = VARIANT_LPCALC | VARIANT_NONNEGATIVE_DIMENSION |
16371                  VARIANT_KEYWORD;
16372   CSSParseResult result =
16373     ParseVariant(radiusX, mask, nsCSSProps::kShapeRadiusKTable);
16374   if (result == CSSParseResult::Error) {
16375     return false;
16376   } else if (result == CSSParseResult::Ok) {
16377     if (aKeyword == eCSSKeyword_ellipse) {
16378       if (ParseVariant(radiusY, mask, nsCSSProps::kShapeRadiusKTable) !=
16379           CSSParseResult::Ok) {
16380         REPORT_UNEXPECTED_TOKEN(PEExpectedRadius);
16381         SkipUntil(')');
16382         return false;
16383       }
16384     }
16385     hasRadius = true;
16386   }
16387 
16388   if (!ExpectSymbol(')', true)) {
16389     if (!GetToken(true)) {
16390       REPORT_UNEXPECTED_EOF(PEPositionEOF);
16391       return false;
16392     }
16393 
16394     if (mToken.mType != eCSSToken_Ident ||
16395         !mToken.mIdent.LowerCaseEqualsLiteral("at") ||
16396         !ParsePositionValueForBasicShape(position)) {
16397       REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
16398       SkipUntil(')');
16399       return false;
16400     }
16401     if (!ExpectSymbol(')', true)) {
16402       REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
16403       SkipUntil(')');
16404       return false;
16405     }
16406     hasPosition = true;
16407   }
16408 
16409   size_t count = aKeyword == eCSSKeyword_circle ? 2 : 3;
16410   RefPtr<nsCSSValue::Array> functionArray =
16411     aValue.InitFunction(aKeyword, count);
16412   if (hasRadius) {
16413     functionArray->Item(1) = radiusX;
16414     if (aKeyword == eCSSKeyword_ellipse) {
16415       functionArray->Item(2) = radiusY;
16416     }
16417   }
16418   if (hasPosition) {
16419     functionArray->Item(count) = position;
16420   }
16421 
16422   return true;
16423 }
16424 
16425 bool
16426 CSSParserImpl::ParseInsetFunction(nsCSSValue& aValue)
16427 {
16428   RefPtr<nsCSSValue::Array> functionArray =
16429     aValue.InitFunction(eCSSKeyword_inset, 5);
16430 
16431   int count = 0;
16432   while (count < 4) {
16433     CSSParseResult result =
16434       ParseVariant(functionArray->Item(count + 1), VARIANT_LPCALC, nullptr);
16435     if (result == CSSParseResult::Error) {
16436       count = 0;
16437       break;
16438     } else if (result == CSSParseResult::NotFound) {
16439       break;
16440     }
16441     ++count;
16442   }
16443 
16444   if (count == 0) {
16445     REPORT_UNEXPECTED_TOKEN(PEExpectedShapeArg);
16446     SkipUntil(')');
16447     return false;
16448   }
16449 
16450   if (!ExpectSymbol(')', true)) {
16451     if (!GetToken(true)) {
16452       NS_NOTREACHED("ExpectSymbol should have returned true");
16453       return false;
16454     }
16455 
16456     RefPtr<nsCSSValue::Array> radiusArray = nsCSSValue::Array::Create(4);
16457     functionArray->Item(5).SetArrayValue(radiusArray, eCSSUnit_Array);
16458     if (mToken.mType != eCSSToken_Ident ||
16459         !mToken.mIdent.LowerCaseEqualsLiteral("round") ||
16460         !ParseBoxCornerRadiiInternals(radiusArray->ItemStorage())) {
16461       REPORT_UNEXPECTED_TOKEN(PEExpectedRadius);
16462       SkipUntil(')');
16463       return false;
16464     }
16465 
16466     if (!ExpectSymbol(')', true)) {
16467       REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
16468       SkipUntil(')');
16469       return false;
16470     }
16471   }
16472 
16473   return true;
16474 }
16475 
16476 bool
16477 CSSParserImpl::ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens)
16478 {
16479   if (!GetToken(true)) {
16480     return false;
16481   }
16482 
16483   if (mToken.mType != eCSSToken_Function) {
16484     UngetToken();
16485     return false;
16486   }
16487 
16488   // Specific shape function parsing always consumes tokens.
16489   *aConsumedTokens = true;
16490   nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
16491   switch (keyword) {
16492   case eCSSKeyword_polygon:
16493     return ParsePolygonFunction(aValue);
16494   case eCSSKeyword_circle:
16495   case eCSSKeyword_ellipse:
16496     return ParseCircleOrEllipseFunction(keyword, aValue);
16497   case eCSSKeyword_inset:
16498     return ParseInsetFunction(aValue);
16499   default:
16500     return false;
16501   }
16502 }
16503 
16504 bool
16505 CSSParserImpl::ParseReferenceBoxAndBasicShape(
16506   nsCSSValue& aValue,
16507   const KTableEntry aBoxKeywordTable[])
16508 {
16509   nsCSSValue referenceBox;
16510   bool hasBox = ParseEnum(referenceBox, aBoxKeywordTable);
16511 
16512   const bool boxCameFirst = hasBox;
16513 
16514   nsCSSValue basicShape;
16515   bool basicShapeConsumedTokens = false;
16516   bool hasShape = ParseBasicShape(basicShape, &basicShapeConsumedTokens);
16517 
16518   // Parsing wasn't successful if ParseBasicShape consumed tokens but failed
16519   // or if the token was neither a reference box nor a basic shape.
16520   if ((!hasShape && basicShapeConsumedTokens) || (!hasBox && !hasShape)) {
16521     return false;
16522   }
16523 
16524   // Check if the second argument is a reference box if the first wasn't.
16525   if (!hasBox) {
16526     hasBox = ParseEnum(referenceBox, aBoxKeywordTable);
16527   }
16528 
16529   RefPtr<nsCSSValue::Array> fullValue =
16530     nsCSSValue::Array::Create((hasBox && hasShape) ? 2 : 1);
16531 
16532   if (hasBox && hasShape) {
16533     fullValue->Item(boxCameFirst ? 0 : 1) = referenceBox;
16534     fullValue->Item(boxCameFirst ? 1 : 0) = basicShape;
16535   } else if (hasBox) {
16536     fullValue->Item(0) = referenceBox;
16537   } else {
16538     MOZ_ASSERT(hasShape, "should've bailed if we got neither box nor shape");
16539     fullValue->Item(0) = basicShape;
16540   }
16541 
16542   aValue.SetArrayValue(fullValue, eCSSUnit_Array);
16543   return true;
16544 }
16545 
16546 // Parse a clip-path url to a <clipPath> element or a basic shape.
16547 bool
16548 CSSParserImpl::ParseClipPath(nsCSSValue& aValue)
16549 {
16550   if (ParseSingleTokenVariant(aValue, VARIANT_HUO, nullptr)) {
16551     return true;
16552   }
16553 
16554   if (!nsLayoutUtils::CSSClipPathShapesEnabled()) {
16555     // With CSS Clip Path Shapes disabled, we should only accept
16556     // SVG clipPath reference and none.
16557     REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
16558     return false;
16559   }
16560 
16561   return ParseReferenceBoxAndBasicShape(
16562     aValue, nsCSSProps::kClipPathGeometryBoxKTable);
16563 }
16564 
16565 // none | [ <basic-shape> || <shape-box> ] | <image>
16566 bool
16567 CSSParserImpl::ParseShapeOutside(nsCSSValue& aValue)
16568 {
16569   if (ParseSingleTokenVariant(aValue, VARIANT_HUO, nullptr)) {
16570     // 'inherit', 'initial', 'unset', 'none', and <image> url must be alone.
16571     return true;
16572   }
16573 
16574   return ParseReferenceBoxAndBasicShape(
16575     aValue, nsCSSProps::kShapeOutsideShapeBoxKTable);
16576 }
16577 
16578 bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
16579 {
16580   nsCSSValuePair position;
16581   if (!ParseBoxPositionValues(position, true))
16582     return false;
16583 
16584   nsCSSPropertyID prop = eCSSProperty_transform_origin;
16585   if (aPerspective) {
16586     prop = eCSSProperty_perspective_origin;
16587   }
16588 
16589   // Unlike many other uses of pairs, this position should always be stored
16590   // as a pair, even if the values are the same, so it always serializes as
16591   // a pair, and to keep the computation code simple.
16592   if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
16593       position.mXValue.GetUnit() == eCSSUnit_Initial ||
16594       position.mXValue.GetUnit() == eCSSUnit_Unset) {
16595     MOZ_ASSERT(position.mXValue == position.mYValue,
16596                "inherit/initial/unset only half?");
16597     AppendValue(prop, position.mXValue);
16598   } else {
16599     nsCSSValue value;
16600     if (aPerspective) {
16601       value.SetPairValue(position.mXValue, position.mYValue);
16602     } else {
16603       nsCSSValue depth;
16604       CSSParseResult result =
16605         ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr);
16606       if (result == CSSParseResult::Error) {
16607         return false;
16608       } else if (result == CSSParseResult::NotFound) {
16609         depth.SetFloatValue(0.0f, eCSSUnit_Pixel);
16610       }
16611       value.SetTripletValue(position.mXValue, position.mYValue, depth);
16612     }
16613 
16614     AppendValue(prop, value);
16615   }
16616   return true;
16617 }
16618 
16619 /**
16620  * Reads a drop-shadow value. At the moment the Filter Effects specification
16621  * just expects one shadow item. Should this ever change to a list of shadow
16622  * items, use ParseShadowList instead.
16623  */
16624 bool
16625 CSSParserImpl::ParseDropShadow(nsCSSValue* aValue)
16626 {
16627   // Use nsCSSValueList to reuse the shadow resolving code in
16628   // nsRuleNode and nsComputedDOMStyle.
16629   nsCSSValue shadow;
16630   nsCSSValueList* cur = shadow.SetListValue();
16631   if (!ParseShadowItem(cur->mValue, false))
16632     return false;
16633 
16634   if (!ExpectSymbol(')', true))
16635     return false;
16636 
16637   nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1);
16638 
16639   // Copy things over.
16640   dropShadow->Item(1) = shadow;
16641 
16642   return true;
16643 }
16644 
16645 /**
16646  * Reads a single url or filter function from the tokenizer stream, reporting an
16647  * error if something goes wrong.
16648  */
16649 bool
16650 CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
16651 {
16652   if (ParseSingleTokenVariant(*aValue, VARIANT_URL, nullptr)) {
16653     return true;
16654   }
16655 
16656   if (!nsLayoutUtils::CSSFiltersEnabled()) {
16657     // With CSS Filters disabled, we should only accept an SVG reference filter.
16658     REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
16659     return false;
16660   }
16661 
16662   if (!GetToken(true)) {
16663     REPORT_UNEXPECTED_EOF(PEFilterEOF);
16664     return false;
16665   }
16666 
16667   if (mToken.mType != eCSSToken_Function) {
16668     REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
16669     UngetToken();
16670     return false;
16671   }
16672 
16673   nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
16674   // Parse drop-shadow independently of the other filter functions
16675   // because of its more complex characteristics.
16676   if (functionName == eCSSKeyword_drop_shadow) {
16677     if (ParseDropShadow(aValue)) {
16678       return true;
16679     } else {
16680       // Unrecognized filter function.
16681       REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
16682       SkipUntil(')');
16683       return false;
16684     }
16685   }
16686 
16687   // Set up the parsing rules based on the filter function.
16688   uint32_t variantMask = VARIANT_PN;
16689   bool rejectNegativeArgument = true;
16690   bool clampArgumentToOne = false;
16691   switch (functionName) {
16692     case eCSSKeyword_blur:
16693       variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
16694       // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
16695       rejectNegativeArgument = false;
16696       break;
16697     case eCSSKeyword_brightness:
16698     case eCSSKeyword_contrast:
16699     case eCSSKeyword_saturate:
16700       break;
16701     case eCSSKeyword_grayscale:
16702     case eCSSKeyword_invert:
16703     case eCSSKeyword_sepia:
16704     case eCSSKeyword_opacity:
16705       clampArgumentToOne = true;
16706       break;
16707     case eCSSKeyword_hue_rotate:
16708       variantMask = VARIANT_ANGLE;
16709       rejectNegativeArgument = false;
16710       break;
16711     default:
16712       // Unrecognized filter function.
16713       REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
16714       SkipUntil(')');
16715       return false;
16716   }
16717 
16718   // Parse the function.
16719   uint16_t minElems = 1U;
16720   uint16_t maxElems = 1U;
16721   uint32_t allVariants = 0;
16722   if (!ParseFunction(functionName, &variantMask, allVariants,
16723                      minElems, maxElems, *aValue)) {
16724     REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError);
16725     return false;
16726   }
16727 
16728   // Get the first and only argument to the filter function.
16729   MOZ_ASSERT(aValue->GetUnit() == eCSSUnit_Function,
16730              "expected a filter function");
16731   MOZ_ASSERT(aValue->UnitHasArrayValue(),
16732              "filter function should be an array");
16733   MOZ_ASSERT(aValue->GetArrayValue()->Count() == 2,
16734              "filter function should have exactly one argument");
16735   nsCSSValue& arg = aValue->GetArrayValue()->Item(1);
16736 
16737   if (rejectNegativeArgument &&
16738       ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) ||
16739        (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) {
16740     REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
16741     return false;
16742   }
16743 
16744   if (clampArgumentToOne) {
16745     if (arg.GetUnit() == eCSSUnit_Number &&
16746         arg.GetFloatValue() > 1.0f) {
16747       arg.SetFloatValue(1.0f, arg.GetUnit());
16748     } else if (arg.GetUnit() == eCSSUnit_Percent &&
16749                arg.GetPercentValue() > 1.0f) {
16750       arg.SetPercentValue(1.0f);
16751     }
16752   }
16753 
16754   return true;
16755 }
16756 
16757 /**
16758  * Parses a filter property value by continuously reading in urls and/or filter
16759  * functions and constructing a list.
16760  *
16761  * When CSS Filters are enabled, the filter property accepts one or more SVG
16762  * reference filters and/or CSS filter functions.
16763  * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%);
16764  *
16765  * When CSS Filters are disabled, the filter property only accepts one SVG
16766  * reference filter.
16767  * e.g. filter: url(#my-filter);
16768  */
16769 bool
16770 CSSParserImpl::ParseFilter()
16771 {
16772   nsCSSValue value;
16773   // 'inherit', 'initial', 'unset' and 'none' must be alone
16774   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
16775                                nullptr)) {
16776     nsCSSValueList* cur = value.SetListValue();
16777     while (cur) {
16778       if (!ParseSingleFilter(&cur->mValue)) {
16779         return false;
16780       }
16781       if (CheckEndProperty()) {
16782         break;
16783       }
16784       if (!nsLayoutUtils::CSSFiltersEnabled()) {
16785         // With CSS Filters disabled, we should only accept one SVG reference
16786         // filter.
16787         REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
16788         return false;
16789       }
16790       cur->mNext = new nsCSSValueList;
16791       cur = cur->mNext;
16792     }
16793   }
16794   AppendValue(eCSSProperty_filter, value);
16795   return true;
16796 }
16797 
16798 bool
16799 CSSParserImpl::ParseTransitionProperty()
16800 {
16801   nsCSSValue value;
16802   // 'inherit', 'initial', 'unset' and 'none' must be alone
16803   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
16804                                nullptr)) {
16805     // Accept a list of arbitrary identifiers.  They should be
16806     // CSS properties, but we want to accept any so that we
16807     // accept properties that we don't know about yet, e.g.
16808     // transition-property: invalid-property, left, opacity;
16809     nsCSSValueList* cur = value.SetListValue();
16810     for (;;) {
16811       if (!ParseSingleTokenVariant(cur->mValue,
16812                                    VARIANT_IDENTIFIER | VARIANT_ALL,
16813                                    nullptr)) {
16814         return false;
16815       }
16816       if (cur->mValue.GetUnit() == eCSSUnit_Ident) {
16817         nsDependentString str(cur->mValue.GetStringBufferValue());
16818         // Exclude 'none', 'inherit', 'initial' and 'unset' according to the
16819         // same rules as for 'counter-reset' in CSS 2.1.
16820         if (str.LowerCaseEqualsLiteral("none") ||
16821             str.LowerCaseEqualsLiteral("inherit") ||
16822             str.LowerCaseEqualsLiteral("initial") ||
16823             (str.LowerCaseEqualsLiteral("unset") &&
16824              nsLayoutUtils::UnsetValueEnabled())) {
16825           return false;
16826         }
16827       }
16828       if (!ExpectSymbol(',', true)) {
16829         break;
16830       }
16831       cur->mNext = new nsCSSValueList;
16832       cur = cur->mNext;
16833     }
16834   }
16835   AppendValue(eCSSProperty_transition_property, value);
16836   return true;
16837 }
16838 
16839 bool
16840 CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue)
16841 {
16842   NS_ASSERTION(!mHavePushBack &&
16843                mToken.mType == eCSSToken_Function &&
16844                mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"),
16845                "unexpected initial state");
16846 
16847   RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(4);
16848 
16849   float x1, x2, y1, y2;
16850   if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) ||
16851       !ParseTransitionTimingFunctionValueComponent(y1, ',', false) ||
16852       !ParseTransitionTimingFunctionValueComponent(x2, ',', true) ||
16853       !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) {
16854     return false;
16855   }
16856 
16857   val->Item(0).SetFloatValue(x1, eCSSUnit_Number);
16858   val->Item(1).SetFloatValue(y1, eCSSUnit_Number);
16859   val->Item(2).SetFloatValue(x2, eCSSUnit_Number);
16860   val->Item(3).SetFloatValue(y2, eCSSUnit_Number);
16861 
16862   aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier);
16863 
16864   return true;
16865 }
16866 
16867 bool
16868 CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent,
16869                                                            char aStop,
16870                                                            bool aIsXPoint)
16871 {
16872   if (!GetToken(true)) {
16873     return false;
16874   }
16875   nsCSSToken* tk = &mToken;
16876   if (tk->mType == eCSSToken_Number) {
16877     float num = tk->mNumber;
16878 
16879     // Clamp infinity or -infinity values to max float or -max float to avoid
16880     // calculations with infinity.
16881     num = mozilla::clamped(num, -std::numeric_limits<float>::max(),
16882                                  std::numeric_limits<float>::max());
16883 
16884     // X control point should be inside [0, 1] range.
16885     if (aIsXPoint && (num < 0.0 || num > 1.0)) {
16886       return false;
16887     }
16888     aComponent = num;
16889     if (ExpectSymbol(aStop, true)) {
16890       return true;
16891     }
16892   }
16893   return false;
16894 }
16895 
16896 bool
16897 CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
16898 {
16899   NS_ASSERTION(!mHavePushBack &&
16900                mToken.mType == eCSSToken_Function &&
16901                mToken.mIdent.LowerCaseEqualsLiteral("steps"),
16902                "unexpected initial state");
16903 
16904   RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
16905 
16906   if (!ParseSingleTokenOneOrLargerVariant(val->Item(0), VARIANT_INTEGER,
16907                                           nullptr)) {
16908     return false;
16909   }
16910 
16911   int32_t type = -1;  // indicates an implicit end value
16912   if (ExpectSymbol(',', true)) {
16913     if (!GetToken(true)) {
16914       return false;
16915     }
16916     if (mToken.mType == eCSSToken_Ident) {
16917       if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
16918         type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
16919       } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) {
16920         type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
16921       }
16922     }
16923     if (type == -1) {
16924       UngetToken();
16925       return false;
16926     }
16927   }
16928   val->Item(1).SetIntValue(type, eCSSUnit_Enumerated);
16929 
16930   if (!ExpectSymbol(')', true)) {
16931     return false;
16932   }
16933 
16934   aValue.SetArrayValue(val, eCSSUnit_Steps);
16935   return true;
16936 }
16937 
16938 static nsCSSValueList*
16939 AppendValueToList(nsCSSValue& aContainer,
16940                   nsCSSValueList* aTail,
16941                   const nsCSSValue& aValue)
16942 {
16943   nsCSSValueList* entry;
16944   if (aContainer.GetUnit() == eCSSUnit_Null) {
16945     MOZ_ASSERT(!aTail, "should not have an entry");
16946     entry = aContainer.SetListValue();
16947   } else {
16948     MOZ_ASSERT(!aTail->mNext, "should not have a next entry");
16949     MOZ_ASSERT(aContainer.GetUnit() == eCSSUnit_List, "not a list");
16950     entry = new nsCSSValueList;
16951     aTail->mNext = entry;
16952   }
16953   entry->mValue = aValue;
16954   return entry;
16955 }
16956 
16957 CSSParserImpl::ParseAnimationOrTransitionShorthandResult
16958 CSSParserImpl::ParseAnimationOrTransitionShorthand(
16959                  const nsCSSPropertyID* aProperties,
16960                  const nsCSSValue* aInitialValues,
16961                  nsCSSValue* aValues,
16962                  size_t aNumProperties)
16963 {
16964   nsCSSValue tempValue;
16965   // first see if 'inherit', 'initial' or 'unset' is specified.  If one is,
16966   // it can be the only thing specified, so don't attempt to parse any
16967   // additional properties
16968   if (ParseSingleTokenVariant(tempValue, VARIANT_INHERIT, nullptr)) {
16969     for (uint32_t i = 0; i < aNumProperties; ++i) {
16970       AppendValue(aProperties[i], tempValue);
16971     }
16972     return eParseAnimationOrTransitionShorthand_Inherit;
16973   }
16974 
16975   static const size_t maxNumProperties = 8;
16976   MOZ_ASSERT(aNumProperties <= maxNumProperties,
16977              "can't handle this many properties");
16978   nsCSSValueList *cur[maxNumProperties];
16979   bool parsedProperty[maxNumProperties];
16980 
16981   for (size_t i = 0; i < aNumProperties; ++i) {
16982     cur[i] = nullptr;
16983   }
16984   bool atEOP = false; // at end of property?
16985   for (;;) { // loop over comma-separated transitions or animations
16986     // whether a particular subproperty was specified for this
16987     // transition or animation
16988     bool haveAnyProperty = false;
16989     for (size_t i = 0; i < aNumProperties; ++i) {
16990       parsedProperty[i] = false;
16991     }
16992     for (;;) { // loop over values within a transition or animation
16993       bool foundProperty = false;
16994       // check to see if we're at the end of one full transition or
16995       // animation definition (either because we hit a comma or because
16996       // we hit the end of the property definition)
16997       if (ExpectSymbol(',', true))
16998         break;
16999       if (CheckEndProperty()) {
17000         atEOP = true;
17001         break;
17002       }
17003 
17004       // else, try to parse the next transition or animation sub-property
17005       for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) {
17006         if (!parsedProperty[i]) {
17007           // if we haven't found this property yet, try to parse it
17008           CSSParseResult result =
17009             ParseSingleValueProperty(tempValue, aProperties[i]);
17010           if (result == CSSParseResult::Error) {
17011             return eParseAnimationOrTransitionShorthand_Error;
17012           }
17013           if (result == CSSParseResult::Ok) {
17014             parsedProperty[i] = true;
17015             cur[i] = AppendValueToList(aValues[i], cur[i], tempValue);
17016             foundProperty = true;
17017             haveAnyProperty = true;
17018             break; // out of inner loop; continue looking for next sub-property
17019           }
17020         }
17021       }
17022       if (!foundProperty) {
17023         // We're not at a ',' or at the end of the property, but we couldn't
17024         // parse any of the sub-properties, so the declaration is invalid.
17025         return eParseAnimationOrTransitionShorthand_Error;
17026       }
17027     }
17028 
17029     if (!haveAnyProperty) {
17030       // Got an empty item.
17031       return eParseAnimationOrTransitionShorthand_Error;
17032     }
17033 
17034     // We hit the end of the property or the end of one transition
17035     // or animation definition, add its components to the list.
17036     for (uint32_t i = 0; i < aNumProperties; ++i) {
17037       // If all of the subproperties were not explicitly specified, fill
17038       // in the missing ones with initial values.
17039       if (!parsedProperty[i]) {
17040         cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]);
17041       }
17042     }
17043 
17044     if (atEOP)
17045       break;
17046     // else we just hit a ',' so continue parsing the next compound transition
17047   }
17048 
17049   return eParseAnimationOrTransitionShorthand_Values;
17050 }
17051 
17052 bool
17053 CSSParserImpl::ParseTransition()
17054 {
17055   static const nsCSSPropertyID kTransitionProperties[] = {
17056     eCSSProperty_transition_duration,
17057     eCSSProperty_transition_timing_function,
17058     // Must check 'transition-delay' after 'transition-duration', since
17059     // that's our assumption about what the spec means for the shorthand
17060     // syntax (the first time given is the duration, and the second
17061     // given is the delay).
17062     eCSSProperty_transition_delay,
17063     // Must check 'transition-property' after
17064     // 'transition-timing-function' since 'transition-property' accepts
17065     // any keyword.
17066     eCSSProperty_transition_property
17067   };
17068   static const uint32_t numProps = MOZ_ARRAY_LENGTH(kTransitionProperties);
17069   // this is a shorthand property that accepts -property, -delay,
17070   // -duration, and -timing-function with some components missing.
17071   // there can be multiple transitions, separated with commas
17072 
17073   nsCSSValue initialValues[numProps];
17074   initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
17075   initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
17076                                eCSSUnit_Enumerated);
17077   initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
17078   initialValues[3].SetAllValue();
17079 
17080   nsCSSValue values[numProps];
17081 
17082   ParseAnimationOrTransitionShorthandResult spres =
17083     ParseAnimationOrTransitionShorthand(kTransitionProperties,
17084                                         initialValues, values, numProps);
17085   if (spres != eParseAnimationOrTransitionShorthand_Values) {
17086     return spres != eParseAnimationOrTransitionShorthand_Error;
17087   }
17088 
17089   // Make two checks on the list for 'transition-property':
17090   //   + If there is more than one item, then none of the items can be
17091   //     'none'.
17092   //   + None of the items can be 'inherit', 'initial' or 'unset'.
17093   {
17094     MOZ_ASSERT(kTransitionProperties[3] == eCSSProperty_transition_property,
17095                "array index mismatch");
17096     nsCSSValueList *l = values[3].GetListValue();
17097     bool multipleItems = !!l->mNext;
17098     do {
17099       const nsCSSValue& val = l->mValue;
17100       if (val.GetUnit() == eCSSUnit_None) {
17101         if (multipleItems) {
17102           // This is a syntax error.
17103           return false;
17104         }
17105 
17106         // Unbox a solitary 'none'.
17107         values[3].SetNoneValue();
17108         break;
17109       }
17110       if (val.GetUnit() == eCSSUnit_Ident) {
17111         nsDependentString str(val.GetStringBufferValue());
17112         if (str.EqualsLiteral("inherit") ||
17113             str.EqualsLiteral("initial") ||
17114             (str.EqualsLiteral("unset") &&
17115              nsLayoutUtils::UnsetValueEnabled())) {
17116           return false;
17117         }
17118       }
17119     } while ((l = l->mNext));
17120   }
17121 
17122   // Save all parsed transition sub-properties in mTempData
17123   for (uint32_t i = 0; i < numProps; ++i) {
17124     AppendValue(kTransitionProperties[i], values[i]);
17125   }
17126   return true;
17127 }
17128 
17129 bool
17130 CSSParserImpl::ParseAnimation()
17131 {
17132   static const nsCSSPropertyID kAnimationProperties[] = {
17133     eCSSProperty_animation_duration,
17134     eCSSProperty_animation_timing_function,
17135     // Must check 'animation-delay' after 'animation-duration', since
17136     // that's our assumption about what the spec means for the shorthand
17137     // syntax (the first time given is the duration, and the second
17138     // given is the delay).
17139     eCSSProperty_animation_delay,
17140     eCSSProperty_animation_direction,
17141     eCSSProperty_animation_fill_mode,
17142     eCSSProperty_animation_iteration_count,
17143     eCSSProperty_animation_play_state,
17144     // Must check 'animation-name' after 'animation-timing-function',
17145     // 'animation-direction', 'animation-fill-mode',
17146     // 'animation-iteration-count', and 'animation-play-state' since
17147     // 'animation-name' accepts any keyword.
17148     eCSSProperty_animation_name
17149   };
17150   static const uint32_t numProps = MOZ_ARRAY_LENGTH(kAnimationProperties);
17151   // this is a shorthand property that accepts -property, -delay,
17152   // -duration, and -timing-function with some components missing.
17153   // there can be multiple animations, separated with commas
17154 
17155   nsCSSValue initialValues[numProps];
17156   initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
17157   initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
17158                                eCSSUnit_Enumerated);
17159   initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
17160   initialValues[3].SetIntValue(static_cast<uint32_t>(mozilla::dom::PlaybackDirection::Normal),
17161                                eCSSUnit_Enumerated);
17162   initialValues[4].SetIntValue(static_cast<uint32_t>(mozilla::dom::FillMode::None),
17163                                eCSSUnit_Enumerated);
17164   initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
17165   initialValues[6].SetIntValue(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING, eCSSUnit_Enumerated);
17166   initialValues[7].SetNoneValue();
17167 
17168   nsCSSValue values[numProps];
17169 
17170   ParseAnimationOrTransitionShorthandResult spres =
17171     ParseAnimationOrTransitionShorthand(kAnimationProperties,
17172                                         initialValues, values, numProps);
17173   if (spres != eParseAnimationOrTransitionShorthand_Values) {
17174     return spres != eParseAnimationOrTransitionShorthand_Error;
17175   }
17176 
17177   // Save all parsed animation sub-properties in mTempData
17178   for (uint32_t i = 0; i < numProps; ++i) {
17179     AppendValue(kAnimationProperties[i], values[i]);
17180   }
17181   return true;
17182 }
17183 
17184 bool
17185 CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow)
17186 {
17187   // A shadow list item is an array, with entries in this sequence:
17188   enum {
17189     IndexX,
17190     IndexY,
17191     IndexRadius,
17192     IndexSpread,  // only for box-shadow
17193     IndexColor,
17194     IndexInset    // only for box-shadow
17195   };
17196 
17197   RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6);
17198 
17199   if (aIsBoxShadow) {
17200     // Optional inset keyword (ignore errors)
17201     ParseSingleTokenVariant(val->Item(IndexInset), VARIANT_KEYWORD,
17202                             nsCSSProps::kBoxShadowTypeKTable);
17203   }
17204 
17205   nsCSSValue xOrColor;
17206   bool haveColor = false;
17207   if (ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC,
17208                    nullptr) != CSSParseResult::Ok) {
17209     return false;
17210   }
17211   if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) {
17212     val->Item(IndexX) = xOrColor;
17213   } else {
17214     // Must be a color (as string or color value)
17215     NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident ||
17216                  xOrColor.GetUnit() == eCSSUnit_EnumColor ||
17217                  xOrColor.IsNumericColorUnit(),
17218                  "Must be a color value");
17219     val->Item(IndexColor) = xOrColor;
17220     haveColor = true;
17221 
17222     // X coordinate mandatory after color
17223     if (ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC,
17224                      nullptr) != CSSParseResult::Ok) {
17225       return false;
17226     }
17227   }
17228 
17229   // Y coordinate; mandatory
17230   if (ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC,
17231                    nullptr) != CSSParseResult::Ok) {
17232     return false;
17233   }
17234 
17235   // Optional radius. Ignore errors except if they pass a negative
17236   // value which we must reject. If we use ParseNonNegativeVariant
17237   // we can't tell the difference between an unspecified radius
17238   // and a negative radius.
17239   CSSParseResult result =
17240     ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC,
17241                  nullptr);
17242   if (result == CSSParseResult::Error) {
17243     return false;
17244   } else if (result == CSSParseResult::Ok) {
17245     if (val->Item(IndexRadius).IsLengthUnit() &&
17246         val->Item(IndexRadius).GetFloatValue() < 0) {
17247       return false;
17248     }
17249   }
17250 
17251   if (aIsBoxShadow) {
17252     // Optional spread
17253     if (ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC,
17254                      nullptr) == CSSParseResult::Error) {
17255       return false;
17256     }
17257   }
17258 
17259   if (!haveColor) {
17260     // Optional color
17261     if (ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr) ==
17262         CSSParseResult::Error) {
17263       return false;
17264     }
17265   }
17266 
17267   if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) {
17268     // Optional inset keyword
17269     ParseSingleTokenVariant(val->Item(IndexInset), VARIANT_KEYWORD,
17270                             nsCSSProps::kBoxShadowTypeKTable);
17271   }
17272 
17273   aValue.SetArrayValue(val, eCSSUnit_Array);
17274   return true;
17275 }
17276 
17277 bool
17278 CSSParserImpl::ParseShadowList(nsCSSPropertyID aProperty)
17279 {
17280   nsAutoParseCompoundProperty compound(this);
17281   bool isBoxShadow = aProperty == eCSSProperty_box_shadow;
17282 
17283   nsCSSValue value;
17284   // 'inherit', 'initial', 'unset' and 'none' must be alone
17285   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
17286                                nullptr)) {
17287     nsCSSValueList* cur = value.SetListValue();
17288     for (;;) {
17289       if (!ParseShadowItem(cur->mValue, isBoxShadow)) {
17290         return false;
17291       }
17292       if (!ExpectSymbol(',', true)) {
17293         break;
17294       }
17295       cur->mNext = new nsCSSValueList;
17296       cur = cur->mNext;
17297     }
17298   }
17299   AppendValue(aProperty, value);
17300   return true;
17301 }
17302 
17303 int32_t
17304 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix)
17305 {
17306   NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
17307 
17308   int32_t nameSpaceID = kNameSpaceID_Unknown;
17309   if (mNameSpaceMap) {
17310     // user-specified identifiers are case-sensitive (bug 416106)
17311     nsCOMPtr<nsIAtom> prefix = NS_Atomize(aPrefix);
17312     nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
17313   }
17314   // else no declared namespaces
17315 
17316   if (nameSpaceID == kNameSpaceID_Unknown) {   // unknown prefix, dump it
17317     REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, aPrefix);
17318   }
17319 
17320   return nameSpaceID;
17321 }
17322 
17323 void
17324 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
17325 {
17326   if (mNameSpaceMap) {
17327     aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nullptr));
17328   } else {
17329     aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
17330   }
17331 }
17332 
17333 bool
17334 CSSParserImpl::ParsePaint(nsCSSPropertyID aPropID)
17335 {
17336   nsCSSValue x, y;
17337 
17338   if (ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL |
17339                       VARIANT_OPENTYPE_SVG_KEYWORD,
17340                    nsCSSProps::kContextPatternKTable) != CSSParseResult::Ok) {
17341     return false;
17342   }
17343 
17344   bool canHaveFallback = x.GetUnit() == eCSSUnit_URL ||
17345                          x.GetUnit() == eCSSUnit_Enumerated;
17346   if (canHaveFallback) {
17347     CSSParseResult result =
17348       ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr);
17349     if (result == CSSParseResult::Error) {
17350       return false;
17351     } else if (result == CSSParseResult::NotFound) {
17352       y.SetNoneValue();
17353     }
17354   }
17355 
17356   if (!canHaveFallback) {
17357     AppendValue(aPropID, x);
17358   } else {
17359     nsCSSValue val;
17360     val.SetPairValue(x, y);
17361     AppendValue(aPropID, val);
17362   }
17363   return true;
17364 }
17365 
17366 bool
17367 CSSParserImpl::ParseDasharray()
17368 {
17369   nsCSSValue value;
17370 
17371   // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
17372   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE |
17373                                       VARIANT_OPENTYPE_SVG_KEYWORD,
17374                                nsCSSProps::kStrokeContextValueKTable)) {
17375     nsCSSValueList *cur = value.SetListValue();
17376     for (;;) {
17377       if (!ParseSingleTokenNonNegativeVariant(cur->mValue, VARIANT_LPN,
17378                                               nullptr)) {
17379         return false;
17380       }
17381       if (CheckEndProperty()) {
17382         break;
17383       }
17384       // skip optional commas between elements
17385       (void)ExpectSymbol(',', true);
17386 
17387       cur->mNext = new nsCSSValueList;
17388       cur = cur->mNext;
17389     }
17390   }
17391   AppendValue(eCSSProperty_stroke_dasharray, value);
17392   return true;
17393 }
17394 
17395 bool
17396 CSSParserImpl::ParseMarker()
17397 {
17398   nsCSSValue marker;
17399   if (ParseSingleValueProperty(marker, eCSSProperty_marker_end) ==
17400       CSSParseResult::Ok) {
17401     AppendValue(eCSSProperty_marker_end, marker);
17402     AppendValue(eCSSProperty_marker_mid, marker);
17403     AppendValue(eCSSProperty_marker_start, marker);
17404     return true;
17405   }
17406   return false;
17407 }
17408 
17409 bool
17410 CSSParserImpl::ParsePaintOrder()
17411 {
17412   static_assert
17413     ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) > NS_STYLE_PAINT_ORDER_LAST_VALUE,
17414      "bitfield width insufficient for paint-order constants");
17415 
17416   static const KTableEntry kPaintOrderKTable[] = {
17417     { eCSSKeyword_normal,  NS_STYLE_PAINT_ORDER_NORMAL },
17418     { eCSSKeyword_fill,    NS_STYLE_PAINT_ORDER_FILL },
17419     { eCSSKeyword_stroke,  NS_STYLE_PAINT_ORDER_STROKE },
17420     { eCSSKeyword_markers, NS_STYLE_PAINT_ORDER_MARKERS },
17421     { eCSSKeyword_UNKNOWN, -1 }
17422   };
17423 
17424   static_assert(MOZ_ARRAY_LENGTH(kPaintOrderKTable) ==
17425                   NS_STYLE_PAINT_ORDER_LAST_VALUE + 2,
17426                 "missing paint-order values in kPaintOrderKTable");
17427 
17428   nsCSSValue value;
17429   if (!ParseSingleTokenVariant(value, VARIANT_HK, kPaintOrderKTable)) {
17430     return false;
17431   }
17432 
17433   uint32_t seen = 0;
17434   uint32_t order = 0;
17435   uint32_t position = 0;
17436 
17437   // Ensure that even cast to a signed int32_t when stored in CSSValue,
17438   // we have enough space for the entire paint-order value.
17439   static_assert
17440     (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE < 32,
17441      "seen and order not big enough");
17442 
17443   if (value.GetUnit() == eCSSUnit_Enumerated) {
17444     uint32_t component = static_cast<uint32_t>(value.GetIntValue());
17445     if (component != NS_STYLE_PAINT_ORDER_NORMAL) {
17446       bool parsedOK = true;
17447       for (;;) {
17448         if (seen & (1 << component)) {
17449           // Already seen this component.
17450           UngetToken();
17451           parsedOK = false;
17452           break;
17453         }
17454         seen |= (1 << component);
17455         order |= (component << position);
17456         position += NS_STYLE_PAINT_ORDER_BITWIDTH;
17457         if (!ParseEnum(value, kPaintOrderKTable)) {
17458           break;
17459         }
17460         component = value.GetIntValue();
17461         if (component == NS_STYLE_PAINT_ORDER_NORMAL) {
17462           // Can't have "normal" in the middle of the list of paint components.
17463           UngetToken();
17464           parsedOK = false;
17465           break;
17466         }
17467       }
17468 
17469       // Fill in the remaining paint-order components in the order of their
17470       // constant values.
17471       if (parsedOK) {
17472         for (component = 1;
17473              component <= NS_STYLE_PAINT_ORDER_LAST_VALUE;
17474              component++) {
17475           if (!(seen & (1 << component))) {
17476             order |= (component << position);
17477             position += NS_STYLE_PAINT_ORDER_BITWIDTH;
17478           }
17479         }
17480       }
17481     }
17482 
17483     static_assert(NS_STYLE_PAINT_ORDER_NORMAL == 0,
17484                   "unexpected value for NS_STYLE_PAINT_ORDER_NORMAL");
17485     value.SetIntValue(static_cast<int32_t>(order), eCSSUnit_Enumerated);
17486   }
17487 
17488   AppendValue(eCSSProperty_paint_order, value);
17489   return true;
17490 }
17491 
17492 bool
17493 CSSParserImpl::BackslashDropped()
17494 {
17495   return mScanner->GetEOFCharacters() &
17496          nsCSSScanner::eEOFCharacters_DropBackslash;
17497 }
17498 
17499 void
17500 CSSParserImpl::AppendImpliedEOFCharacters(nsAString& aResult)
17501 {
17502   nsCSSScanner::AppendImpliedEOFCharacters(mScanner->GetEOFCharacters(),
17503                                            aResult);
17504 }
17505 
17506 bool
17507 CSSParserImpl::ParseAll()
17508 {
17509   nsCSSValue value;
17510   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
17511     return false;
17512   }
17513 
17514   // It's unlikely we'll want to use 'all' from within a UA style sheet, so
17515   // instead of computing the correct EnabledState value we just expand out
17516   // to all content-visible properties.
17517   CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all,
17518                                        CSSEnabledState::eForAllContent) {
17519     AppendValue(*p, value);
17520   }
17521   return true;
17522 }
17523 
17524 bool
17525 CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
17526                                         nsString& aValue)
17527 {
17528   CSSVariableDeclarations::Type type;
17529   nsString variableValue;
17530   bool dropBackslash;
17531   nsString impliedCharacters;
17532 
17533   // Record the token stream while parsing a variable value.
17534   if (!mInSupportsCondition) {
17535     mScanner->StartRecording();
17536   }
17537   if (!ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
17538                                nullptr, nullptr)) {
17539     if (!mInSupportsCondition) {
17540       mScanner->StopRecording();
17541     }
17542     return false;
17543   }
17544 
17545   if (!mInSupportsCondition) {
17546     if (type == CSSVariableDeclarations::eTokenStream) {
17547       // This was indeed a token stream value, so store it in variableValue.
17548       mScanner->StopRecording(variableValue);
17549       if (dropBackslash) {
17550         MOZ_ASSERT(!variableValue.IsEmpty() &&
17551                    variableValue[variableValue.Length() - 1] == '\\');
17552         variableValue.Truncate(variableValue.Length() - 1);
17553       }
17554       variableValue.Append(impliedCharacters);
17555     } else {
17556       // This was either 'inherit' or 'initial'; we don't need the recorded
17557       // input.
17558       mScanner->StopRecording();
17559     }
17560   }
17561 
17562   if (mHavePushBack && type == CSSVariableDeclarations::eTokenStream) {
17563     // If we came to the end of a valid variable declaration and a token was
17564     // pushed back, then it would have been ended by '!', ')', ';', ']' or '}'.
17565     // We need to remove it from the recorded variable value.
17566     MOZ_ASSERT(mToken.IsSymbol('!') ||
17567                mToken.IsSymbol(')') ||
17568                mToken.IsSymbol(';') ||
17569                mToken.IsSymbol(']') ||
17570                mToken.IsSymbol('}'));
17571     if (!mInSupportsCondition) {
17572       MOZ_ASSERT(!variableValue.IsEmpty());
17573       MOZ_ASSERT(variableValue[variableValue.Length() - 1] == mToken.mSymbol);
17574       variableValue.Truncate(variableValue.Length() - 1);
17575     }
17576   }
17577 
17578   *aType = type;
17579   aValue = variableValue;
17580   return true;
17581 }
17582 
17583 bool
17584 CSSParserImpl::ParseScrollSnapType()
17585 {
17586   nsCSSValue value;
17587   if (!ParseSingleTokenVariant(value, VARIANT_HK,
17588                                nsCSSProps::kScrollSnapTypeKTable)) {
17589     return false;
17590   }
17591   AppendValue(eCSSProperty_scroll_snap_type_x, value);
17592   AppendValue(eCSSProperty_scroll_snap_type_y, value);
17593   return true;
17594 }
17595 
17596 bool
17597 CSSParserImpl::ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSPropertyID aPropID)
17598 {
17599   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE,
17600                               nullptr)) {
17601     return true;
17602   }
17603   if (!GetToken(true)) {
17604     return false;
17605   }
17606   if (mToken.mType == eCSSToken_Function &&
17607       nsCSSKeywords::LookupKeyword(mToken.mIdent) == eCSSKeyword_repeat) {
17608     nsCSSValue lengthValue;
17609     if (ParseNonNegativeVariant(lengthValue,
17610                                 VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_CALC,
17611                                 nullptr) != CSSParseResult::Ok) {
17612       REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
17613       SkipUntil(')');
17614       return false;
17615     }
17616     if (!ExpectSymbol(')', true)) {
17617       REPORT_UNEXPECTED(PEExpectedCloseParen);
17618       SkipUntil(')');
17619       return false;
17620     }
17621     RefPtr<nsCSSValue::Array> functionArray =
17622       aValue.InitFunction(eCSSKeyword_repeat, 1);
17623     functionArray->Item(1) = lengthValue;
17624     return true;
17625   }
17626   UngetToken();
17627   return false;
17628 }
17629 
17630 
17631 bool
17632 CSSParserImpl::ParseScrollSnapDestination(nsCSSValue& aValue)
17633 {
17634   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
17635     return true;
17636   }
17637   nsCSSValue itemValue;
17638   if (!ParsePositionValue(aValue)) {
17639     REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
17640     return false;
17641   }
17642   return true;
17643 }
17644 
17645 // This function is very similar to ParseImageLayerPosition, and ParseImageLayerSize.
17646 bool
17647 CSSParserImpl::ParseScrollSnapCoordinate(nsCSSValue& aValue)
17648 {
17649   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE,
17650                               nullptr)) {
17651     return true;
17652   }
17653   nsCSSValue itemValue;
17654   if (!ParsePositionValue(itemValue)) {
17655     REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
17656     return false;
17657   }
17658   nsCSSValueList* item = aValue.SetListValue();
17659   for (;;) {
17660     item->mValue = itemValue;
17661     if (!ExpectSymbol(',', true)) {
17662       break;
17663     }
17664     if (!ParsePositionValue(itemValue)) {
17665       REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
17666       return false;
17667     }
17668     item->mNext = new nsCSSValueList;
17669     item = item->mNext;
17670   }
17671   return true;
17672 }
17673 
17674 bool
17675 CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
17676                                        bool* aDropBackslash,
17677                                        nsString& aImpliedCharacters,
17678                                        void (*aFunc)(const nsAString&, void*),
17679                                        void* aData)
17680 {
17681   // A property value is invalid if it contains variable references and also:
17682   //
17683   //   * has unbalanced parens, brackets or braces
17684   //   * has any BAD_STRING or BAD_URL tokens
17685   //   * has any ';' or '!' tokens at the top level of a variable reference's
17686   //     fallback
17687   //
17688   // If the property is a custom property (i.e. a variable declaration), then
17689   // it is also invalid if it consists of no tokens, such as:
17690   //
17691   //   --invalid:;
17692   //
17693   // Note that is valid for a custom property to have a value that consists
17694   // solely of white space, such as:
17695   //
17696   //   --valid: ;
17697 
17698   // Stack of closing characters for currently open constructs.
17699   StopSymbolCharStack stack;
17700 
17701   // Indexes into ')' characters in |stack| that correspond to "var(".  This
17702   // is used to stop parsing when we encounter a '!' or ';' at the top level
17703   // of a variable reference's fallback.
17704   AutoTArray<uint32_t, 16> references;
17705 
17706   if (!GetToken(false)) {
17707     // Variable value was empty since we reached EOF.
17708     REPORT_UNEXPECTED_EOF(PEVariableEOF);
17709     return false;
17710   }
17711 
17712   if (mToken.mType == eCSSToken_Symbol &&
17713       (mToken.mSymbol == '!' ||
17714        mToken.mSymbol == ')' ||
17715        mToken.mSymbol == ';' ||
17716        mToken.mSymbol == ']' ||
17717        mToken.mSymbol == '}')) {
17718     // Variable value was empty since we reached the end of the construct.
17719     UngetToken();
17720     REPORT_UNEXPECTED_TOKEN(PEVariableEmpty);
17721     return false;
17722   }
17723 
17724   if (mToken.mType == eCSSToken_Whitespace) {
17725     if (!GetToken(true)) {
17726       // Variable value was white space only.  This is valid.
17727       MOZ_ASSERT(!BackslashDropped());
17728       *aType = CSSVariableDeclarations::eTokenStream;
17729       *aDropBackslash = false;
17730       AppendImpliedEOFCharacters(aImpliedCharacters);
17731       return true;
17732     }
17733   }
17734 
17735   // Look for 'initial', 'inherit' or 'unset' as the first non-white space
17736   // token.
17737   CSSVariableDeclarations::Type type = CSSVariableDeclarations::eTokenStream;
17738   if (mToken.mType == eCSSToken_Ident) {
17739     if (mToken.mIdent.LowerCaseEqualsLiteral("initial")) {
17740       type = CSSVariableDeclarations::eInitial;
17741     } else if (mToken.mIdent.LowerCaseEqualsLiteral("inherit")) {
17742       type = CSSVariableDeclarations::eInherit;
17743     } else if (mToken.mIdent.LowerCaseEqualsLiteral("unset")) {
17744       type = CSSVariableDeclarations::eUnset;
17745     }
17746   }
17747 
17748   if (type != CSSVariableDeclarations::eTokenStream) {
17749     if (!GetToken(true)) {
17750       // Variable value was 'initial' or 'inherit' followed by EOF.
17751       MOZ_ASSERT(!BackslashDropped());
17752       *aType = type;
17753       *aDropBackslash = false;
17754       AppendImpliedEOFCharacters(aImpliedCharacters);
17755       return true;
17756     }
17757     UngetToken();
17758     if (mToken.mType == eCSSToken_Symbol &&
17759         (mToken.mSymbol == '!' ||
17760          mToken.mSymbol == ')' ||
17761          mToken.mSymbol == ';' ||
17762          mToken.mSymbol == ']' ||
17763          mToken.mSymbol == '}')) {
17764       // Variable value was 'initial' or 'inherit' followed by the end
17765       // of the declaration.
17766       MOZ_ASSERT(!BackslashDropped());
17767       *aType = type;
17768       *aDropBackslash = false;
17769       return true;
17770     }
17771   }
17772 
17773   do {
17774     switch (mToken.mType) {
17775       case eCSSToken_Symbol:
17776         if (mToken.mSymbol == '(') {
17777           stack.AppendElement(')');
17778         } else if (mToken.mSymbol == '[') {
17779           stack.AppendElement(']');
17780         } else if (mToken.mSymbol == '{') {
17781           stack.AppendElement('}');
17782         } else if (mToken.mSymbol == ';' ||
17783                    mToken.mSymbol == '!') {
17784           if (stack.IsEmpty()) {
17785             UngetToken();
17786             MOZ_ASSERT(!BackslashDropped());
17787             *aType = CSSVariableDeclarations::eTokenStream;
17788             *aDropBackslash = false;
17789             return true;
17790           } else if (!references.IsEmpty() &&
17791                      references.LastElement() == stack.Length() - 1) {
17792             REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback);
17793             SkipUntilAllOf(stack);
17794             return false;
17795           }
17796         } else if (mToken.mSymbol == ')' ||
17797                    mToken.mSymbol == ']' ||
17798                    mToken.mSymbol == '}') {
17799           for (;;) {
17800             if (stack.IsEmpty()) {
17801               UngetToken();
17802               MOZ_ASSERT(!BackslashDropped());
17803               *aType = CSSVariableDeclarations::eTokenStream;
17804               *aDropBackslash = false;
17805               return true;
17806             }
17807             char16_t c = stack.LastElement();
17808             stack.TruncateLength(stack.Length() - 1);
17809             if (!references.IsEmpty() &&
17810                 references.LastElement() == stack.Length()) {
17811               references.TruncateLength(references.Length() - 1);
17812             }
17813             if (mToken.mSymbol == c) {
17814               break;
17815             }
17816           }
17817         }
17818         break;
17819 
17820       case eCSSToken_Function:
17821         if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
17822           if (!GetToken(true)) {
17823             // EOF directly after "var(".
17824             REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF);
17825             return false;
17826           }
17827           if (mToken.mType != eCSSToken_Ident ||
17828               !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
17829             // There must be an identifier directly after the "var(" and
17830             // it must be a custom property name.
17831             UngetToken();
17832             REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName);
17833             SkipUntil(')');
17834             SkipUntilAllOf(stack);
17835             return false;
17836           }
17837           if (aFunc) {
17838             MOZ_ASSERT(Substring(mToken.mIdent, 0,
17839                                  CSS_CUSTOM_NAME_PREFIX_LENGTH).
17840                          EqualsLiteral("--"));
17841             // remove '--'
17842             const nsDependentSubstring varName =
17843               Substring(mToken.mIdent, CSS_CUSTOM_NAME_PREFIX_LENGTH);
17844             aFunc(varName, aData);
17845           }
17846           if (!GetToken(true)) {
17847             // EOF right after "var(<ident>".
17848             stack.AppendElement(')');
17849           } else if (mToken.IsSymbol(',')) {
17850             // Variable reference with fallback.
17851             if (!GetToken(false) || mToken.IsSymbol(')')) {
17852               // Comma must be followed by at least one fallback token.
17853               REPORT_UNEXPECTED(PEExpectedVariableFallback);
17854               SkipUntilAllOf(stack);
17855               return false;
17856             }
17857             UngetToken();
17858             references.AppendElement(stack.Length());
17859             stack.AppendElement(')');
17860           } else if (mToken.IsSymbol(')')) {
17861             // Correctly closed variable reference.
17862           } else {
17863             // Malformed variable reference.
17864             REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen);
17865             SkipUntil(')');
17866             SkipUntilAllOf(stack);
17867             return false;
17868           }
17869         } else {
17870           stack.AppendElement(')');
17871         }
17872         break;
17873 
17874       case eCSSToken_Bad_String:
17875         SkipUntilAllOf(stack);
17876         return false;
17877 
17878       case eCSSToken_Bad_URL:
17879         SkipUntil(')');
17880         SkipUntilAllOf(stack);
17881         return false;
17882 
17883       default:
17884         break;
17885     }
17886   } while (GetToken(true));
17887 
17888   // Append any implied closing characters.
17889   *aDropBackslash = BackslashDropped();
17890   AppendImpliedEOFCharacters(aImpliedCharacters);
17891   uint32_t i = stack.Length();
17892   while (i--) {
17893     aImpliedCharacters.Append(stack[i]);
17894   }
17895 
17896   *aType = type;
17897   return true;
17898 }
17899 
17900 bool
17901 CSSParserImpl::IsValueValidForProperty(const nsCSSPropertyID aPropID,
17902                                        const nsAString& aPropValue)
17903 {
17904   mData.AssertInitialState();
17905   mTempData.AssertInitialState();
17906 
17907   nsCSSScanner scanner(aPropValue, 0);
17908   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr);
17909   InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
17910 
17911   // We normally would need to pass in a sheet principal to InitScanner,
17912   // because we might parse a URL value.  However, we will never use the
17913   // parsed nsCSSValue (and so whether we have a sheet principal or not
17914   // doesn't really matter), so to avoid failing the assertion in
17915   // SetValueToURL, we set mSheetPrincipalRequired to false to declare
17916   // that it's safe to skip the assertion.
17917   AutoRestore<bool> autoRestore(mSheetPrincipalRequired);
17918   mSheetPrincipalRequired = false;
17919 
17920   nsAutoSuppressErrors suppressErrors(this);
17921 
17922   mSection = eCSSSection_General;
17923   scanner.SetSVGMode(false);
17924 
17925   // Check for unknown properties
17926   if (eCSSProperty_UNKNOWN == aPropID) {
17927     ReleaseScanner();
17928     return false;
17929   }
17930 
17931   // Check that the property and value parse successfully
17932   bool parsedOK = ParseProperty(aPropID);
17933 
17934   // Check for priority
17935   parsedOK = parsedOK && ParsePriority() != ePriority_Error;
17936 
17937   // We should now be at EOF
17938   parsedOK = parsedOK && !GetToken(true);
17939 
17940   mTempData.ClearProperty(aPropID);
17941   mTempData.AssertInitialState();
17942   mData.AssertInitialState();
17943 
17944   CLEAR_ERROR();
17945   ReleaseScanner();
17946 
17947   return parsedOK;
17948 }
17949 
17950 } // namespace
17951 
17952 // Recycling of parser implementation objects
17953 
17954 static CSSParserImpl* gFreeList = nullptr;
17955 
17956 /* static */ void
17957 nsCSSParser::Startup()
17958 {
17959   Preferences::AddBoolVarCache(&sOpentypeSVGEnabled,
17960                                "gfx.font_rendering.opentype_svg.enabled");
17961   Preferences::AddBoolVarCache(&sWebkitPrefixedAliasesEnabled,
17962                                "layout.css.prefixes.webkit");
17963   Preferences::AddBoolVarCache(&sWebkitDevicePixelRatioEnabled,
17964                                "layout.css.prefixes.device-pixel-ratio-webkit");
17965   Preferences::AddBoolVarCache(&sUnprefixingServiceEnabled,
17966                                "layout.css.unprefixing-service.enabled");
17967 #ifdef NIGHTLY_BUILD
17968   Preferences::AddBoolVarCache(&sUnprefixingServiceGloballyWhitelisted,
17969                                "layout.css.unprefixing-service.globally-whitelisted");
17970 #endif
17971   Preferences::AddBoolVarCache(&sMozGradientsEnabled,
17972                                "layout.css.prefixes.gradients");
17973   Preferences::AddBoolVarCache(&sControlCharVisibility,
17974                                "layout.css.control-characters.visible");
17975 }
17976 
17977 nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
17978                          CSSStyleSheet* aSheet)
17979 {
17980   CSSParserImpl *impl = gFreeList;
17981   if (impl) {
17982     gFreeList = impl->mNextFree;
17983     impl->mNextFree = nullptr;
17984   } else {
17985     impl = new CSSParserImpl();
17986   }
17987 
17988   if (aLoader) {
17989     impl->SetChildLoader(aLoader);
17990     impl->SetQuirkMode(aLoader->GetCompatibilityMode() ==
17991                        eCompatibility_NavQuirks);
17992   }
17993   if (aSheet) {
17994     impl->SetStyleSheet(aSheet);
17995   }
17996 
17997   mImpl = static_cast<void*>(impl);
17998 }
17999 
18000 nsCSSParser::~nsCSSParser()
18001 {
18002   CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl);
18003   impl->Reset();
18004   impl->mNextFree = gFreeList;
18005   gFreeList = impl;
18006 }
18007 
18008 /* static */ void
18009 nsCSSParser::Shutdown()
18010 {
18011   CSSParserImpl *tofree = gFreeList;
18012   CSSParserImpl *next;
18013   while (tofree)
18014     {
18015       next = tofree->mNextFree;
18016       delete tofree;
18017       tofree = next;
18018     }
18019 }
18020 
18021 // Wrapper methods
18022 
18023 nsresult
18024 nsCSSParser::ParseSheet(const nsAString& aInput,
18025                         nsIURI*          aSheetURI,
18026                         nsIURI*          aBaseURI,
18027                         nsIPrincipal*    aSheetPrincipal,
18028                         uint32_t         aLineNumber,
18029                         css::LoaderReusableStyleSheets* aReusableSheets)
18030 {
18031   return static_cast<CSSParserImpl*>(mImpl)->
18032     ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
18033                aReusableSheets);
18034 }
18035 
18036 already_AddRefed<css::Declaration>
18037 nsCSSParser::ParseStyleAttribute(const nsAString&  aAttributeValue,
18038                                  nsIURI*           aDocURI,
18039                                  nsIURI*           aBaseURI,
18040                                  nsIPrincipal*     aNodePrincipal)
18041 {
18042   return static_cast<CSSParserImpl*>(mImpl)->
18043     ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI, aNodePrincipal);
18044 }
18045 
18046 nsresult
18047 nsCSSParser::ParseDeclarations(const nsAString&  aBuffer,
18048                                nsIURI*           aSheetURI,
18049                                nsIURI*           aBaseURI,
18050                                nsIPrincipal*     aSheetPrincipal,
18051                                css::Declaration* aDeclaration,
18052                                bool*           aChanged)
18053 {
18054   return static_cast<CSSParserImpl*>(mImpl)->
18055     ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal,
18056                       aDeclaration, aChanged);
18057 }
18058 
18059 nsresult
18060 nsCSSParser::ParseRule(const nsAString&        aRule,
18061                        nsIURI*                 aSheetURI,
18062                        nsIURI*                 aBaseURI,
18063                        nsIPrincipal*           aSheetPrincipal,
18064                        css::Rule**             aResult)
18065 {
18066   return static_cast<CSSParserImpl*>(mImpl)->
18067     ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult);
18068 }
18069 
18070 void
18071 nsCSSParser::ParseProperty(const nsCSSPropertyID aPropID,
18072                            const nsAString&    aPropValue,
18073                            nsIURI*             aSheetURI,
18074                            nsIURI*             aBaseURI,
18075                            nsIPrincipal*       aSheetPrincipal,
18076                            css::Declaration*   aDeclaration,
18077                            bool*               aChanged,
18078                            bool                aIsImportant,
18079                            bool                aIsSVGMode)
18080 {
18081   static_cast<CSSParserImpl*>(mImpl)->
18082     ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
18083                   aSheetPrincipal, aDeclaration, aChanged,
18084                   aIsImportant, aIsSVGMode);
18085 }
18086 
18087 void
18088 nsCSSParser::ParseLonghandProperty(const nsCSSPropertyID aPropID,
18089                                    const nsAString&    aPropValue,
18090                                    nsIURI*             aSheetURI,
18091                                    nsIURI*             aBaseURI,
18092                                    nsIPrincipal*       aSheetPrincipal,
18093                                    nsCSSValue&         aResult)
18094 {
18095   static_cast<CSSParserImpl*>(mImpl)->
18096     ParseLonghandProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
18097                           aSheetPrincipal, aResult);
18098 }
18099 
18100 bool
18101 nsCSSParser::ParseTransformProperty(const nsAString& aPropValue,
18102                                     bool             aDisallowRelativeValues,
18103                                     nsCSSValue&      aResult)
18104 {
18105   return static_cast<CSSParserImpl*>(mImpl)->
18106     ParseTransformProperty(aPropValue, aDisallowRelativeValues, aResult);
18107 }
18108 
18109 void
18110 nsCSSParser::ParseVariable(const nsAString&    aVariableName,
18111                            const nsAString&    aPropValue,
18112                            nsIURI*             aSheetURI,
18113                            nsIURI*             aBaseURI,
18114                            nsIPrincipal*       aSheetPrincipal,
18115                            css::Declaration*   aDeclaration,
18116                            bool*               aChanged,
18117                            bool                aIsImportant)
18118 {
18119   static_cast<CSSParserImpl*>(mImpl)->
18120     ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI,
18121                   aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
18122 }
18123 
18124 void
18125 nsCSSParser::ParseMediaList(const nsSubstring& aBuffer,
18126                             nsIURI*            aURI,
18127                             uint32_t           aLineNumber,
18128                             nsMediaList*       aMediaList,
18129                             bool               aHTMLMode)
18130 {
18131   static_cast<CSSParserImpl*>(mImpl)->
18132     ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode);
18133 }
18134 
18135 bool
18136 nsCSSParser::ParseSourceSizeList(const nsAString& aBuffer,
18137                                  nsIURI* aURI,
18138                                  uint32_t aLineNumber,
18139                                  InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
18140                                  InfallibleTArray<nsCSSValue>& aValues,
18141                                  bool aHTMLMode)
18142 {
18143   return static_cast<CSSParserImpl*>(mImpl)->
18144     ParseSourceSizeList(aBuffer, aURI, aLineNumber, aQueries, aValues,
18145                         aHTMLMode);
18146 }
18147 
18148 bool
18149 nsCSSParser::ParseFontFamilyListString(const nsSubstring& aBuffer,
18150                                        nsIURI*            aURI,
18151                                        uint32_t           aLineNumber,
18152                                        nsCSSValue&        aValue)
18153 {
18154   return static_cast<CSSParserImpl*>(mImpl)->
18155     ParseFontFamilyListString(aBuffer, aURI, aLineNumber, aValue);
18156 }
18157 
18158 bool
18159 nsCSSParser::ParseColorString(const nsSubstring& aBuffer,
18160                               nsIURI*            aURI,
18161                               uint32_t           aLineNumber,
18162                               nsCSSValue&        aValue,
18163                               bool               aSuppressErrors /* false */)
18164 {
18165   return static_cast<CSSParserImpl*>(mImpl)->
18166     ParseColorString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors);
18167 }
18168 
18169 bool
18170 nsCSSParser::ParseMarginString(const nsSubstring& aBuffer,
18171                                nsIURI*            aURI,
18172                                uint32_t           aLineNumber,
18173                                nsCSSValue&        aValue,
18174                                bool               aSuppressErrors /* false */)
18175 {
18176   return static_cast<CSSParserImpl*>(mImpl)->
18177     ParseMarginString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors);
18178 }
18179 
18180 nsresult
18181 nsCSSParser::ParseSelectorString(const nsSubstring&  aSelectorString,
18182                                  nsIURI*             aURI,
18183                                  uint32_t            aLineNumber,
18184                                  nsCSSSelectorList** aSelectorList)
18185 {
18186   return static_cast<CSSParserImpl*>(mImpl)->
18187     ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList);
18188 }
18189 
18190 already_AddRefed<nsCSSKeyframeRule>
18191 nsCSSParser::ParseKeyframeRule(const nsSubstring& aBuffer,
18192                                nsIURI*            aURI,
18193                                uint32_t           aLineNumber)
18194 {
18195   return static_cast<CSSParserImpl*>(mImpl)->
18196     ParseKeyframeRule(aBuffer, aURI, aLineNumber);
18197 }
18198 
18199 bool
18200 nsCSSParser::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
18201                                          nsIURI*            aURI,
18202                                          uint32_t           aLineNumber,
18203                                          InfallibleTArray<float>& aSelectorList)
18204 {
18205   return static_cast<CSSParserImpl*>(mImpl)->
18206     ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber,
18207                                 aSelectorList);
18208 }
18209 
18210 bool
18211 nsCSSParser::EvaluateSupportsDeclaration(const nsAString& aProperty,
18212                                          const nsAString& aValue,
18213                                          nsIURI* aDocURL,
18214                                          nsIURI* aBaseURL,
18215                                          nsIPrincipal* aDocPrincipal)
18216 {
18217   return static_cast<CSSParserImpl*>(mImpl)->
18218     EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL,
18219                                 aDocPrincipal);
18220 }
18221 
18222 bool
18223 nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition,
18224                                        nsIURI* aDocURL,
18225                                        nsIURI* aBaseURL,
18226                                        nsIPrincipal* aDocPrincipal)
18227 {
18228   return static_cast<CSSParserImpl*>(mImpl)->
18229     EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL, aDocPrincipal);
18230 }
18231 
18232 bool
18233 nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue,
18234                                          VariableEnumFunc aFunc,
18235                                          void* aData)
18236 {
18237   return static_cast<CSSParserImpl*>(mImpl)->
18238     EnumerateVariableReferences(aPropertyValue, aFunc, aData);
18239 }
18240 
18241 bool
18242 nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue,
18243                                   const CSSVariableValues* aVariables,
18244                                   nsString& aResult,
18245                                   nsCSSTokenSerializationType& aFirstToken,
18246                                   nsCSSTokenSerializationType& aLastToken)
18247 {
18248   return static_cast<CSSParserImpl*>(mImpl)->
18249     ResolveVariableValue(aPropertyValue, aVariables,
18250                          aResult, aFirstToken, aLastToken);
18251 }
18252 
18253 void
18254 nsCSSParser::ParsePropertyWithVariableReferences(
18255                                             nsCSSPropertyID aPropertyID,
18256                                             nsCSSPropertyID aShorthandPropertyID,
18257                                             const nsAString& aValue,
18258                                             const CSSVariableValues* aVariables,
18259                                             nsRuleData* aRuleData,
18260                                             nsIURI* aDocURL,
18261                                             nsIURI* aBaseURL,
18262                                             nsIPrincipal* aDocPrincipal,
18263                                             CSSStyleSheet* aSheet,
18264                                             uint32_t aLineNumber,
18265                                             uint32_t aLineOffset)
18266 {
18267   static_cast<CSSParserImpl*>(mImpl)->
18268     ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID,
18269                                         aValue, aVariables, aRuleData, aDocURL,
18270                                         aBaseURL, aDocPrincipal, aSheet,
18271                                         aLineNumber, aLineOffset);
18272 }
18273 
18274 bool
18275 nsCSSParser::ParseCounterStyleName(const nsAString& aBuffer,
18276                                    nsIURI* aURL,
18277                                    nsAString& aName)
18278 {
18279   return static_cast<CSSParserImpl*>(mImpl)->
18280     ParseCounterStyleName(aBuffer, aURL, aName);
18281 }
18282 
18283 bool
18284 nsCSSParser::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
18285                                     const nsAString& aBuffer,
18286                                     nsIURI* aSheetURL,
18287                                     nsIURI* aBaseURL,
18288                                     nsIPrincipal* aSheetPrincipal,
18289                                     nsCSSValue& aValue)
18290 {
18291   return static_cast<CSSParserImpl*>(mImpl)->
18292     ParseCounterDescriptor(aDescID, aBuffer,
18293                            aSheetURL, aBaseURL, aSheetPrincipal, aValue);
18294 }
18295 
18296 bool
18297 nsCSSParser::ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
18298                                      const nsAString& aBuffer,
18299                                      nsIURI* aSheetURL,
18300                                      nsIURI* aBaseURL,
18301                                      nsIPrincipal* aSheetPrincipal,
18302                                      nsCSSValue& aValue)
18303 {
18304   return static_cast<CSSParserImpl*>(mImpl)->
18305     ParseFontFaceDescriptor(aDescID, aBuffer,
18306                            aSheetURL, aBaseURL, aSheetPrincipal, aValue);
18307 }
18308 
18309 bool
18310 nsCSSParser::IsValueValidForProperty(const nsCSSPropertyID aPropID,
18311                                      const nsAString&    aPropValue)
18312 {
18313   return static_cast<CSSParserImpl*>(mImpl)->
18314     IsValueValidForProperty(aPropID, aPropValue);
18315 }
18316 
18317 /* static */
18318 uint8_t
18319 nsCSSParser::ControlCharVisibilityDefault()
18320 {
18321   return sControlCharVisibility
18322     ? NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE
18323     : NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN;
18324 }
18325