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