Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=78: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/DebugOnly.h"
12 #include "nsCSSParser.h"
13 #include "nsCSSProps.h"
14 #include "nsCSSKeywords.h"
15 #include "nsCSSScanner.h"
16 #include "mozilla/css/ErrorReporter.h"
17 #include "mozilla/css/Loader.h"
18 #include "mozilla/css/StyleRule.h"
19 #include "mozilla/css/ImportRule.h"
20 #include "nsCSSRules.h"
21 #include "mozilla/css/NameSpaceRule.h"
22 #include "nsTArray.h"
23 #include "nsCSSStyleSheet.h"
24 #include "mozilla/css/Declaration.h"
25 #include "nsStyleConsts.h"
26 #include "nsNetUtil.h"
27 #include "nsCOMPtr.h"
28 #include "nsString.h"
29 #include "nsIAtom.h"
30 #include "nsColor.h"
31 #include "nsCSSPseudoClasses.h"
32 #include "nsCSSPseudoElements.h"
33 #include "nsNameSpaceManager.h"
34 #include "nsXMLNameSpaceMap.h"
35 #include "nsError.h"
36 #include "nsIMediaList.h"
37 #include "nsStyleUtil.h"
38 #include "nsIPrincipal.h"
39 #include "prprf.h"
40 #include "nsContentUtils.h"
41 #include "nsAutoPtr.h"
42 #include "CSSCalc.h"
43 #include "nsMediaFeatures.h"
44 #include "nsLayoutUtils.h"
45 #include "mozilla/Preferences.h"
46 #include "nsRuleData.h"
47 #include "mozilla/CSSVariableValues.h"
48 #include "mozilla/dom/URL.h"
50 using namespace mozilla;
52 typedef nsCSSProps::KTableValue KTableValue;
54 const uint32_t
55 nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
56 #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \
57 stylestruct_, stylestructoffset_, animtype_) \
58 parsevariant_,
59 #include "nsCSSPropList.h"
60 #undef CSS_PROP
61 };
63 // Maximum number of repetitions for the repeat() function
64 // in the grid-template-columns and grid-template-rows properties,
65 // to limit high memory usage from small stylesheets.
66 // Must be a positive integer. Should be large-ish.
67 #define GRID_TEMPLATE_MAX_REPETITIONS 10000
69 // End-of-array marker for mask arguments to ParseBitmaskValues
70 #define MASK_END_VALUE (-1)
72 MOZ_BEGIN_ENUM_CLASS(CSSParseResult, int32_t)
73 // Parsed something successfully:
74 Ok,
75 // Did not find what we were looking for, but did not consume any token:
76 NotFound,
77 // Unexpected token or token value, too late for UngetToken() to be enough:
78 Error
79 MOZ_END_ENUM_CLASS(CSSParseResult)
81 namespace {
83 // Rule processing function
84 typedef void (* RuleAppendFunc) (css::Rule* aRule, void* aData);
85 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer);
86 static void AppendRuleToSheet(css::Rule* aRule, void* aParser);
88 struct CSSParserInputState {
89 nsCSSScannerPosition mPosition;
90 nsCSSToken mToken;
91 bool mHavePushBack;
92 };
94 // Your basic top-down recursive descent style parser
95 // The exposed methods and members of this class are precisely those
96 // needed by nsCSSParser, far below.
97 class CSSParserImpl {
98 public:
99 CSSParserImpl();
100 ~CSSParserImpl();
102 nsresult SetStyleSheet(nsCSSStyleSheet* aSheet);
104 nsresult SetQuirkMode(bool aQuirkMode);
106 nsresult SetChildLoader(mozilla::css::Loader* aChildLoader);
108 // Clears everything set by the above Set*() functions.
109 void Reset();
111 nsresult ParseSheet(const nsAString& aInput,
112 nsIURI* aSheetURI,
113 nsIURI* aBaseURI,
114 nsIPrincipal* aSheetPrincipal,
115 uint32_t aLineNumber,
116 bool aAllowUnsafeRules);
118 nsresult ParseStyleAttribute(const nsAString& aAttributeValue,
119 nsIURI* aDocURL,
120 nsIURI* aBaseURL,
121 nsIPrincipal* aNodePrincipal,
122 css::StyleRule** aResult);
124 nsresult ParseDeclarations(const nsAString& aBuffer,
125 nsIURI* aSheetURL,
126 nsIURI* aBaseURL,
127 nsIPrincipal* aSheetPrincipal,
128 css::Declaration* aDeclaration,
129 bool* aChanged);
131 nsresult ParseRule(const nsAString& aRule,
132 nsIURI* aSheetURL,
133 nsIURI* aBaseURL,
134 nsIPrincipal* aSheetPrincipal,
135 css::Rule** aResult);
137 nsresult ParseProperty(const nsCSSProperty aPropID,
138 const nsAString& aPropValue,
139 nsIURI* aSheetURL,
140 nsIURI* aBaseURL,
141 nsIPrincipal* aSheetPrincipal,
142 css::Declaration* aDeclaration,
143 bool* aChanged,
144 bool aIsImportant,
145 bool aIsSVGMode);
147 void ParseMediaList(const nsSubstring& aBuffer,
148 nsIURI* aURL, // for error reporting
149 uint32_t aLineNumber, // for error reporting
150 nsMediaList* aMediaList,
151 bool aHTMLMode);
153 nsresult ParseVariable(const nsAString& aVariableName,
154 const nsAString& aPropValue,
155 nsIURI* aSheetURL,
156 nsIURI* aBaseURL,
157 nsIPrincipal* aSheetPrincipal,
158 css::Declaration* aDeclaration,
159 bool* aChanged,
160 bool aIsImportant);
162 bool ParseColorString(const nsSubstring& aBuffer,
163 nsIURI* aURL, // for error reporting
164 uint32_t aLineNumber, // for error reporting
165 nsCSSValue& aValue);
167 nsresult ParseSelectorString(const nsSubstring& aSelectorString,
168 nsIURI* aURL, // for error reporting
169 uint32_t aLineNumber, // for error reporting
170 nsCSSSelectorList **aSelectorList);
172 already_AddRefed<nsCSSKeyframeRule>
173 ParseKeyframeRule(const nsSubstring& aBuffer,
174 nsIURI* aURL,
175 uint32_t aLineNumber);
177 bool ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
178 nsIURI* aURL, // for error reporting
179 uint32_t aLineNumber, // for error reporting
180 InfallibleTArray<float>& aSelectorList);
182 bool EvaluateSupportsDeclaration(const nsAString& aProperty,
183 const nsAString& aValue,
184 nsIURI* aDocURL,
185 nsIURI* aBaseURL,
186 nsIPrincipal* aDocPrincipal);
188 bool EvaluateSupportsCondition(const nsAString& aCondition,
189 nsIURI* aDocURL,
190 nsIURI* aBaseURL,
191 nsIPrincipal* aDocPrincipal);
193 typedef nsCSSParser::VariableEnumFunc VariableEnumFunc;
195 /**
196 * Parses a CSS token stream value and invokes a callback function for each
197 * variable reference that is encountered.
198 *
199 * @param aPropertyValue The CSS token stream value.
200 * @param aFunc The callback function to invoke; its parameters are the
201 * variable name found and the aData argument passed in to this function.
202 * @param aData User data to pass in to the callback.
203 * @return Whether aPropertyValue could be parsed as a valid CSS token stream
204 * value (e.g., without syntactic errors in variable references).
205 */
206 bool EnumerateVariableReferences(const nsAString& aPropertyValue,
207 VariableEnumFunc aFunc,
208 void* aData);
210 /**
211 * Parses aPropertyValue as a CSS token stream value and resolves any
212 * variable references using the variables in aVariables.
213 *
214 * @param aPropertyValue The CSS token stream value.
215 * @param aVariables The set of variable values to use when resolving variable
216 * references.
217 * @param aResult Out parameter that gets the resolved value.
218 * @param aFirstToken Out parameter that gets the type of the first token in
219 * aResult.
220 * @param aLastToken Out parameter that gets the type of the last token in
221 * aResult.
222 * @return Whether aResult could be parsed successfully and variable reference
223 * substitution succeeded.
224 */
225 bool ResolveVariableValue(const nsAString& aPropertyValue,
226 const CSSVariableValues* aVariables,
227 nsString& aResult,
228 nsCSSTokenSerializationType& aFirstToken,
229 nsCSSTokenSerializationType& aLastToken);
231 /**
232 * Parses a string as a CSS token stream value for particular property,
233 * resolving any variable references. The parsed property value is stored
234 * in the specified nsRuleData object. If aShorthandPropertyID has a value
235 * other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
236 * otherwise, aPropertyID will be parsed. Either way, only aPropertyID,
237 * a longhand property, will be copied over to the rule data.
238 *
239 * If the property cannot be parsed, it will be treated as if 'initial' or
240 * 'inherit' were specified, for non-inherited and inherited properties
241 * respectively.
242 *
243 * @param aPropertyID The ID of the longhand property whose value is to be
244 * copied to the rule data.
245 * @param aShorthandPropertyID The ID of the shorthand property to be parsed.
246 * If a longhand property is to be parsed, aPropertyID is that property,
247 * and aShorthandPropertyID must be eCSSProperty_UNKNOWN.
248 * @param aValue The CSS token stream value.
249 * @param aVariables The set of variable values to use when resolving variable
250 * references.
251 * @param aRuleData The rule data object into which parsed property value for
252 * aPropertyID will be stored.
253 */
254 void ParsePropertyWithVariableReferences(nsCSSProperty aPropertyID,
255 nsCSSProperty aShorthandPropertyID,
256 const nsAString& aValue,
257 const CSSVariableValues* aVariables,
258 nsRuleData* aRuleData,
259 nsIURI* aDocURL,
260 nsIURI* aBaseURL,
261 nsIPrincipal* aDocPrincipal,
262 nsCSSStyleSheet* aSheet,
263 uint32_t aLineNumber,
264 uint32_t aLineOffset);
266 nsCSSProperty LookupEnabledProperty(const nsAString& aProperty) {
267 static_assert(nsCSSProps::eEnabledForAllContent == 0,
268 "nsCSSProps::eEnabledForAllContent should be zero for "
269 "this bitfield to work");
270 nsCSSProps::EnabledState enabledState = nsCSSProps::eEnabledForAllContent;
271 if (mUnsafeRulesEnabled) {
272 enabledState |= nsCSSProps::eEnabledInUASheets;
273 }
274 if (mIsChromeOrCertifiedApp) {
275 enabledState |= nsCSSProps::eEnabledInChromeOrCertifiedApp;
276 }
277 return nsCSSProps::LookupProperty(aProperty, enabledState);
278 }
280 protected:
281 class nsAutoParseCompoundProperty;
282 friend class nsAutoParseCompoundProperty;
284 class nsAutoFailingSupportsRule;
285 friend class nsAutoFailingSupportsRule;
287 class nsAutoSuppressErrors;
288 friend class nsAutoSuppressErrors;
290 void AppendRule(css::Rule* aRule);
291 friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule
293 /**
294 * This helper class automatically calls SetParsingCompoundProperty in its
295 * constructor and takes care of resetting it to false in its destructor.
296 */
297 class nsAutoParseCompoundProperty {
298 public:
299 nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
300 {
301 NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
302 "already parsing compound property");
303 NS_ASSERTION(aParser, "Null parser?");
304 aParser->SetParsingCompoundProperty(true);
305 }
307 ~nsAutoParseCompoundProperty()
308 {
309 mParser->SetParsingCompoundProperty(false);
310 }
311 private:
312 CSSParserImpl* mParser;
313 };
315 /**
316 * This helper class conditionally sets mInFailingSupportsRule to
317 * true if aCondition = false, and resets it to its original value in its
318 * destructor. If we are already somewhere within a failing @supports
319 * rule, passing in aCondition = true does not change mInFailingSupportsRule.
320 */
321 class nsAutoFailingSupportsRule {
322 public:
323 nsAutoFailingSupportsRule(CSSParserImpl* aParser,
324 bool aCondition)
325 : mParser(aParser),
326 mOriginalValue(aParser->mInFailingSupportsRule)
327 {
328 if (!aCondition) {
329 mParser->mInFailingSupportsRule = true;
330 }
331 }
333 ~nsAutoFailingSupportsRule()
334 {
335 mParser->mInFailingSupportsRule = mOriginalValue;
336 }
338 private:
339 CSSParserImpl* mParser;
340 bool mOriginalValue;
341 };
343 /**
344 * Auto class to set aParser->mSuppressErrors to the specified value
345 * and restore it to its original value later.
346 */
347 class nsAutoSuppressErrors {
348 public:
349 nsAutoSuppressErrors(CSSParserImpl* aParser,
350 bool aSuppressErrors = true)
351 : mParser(aParser),
352 mOriginalValue(aParser->mSuppressErrors)
353 {
354 mParser->mSuppressErrors = aSuppressErrors;
355 }
357 ~nsAutoSuppressErrors()
358 {
359 mParser->mSuppressErrors = mOriginalValue;
360 }
362 private:
363 CSSParserImpl* mParser;
364 bool mOriginalValue;
365 };
367 // the caller must hold on to aString until parsing is done
368 void InitScanner(nsCSSScanner& aScanner,
369 css::ErrorReporter& aReporter,
370 nsIURI* aSheetURI, nsIURI* aBaseURI,
371 nsIPrincipal* aSheetPrincipal);
372 void ReleaseScanner(void);
373 bool IsSVGMode() const {
374 return mScanner->IsSVGMode();
375 }
377 /**
378 * Saves the current input state, which includes any currently pushed
379 * back token, and the current position of the scanner.
380 */
381 void SaveInputState(CSSParserInputState& aState);
383 /**
384 * Restores the saved input state by pushing back any saved pushback
385 * token and calling RestoreSavedPosition on the scanner.
386 */
387 void RestoreSavedInputState(const CSSParserInputState& aState);
389 bool GetToken(bool aSkipWS);
390 void UngetToken();
391 bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum);
393 bool ExpectSymbol(char16_t aSymbol, bool aSkipWS);
394 bool ExpectEndProperty();
395 bool CheckEndProperty();
396 nsSubstring* NextIdent();
398 // returns true when the stop symbol is found, and false for EOF
399 bool SkipUntil(char16_t aStopSymbol);
400 void SkipUntilOneOf(const char16_t* aStopSymbolChars);
401 // For each character in aStopSymbolChars from the end of the array
402 // to the start, calls SkipUntil with that character.
403 typedef nsAutoTArray<char16_t, 16> StopSymbolCharStack;
404 void SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars);
405 // returns true if the stop symbol or EOF is found, and false for an
406 // unexpected ')', ']' or '}'; this not safe to call outside variable
407 // resolution, as it doesn't handle mismatched content
408 bool SkipBalancedContentUntil(char16_t aStopSymbol);
410 void SkipRuleSet(bool aInsideBraces);
411 bool SkipAtRule(bool aInsideBlock);
412 bool SkipDeclaration(bool aCheckForBraces);
414 void PushGroup(css::GroupRule* aRule);
415 void PopGroup();
417 bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData,
418 bool aInsideBraces = false);
419 bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData,
420 bool aInAtRule);
421 bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
422 bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
423 bool ParseURLOrString(nsString& aURL);
424 bool GatherMedia(nsMediaList* aMedia,
425 bool aInAtRule);
426 bool ParseMediaQuery(bool aInAtRule, nsMediaQuery **aQuery,
427 bool *aHitStop);
428 bool ParseMediaQueryExpression(nsMediaQuery* aQuery);
429 void ProcessImport(const nsString& aURLSpec,
430 nsMediaList* aMedia,
431 RuleAppendFunc aAppendFunc,
432 void* aProcessData);
433 bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc,
434 void* aProcessData);
435 bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
436 bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
437 bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
438 void ProcessNameSpace(const nsString& aPrefix,
439 const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
440 void* aProcessData);
442 bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
443 bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
444 void* aProcessData);
445 bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule);
446 bool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
447 bool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
448 nsCSSValue& aValue);
450 bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
451 bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData);
452 already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule();
453 bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList);
455 bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData);
456 bool ParseSupportsCondition(bool& aConditionMet);
457 bool ParseSupportsConditionNegation(bool& aConditionMet);
458 bool ParseSupportsConditionInParens(bool& aConditionMet);
459 bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet);
460 bool ParseSupportsConditionTerms(bool& aConditionMet);
461 enum SupportsConditionTermOperator { eAnd, eOr };
462 bool ParseSupportsConditionTermsAfterOperator(
463 bool& aConditionMet,
464 SupportsConditionTermOperator aOperator);
466 /**
467 * Parses the current input stream for a CSS token stream value and resolves
468 * any variable references using the variables in aVariables.
469 *
470 * @param aVariables The set of variable values to use when resolving variable
471 * references.
472 * @param aResult Out parameter that, if the function returns true, will be
473 * replaced with the resolved value.
474 * @return Whether aResult could be parsed successfully and variable reference
475 * substitution succeeded.
476 */
477 bool ResolveValueWithVariableReferences(
478 const CSSVariableValues* aVariables,
479 nsString& aResult,
480 nsCSSTokenSerializationType& aResultFirstToken,
481 nsCSSTokenSerializationType& aResultLastToken);
482 // Helper function for ResolveValueWithVariableReferences.
483 bool ResolveValueWithVariableReferencesRec(
484 nsString& aResult,
485 nsCSSTokenSerializationType& aResultFirstToken,
486 nsCSSTokenSerializationType& aResultLastToken,
487 const CSSVariableValues* aVariables);
489 enum nsSelectorParsingStatus {
490 // we have parsed a selector and we saw a token that cannot be
491 // part of a selector:
492 eSelectorParsingStatus_Done,
493 // we should continue parsing the selector:
494 eSelectorParsingStatus_Continue,
495 // we saw an unexpected token or token value,
496 // or we saw end-of-file with an unfinished selector:
497 eSelectorParsingStatus_Error
498 };
499 nsSelectorParsingStatus ParseIDSelector(int32_t& aDataMask,
500 nsCSSSelector& aSelector);
502 nsSelectorParsingStatus ParseClassSelector(int32_t& aDataMask,
503 nsCSSSelector& aSelector);
505 // aPseudoElement and aPseudoElementArgs are the location where
506 // pseudo-elements (as opposed to pseudo-classes) are stored;
507 // pseudo-classes are stored on aSelector. aPseudoElement and
508 // aPseudoElementArgs must be non-null iff !aIsNegated.
509 nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask,
510 nsCSSSelector& aSelector,
511 bool aIsNegated,
512 nsIAtom** aPseudoElement,
513 nsAtomList** aPseudoElementArgs,
514 nsCSSPseudoElements::Type* aPseudoElementType);
516 nsSelectorParsingStatus ParseAttributeSelector(int32_t& aDataMask,
517 nsCSSSelector& aSelector);
519 nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask,
520 nsCSSSelector& aSelector,
521 bool aIsNegated);
523 nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
524 nsCSSPseudoClasses::Type aType);
526 nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
527 nsCSSPseudoClasses::Type aType);
529 nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
530 nsCSSPseudoClasses::Type aType);
532 nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask,
533 nsCSSSelector& aSelector);
535 // If aStopChar is non-zero, the selector list is done when we hit
536 // aStopChar. Otherwise, it's done when we hit EOF.
537 bool ParseSelectorList(nsCSSSelectorList*& aListHead,
538 char16_t aStopChar);
539 bool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
540 bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator);
542 enum {
543 eParseDeclaration_InBraces = 1 << 0,
544 eParseDeclaration_AllowImportant = 1 << 1
545 };
546 enum nsCSSContextType {
547 eCSSContext_General,
548 eCSSContext_Page
549 };
551 css::Declaration* ParseDeclarationBlock(uint32_t aFlags,
552 nsCSSContextType aContext = eCSSContext_General);
553 bool ParseDeclaration(css::Declaration* aDeclaration,
554 uint32_t aFlags,
555 bool aMustCallValueAppended,
556 bool* aChanged,
557 nsCSSContextType aContext = eCSSContext_General);
559 bool ParseProperty(nsCSSProperty aPropID);
560 bool ParsePropertyByFunction(nsCSSProperty aPropID);
561 bool ParseSingleValueProperty(nsCSSValue& aValue,
562 nsCSSProperty aPropID);
564 enum PriorityParsingStatus {
565 ePriority_None,
566 ePriority_Important,
567 ePriority_Error
568 };
569 PriorityParsingStatus ParsePriority();
571 #ifdef MOZ_XUL
572 bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs);
573 #endif
575 void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties);
577 // Property specific parsing routines
578 bool ParseBackground();
580 struct BackgroundParseState {
581 nsCSSValue& mColor;
582 nsCSSValueList* mImage;
583 nsCSSValuePairList* mRepeat;
584 nsCSSValueList* mAttachment;
585 nsCSSValueList* mClip;
586 nsCSSValueList* mOrigin;
587 nsCSSValueList* mPosition;
588 nsCSSValuePairList* mSize;
589 BackgroundParseState(
590 nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
591 nsCSSValueList* aAttachment, nsCSSValueList* aClip,
592 nsCSSValueList* aOrigin, nsCSSValueList* aPosition,
593 nsCSSValuePairList* aSize) :
594 mColor(aColor), mImage(aImage), mRepeat(aRepeat),
595 mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
596 mPosition(aPosition), mSize(aSize) {};
597 };
599 bool ParseBackgroundItem(BackgroundParseState& aState);
601 bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id
602 bool ParseBackgroundRepeat();
603 bool ParseBackgroundRepeatValues(nsCSSValuePair& aValue);
604 bool ParseBackgroundPosition();
606 // ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
607 // which is still used by some properties. See ParseBackgroundPositionValues
608 // for the css3-background syntax.
609 bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit,
610 bool aAllowExplicitCenter = true); // deprecated
611 bool ParseBackgroundPositionValues(nsCSSValue& aOut, bool aAcceptsInherit);
613 bool ParseBackgroundSize();
614 bool ParseBackgroundSizeValues(nsCSSValuePair& aOut);
615 bool ParseBorderColor();
616 bool ParseBorderColors(nsCSSProperty aProperty);
617 void SetBorderImageInitialValues();
618 bool ParseBorderImageRepeat(bool aAcceptsInherit);
619 // If ParseBorderImageSlice returns false, aConsumedTokens indicates
620 // whether or not any tokens were consumed (in other words, was the property
621 // in error or just not present). If ParseBorderImageSlice returns true
622 // aConsumedTokens is always true.
623 bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens);
624 bool ParseBorderImageWidth(bool aAcceptsInherit);
625 bool ParseBorderImageOutset(bool aAcceptsInherit);
626 bool ParseBorderImage();
627 bool ParseBorderSpacing();
628 bool ParseBorderSide(const nsCSSProperty aPropIDs[],
629 bool aSetAllSides);
630 bool ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
631 int32_t aSourceType);
632 bool ParseBorderStyle();
633 bool ParseBorderWidth();
635 bool ParseCalc(nsCSSValue &aValue, int32_t aVariantMask);
636 bool ParseCalcAdditiveExpression(nsCSSValue& aValue,
637 int32_t& aVariantMask);
638 bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
639 int32_t& aVariantMask,
640 bool *aHadFinalWS);
641 bool ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask);
642 bool RequireWhitespace();
644 // For "flex" shorthand property, defined in CSS Flexbox spec
645 bool ParseFlex();
646 // For "flex-flow" shorthand property, defined in CSS Flexbox spec
647 bool ParseFlexFlow();
649 // CSS Grid
650 bool ParseGridAutoFlow();
652 // Parse a <line-names> expression.
653 // If successful, either leave aValue untouched,
654 // to indicate that we parsed the empty list,
655 // or set it to a eCSSUnit_List of eCSSUnit_Ident.
656 //
657 // To parse an optional <line-names> (ie. if not finding an open paren
658 // is considered the same as an empty list),
659 // treat CSSParseResult::NotFound the same as CSSParseResult::Ok.
660 //
661 // If aValue is already a eCSSUnit_List, append to that list.
662 CSSParseResult ParseGridLineNames(nsCSSValue& aValue);
663 bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr);
664 bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue);
665 bool ParseGridTrackBreadth(nsCSSValue& aValue);
666 CSSParseResult ParseGridTrackSize(nsCSSValue& aValue);
667 bool ParseGridAutoColumnsRows(nsCSSProperty aPropID);
668 bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr);
670 // Assuming a [ <line-names>? ] has already been parsed,
671 // parse the rest of a <track-list>.
672 //
673 // This exists because [ <line-names>? ] is ambiguous in the
674 // 'grid-template' shorthand: it can be either the start of a <track-list>,
675 // or of the intertwined syntax that sets both
676 // grid-template-rows and grid-template-areas.
677 //
678 // On success, |aValue| will be a list of odd length >= 3,
679 // starting with a <line-names> (which is itself a list)
680 // and alternating between that and <track-size>.
681 bool ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
682 const nsCSSValue& aFirstLineNames);
683 bool ParseGridTemplateColumnsRows(nsCSSProperty aPropID);
685 // |aAreaIndices| is a lookup table to help us parse faster,
686 // mapping area names to indices in |aResult.mNamedAreas|.
687 bool ParseGridTemplateAreasLine(const nsAutoString& aInput,
688 css::GridTemplateAreasValue* aResult,
689 nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices);
690 bool ParseGridTemplateAreas();
691 bool ParseGridTemplate();
692 bool ParseGridTemplateAfterSlash(bool aColumnsIsTrackList);
693 bool ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames);
694 bool ParseGrid();
695 bool ParseGridShorthandAutoProps();
696 bool ParseGridLine(nsCSSValue& aValue);
697 bool ParseGridAutoPosition();
698 bool ParseGridColumnRowStartEnd(nsCSSProperty aPropID);
699 bool ParseGridColumnRow(nsCSSProperty aStartPropID,
700 nsCSSProperty aEndPropID);
701 bool ParseGridArea();
703 // for 'clip' and '-moz-image-region'
704 bool ParseRect(nsCSSProperty aPropID);
705 bool ParseColumns();
706 bool ParseContent();
707 bool ParseCounterData(nsCSSProperty aPropID);
708 bool ParseCursor();
709 bool ParseFont();
710 bool ParseFontSynthesis(nsCSSValue& aValue);
711 bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue);
712 bool ParseFontVariantAlternates(nsCSSValue& aValue);
713 bool ParseBitmaskValues(nsCSSValue& aValue,
714 const KTableValue aKeywordTable[],
715 const int32_t aMasks[]);
716 bool ParseFontVariantEastAsian(nsCSSValue& aValue);
717 bool ParseFontVariantLigatures(nsCSSValue& aValue);
718 bool ParseFontVariantNumeric(nsCSSValue& aValue);
719 bool ParseFontWeight(nsCSSValue& aValue);
720 bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword);
721 bool ParseFamily(nsCSSValue& aValue);
722 bool ParseFontFeatureSettings(nsCSSValue& aValue);
723 bool ParseFontSrc(nsCSSValue& aValue);
724 bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
725 bool ParseFontRanges(nsCSSValue& aValue);
726 bool ParseListStyle();
727 bool ParseMargin();
728 bool ParseMarks(nsCSSValue& aValue);
729 bool ParseTransform(bool aIsPrefixed);
730 bool ParseOutline();
731 bool ParseOverflow();
732 bool ParsePadding();
733 bool ParseQuotes();
734 bool ParseSize();
735 bool ParseTextAlign(nsCSSValue& aValue,
736 const KTableValue aTable[]);
737 bool ParseTextAlign(nsCSSValue& aValue);
738 bool ParseTextAlignLast(nsCSSValue& aValue);
739 bool ParseTextDecoration();
740 bool ParseTextDecorationLine(nsCSSValue& aValue);
741 bool ParseTextCombineUpright(nsCSSValue& aValue);
742 bool ParseTextOverflow(nsCSSValue& aValue);
743 bool ParseTouchAction(nsCSSValue& aValue);
745 bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow);
746 bool ParseShadowList(nsCSSProperty aProperty);
747 bool ParseTransitionProperty();
748 bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
749 bool ParseTransitionTimingFunctionValueComponent(float& aComponent,
750 char aStop,
751 bool aCheckRange);
752 bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
753 enum ParseAnimationOrTransitionShorthandResult {
754 eParseAnimationOrTransitionShorthand_Values,
755 eParseAnimationOrTransitionShorthand_Inherit,
756 eParseAnimationOrTransitionShorthand_Error
757 };
758 ParseAnimationOrTransitionShorthandResult
759 ParseAnimationOrTransitionShorthand(const nsCSSProperty* aProperties,
760 const nsCSSValue* aInitialValues,
761 nsCSSValue* aValues,
762 size_t aNumProperties);
763 bool ParseTransition();
764 bool ParseAnimation();
765 bool ParseWillChange();
767 bool ParsePaint(nsCSSProperty aPropID);
768 bool ParseDasharray();
769 bool ParseMarker();
770 bool ParsePaintOrder();
771 bool ParseAll();
773 /**
774 * Parses a variable value from a custom property declaration.
775 *
776 * @param aType Out parameter into which will be stored the type of variable
777 * value, indicating whether the parsed value was a token stream or one of
778 * the CSS-wide keywords.
779 * @param aValue Out parameter into which will be stored the token stream
780 * as a string, if the parsed custom property did take a token stream.
781 * @return Whether parsing succeeded.
782 */
783 bool ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
784 nsString& aValue);
786 /**
787 * Parses a CSS variable value. This could be 'initial', 'inherit', 'unset'
788 * or a token stream, which may or may not include variable references.
789 *
790 * @param aType Out parameter into which the type of the variable value
791 * will be stored.
792 * @param aDropBackslash Out parameter indicating whether during variable
793 * value parsing there was a trailing backslash before EOF that must
794 * be dropped when serializing the variable value.
795 * @param aImpliedCharacters Out parameter appended to which will be any
796 * characters that were implied when encountering EOF and which
797 * must be included at the end of the serialized variable value.
798 * @param aFunc A callback function to invoke when a variable reference
799 * is encountered. May be null. Arguments are the variable name
800 * and the aData argument passed in to this function.
801 * @param User data to pass in to the callback.
802 * @return Whether parsing succeeded.
803 */
804 bool ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
805 bool* aDropBackslash,
806 nsString& aImpliedCharacters,
807 void (*aFunc)(const nsAString&, void*),
808 void* aData);
810 /**
811 * Returns whether the scanner dropped a backslash just before EOF.
812 */
813 bool BackslashDropped();
815 /**
816 * Calls AppendImpliedEOFCharacters on mScanner.
817 */
818 void AppendImpliedEOFCharacters(nsAString& aResult);
820 // Reused utility parsing routines
821 void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
822 bool ParseBoxProperties(const nsCSSProperty aPropIDs[]);
823 bool ParseGroupedBoxProperty(int32_t aVariantMask,
824 nsCSSValue& aValue);
825 bool ParseDirectionalBoxProperty(nsCSSProperty aProperty,
826 int32_t aSourceType);
827 bool ParseBoxCornerRadius(const nsCSSProperty aPropID);
828 bool ParseBoxCornerRadii(const nsCSSProperty aPropIDs[]);
829 int32_t ParseChoice(nsCSSValue aValues[],
830 const nsCSSProperty aPropIDs[], int32_t aNumIDs);
831 bool ParseColor(nsCSSValue& aValue);
832 bool ParseNumberColorComponent(uint8_t& aComponent, char aStop);
833 bool ParsePercentageColorComponent(float& aComponent, char aStop);
834 // ParseHSLColor parses everything starting with the opening '('
835 // up through and including the aStop char.
836 bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
837 char aStop);
838 // ParseColorOpacity will enforce that the color ends with a ')'
839 // after the opacity
840 bool ParseColorOpacity(uint8_t& aOpacity);
841 bool ParseColorOpacity(float& aOpacity);
842 bool ParseEnum(nsCSSValue& aValue,
843 const KTableValue aKeywordTable[]);
844 bool ParseVariant(nsCSSValue& aValue,
845 int32_t aVariantMask,
846 const KTableValue aKeywordTable[]);
847 bool ParseNonNegativeVariant(nsCSSValue& aValue,
848 int32_t aVariantMask,
849 const KTableValue aKeywordTable[]);
850 bool ParseOneOrLargerVariant(nsCSSValue& aValue,
851 int32_t aVariantMask,
852 const KTableValue aKeywordTable[]);
854 // http://dev.w3.org/csswg/css-values/#custom-idents
855 // Parse an identifier that is none of:
856 // * a CSS-wide keyword
857 // * "default"
858 // * a keyword in |aExcludedKeywords|
859 // * a keyword in |aPropertyKTable|
860 //
861 // |aExcludedKeywords| is an array of nsCSSKeyword
862 // that ends with a eCSSKeyword_UNKNOWN marker.
863 //
864 // |aPropertyKTable| can be used if some of the keywords to exclude
865 // also appear in an existing nsCSSProps::KTableValue,
866 // to avoid duplicating them.
867 bool ParseCustomIdent(nsCSSValue& aValue,
868 const nsAutoString& aIdentValue,
869 const nsCSSKeyword aExcludedKeywords[] = nullptr,
870 const nsCSSProps::KTableValue aPropertyKTable[] = nullptr);
871 bool ParseCounter(nsCSSValue& aValue);
872 bool ParseAttr(nsCSSValue& aValue);
873 bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL);
874 bool TranslateDimension(nsCSSValue& aValue, int32_t aVariantMask,
875 float aNumber, const nsString& aUnit);
876 bool ParseImageOrientation(nsCSSValue& aAngle);
877 bool ParseImageRect(nsCSSValue& aImage);
878 bool ParseElement(nsCSSValue& aValue);
879 bool ParseColorStop(nsCSSValueGradient* aGradient);
880 bool ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating,
881 bool aIsLegacy);
882 bool ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating,
883 bool aIsLegacy);
884 bool IsLegacyGradientLine(const nsCSSTokenType& aType,
885 const nsString& aId);
886 bool ParseGradientColorStops(nsCSSValueGradient* aGradient,
887 nsCSSValue& aValue);
889 void SetParsingCompoundProperty(bool aBool) {
890 mParsingCompoundProperty = aBool;
891 }
892 bool IsParsingCompoundProperty(void) const {
893 return mParsingCompoundProperty;
894 }
896 /* Functions for transform Parsing */
897 bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue);
898 bool ParseFunction(nsCSSKeyword aFunction, const int32_t aAllowedTypes[],
899 int32_t aVariantMaskAll, uint16_t aMinElems,
900 uint16_t aMaxElems, nsCSSValue &aValue);
901 bool ParseFunctionInternals(const int32_t aVariantMask[],
902 int32_t aVariantMaskAll,
903 uint16_t aMinElems,
904 uint16_t aMaxElems,
905 InfallibleTArray<nsCSSValue>& aOutput);
907 /* Functions for transform-origin/perspective-origin Parsing */
908 bool ParseTransformOrigin(bool aPerspective);
910 /* Functions for filter parsing */
911 bool ParseFilter();
912 bool ParseSingleFilter(nsCSSValue* aValue);
913 bool ParseDropShadow(nsCSSValue* aValue);
915 /* Find and return the namespace ID associated with aPrefix.
916 If aPrefix has not been declared in an @namespace rule, returns
917 kNameSpaceID_Unknown. */
918 int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
920 /* Find the correct default namespace, and set it on aSelector. */
921 void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
923 // Current token. The value is valid after calling GetToken and invalidated
924 // by UngetToken.
925 nsCSSToken mToken;
927 // Our scanner.
928 nsCSSScanner* mScanner;
930 // Our error reporter.
931 css::ErrorReporter* mReporter;
933 // The URI to be used as a base for relative URIs.
934 nsCOMPtr<nsIURI> mBaseURI;
936 // The URI to be used as an HTTP "Referer" and for error reporting.
937 nsCOMPtr<nsIURI> mSheetURI;
939 // The principal of the sheet involved
940 nsCOMPtr<nsIPrincipal> mSheetPrincipal;
942 // The sheet we're parsing into
943 nsRefPtr<nsCSSStyleSheet> mSheet;
945 // Used for @import rules
946 mozilla::css::Loader* mChildLoader; // not ref counted, it owns us
948 // Sheet section we're in. This is used to enforce correct ordering of the
949 // various rule types (eg the fact that a @charset rule must come before
950 // anything else). Note that there are checks of similar things in various
951 // places in nsCSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
952 enum nsCSSSection {
953 eCSSSection_Charset,
954 eCSSSection_Import,
955 eCSSSection_NameSpace,
956 eCSSSection_General
957 };
958 nsCSSSection mSection;
960 nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it
962 // After an UngetToken is done this flag is true. The next call to
963 // GetToken clears the flag.
964 bool mHavePushBack : 1;
966 // True if we are in quirks mode; false in standards or almost standards mode
967 bool mNavQuirkMode : 1;
969 // True when the hashless color quirk applies.
970 bool mHashlessColorQuirk : 1;
972 // True when the unitless length quirk applies.
973 bool mUnitlessLengthQuirk : 1;
975 // True if unsafe rules should be allowed
976 bool mUnsafeRulesEnabled : 1;
978 // True if we are in parsing rules for Chrome or Certified App content,
979 // in which case CSS properties with the
980 // CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP
981 // flag should be allowed.
982 bool mIsChromeOrCertifiedApp : 1;
984 // True if viewport units should be allowed.
985 bool mViewportUnitsEnabled : 1;
987 // True for parsing media lists for HTML attributes, where we have to
988 // ignore CSS comments.
989 bool mHTMLMediaMode : 1;
991 // This flag is set when parsing a non-box shorthand; it's used to not apply
992 // some quirks during shorthand parsing
993 bool mParsingCompoundProperty : 1;
995 // True if we are in the middle of parsing an @supports condition.
996 // This is used to avoid recording the input stream when variable references
997 // are encountered in a property declaration in the @supports condition.
998 bool mInSupportsCondition : 1;
1000 // True if we are somewhere within a @supports rule whose condition is
1001 // false.
1002 bool mInFailingSupportsRule : 1;
1004 // True if we will suppress all parse errors (except unexpected EOFs).
1005 // This is used to prevent errors for declarations inside a failing
1006 // @supports rule.
1007 bool mSuppressErrors : 1;
1009 // Stack of rule groups; used for @media and such.
1010 InfallibleTArray<nsRefPtr<css::GroupRule> > mGroupStack;
1012 // During the parsing of a property (which may be a shorthand), the data
1013 // are stored in |mTempData|. (It is needed to ensure that parser
1014 // errors cause the data to be ignored, and to ensure that a
1015 // non-'!important' declaration does not override an '!important'
1016 // one.)
1017 nsCSSExpandedDataBlock mTempData;
1019 // All data from successfully parsed properties are placed into |mData|.
1020 nsCSSExpandedDataBlock mData;
1022 public:
1023 // Used from nsCSSParser constructors and destructors
1024 CSSParserImpl* mNextFree;
1025 };
1027 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer)
1028 {
1029 css::Rule **pointer = static_cast<css::Rule**>(aPointer);
1030 NS_ADDREF(*pointer = aRule);
1031 }
1033 static void AppendRuleToSheet(css::Rule* aRule, void* aParser)
1034 {
1035 CSSParserImpl* parser = (CSSParserImpl*) aParser;
1036 parser->AppendRule(aRule);
1037 }
1039 #define REPORT_UNEXPECTED(msg_) \
1040 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_); }
1042 #define REPORT_UNEXPECTED_P(msg_, param_) \
1043 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_); }
1045 #define REPORT_UNEXPECTED_TOKEN(msg_) \
1046 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken); }
1048 #define REPORT_UNEXPECTED_TOKEN_CHAR(msg_, ch_) \
1049 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken, ch_); }
1051 #define REPORT_UNEXPECTED_EOF(lf_) \
1052 mReporter->ReportUnexpectedEOF(#lf_)
1054 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
1055 mReporter->ReportUnexpectedEOF(ch_)
1057 #define OUTPUT_ERROR() \
1058 mReporter->OutputError()
1060 #define OUTPUT_ERROR_WITH_POSITION(linenum_, lineoff_) \
1061 mReporter->OutputError(linenum_, lineoff_)
1063 #define CLEAR_ERROR() \
1064 mReporter->ClearError()
1066 CSSParserImpl::CSSParserImpl()
1067 : mToken(),
1068 mScanner(nullptr),
1069 mReporter(nullptr),
1070 mChildLoader(nullptr),
1071 mSection(eCSSSection_Charset),
1072 mNameSpaceMap(nullptr),
1073 mHavePushBack(false),
1074 mNavQuirkMode(false),
1075 mHashlessColorQuirk(false),
1076 mUnitlessLengthQuirk(false),
1077 mUnsafeRulesEnabled(false),
1078 mIsChromeOrCertifiedApp(false),
1079 mViewportUnitsEnabled(true),
1080 mHTMLMediaMode(false),
1081 mParsingCompoundProperty(false),
1082 mInSupportsCondition(false),
1083 mInFailingSupportsRule(false),
1084 mSuppressErrors(false),
1085 mNextFree(nullptr)
1086 {
1087 }
1089 CSSParserImpl::~CSSParserImpl()
1090 {
1091 mData.AssertInitialState();
1092 mTempData.AssertInitialState();
1093 }
1095 nsresult
1096 CSSParserImpl::SetStyleSheet(nsCSSStyleSheet* aSheet)
1097 {
1098 if (aSheet != mSheet) {
1099 // Switch to using the new sheet, if any
1100 mGroupStack.Clear();
1101 mSheet = aSheet;
1102 if (mSheet) {
1103 mNameSpaceMap = mSheet->GetNameSpaceMap();
1104 } else {
1105 mNameSpaceMap = nullptr;
1106 }
1107 } else if (mSheet) {
1108 mNameSpaceMap = mSheet->GetNameSpaceMap();
1109 }
1111 return NS_OK;
1112 }
1114 nsresult
1115 CSSParserImpl::SetQuirkMode(bool aQuirkMode)
1116 {
1117 mNavQuirkMode = aQuirkMode;
1118 return NS_OK;
1119 }
1121 nsresult
1122 CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader)
1123 {
1124 mChildLoader = aChildLoader; // not ref counted, it owns us
1125 return NS_OK;
1126 }
1128 void
1129 CSSParserImpl::Reset()
1130 {
1131 NS_ASSERTION(!mScanner, "resetting with scanner active");
1132 SetStyleSheet(nullptr);
1133 SetQuirkMode(false);
1134 SetChildLoader(nullptr);
1135 }
1137 void
1138 CSSParserImpl::InitScanner(nsCSSScanner& aScanner,
1139 css::ErrorReporter& aReporter,
1140 nsIURI* aSheetURI, nsIURI* aBaseURI,
1141 nsIPrincipal* aSheetPrincipal)
1142 {
1143 NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
1144 NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
1145 NS_PRECONDITION(!mScanner, "already have scanner");
1147 mScanner = &aScanner;
1148 mReporter = &aReporter;
1149 mScanner->SetErrorReporter(mReporter);
1151 mBaseURI = aBaseURI;
1152 mSheetURI = aSheetURI;
1153 mSheetPrincipal = aSheetPrincipal;
1154 mHavePushBack = false;
1155 }
1157 void
1158 CSSParserImpl::ReleaseScanner()
1159 {
1160 mScanner = nullptr;
1161 mReporter = nullptr;
1162 mBaseURI = nullptr;
1163 mSheetURI = nullptr;
1164 mSheetPrincipal = nullptr;
1165 }
1167 nsresult
1168 CSSParserImpl::ParseSheet(const nsAString& aInput,
1169 nsIURI* aSheetURI,
1170 nsIURI* aBaseURI,
1171 nsIPrincipal* aSheetPrincipal,
1172 uint32_t aLineNumber,
1173 bool aAllowUnsafeRules)
1174 {
1175 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1176 NS_PRECONDITION(aBaseURI, "need base URI");
1177 NS_PRECONDITION(aSheetURI, "need sheet URI");
1178 NS_PRECONDITION(mSheet, "Must have sheet to parse into");
1179 NS_ENSURE_STATE(mSheet);
1181 #ifdef DEBUG
1182 nsIURI* uri = mSheet->GetSheetURI();
1183 bool equal;
1184 NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
1185 "Sheet URI does not match passed URI");
1186 NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
1187 &equal)) &&
1188 equal,
1189 "Sheet principal does not match passed principal");
1190 #endif
1192 nsCSSScanner scanner(aInput, aLineNumber);
1193 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1194 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1196 int32_t ruleCount = mSheet->StyleRuleCount();
1197 if (0 < ruleCount) {
1198 const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1);
1199 if (lastRule) {
1200 switch (lastRule->GetType()) {
1201 case css::Rule::CHARSET_RULE:
1202 case css::Rule::IMPORT_RULE:
1203 mSection = eCSSSection_Import;
1204 break;
1205 case css::Rule::NAMESPACE_RULE:
1206 mSection = eCSSSection_NameSpace;
1207 break;
1208 default:
1209 mSection = eCSSSection_General;
1210 break;
1211 }
1212 }
1213 }
1214 else {
1215 mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
1216 }
1218 mUnsafeRulesEnabled = aAllowUnsafeRules;
1219 mIsChromeOrCertifiedApp =
1220 dom::IsChromeURI(aSheetURI) ||
1221 aSheetPrincipal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
1223 nsCSSToken* tk = &mToken;
1224 for (;;) {
1225 // Get next non-whitespace token
1226 if (!GetToken(true)) {
1227 OUTPUT_ERROR();
1228 break;
1229 }
1230 if (eCSSToken_HTMLComment == tk->mType) {
1231 continue; // legal here only
1232 }
1233 if (eCSSToken_AtKeyword == tk->mType) {
1234 ParseAtRule(AppendRuleToSheet, this, false);
1235 continue;
1236 }
1237 UngetToken();
1238 if (ParseRuleSet(AppendRuleToSheet, this)) {
1239 mSection = eCSSSection_General;
1240 }
1241 }
1242 ReleaseScanner();
1244 mUnsafeRulesEnabled = false;
1245 mIsChromeOrCertifiedApp = false;
1247 // XXX check for low level errors
1248 return NS_OK;
1249 }
1251 /**
1252 * Determines whether the identifier contained in the given string is a
1253 * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
1254 */
1255 static bool
1256 NonMozillaVendorIdentifier(const nsAString& ident)
1257 {
1258 return (ident.First() == char16_t('-') &&
1259 !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
1260 ident.First() == char16_t('_');
1262 }
1264 nsresult
1265 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
1266 nsIURI* aDocURI,
1267 nsIURI* aBaseURI,
1268 nsIPrincipal* aNodePrincipal,
1269 css::StyleRule** aResult)
1270 {
1271 NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
1272 NS_PRECONDITION(aBaseURI, "need base URI");
1274 // XXX line number?
1275 nsCSSScanner scanner(aAttributeValue, 0);
1276 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI);
1277 InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal);
1279 mSection = eCSSSection_General;
1281 uint32_t parseFlags = eParseDeclaration_AllowImportant;
1283 css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
1284 if (declaration) {
1285 // Create a style rule for the declaration
1286 NS_ADDREF(*aResult = new css::StyleRule(nullptr, declaration));
1287 } else {
1288 *aResult = nullptr;
1289 }
1291 ReleaseScanner();
1293 // XXX check for low level errors
1294 return NS_OK;
1295 }
1297 nsresult
1298 CSSParserImpl::ParseDeclarations(const nsAString& aBuffer,
1299 nsIURI* aSheetURI,
1300 nsIURI* aBaseURI,
1301 nsIPrincipal* aSheetPrincipal,
1302 css::Declaration* aDeclaration,
1303 bool* aChanged)
1304 {
1305 *aChanged = false;
1307 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1309 nsCSSScanner scanner(aBuffer, 0);
1310 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1311 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1313 mSection = eCSSSection_General;
1315 mData.AssertInitialState();
1316 aDeclaration->ClearData();
1317 // We could check if it was already empty, but...
1318 *aChanged = true;
1320 for (;;) {
1321 // If we cleared the old decl, then we want to be calling
1322 // ValueAppended as we parse.
1323 if (!ParseDeclaration(aDeclaration, eParseDeclaration_AllowImportant,
1324 true, aChanged)) {
1325 if (!SkipDeclaration(false)) {
1326 break;
1327 }
1328 }
1329 }
1331 aDeclaration->CompressFrom(&mData);
1332 ReleaseScanner();
1333 return NS_OK;
1334 }
1336 nsresult
1337 CSSParserImpl::ParseRule(const nsAString& aRule,
1338 nsIURI* aSheetURI,
1339 nsIURI* aBaseURI,
1340 nsIPrincipal* aSheetPrincipal,
1341 css::Rule** aResult)
1342 {
1343 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1344 NS_PRECONDITION(aBaseURI, "need base URI");
1346 *aResult = nullptr;
1348 nsCSSScanner scanner(aRule, 0);
1349 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1350 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1352 mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
1354 nsCSSToken* tk = &mToken;
1355 // Get first non-whitespace token
1356 nsresult rv = NS_OK;
1357 if (!GetToken(true)) {
1358 REPORT_UNEXPECTED(PEParseRuleWSOnly);
1359 OUTPUT_ERROR();
1360 rv = NS_ERROR_DOM_SYNTAX_ERR;
1361 } else {
1362 if (eCSSToken_AtKeyword == tk->mType) {
1363 // FIXME: perhaps aInsideBlock should be true when we are?
1364 ParseAtRule(AssignRuleToPointer, aResult, false);
1365 } else {
1366 UngetToken();
1367 ParseRuleSet(AssignRuleToPointer, aResult);
1368 }
1370 if (*aResult && GetToken(true)) {
1371 // garbage after rule
1372 REPORT_UNEXPECTED_TOKEN(PERuleTrailing);
1373 NS_RELEASE(*aResult);
1374 }
1376 if (!*aResult) {
1377 rv = NS_ERROR_DOM_SYNTAX_ERR;
1378 OUTPUT_ERROR();
1379 }
1380 }
1382 ReleaseScanner();
1383 return rv;
1384 }
1386 // See Bug 723197
1387 #ifdef _MSC_VER
1388 #pragma optimize( "", off )
1389 #pragma warning( push )
1390 #pragma warning( disable : 4748 )
1391 #endif
1393 nsresult
1394 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
1395 const nsAString& aPropValue,
1396 nsIURI* aSheetURI,
1397 nsIURI* aBaseURI,
1398 nsIPrincipal* aSheetPrincipal,
1399 css::Declaration* aDeclaration,
1400 bool* aChanged,
1401 bool aIsImportant,
1402 bool aIsSVGMode)
1403 {
1404 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1405 NS_PRECONDITION(aBaseURI, "need base URI");
1406 NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
1408 mData.AssertInitialState();
1409 mTempData.AssertInitialState();
1410 aDeclaration->AssertMutable();
1412 nsCSSScanner scanner(aPropValue, 0);
1413 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1414 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1415 mSection = eCSSSection_General;
1416 scanner.SetSVGMode(aIsSVGMode);
1418 *aChanged = false;
1420 // Check for unknown or preffed off properties
1421 if (eCSSProperty_UNKNOWN == aPropID ||
1422 !(nsCSSProps::IsEnabled(aPropID) ||
1423 (mUnsafeRulesEnabled &&
1424 nsCSSProps::PropHasFlags(aPropID,
1425 CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS)))) {
1426 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1427 REPORT_UNEXPECTED_P(PEUnknownProperty, propName);
1428 REPORT_UNEXPECTED(PEDeclDropped);
1429 OUTPUT_ERROR();
1430 ReleaseScanner();
1431 return NS_OK;
1432 }
1434 bool parsedOK = ParseProperty(aPropID);
1435 // We should now be at EOF
1436 if (parsedOK && GetToken(true)) {
1437 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1438 parsedOK = false;
1439 }
1441 if (!parsedOK) {
1442 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1443 REPORT_UNEXPECTED_P(PEValueParsingError, propName);
1444 REPORT_UNEXPECTED(PEDeclDropped);
1445 OUTPUT_ERROR();
1446 mTempData.ClearProperty(aPropID);
1447 } else {
1449 // We know we don't need to force a ValueAppended call for the new
1450 // value. So if we are not processing a shorthand, and there's
1451 // already a value for this property in the declaration at the
1452 // same importance level, then we can just copy our parsed value
1453 // directly into the declaration without going through the whole
1454 // expand/compress thing.
1455 if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData,
1456 aChanged)) {
1457 // Do it the slow way
1458 aDeclaration->ExpandTo(&mData);
1459 *aChanged = mData.TransferFromBlock(mTempData, aPropID, aIsImportant,
1460 true, false, aDeclaration);
1461 aDeclaration->CompressFrom(&mData);
1462 }
1463 CLEAR_ERROR();
1464 }
1466 mTempData.AssertInitialState();
1468 ReleaseScanner();
1469 return NS_OK;
1470 }
1472 nsresult
1473 CSSParserImpl::ParseVariable(const nsAString& aVariableName,
1474 const nsAString& aPropValue,
1475 nsIURI* aSheetURI,
1476 nsIURI* aBaseURI,
1477 nsIPrincipal* aSheetPrincipal,
1478 css::Declaration* aDeclaration,
1479 bool* aChanged,
1480 bool aIsImportant)
1481 {
1482 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1483 NS_PRECONDITION(aBaseURI, "need base URI");
1484 NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
1485 NS_PRECONDITION(nsLayoutUtils::CSSVariablesEnabled(),
1486 "expected Variables to be enabled");
1488 mData.AssertInitialState();
1489 mTempData.AssertInitialState();
1490 aDeclaration->AssertMutable();
1492 nsCSSScanner scanner(aPropValue, 0);
1493 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1494 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1495 mSection = eCSSSection_General;
1497 *aChanged = false;
1499 CSSVariableDeclarations::Type variableType;
1500 nsString variableValue;
1502 bool parsedOK = ParseVariableDeclaration(&variableType, variableValue);
1504 // We should now be at EOF
1505 if (parsedOK && GetToken(true)) {
1506 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1507 parsedOK = false;
1508 }
1510 if (!parsedOK) {
1511 REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("--") +
1512 aVariableName);
1513 REPORT_UNEXPECTED(PEDeclDropped);
1514 OUTPUT_ERROR();
1515 } else {
1516 CLEAR_ERROR();
1517 aDeclaration->AddVariableDeclaration(aVariableName, variableType,
1518 variableValue, aIsImportant, true);
1519 *aChanged = true;
1520 }
1522 mTempData.AssertInitialState();
1524 ReleaseScanner();
1525 return NS_OK;
1526 }
1528 #ifdef _MSC_VER
1529 #pragma warning( pop )
1530 #pragma optimize( "", on )
1531 #endif
1533 void
1534 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
1535 nsIURI* aURI, // for error reporting
1536 uint32_t aLineNumber, // for error reporting
1537 nsMediaList* aMediaList,
1538 bool aHTMLMode)
1539 {
1540 // XXX Are there cases where the caller wants to keep what it already
1541 // has in case of parser error? If GatherMedia ever changes to return
1542 // a value other than true, we probably should avoid modifying aMediaList.
1543 aMediaList->Clear();
1545 // fake base URI since media lists don't have URIs in them
1546 nsCSSScanner scanner(aBuffer, aLineNumber);
1547 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1548 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1550 mHTMLMediaMode = aHTMLMode;
1552 // XXXldb We need to make the scanner not skip CSS comments! (Or
1553 // should we?)
1555 // For aHTMLMode, we used to follow the parsing rules in
1556 // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
1557 // which wouldn't work for media queries since they remove all but the
1558 // first word. However, they're changed in
1559 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
1560 // (as of 2008-05-29) which says that the media attribute just points
1561 // to a media query. (The main substative difference is the relative
1562 // precedence of commas and paretheses.)
1564 DebugOnly<bool> parsedOK = GatherMedia(aMediaList, false);
1565 NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid "
1566 "trashing aMediaList");
1568 CLEAR_ERROR();
1569 ReleaseScanner();
1570 mHTMLMediaMode = false;
1571 }
1573 bool
1574 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
1575 nsIURI* aURI, // for error reporting
1576 uint32_t aLineNumber, // for error reporting
1577 nsCSSValue& aValue)
1578 {
1579 nsCSSScanner scanner(aBuffer, aLineNumber);
1580 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1581 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1583 // Parse a color, and check that there's nothing else after it.
1584 bool colorParsed = ParseColor(aValue) && !GetToken(true);
1585 OUTPUT_ERROR();
1586 ReleaseScanner();
1587 return colorParsed;
1588 }
1590 nsresult
1591 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
1592 nsIURI* aURI, // for error reporting
1593 uint32_t aLineNumber, // for error reporting
1594 nsCSSSelectorList **aSelectorList)
1595 {
1596 nsCSSScanner scanner(aSelectorString, aLineNumber);
1597 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1598 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1600 bool success = ParseSelectorList(*aSelectorList, char16_t(0));
1602 // We deliberately do not call OUTPUT_ERROR here, because all our
1603 // callers map a failure return to a JS exception, and if that JS
1604 // exception is caught, people don't want to see parser diagnostics;
1605 // see e.g. http://bugs.jquery.com/ticket/7535
1606 // It would be nice to be able to save the parser diagnostics into
1607 // the exception, so that if it _isn't_ caught we can report them
1608 // along with the usual uncaught-exception message, but we don't
1609 // have any way to do that at present; see bug 631621.
1610 CLEAR_ERROR();
1611 ReleaseScanner();
1613 if (success) {
1614 NS_ASSERTION(*aSelectorList, "Should have list!");
1615 return NS_OK;
1616 }
1618 NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
1620 return NS_ERROR_DOM_SYNTAX_ERR;
1621 }
1624 already_AddRefed<nsCSSKeyframeRule>
1625 CSSParserImpl::ParseKeyframeRule(const nsSubstring& aBuffer,
1626 nsIURI* aURI,
1627 uint32_t aLineNumber)
1628 {
1629 nsCSSScanner scanner(aBuffer, aLineNumber);
1630 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1631 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1633 nsRefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule();
1634 if (GetToken(true)) {
1635 // extra garbage at the end
1636 result = nullptr;
1637 }
1639 OUTPUT_ERROR();
1640 ReleaseScanner();
1642 return result.forget();
1643 }
1645 bool
1646 CSSParserImpl::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
1647 nsIURI* aURI, // for error reporting
1648 uint32_t aLineNumber, // for error reporting
1649 InfallibleTArray<float>& aSelectorList)
1650 {
1651 NS_ABORT_IF_FALSE(aSelectorList.IsEmpty(), "given list should start empty");
1653 nsCSSScanner scanner(aSelectorString, aLineNumber);
1654 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
1655 InitScanner(scanner, reporter, aURI, aURI, nullptr);
1657 bool success = ParseKeyframeSelectorList(aSelectorList) &&
1658 // must consume entire input string
1659 !GetToken(true);
1661 OUTPUT_ERROR();
1662 ReleaseScanner();
1664 if (success) {
1665 NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty");
1666 } else {
1667 aSelectorList.Clear();
1668 }
1670 return success;
1671 }
1673 bool
1674 CSSParserImpl::EvaluateSupportsDeclaration(const nsAString& aProperty,
1675 const nsAString& aValue,
1676 nsIURI* aDocURL,
1677 nsIURI* aBaseURL,
1678 nsIPrincipal* aDocPrincipal)
1679 {
1680 nsCSSProperty propID = LookupEnabledProperty(aProperty);
1681 if (propID == eCSSProperty_UNKNOWN) {
1682 return false;
1683 }
1685 nsCSSScanner scanner(aValue, 0);
1686 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
1687 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
1688 nsAutoSuppressErrors suppressErrors(this);
1690 bool parsedOK;
1692 if (propID == eCSSPropertyExtra_variable) {
1693 MOZ_ASSERT(Substring(aProperty, 0,
1694 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
1695 const nsDependentSubstring varName =
1696 Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH); // remove '--'
1697 CSSVariableDeclarations::Type variableType;
1698 nsString variableValue;
1699 parsedOK = ParseVariableDeclaration(&variableType, variableValue) &&
1700 !GetToken(true);
1701 } else {
1702 parsedOK = ParseProperty(propID) && !GetToken(true);
1704 mTempData.ClearProperty(propID);
1705 mTempData.AssertInitialState();
1706 }
1708 CLEAR_ERROR();
1709 ReleaseScanner();
1711 return parsedOK;
1712 }
1714 bool
1715 CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration,
1716 nsIURI* aDocURL,
1717 nsIURI* aBaseURL,
1718 nsIPrincipal* aDocPrincipal)
1719 {
1720 nsCSSScanner scanner(aDeclaration, 0);
1721 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
1722 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
1723 nsAutoSuppressErrors suppressErrors(this);
1725 bool conditionMet;
1726 bool parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true);
1728 CLEAR_ERROR();
1729 ReleaseScanner();
1731 return parsedOK && conditionMet;
1732 }
1734 bool
1735 CSSParserImpl::EnumerateVariableReferences(const nsAString& aPropertyValue,
1736 VariableEnumFunc aFunc,
1737 void* aData)
1738 {
1739 nsCSSScanner scanner(aPropertyValue, 0);
1740 css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
1741 InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
1742 nsAutoSuppressErrors suppressErrors(this);
1744 CSSVariableDeclarations::Type type;
1745 bool dropBackslash;
1746 nsString impliedCharacters;
1747 bool result = ParseValueWithVariables(&type, &dropBackslash,
1748 impliedCharacters, aFunc, aData) &&
1749 !GetToken(true);
1751 ReleaseScanner();
1753 return result;
1754 }
1756 static bool
1757 SeparatorRequiredBetweenTokens(nsCSSTokenSerializationType aToken1,
1758 nsCSSTokenSerializationType aToken2)
1759 {
1760 // The two lines marked with (*) do not correspond to entries in
1761 // the table in the css-syntax spec but which we need to handle,
1762 // as we treat them as whole tokens.
1763 switch (aToken1) {
1764 case eCSSTokenSerialization_Ident:
1765 return aToken2 == eCSSTokenSerialization_Ident ||
1766 aToken2 == eCSSTokenSerialization_Function ||
1767 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1768 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1769 aToken2 == eCSSTokenSerialization_Number ||
1770 aToken2 == eCSSTokenSerialization_Percentage ||
1771 aToken2 == eCSSTokenSerialization_Dimension ||
1772 aToken2 == eCSSTokenSerialization_URange ||
1773 aToken2 == eCSSTokenSerialization_CDC ||
1774 aToken2 == eCSSTokenSerialization_Symbol_OpenParen;
1775 case eCSSTokenSerialization_AtKeyword_or_Hash:
1776 case eCSSTokenSerialization_Dimension:
1777 return aToken2 == eCSSTokenSerialization_Ident ||
1778 aToken2 == eCSSTokenSerialization_Function ||
1779 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1780 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1781 aToken2 == eCSSTokenSerialization_Number ||
1782 aToken2 == eCSSTokenSerialization_Percentage ||
1783 aToken2 == eCSSTokenSerialization_Dimension ||
1784 aToken2 == eCSSTokenSerialization_URange ||
1785 aToken2 == eCSSTokenSerialization_CDC;
1786 case eCSSTokenSerialization_Symbol_Hash:
1787 return aToken2 == eCSSTokenSerialization_Ident ||
1788 aToken2 == eCSSTokenSerialization_Function ||
1789 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1790 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1791 aToken2 == eCSSTokenSerialization_Number ||
1792 aToken2 == eCSSTokenSerialization_Percentage ||
1793 aToken2 == eCSSTokenSerialization_Dimension ||
1794 aToken2 == eCSSTokenSerialization_URange;
1795 case eCSSTokenSerialization_Symbol_Minus:
1796 case eCSSTokenSerialization_Number:
1797 return aToken2 == eCSSTokenSerialization_Ident ||
1798 aToken2 == eCSSTokenSerialization_Function ||
1799 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1800 aToken2 == eCSSTokenSerialization_Number ||
1801 aToken2 == eCSSTokenSerialization_Percentage ||
1802 aToken2 == eCSSTokenSerialization_Dimension ||
1803 aToken2 == eCSSTokenSerialization_URange;
1804 case eCSSTokenSerialization_Symbol_At:
1805 return aToken2 == eCSSTokenSerialization_Ident ||
1806 aToken2 == eCSSTokenSerialization_Function ||
1807 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
1808 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
1809 aToken2 == eCSSTokenSerialization_URange;
1810 case eCSSTokenSerialization_URange:
1811 return aToken2 == eCSSTokenSerialization_Ident ||
1812 aToken2 == eCSSTokenSerialization_Function ||
1813 aToken2 == eCSSTokenSerialization_Number ||
1814 aToken2 == eCSSTokenSerialization_Percentage ||
1815 aToken2 == eCSSTokenSerialization_Dimension ||
1816 aToken2 == eCSSTokenSerialization_Symbol_Question;
1817 case eCSSTokenSerialization_Symbol_Dot_or_Plus:
1818 return aToken2 == eCSSTokenSerialization_Number ||
1819 aToken2 == eCSSTokenSerialization_Percentage ||
1820 aToken2 == eCSSTokenSerialization_Dimension;
1821 case eCSSTokenSerialization_Symbol_Assorted:
1822 case eCSSTokenSerialization_Symbol_Asterisk:
1823 return aToken2 == eCSSTokenSerialization_Symbol_Equals;
1824 case eCSSTokenSerialization_Symbol_Bar:
1825 return aToken2 == eCSSTokenSerialization_Symbol_Equals ||
1826 aToken2 == eCSSTokenSerialization_Symbol_Bar ||
1827 aToken2 == eCSSTokenSerialization_DashMatch; // (*)
1828 case eCSSTokenSerialization_Symbol_Slash:
1829 return aToken2 == eCSSTokenSerialization_Symbol_Asterisk ||
1830 aToken2 == eCSSTokenSerialization_ContainsMatch; // (*)
1831 default:
1832 MOZ_ASSERT(aToken1 == eCSSTokenSerialization_Nothing ||
1833 aToken1 == eCSSTokenSerialization_Whitespace ||
1834 aToken1 == eCSSTokenSerialization_Percentage ||
1835 aToken1 == eCSSTokenSerialization_URL_or_BadURL ||
1836 aToken1 == eCSSTokenSerialization_Function ||
1837 aToken1 == eCSSTokenSerialization_CDC ||
1838 aToken1 == eCSSTokenSerialization_Symbol_OpenParen ||
1839 aToken1 == eCSSTokenSerialization_Symbol_Question ||
1840 aToken1 == eCSSTokenSerialization_Symbol_Assorted ||
1841 aToken1 == eCSSTokenSerialization_Symbol_Asterisk ||
1842 aToken1 == eCSSTokenSerialization_Symbol_Equals ||
1843 aToken1 == eCSSTokenSerialization_Symbol_Bar ||
1844 aToken1 == eCSSTokenSerialization_Symbol_Slash ||
1845 aToken1 == eCSSTokenSerialization_Other ||
1846 "unexpected nsCSSTokenSerializationType value");
1847 return false;
1848 }
1849 }
1851 /**
1852 * Appends aValue to aResult, possibly inserting an empty CSS
1853 * comment between the two to ensure that tokens from both strings
1854 * remain separated.
1855 */
1856 static void
1857 AppendTokens(nsAString& aResult,
1858 nsCSSTokenSerializationType& aResultFirstToken,
1859 nsCSSTokenSerializationType& aResultLastToken,
1860 nsCSSTokenSerializationType aValueFirstToken,
1861 nsCSSTokenSerializationType aValueLastToken,
1862 const nsAString& aValue)
1863 {
1864 if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
1865 aResult.AppendLiteral("/**/");
1866 }
1867 aResult.Append(aValue);
1868 if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
1869 aResultFirstToken = aValueFirstToken;
1870 }
1871 if (aValueLastToken != eCSSTokenSerialization_Nothing) {
1872 aResultLastToken = aValueLastToken;
1873 }
1874 }
1876 /**
1877 * Stops the given scanner recording, and appends the recorded result
1878 * to aResult, possibly inserting an empty CSS comment between the two to
1879 * ensure that tokens from both strings remain separated.
1880 */
1881 static void
1882 StopRecordingAndAppendTokens(nsString& aResult,
1883 nsCSSTokenSerializationType& aResultFirstToken,
1884 nsCSSTokenSerializationType& aResultLastToken,
1885 nsCSSTokenSerializationType aValueFirstToken,
1886 nsCSSTokenSerializationType aValueLastToken,
1887 nsCSSScanner* aScanner)
1888 {
1889 if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
1890 aResult.AppendLiteral("/**/");
1891 }
1892 aScanner->StopRecording(aResult);
1893 if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
1894 aResultFirstToken = aValueFirstToken;
1895 }
1896 if (aValueLastToken != eCSSTokenSerialization_Nothing) {
1897 aResultLastToken = aValueLastToken;
1898 }
1899 }
1901 bool
1902 CSSParserImpl::ResolveValueWithVariableReferencesRec(
1903 nsString& aResult,
1904 nsCSSTokenSerializationType& aResultFirstToken,
1905 nsCSSTokenSerializationType& aResultLastToken,
1906 const CSSVariableValues* aVariables)
1907 {
1908 // This function assumes we are already recording, and will leave the scanner
1909 // recording when it returns.
1910 MOZ_ASSERT(mScanner->IsRecording());
1911 MOZ_ASSERT(aResult.IsEmpty());
1913 // Stack of closing characters for currently open constructs.
1914 nsAutoTArray<char16_t, 16> stack;
1916 // The resolved value for this ResolveValueWithVariableReferencesRec call.
1917 nsString value;
1919 // The length of the scanner's recording before the currently parsed token.
1920 // This is used so that when we encounter a "var(" token, we can strip
1921 // it off the end of the recording, regardless of how long the token was.
1922 // (With escapes, it could be longer than four characters.)
1923 uint32_t lengthBeforeVar = 0;
1925 // Tracking the type of token that appears at the start and end of |value|
1926 // and that appears at the start and end of the scanner recording. These are
1927 // used to determine whether we need to insert "/**/" when pasting token
1928 // streams together.
1929 nsCSSTokenSerializationType valueFirstToken = eCSSTokenSerialization_Nothing,
1930 valueLastToken = eCSSTokenSerialization_Nothing,
1931 recFirstToken = eCSSTokenSerialization_Nothing,
1932 recLastToken = eCSSTokenSerialization_Nothing;
1934 #define UPDATE_RECORDING_TOKENS(type) \
1935 if (recFirstToken == eCSSTokenSerialization_Nothing) { \
1936 recFirstToken = type; \
1937 } \
1938 recLastToken = type;
1940 while (GetToken(false)) {
1941 switch (mToken.mType) {
1942 case eCSSToken_Symbol: {
1943 nsCSSTokenSerializationType type = eCSSTokenSerialization_Other;
1944 if (mToken.mSymbol == '(') {
1945 stack.AppendElement(')');
1946 type = eCSSTokenSerialization_Symbol_OpenParen;
1947 } else if (mToken.mSymbol == '[') {
1948 stack.AppendElement(']');
1949 } else if (mToken.mSymbol == '{') {
1950 stack.AppendElement('}');
1951 } else if (mToken.mSymbol == ';') {
1952 if (stack.IsEmpty()) {
1953 // A ';' that is at the top level of the value or at the top level
1954 // of a variable reference's fallback is invalid.
1955 return false;
1956 }
1957 } else if (mToken.mSymbol == '!') {
1958 if (stack.IsEmpty()) {
1959 // An '!' that is at the top level of the value or at the top level
1960 // of a variable reference's fallback is invalid.
1961 return false;
1962 }
1963 } else if (mToken.mSymbol == ')' &&
1964 stack.IsEmpty()) {
1965 // We're closing a "var(".
1966 nsString finalTokens;
1967 mScanner->StopRecording(finalTokens);
1968 MOZ_ASSERT(finalTokens[finalTokens.Length() - 1] == ')');
1969 finalTokens.Truncate(finalTokens.Length() - 1);
1970 aResult.Append(value);
1972 AppendTokens(aResult, valueFirstToken, valueLastToken,
1973 recFirstToken, recLastToken, finalTokens);
1975 mScanner->StartRecording();
1976 UngetToken();
1977 aResultFirstToken = valueFirstToken;
1978 aResultLastToken = valueLastToken;
1979 return true;
1980 } else if (mToken.mSymbol == ')' ||
1981 mToken.mSymbol == ']' ||
1982 mToken.mSymbol == '}') {
1983 if (stack.IsEmpty() ||
1984 stack.LastElement() != mToken.mSymbol) {
1985 // A mismatched closing bracket is invalid.
1986 return false;
1987 }
1988 stack.TruncateLength(stack.Length() - 1);
1989 } else if (mToken.mSymbol == '#') {
1990 type = eCSSTokenSerialization_Symbol_Hash;
1991 } else if (mToken.mSymbol == '@') {
1992 type = eCSSTokenSerialization_Symbol_At;
1993 } else if (mToken.mSymbol == '.' ||
1994 mToken.mSymbol == '+') {
1995 type = eCSSTokenSerialization_Symbol_Dot_or_Plus;
1996 } else if (mToken.mSymbol == '-') {
1997 type = eCSSTokenSerialization_Symbol_Minus;
1998 } else if (mToken.mSymbol == '?') {
1999 type = eCSSTokenSerialization_Symbol_Question;
2000 } else if (mToken.mSymbol == '$' ||
2001 mToken.mSymbol == '^' ||
2002 mToken.mSymbol == '~') {
2003 type = eCSSTokenSerialization_Symbol_Assorted;
2004 } else if (mToken.mSymbol == '=') {
2005 type = eCSSTokenSerialization_Symbol_Equals;
2006 } else if (mToken.mSymbol == '|') {
2007 type = eCSSTokenSerialization_Symbol_Bar;
2008 } else if (mToken.mSymbol == '/') {
2009 type = eCSSTokenSerialization_Symbol_Slash;
2010 } else if (mToken.mSymbol == '*') {
2011 type = eCSSTokenSerialization_Symbol_Asterisk;
2012 }
2013 UPDATE_RECORDING_TOKENS(type);
2014 break;
2015 }
2017 case eCSSToken_Function:
2018 if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
2019 // Save the tokens before the "var(" to our resolved value.
2020 nsString recording;
2021 mScanner->StopRecording(recording);
2022 recording.Truncate(lengthBeforeVar);
2023 AppendTokens(value, valueFirstToken, valueLastToken,
2024 recFirstToken, recLastToken, recording);
2025 recFirstToken = eCSSTokenSerialization_Nothing;
2026 recLastToken = eCSSTokenSerialization_Nothing;
2028 if (!GetToken(true) ||
2029 mToken.mType != eCSSToken_Ident ||
2030 !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
2031 // "var(" must be followed by an identifier, and it must be a
2032 // custom property name.
2033 return false;
2034 }
2036 // Turn the custom property name into a variable name by removing the
2037 // '--' prefix.
2038 MOZ_ASSERT(Substring(mToken.mIdent, 0,
2039 CSS_CUSTOM_NAME_PREFIX_LENGTH).
2040 EqualsLiteral("--"));
2041 nsDependentString variableName(mToken.mIdent,
2042 CSS_CUSTOM_NAME_PREFIX_LENGTH);
2044 // Get the value of the identified variable. Note that we
2045 // check if the variable value is the empty string, as that means
2046 // that the variable was invalid at computed value time due to
2047 // unresolveable variable references or cycles.
2048 nsString variableValue;
2049 nsCSSTokenSerializationType varFirstToken, varLastToken;
2050 bool valid = aVariables->Get(variableName, variableValue,
2051 varFirstToken, varLastToken) &&
2052 !variableValue.IsEmpty();
2054 if (!GetToken(true) ||
2055 mToken.IsSymbol(')')) {
2056 mScanner->StartRecording();
2057 if (!valid) {
2058 // Invalid variable with no fallback.
2059 return false;
2060 }
2061 // Valid variable with no fallback.
2062 AppendTokens(value, valueFirstToken, valueLastToken,
2063 varFirstToken, varLastToken, variableValue);
2064 } else if (mToken.IsSymbol(',')) {
2065 mScanner->StartRecording();
2066 if (!GetToken(false) ||
2067 mToken.IsSymbol(')')) {
2068 // Comma must be followed by at least one fallback token.
2069 return false;
2070 }
2071 UngetToken();
2072 if (valid) {
2073 // Valid variable with ignored fallback.
2074 mScanner->StopRecording();
2075 AppendTokens(value, valueFirstToken, valueLastToken,
2076 varFirstToken, varLastToken, variableValue);
2077 bool ok = SkipBalancedContentUntil(')');
2078 mScanner->StartRecording();
2079 if (!ok) {
2080 return false;
2081 }
2082 } else {
2083 nsString fallback;
2084 if (!ResolveValueWithVariableReferencesRec(fallback,
2085 varFirstToken,
2086 varLastToken,
2087 aVariables)) {
2088 // Fallback value had invalid tokens or an invalid variable reference
2089 // that itself had no fallback.
2090 return false;
2091 }
2092 AppendTokens(value, valueFirstToken, valueLastToken,
2093 varFirstToken, varLastToken, fallback);
2094 // Now we're either at the pushed back ')' that finished the
2095 // fallback or at EOF.
2096 DebugOnly<bool> gotToken = GetToken(false);
2097 MOZ_ASSERT(!gotToken || mToken.IsSymbol(')'));
2098 }
2099 } else {
2100 // Expected ',' or ')' after the variable name.
2101 mScanner->StartRecording();
2102 return false;
2103 }
2104 } else {
2105 stack.AppendElement(')');
2106 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function);
2107 }
2108 break;
2110 case eCSSToken_Bad_String:
2111 case eCSSToken_Bad_URL:
2112 return false;
2114 case eCSSToken_Whitespace:
2115 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace);
2116 break;
2118 case eCSSToken_AtKeyword:
2119 case eCSSToken_Hash:
2120 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash);
2121 break;
2123 case eCSSToken_Number:
2124 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number);
2125 break;
2127 case eCSSToken_Dimension:
2128 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension);
2129 break;
2131 case eCSSToken_Ident:
2132 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident);
2133 break;
2135 case eCSSToken_Percentage:
2136 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage);
2137 break;
2139 case eCSSToken_URange:
2140 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange);
2141 break;
2143 case eCSSToken_URL:
2144 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL);
2145 break;
2147 case eCSSToken_HTMLComment:
2148 if (mToken.mIdent[0] == '-') {
2149 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC);
2150 } else {
2151 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
2152 }
2153 break;
2155 case eCSSToken_Dashmatch:
2156 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch);
2157 break;
2159 case eCSSToken_Containsmatch:
2160 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch);
2161 break;
2163 default:
2164 NS_NOTREACHED("unexpected token type");
2165 // fall through
2166 case eCSSToken_ID:
2167 case eCSSToken_String:
2168 case eCSSToken_Includes:
2169 case eCSSToken_Beginsmatch:
2170 case eCSSToken_Endsmatch:
2171 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
2172 break;
2173 }
2175 lengthBeforeVar = mScanner->RecordingLength();
2176 }
2178 #undef UPDATE_RECORDING_TOKENS
2180 aResult.Append(value);
2181 StopRecordingAndAppendTokens(aResult, valueFirstToken, valueLastToken,
2182 recFirstToken, recLastToken, mScanner);
2184 // Append any implicitly closed brackets.
2185 if (!stack.IsEmpty()) {
2186 do {
2187 aResult.Append(stack.LastElement());
2188 stack.TruncateLength(stack.Length() - 1);
2189 } while (!stack.IsEmpty());
2190 valueLastToken = eCSSTokenSerialization_Other;
2191 }
2193 mScanner->StartRecording();
2194 aResultFirstToken = valueFirstToken;
2195 aResultLastToken = valueLastToken;
2196 return true;
2197 }
2199 bool
2200 CSSParserImpl::ResolveValueWithVariableReferences(
2201 const CSSVariableValues* aVariables,
2202 nsString& aResult,
2203 nsCSSTokenSerializationType& aFirstToken,
2204 nsCSSTokenSerializationType& aLastToken)
2205 {
2206 aResult.Truncate(0);
2208 // Start recording before we read the first token.
2209 mScanner->StartRecording();
2211 if (!GetToken(false)) {
2212 // Value was empty since we reached EOF.
2213 mScanner->StopRecording();
2214 return false;
2215 }
2217 UngetToken();
2219 nsString value;
2220 nsCSSTokenSerializationType firstToken, lastToken;
2221 bool ok = ResolveValueWithVariableReferencesRec(value, firstToken, lastToken, aVariables) &&
2222 !GetToken(true);
2224 mScanner->StopRecording();
2226 if (ok) {
2227 aResult = value;
2228 aFirstToken = firstToken;
2229 aLastToken = lastToken;
2230 }
2231 return ok;
2232 }
2234 bool
2235 CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue,
2236 const CSSVariableValues* aVariables,
2237 nsString& aResult,
2238 nsCSSTokenSerializationType& aFirstToken,
2239 nsCSSTokenSerializationType& aLastToken)
2240 {
2241 nsCSSScanner scanner(aPropertyValue, 0);
2243 // At this point, we know that aPropertyValue is syntactically correct
2244 // for a token stream that has variable references. We also won't be
2245 // interpreting any of the stream as we parse it, apart from expanding
2246 // var() references, so we don't need a base URL etc. or any useful
2247 // error reporting.
2248 css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
2249 InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
2251 bool valid = ResolveValueWithVariableReferences(aVariables, aResult,
2252 aFirstToken, aLastToken);
2254 ReleaseScanner();
2255 return valid;
2256 }
2258 void
2259 CSSParserImpl::ParsePropertyWithVariableReferences(
2260 nsCSSProperty aPropertyID,
2261 nsCSSProperty aShorthandPropertyID,
2262 const nsAString& aValue,
2263 const CSSVariableValues* aVariables,
2264 nsRuleData* aRuleData,
2265 nsIURI* aDocURL,
2266 nsIURI* aBaseURL,
2267 nsIPrincipal* aDocPrincipal,
2268 nsCSSStyleSheet* aSheet,
2269 uint32_t aLineNumber,
2270 uint32_t aLineOffset)
2271 {
2272 mTempData.AssertInitialState();
2274 bool valid;
2275 nsString expandedValue;
2277 // Resolve any variable references in the property value.
2278 {
2279 nsCSSScanner scanner(aValue, 0);
2280 css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
2281 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2283 nsCSSTokenSerializationType firstToken, lastToken;
2284 valid = ResolveValueWithVariableReferences(aVariables, expandedValue,
2285 firstToken, lastToken);
2286 if (!valid) {
2287 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropertyID));
2288 REPORT_UNEXPECTED(PEInvalidVariableReference);
2289 REPORT_UNEXPECTED_P(PEValueParsingError, propName);
2290 if (nsCSSProps::IsInherited(aPropertyID)) {
2291 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
2292 } else {
2293 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
2294 }
2295 OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
2296 }
2297 ReleaseScanner();
2298 }
2300 nsCSSProperty propertyToParse =
2301 aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID :
2302 aPropertyID;
2304 // Parse the property with that resolved value.
2305 if (valid) {
2306 nsCSSScanner scanner(expandedValue, 0);
2307 css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
2308 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2309 valid = ParseProperty(propertyToParse);
2310 if (valid && GetToken(true)) {
2311 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
2312 valid = false;
2313 }
2314 if (!valid) {
2315 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(
2316 propertyToParse));
2317 REPORT_UNEXPECTED_P(PEValueWithVariablesParsingError, propName);
2318 if (nsCSSProps::IsInherited(aPropertyID)) {
2319 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
2320 } else {
2321 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
2322 }
2323 OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
2324 }
2325 ReleaseScanner();
2326 }
2328 // If the property could not be parsed with the resolved value, then we
2329 // treat it as if the value were 'initial' or 'inherit', depending on whether
2330 // the property is an inherited property.
2331 if (!valid) {
2332 nsCSSValue defaultValue;
2333 if (nsCSSProps::IsInherited(aPropertyID)) {
2334 defaultValue.SetInheritValue();
2335 } else {
2336 defaultValue.SetInitialValue();
2337 }
2338 mTempData.AddLonghandProperty(aPropertyID, defaultValue);
2339 }
2341 // Copy the property value into the rule data.
2342 mTempData.MapRuleInfoInto(aPropertyID, aRuleData);
2344 mTempData.ClearProperty(propertyToParse);
2345 mTempData.AssertInitialState();
2346 }
2348 //----------------------------------------------------------------------
2350 bool
2351 CSSParserImpl::GetToken(bool aSkipWS)
2352 {
2353 if (mHavePushBack) {
2354 mHavePushBack = false;
2355 if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) {
2356 return true;
2357 }
2358 }
2359 return mScanner->Next(mToken, aSkipWS);
2360 }
2362 void
2363 CSSParserImpl::UngetToken()
2364 {
2365 NS_PRECONDITION(!mHavePushBack, "double pushback");
2366 mHavePushBack = true;
2367 }
2369 bool
2370 CSSParserImpl::GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum)
2371 {
2372 // Peek at next token so that mScanner updates line and column vals
2373 if (!GetToken(aSkipWS)) {
2374 return false;
2375 }
2376 UngetToken();
2377 // The scanner uses one-indexing for line numbers but zero-indexing
2378 // for column numbers.
2379 *linenum = mScanner->GetLineNumber();
2380 *colnum = 1 + mScanner->GetColumnNumber();
2381 return true;
2382 }
2384 bool
2385 CSSParserImpl::ExpectSymbol(char16_t aSymbol,
2386 bool aSkipWS)
2387 {
2388 if (!GetToken(aSkipWS)) {
2389 // CSS2.1 specifies that all "open constructs" are to be closed at
2390 // EOF. It simplifies higher layers if we claim to have found an
2391 // ), ], }, or ; if we encounter EOF while looking for one of them.
2392 // Do still issue a diagnostic, to aid debugging.
2393 if (aSymbol == ')' || aSymbol == ']' ||
2394 aSymbol == '}' || aSymbol == ';') {
2395 REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
2396 return true;
2397 }
2398 else
2399 return false;
2400 }
2401 if (mToken.IsSymbol(aSymbol)) {
2402 return true;
2403 }
2404 UngetToken();
2405 return false;
2406 }
2408 // Checks to see if we're at the end of a property. If an error occurs during
2409 // the check, does not signal a parse error.
2410 bool
2411 CSSParserImpl::CheckEndProperty()
2412 {
2413 if (!GetToken(true)) {
2414 return true; // properties may end with eof
2415 }
2416 if ((eCSSToken_Symbol == mToken.mType) &&
2417 ((';' == mToken.mSymbol) ||
2418 ('!' == mToken.mSymbol) ||
2419 ('}' == mToken.mSymbol) ||
2420 (')' == mToken.mSymbol))) {
2421 // XXX need to verify that ! is only followed by "important [;|}]
2422 // XXX this requires a multi-token pushback buffer
2423 UngetToken();
2424 return true;
2425 }
2426 UngetToken();
2427 return false;
2428 }
2430 // Checks if we're at the end of a property, raising an error if we're not.
2431 bool
2432 CSSParserImpl::ExpectEndProperty()
2433 {
2434 if (CheckEndProperty())
2435 return true;
2437 // If we're here, we read something incorrect, so we should report it.
2438 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
2439 return false;
2440 }
2442 // Parses the priority suffix on a property, which at present may be
2443 // either '!important' or nothing.
2444 CSSParserImpl::PriorityParsingStatus
2445 CSSParserImpl::ParsePriority()
2446 {
2447 if (!GetToken(true)) {
2448 return ePriority_None; // properties may end with EOF
2449 }
2450 if (!mToken.IsSymbol('!')) {
2451 UngetToken();
2452 return ePriority_None; // dunno what it is, but it's not a priority
2453 }
2455 if (!GetToken(true)) {
2456 // EOF is not ok after !
2457 REPORT_UNEXPECTED_EOF(PEImportantEOF);
2458 return ePriority_Error;
2459 }
2461 if (mToken.mType != eCSSToken_Ident ||
2462 !mToken.mIdent.LowerCaseEqualsLiteral("important")) {
2463 REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
2464 UngetToken();
2465 return ePriority_Error;
2466 }
2468 return ePriority_Important;
2469 }
2471 nsSubstring*
2472 CSSParserImpl::NextIdent()
2473 {
2474 // XXX Error reporting?
2475 if (!GetToken(true)) {
2476 return nullptr;
2477 }
2478 if (eCSSToken_Ident != mToken.mType) {
2479 UngetToken();
2480 return nullptr;
2481 }
2482 return &mToken.mIdent;
2483 }
2485 bool
2486 CSSParserImpl::SkipAtRule(bool aInsideBlock)
2487 {
2488 for (;;) {
2489 if (!GetToken(true)) {
2490 REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2);
2491 return false;
2492 }
2493 if (eCSSToken_Symbol == mToken.mType) {
2494 char16_t symbol = mToken.mSymbol;
2495 if (symbol == ';') {
2496 break;
2497 }
2498 if (aInsideBlock && symbol == '}') {
2499 // The closing } doesn't belong to us.
2500 UngetToken();
2501 break;
2502 }
2503 if (symbol == '{') {
2504 SkipUntil('}');
2505 break;
2506 } else if (symbol == '(') {
2507 SkipUntil(')');
2508 } else if (symbol == '[') {
2509 SkipUntil(']');
2510 }
2511 } else if (eCSSToken_Function == mToken.mType ||
2512 eCSSToken_Bad_URL == mToken.mType) {
2513 SkipUntil(')');
2514 }
2515 }
2516 return true;
2517 }
2519 bool
2520 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
2521 void* aData,
2522 bool aInAtRule)
2523 {
2525 nsCSSSection newSection;
2526 bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*);
2528 if ((mSection <= eCSSSection_Charset) &&
2529 (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
2530 parseFunc = &CSSParserImpl::ParseCharsetRule;
2531 newSection = eCSSSection_Import; // only one charset allowed
2533 } else if ((mSection <= eCSSSection_Import) &&
2534 mToken.mIdent.LowerCaseEqualsLiteral("import")) {
2535 parseFunc = &CSSParserImpl::ParseImportRule;
2536 newSection = eCSSSection_Import;
2538 } else if ((mSection <= eCSSSection_NameSpace) &&
2539 mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
2540 parseFunc = &CSSParserImpl::ParseNameSpaceRule;
2541 newSection = eCSSSection_NameSpace;
2543 } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
2544 parseFunc = &CSSParserImpl::ParseMediaRule;
2545 newSection = eCSSSection_General;
2547 } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
2548 parseFunc = &CSSParserImpl::ParseMozDocumentRule;
2549 newSection = eCSSSection_General;
2551 } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
2552 parseFunc = &CSSParserImpl::ParseFontFaceRule;
2553 newSection = eCSSSection_General;
2555 } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values") &&
2556 nsCSSFontFeatureValuesRule::PrefEnabled()) {
2557 parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule;
2558 newSection = eCSSSection_General;
2560 } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
2561 parseFunc = &CSSParserImpl::ParsePageRule;
2562 newSection = eCSSSection_General;
2564 } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation) &&
2565 mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) ||
2566 mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) {
2567 parseFunc = &CSSParserImpl::ParseKeyframesRule;
2568 newSection = eCSSSection_General;
2570 } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports") &&
2571 CSSSupportsRule::PrefEnabled()) {
2572 parseFunc = &CSSParserImpl::ParseSupportsRule;
2573 newSection = eCSSSection_General;
2575 } else {
2576 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
2577 REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
2578 OUTPUT_ERROR();
2579 }
2580 // Skip over unsupported at rule, don't advance section
2581 return SkipAtRule(aInAtRule);
2582 }
2584 // Inside of @-rules, only the rules that can occur anywhere
2585 // are allowed.
2586 bool unnestable = aInAtRule && newSection != eCSSSection_General;
2587 if (unnestable) {
2588 REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule);
2589 }
2591 if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) {
2592 // Skip over invalid at rule, don't advance section
2593 OUTPUT_ERROR();
2594 return SkipAtRule(aInAtRule);
2595 }
2597 // Nested @-rules don't affect the top-level rule chain requirement
2598 if (!aInAtRule) {
2599 mSection = newSection;
2600 }
2602 return true;
2603 }
2605 bool
2606 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
2607 void* aData)
2608 {
2609 if (!GetToken(true)) {
2610 REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
2611 return false;
2612 }
2614 if (eCSSToken_String != mToken.mType) {
2615 UngetToken();
2616 REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
2617 return false;
2618 }
2620 nsAutoString charset = mToken.mIdent;
2622 if (!ExpectSymbol(';', true)) {
2623 return false;
2624 }
2626 nsRefPtr<css::CharsetRule> rule = new css::CharsetRule(charset);
2627 (*aAppendFunc)(rule, aData);
2629 return true;
2630 }
2632 bool
2633 CSSParserImpl::ParseURLOrString(nsString& aURL)
2634 {
2635 if (!GetToken(true)) {
2636 return false;
2637 }
2638 if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) {
2639 aURL = mToken.mIdent;
2640 return true;
2641 }
2642 UngetToken();
2643 return false;
2644 }
2646 bool
2647 CSSParserImpl::ParseMediaQuery(bool aInAtRule,
2648 nsMediaQuery **aQuery,
2649 bool *aHitStop)
2650 {
2651 *aQuery = nullptr;
2652 *aHitStop = false;
2654 // "If the comma-separated list is the empty list it is assumed to
2655 // specify the media query 'all'." (css3-mediaqueries, section
2656 // "Media Queries")
2657 if (!GetToken(true)) {
2658 *aHitStop = true;
2659 // expected termination by EOF
2660 if (!aInAtRule)
2661 return true;
2663 // unexpected termination by EOF
2664 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
2665 return true;
2666 }
2668 if (eCSSToken_Symbol == mToken.mType && aInAtRule &&
2669 (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) {
2670 *aHitStop = true;
2671 UngetToken();
2672 return true;
2673 }
2674 UngetToken();
2676 nsMediaQuery* query = new nsMediaQuery;
2677 *aQuery = query;
2679 if (ExpectSymbol('(', true)) {
2680 // we got an expression without a media type
2681 UngetToken(); // so ParseMediaQueryExpression can handle it
2682 query->SetType(nsGkAtoms::all);
2683 query->SetTypeOmitted();
2684 // Just parse the first expression here.
2685 if (!ParseMediaQueryExpression(query)) {
2686 OUTPUT_ERROR();
2687 query->SetHadUnknownExpression();
2688 }
2689 } else {
2690 nsCOMPtr<nsIAtom> mediaType;
2691 bool gotNotOrOnly = false;
2692 for (;;) {
2693 if (!GetToken(true)) {
2694 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
2695 return false;
2696 }
2697 if (eCSSToken_Ident != mToken.mType) {
2698 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
2699 UngetToken();
2700 return false;
2701 }
2702 // case insensitive from CSS - must be lower cased
2703 nsContentUtils::ASCIIToLower(mToken.mIdent);
2704 mediaType = do_GetAtom(mToken.mIdent);
2705 if (!mediaType) {
2706 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
2707 }
2708 if (!gotNotOrOnly && mediaType == nsGkAtoms::_not) {
2709 gotNotOrOnly = true;
2710 query->SetNegated();
2711 } else if (!gotNotOrOnly && mediaType == nsGkAtoms::only) {
2712 gotNotOrOnly = true;
2713 query->SetHasOnly();
2714 } else if (mediaType == nsGkAtoms::_not ||
2715 mediaType == nsGkAtoms::only ||
2716 mediaType == nsGkAtoms::_and ||
2717 mediaType == nsGkAtoms::_or) {
2718 REPORT_UNEXPECTED_TOKEN(PEGatherMediaReservedMediaType);
2719 UngetToken();
2720 return false;
2721 } else {
2722 // valid media type
2723 break;
2724 }
2725 }
2726 query->SetType(mediaType);
2727 }
2729 for (;;) {
2730 if (!GetToken(true)) {
2731 *aHitStop = true;
2732 // expected termination by EOF
2733 if (!aInAtRule)
2734 break;
2736 // unexpected termination by EOF
2737 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
2738 break;
2739 }
2741 if (eCSSToken_Symbol == mToken.mType && aInAtRule &&
2742 (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) {
2743 *aHitStop = true;
2744 UngetToken();
2745 break;
2746 }
2747 if (eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
2748 // Done with the expressions for this query
2749 break;
2750 }
2751 if (eCSSToken_Ident != mToken.mType ||
2752 !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
2753 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
2754 UngetToken();
2755 return false;
2756 }
2757 if (!ParseMediaQueryExpression(query)) {
2758 OUTPUT_ERROR();
2759 query->SetHadUnknownExpression();
2760 }
2761 }
2762 return true;
2763 }
2765 // Returns false only when there is a low-level error in the scanner
2766 // (out-of-memory).
2767 bool
2768 CSSParserImpl::GatherMedia(nsMediaList* aMedia,
2769 bool aInAtRule)
2770 {
2771 for (;;) {
2772 nsAutoPtr<nsMediaQuery> query;
2773 bool hitStop;
2774 if (!ParseMediaQuery(aInAtRule, getter_Transfers(query),
2775 &hitStop)) {
2776 NS_ASSERTION(!hitStop, "should return true when hit stop");
2777 OUTPUT_ERROR();
2778 if (query) {
2779 query->SetHadUnknownExpression();
2780 }
2781 if (aInAtRule) {
2782 const char16_t stopChars[] =
2783 { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) };
2784 SkipUntilOneOf(stopChars);
2785 } else {
2786 SkipUntil(',');
2787 }
2788 // Rely on SkipUntilOneOf leaving mToken around as the last token read.
2789 if (mToken.mType == eCSSToken_Symbol && aInAtRule &&
2790 (mToken.mSymbol == '{' || mToken.mSymbol == ';' || mToken.mSymbol == '}')) {
2791 UngetToken();
2792 hitStop = true;
2793 }
2794 }
2795 if (query) {
2796 aMedia->AppendQuery(query);
2797 }
2798 if (hitStop) {
2799 break;
2800 }
2801 }
2802 return true;
2803 }
2805 bool
2806 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
2807 {
2808 if (!ExpectSymbol('(', true)) {
2809 REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
2810 return false;
2811 }
2812 if (! GetToken(true)) {
2813 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
2814 return false;
2815 }
2816 if (eCSSToken_Ident != mToken.mType) {
2817 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
2818 UngetToken();
2819 SkipUntil(')');
2820 return false;
2821 }
2823 nsMediaExpression *expr = aQuery->NewExpression();
2825 // case insensitive from CSS - must be lower cased
2826 nsContentUtils::ASCIIToLower(mToken.mIdent);
2827 const char16_t *featureString;
2828 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
2829 expr->mRange = nsMediaExpression::eMin;
2830 featureString = mToken.mIdent.get() + 4;
2831 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
2832 expr->mRange = nsMediaExpression::eMax;
2833 featureString = mToken.mIdent.get() + 4;
2834 } else {
2835 expr->mRange = nsMediaExpression::eEqual;
2836 featureString = mToken.mIdent.get();
2837 }
2839 nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
2840 if (!mediaFeatureAtom) {
2841 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
2842 }
2843 const nsMediaFeature *feature = nsMediaFeatures::features;
2844 for (; feature->mName; ++feature) {
2845 if (*(feature->mName) == mediaFeatureAtom) {
2846 break;
2847 }
2848 }
2849 if (!feature->mName ||
2850 (expr->mRange != nsMediaExpression::eEqual &&
2851 feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
2852 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
2853 SkipUntil(')');
2854 return false;
2855 }
2856 expr->mFeature = feature;
2858 if (!GetToken(true) || mToken.IsSymbol(')')) {
2859 // Query expressions for any feature can be given without a value.
2860 // However, min/max prefixes are not allowed.
2861 if (expr->mRange != nsMediaExpression::eEqual) {
2862 REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
2863 return false;
2864 }
2865 expr->mValue.Reset();
2866 return true;
2867 }
2869 if (!mToken.IsSymbol(':')) {
2870 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
2871 UngetToken();
2872 SkipUntil(')');
2873 return false;
2874 }
2876 bool rv = false;
2877 switch (feature->mValueType) {
2878 case nsMediaFeature::eLength:
2879 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_LENGTH, nullptr);
2880 break;
2881 case nsMediaFeature::eInteger:
2882 case nsMediaFeature::eBoolInteger:
2883 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_INTEGER, nullptr);
2884 // Enforce extra restrictions for eBoolInteger
2885 if (rv &&
2886 feature->mValueType == nsMediaFeature::eBoolInteger &&
2887 expr->mValue.GetIntValue() > 1)
2888 rv = false;
2889 break;
2890 case nsMediaFeature::eFloat:
2891 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_NUMBER, nullptr);
2892 break;
2893 case nsMediaFeature::eIntRatio:
2894 {
2895 // Two integers separated by '/', with optional whitespace on
2896 // either side of the '/'.
2897 nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
2898 expr->mValue.SetArrayValue(a, eCSSUnit_Array);
2899 // We don't bother with ParseNonNegativeVariant since we have to
2900 // check for != 0 as well; no need to worry about the UngetToken
2901 // since we're throwing out up to the next ')' anyway.
2902 rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nullptr) &&
2903 a->Item(0).GetIntValue() > 0 &&
2904 ExpectSymbol('/', true) &&
2905 ParseVariant(a->Item(1), VARIANT_INTEGER, nullptr) &&
2906 a->Item(1).GetIntValue() > 0;
2907 }
2908 break;
2909 case nsMediaFeature::eResolution:
2910 rv = GetToken(true);
2911 if (!rv)
2912 break;
2913 rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f;
2914 if (!rv) {
2915 UngetToken();
2916 break;
2917 }
2918 // No worries about whether unitless zero is allowed, since the
2919 // value must be positive (and we checked that above).
2920 NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
2921 if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
2922 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
2923 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) {
2924 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
2925 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
2926 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
2927 } else {
2928 rv = false;
2929 }
2930 break;
2931 case nsMediaFeature::eEnumerated:
2932 rv = ParseVariant(expr->mValue, VARIANT_KEYWORD,
2933 feature->mData.mKeywordTable);
2934 break;
2935 case nsMediaFeature::eIdent:
2936 rv = ParseVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr);
2937 break;
2938 }
2939 if (!rv || !ExpectSymbol(')', true)) {
2940 REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
2941 SkipUntil(')');
2942 return false;
2943 }
2945 return true;
2946 }
2948 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
2949 bool
2950 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
2951 {
2952 nsRefPtr<nsMediaList> media = new nsMediaList();
2954 nsAutoString url;
2955 if (!ParseURLOrString(url)) {
2956 REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
2957 return false;
2958 }
2960 if (!ExpectSymbol(';', true)) {
2961 if (!GatherMedia(media, true) ||
2962 !ExpectSymbol(';', true)) {
2963 REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
2964 // don't advance section, simply ignore invalid @import
2965 return false;
2966 }
2968 // Safe to assert this, since we ensured that there is something
2969 // other than the ';' coming after the @import's url() token.
2970 NS_ASSERTION(media->Length() != 0, "media list must be nonempty");
2971 }
2973 ProcessImport(url, media, aAppendFunc, aData);
2974 return true;
2975 }
2978 void
2979 CSSParserImpl::ProcessImport(const nsString& aURLSpec,
2980 nsMediaList* aMedia,
2981 RuleAppendFunc aAppendFunc,
2982 void* aData)
2983 {
2984 nsRefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec);
2985 (*aAppendFunc)(rule, aData);
2987 // Diagnose bad URIs even if we don't have a child loader.
2988 nsCOMPtr<nsIURI> url;
2989 // Charset will be deduced from mBaseURI, which is more or less correct.
2990 nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI);
2992 if (NS_FAILED(rv)) {
2993 if (rv == NS_ERROR_MALFORMED_URI) {
2994 // import url is bad
2995 REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
2996 OUTPUT_ERROR();
2997 }
2998 return;
2999 }
3001 if (mChildLoader) {
3002 mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
3003 }
3004 }
3006 // Parse the {} part of an @media or @-moz-document rule.
3007 bool
3008 CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
3009 RuleAppendFunc aAppendFunc,
3010 void* aData)
3011 {
3012 // XXXbz this could use better error reporting throughout the method
3013 if (!ExpectSymbol('{', true)) {
3014 return false;
3015 }
3017 // push rule on stack, loop over children
3018 PushGroup(aRule);
3019 nsCSSSection holdSection = mSection;
3020 mSection = eCSSSection_General;
3022 for (;;) {
3023 // Get next non-whitespace token
3024 if (! GetToken(true)) {
3025 REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2);
3026 break;
3027 }
3028 if (mToken.IsSymbol('}')) { // done!
3029 UngetToken();
3030 break;
3031 }
3032 if (eCSSToken_AtKeyword == mToken.mType) {
3033 // Parse for nested rules
3034 ParseAtRule(aAppendFunc, aData, true);
3035 continue;
3036 }
3037 UngetToken();
3038 ParseRuleSet(AppendRuleToSheet, this, true);
3039 }
3040 PopGroup();
3042 if (!ExpectSymbol('}', true)) {
3043 mSection = holdSection;
3044 return false;
3045 }
3046 (*aAppendFunc)(aRule, aData);
3047 return true;
3048 }
3050 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
3051 bool
3052 CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
3053 {
3054 nsRefPtr<nsMediaList> media = new nsMediaList();
3056 if (GatherMedia(media, true)) {
3057 // XXXbz this could use better error reporting throughout the method
3058 nsRefPtr<css::MediaRule> rule = new css::MediaRule();
3059 // Append first, so when we do SetMedia() the rule
3060 // knows what its stylesheet is.
3061 if (ParseGroupRule(rule, aAppendFunc, aData)) {
3062 rule->SetMedia(media);
3063 return true;
3064 }
3065 }
3067 return false;
3068 }
3070 // Parse a @-moz-document rule. This is like an @media rule, but instead
3071 // of a medium it has a nonempty list of items where each item is either
3072 // url(), url-prefix(), or domain().
3073 bool
3074 CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
3075 {
3076 css::DocumentRule::URL *urls = nullptr;
3077 css::DocumentRule::URL **next = &urls;
3078 do {
3079 if (!GetToken(true)) {
3080 REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF);
3081 delete urls;
3082 return false;
3083 }
3085 if (!(eCSSToken_URL == mToken.mType ||
3086 (eCSSToken_Function == mToken.mType &&
3087 (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
3088 mToken.mIdent.LowerCaseEqualsLiteral("domain") ||
3089 mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) {
3090 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc2);
3091 UngetToken();
3092 delete urls;
3093 return false;
3094 }
3095 css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL;
3096 next = &cur->next;
3097 if (mToken.mType == eCSSToken_URL) {
3098 cur->func = css::DocumentRule::eURL;
3099 CopyUTF16toUTF8(mToken.mIdent, cur->url);
3100 } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) {
3101 // regexp() is different from url-prefix() and domain() (but
3102 // probably the way they *should* have been* in that it requires a
3103 // string argument, and doesn't try to behave like url().
3104 cur->func = css::DocumentRule::eRegExp;
3105 GetToken(true);
3106 // copy before we know it's valid (but before ExpectSymbol changes
3107 // mToken.mIdent)
3108 CopyUTF16toUTF8(mToken.mIdent, cur->url);
3109 if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) {
3110 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString);
3111 SkipUntil(')');
3112 delete urls;
3113 return false;
3114 }
3115 } else {
3116 if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
3117 cur->func = css::DocumentRule::eURLPrefix;
3118 } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
3119 cur->func = css::DocumentRule::eDomain;
3120 }
3122 NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point");
3123 if (!mScanner->NextURL(mToken) || mToken.mType != eCSSToken_URL) {
3124 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
3125 SkipUntil(')');
3126 delete urls;
3127 return false;
3128 }
3130 // We could try to make the URL (as long as it's not domain())
3131 // canonical and absolute with NS_NewURI and GetSpec, but I'm
3132 // inclined to think we shouldn't.
3133 CopyUTF16toUTF8(mToken.mIdent, cur->url);
3134 }
3135 } while (ExpectSymbol(',', true));
3137 nsRefPtr<css::DocumentRule> rule = new css::DocumentRule();
3138 rule->SetURLs(urls);
3140 return ParseGroupRule(rule, aAppendFunc, aData);
3141 }
3143 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
3144 bool
3145 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
3146 {
3147 if (!GetToken(true)) {
3148 REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
3149 return false;
3150 }
3152 nsAutoString prefix;
3153 nsAutoString url;
3155 if (eCSSToken_Ident == mToken.mType) {
3156 prefix = mToken.mIdent;
3157 // user-specified identifiers are case-sensitive (bug 416106)
3158 } else {
3159 UngetToken();
3160 }
3162 if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) {
3163 if (mHavePushBack) {
3164 REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
3165 } else {
3166 REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
3167 }
3168 return false;
3169 }
3171 ProcessNameSpace(prefix, url, aAppendFunc, aData);
3172 return true;
3173 }
3175 void
3176 CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
3177 const nsString& aURLSpec,
3178 RuleAppendFunc aAppendFunc,
3179 void* aData)
3180 {
3181 nsCOMPtr<nsIAtom> prefix;
3183 if (!aPrefix.IsEmpty()) {
3184 prefix = do_GetAtom(aPrefix);
3185 if (!prefix) {
3186 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
3187 }
3188 }
3190 nsRefPtr<css::NameSpaceRule> rule = new css::NameSpaceRule(prefix, aURLSpec);
3191 (*aAppendFunc)(rule, aData);
3193 // If this was the first namespace rule encountered, it will trigger
3194 // creation of a namespace map.
3195 if (!mNameSpaceMap) {
3196 mNameSpaceMap = mSheet->GetNameSpaceMap();
3197 }
3198 }
3200 // font-face-rule: '@font-face' '{' font-description '}'
3201 // font-description: font-descriptor+
3202 bool
3203 CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
3204 {
3205 if (!ExpectSymbol('{', true)) {
3206 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart);
3207 return false;
3208 }
3210 nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule());
3212 for (;;) {
3213 if (!GetToken(true)) {
3214 REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
3215 break;
3216 }
3217 if (mToken.IsSymbol('}')) { // done!
3218 UngetToken();
3219 break;
3220 }
3222 // ignore extra semicolons
3223 if (mToken.IsSymbol(';'))
3224 continue;
3226 if (!ParseFontDescriptor(rule)) {
3227 REPORT_UNEXPECTED(PEDeclSkipped);
3228 OUTPUT_ERROR();
3229 if (!SkipDeclaration(true))
3230 break;
3231 }
3232 }
3233 if (!ExpectSymbol('}', true)) {
3234 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd);
3235 return false;
3236 }
3237 (*aAppendFunc)(rule, aData);
3238 return true;
3239 }
3241 // font-descriptor: font-family-desc
3242 // | font-style-desc
3243 // | font-weight-desc
3244 // | font-stretch-desc
3245 // | font-src-desc
3246 // | unicode-range-desc
3247 //
3248 // All font-*-desc productions follow the pattern
3249 // IDENT ':' value ';'
3250 //
3251 // On entry to this function, mToken is the IDENT.
3253 bool
3254 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
3255 {
3256 if (eCSSToken_Ident != mToken.mType) {
3257 REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
3258 return false;
3259 }
3261 nsString descName = mToken.mIdent;
3262 if (!ExpectSymbol(':', true)) {
3263 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3264 OUTPUT_ERROR();
3265 return false;
3266 }
3268 nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
3269 nsCSSValue value;
3271 if (descID == eCSSFontDesc_UNKNOWN) {
3272 if (NonMozillaVendorIdentifier(descName)) {
3273 // silently skip other vendors' extensions
3274 SkipDeclaration(true);
3275 return true;
3276 } else {
3277 REPORT_UNEXPECTED_P(PEUnknownFontDesc, descName);
3278 return false;
3279 }
3280 }
3282 if (!ParseFontDescriptorValue(descID, value)) {
3283 REPORT_UNEXPECTED_P(PEValueParsingError, descName);
3284 return false;
3285 }
3287 if (!ExpectEndProperty())
3288 return false;
3290 aRule->SetDesc(descID, value);
3291 return true;
3292 }
3294 // @font-feature-values <font-family># {
3295 // @<feature-type> {
3296 // <feature-ident> : <feature-index>+;
3297 // <feature-ident> : <feature-index>+;
3298 // ...
3299 // }
3300 // ...
3301 // }
3303 bool
3304 CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
3305 void* aData)
3306 {
3307 nsRefPtr<nsCSSFontFeatureValuesRule>
3308 valuesRule(new nsCSSFontFeatureValuesRule());
3310 // parse family list
3311 nsCSSValue familyValue;
3313 if (!ParseFamily(familyValue) ||
3314 familyValue.GetUnit() != eCSSUnit_Families)
3315 {
3316 REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily);
3317 return false;
3318 }
3320 // add family to rule
3321 nsAutoString familyList;
3322 bool hasGeneric;
3323 familyValue.GetStringValue(familyList);
3324 valuesRule->SetFamilyList(familyList, hasGeneric);
3326 // family list has generic ==> parse error
3327 if (hasGeneric) {
3328 REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList);
3329 return false;
3330 }
3332 // open brace
3333 if (!ExpectSymbol('{', true)) {
3334 REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart);
3335 return false;
3336 }
3338 // list of sets of feature values, each set bound to a specific
3339 // feature-type (e.g. swash, annotation)
3340 for (;;) {
3341 if (!GetToken(true)) {
3342 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
3343 break;
3344 }
3345 if (mToken.IsSymbol('}')) { // done!
3346 UngetToken();
3347 break;
3348 }
3350 if (!ParseFontFeatureValueSet(valuesRule)) {
3351 if (!SkipAtRule(false)) {
3352 break;
3353 }
3354 }
3355 }
3356 if (!ExpectSymbol('}', true)) {
3357 REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd);
3358 SkipUntil('}');
3359 return false;
3360 }
3362 (*aAppendFunc)(valuesRule, aData);
3363 return true;
3364 }
3366 #define NUMVALUES_NO_LIMIT 0xFFFF
3368 // parse a single value set containing name-value pairs for a single feature type
3369 // @<feature-type> { [ <feature-ident> : <feature-index>+ ; ]* }
3370 // Ex: @swash { flowing: 1; delicate: 2; }
3371 bool
3372 CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule
3373 *aFeatureValuesRule)
3374 {
3375 // -- @keyword (e.g. swash, styleset)
3376 if (eCSSToken_AtKeyword != mToken.mType) {
3377 REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt);
3378 OUTPUT_ERROR();
3379 UngetToken();
3380 return false;
3381 }
3383 // which font-specific variant of font-variant-alternates
3384 int32_t whichVariant;
3385 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
3386 if (keyword == eCSSKeyword_UNKNOWN ||
3387 !nsCSSProps::FindKeyword(keyword,
3388 nsCSSProps::kFontVariantAlternatesFuncsKTable,
3389 whichVariant))
3390 {
3391 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
3392 REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue);
3393 OUTPUT_ERROR();
3394 }
3395 UngetToken();
3396 return false;
3397 }
3399 nsAutoString featureType(mToken.mIdent);
3401 // open brace
3402 if (!ExpectSymbol('{', true)) {
3403 REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart);
3404 return false;
3405 }
3407 // styleset and character-variant can be multi-valued, otherwise single value
3408 int32_t limitNumValues;
3410 switch (keyword) {
3411 case eCSSKeyword_styleset:
3412 limitNumValues = NUMVALUES_NO_LIMIT;
3413 break;
3414 case eCSSKeyword_character_variant:
3415 limitNumValues = 2;
3416 break;
3417 default:
3418 limitNumValues = 1;
3419 break;
3420 }
3422 // -- ident integer+ [, ident integer+]
3423 nsAutoTArray<gfxFontFeatureValueSet::ValueList, 5> values;
3425 // list of font-feature-values-declaration's
3426 for (;;) {
3427 nsAutoString valueId;
3429 if (!GetToken(true)) {
3430 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
3431 break;
3432 }
3434 // ignore extra semicolons
3435 if (mToken.IsSymbol(';')) {
3436 continue;
3437 }
3439 // close brace ==> done
3440 if (mToken.IsSymbol('}')) {
3441 break;
3442 }
3444 // ident
3445 if (eCSSToken_Ident != mToken.mType) {
3446 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent);
3447 if (!SkipDeclaration(true)) {
3448 break;
3449 }
3450 continue;
3451 }
3453 valueId.Assign(mToken.mIdent);
3455 // colon
3456 if (!ExpectSymbol(':', true)) {
3457 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3458 OUTPUT_ERROR();
3459 if (!SkipDeclaration(true)) {
3460 break;
3461 }
3462 continue;
3463 }
3465 // value list
3466 nsAutoTArray<uint32_t,4> featureSelectors;
3468 nsCSSValue intValue;
3469 while (ParseNonNegativeVariant(intValue, VARIANT_INTEGER, nullptr)) {
3470 featureSelectors.AppendElement(uint32_t(intValue.GetIntValue()));
3471 }
3473 int32_t numValues = featureSelectors.Length();
3475 if (numValues == 0) {
3476 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue);
3477 OUTPUT_ERROR();
3478 if (!SkipDeclaration(true)) {
3479 break;
3480 }
3481 continue;
3482 }
3484 if (numValues > limitNumValues) {
3485 REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType);
3486 OUTPUT_ERROR();
3487 if (!SkipDeclaration(true)) {
3488 break;
3489 }
3490 continue;
3491 }
3493 if (!GetToken(true)) {
3494 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
3495 gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
3496 values.AppendElement(v);
3497 break;
3498 }
3500 // ';' or '}' to end definition
3501 if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) {
3502 REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing);
3503 OUTPUT_ERROR();
3504 if (!SkipDeclaration(true)) {
3505 break;
3506 }
3507 continue;
3508 }
3510 gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
3511 values.AppendElement(v);
3513 if (mToken.IsSymbol('}')) {
3514 break;
3515 }
3516 }
3518 aFeatureValuesRule->AddValueList(whichVariant, values);
3519 return true;
3520 }
3522 bool
3523 CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData)
3524 {
3525 if (!GetToken(true)) {
3526 REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF);
3527 return false;
3528 }
3530 if (mToken.mType != eCSSToken_Ident) {
3531 REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
3532 UngetToken();
3533 return false;
3534 }
3535 nsString name(mToken.mIdent);
3537 if (!ExpectSymbol('{', true)) {
3538 REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace);
3539 return false;
3540 }
3542 nsRefPtr<nsCSSKeyframesRule> rule = new nsCSSKeyframesRule(name);
3544 while (!ExpectSymbol('}', true)) {
3545 nsRefPtr<nsCSSKeyframeRule> kid = ParseKeyframeRule();
3546 if (kid) {
3547 rule->AppendStyleRule(kid);
3548 } else {
3549 OUTPUT_ERROR();
3550 SkipRuleSet(true);
3551 }
3552 }
3554 (*aAppendFunc)(rule, aData);
3555 return true;
3556 }
3558 bool
3559 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
3560 {
3561 // TODO: There can be page selectors after @page such as ":first", ":left".
3562 uint32_t parseFlags = eParseDeclaration_InBraces |
3563 eParseDeclaration_AllowImportant;
3565 // Forbid viewport units in @page rules. See bug 811391.
3566 NS_ABORT_IF_FALSE(mViewportUnitsEnabled,
3567 "Viewport units should be enabled outside of @page rules.");
3568 mViewportUnitsEnabled = false;
3569 nsAutoPtr<css::Declaration> declaration(
3570 ParseDeclarationBlock(parseFlags,
3571 eCSSContext_Page));
3572 mViewportUnitsEnabled = true;
3574 if (!declaration) {
3575 return false;
3576 }
3578 // Takes ownership of declaration.
3579 nsRefPtr<nsCSSPageRule> rule = new nsCSSPageRule(declaration);
3581 (*aAppendFunc)(rule, aData);
3582 return true;
3583 }
3585 already_AddRefed<nsCSSKeyframeRule>
3586 CSSParserImpl::ParseKeyframeRule()
3587 {
3588 InfallibleTArray<float> selectorList;
3589 if (!ParseKeyframeSelectorList(selectorList)) {
3590 REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
3591 return nullptr;
3592 }
3594 // Ignore !important in keyframe rules
3595 uint32_t parseFlags = eParseDeclaration_InBraces;
3596 nsAutoPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags));
3597 if (!declaration) {
3598 return nullptr;
3599 }
3601 // Takes ownership of declaration, and steals contents of selectorList.
3602 nsRefPtr<nsCSSKeyframeRule> rule =
3603 new nsCSSKeyframeRule(selectorList, declaration);
3605 return rule.forget();
3606 }
3608 bool
3609 CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList)
3610 {
3611 for (;;) {
3612 if (!GetToken(true)) {
3613 // The first time through the loop, this means we got an empty
3614 // list. Otherwise, it means we have a trailing comma.
3615 return false;
3616 }
3617 float value;
3618 switch (mToken.mType) {
3619 case eCSSToken_Percentage:
3620 value = mToken.mNumber;
3621 break;
3622 case eCSSToken_Ident:
3623 if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
3624 value = 0.0f;
3625 break;
3626 }
3627 if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
3628 value = 1.0f;
3629 break;
3630 }
3631 // fall through
3632 default:
3633 UngetToken();
3634 // The first time through the loop, this means we got an empty
3635 // list. Otherwise, it means we have a trailing comma.
3636 return false;
3637 }
3638 aSelectorList.AppendElement(value);
3639 if (!ExpectSymbol(',', true)) {
3640 return true;
3641 }
3642 }
3643 }
3645 // supports_rule
3646 // : "@supports" supports_condition group_rule_body
3647 // ;
3648 bool
3649 CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData)
3650 {
3651 bool conditionMet = false;
3652 nsString condition;
3654 mScanner->StartRecording();
3655 bool parsed = ParseSupportsCondition(conditionMet);
3657 if (!parsed) {
3658 mScanner->StopRecording();
3659 return false;
3660 }
3662 if (!ExpectSymbol('{', true)) {
3663 REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart);
3664 mScanner->StopRecording();
3665 return false;
3666 }
3668 UngetToken();
3669 mScanner->StopRecording(condition);
3671 // Remove the "{" that would follow the condition.
3672 if (condition.Length() != 0) {
3673 condition.Truncate(condition.Length() - 1);
3674 }
3676 // Remove spaces from the start and end of the recorded supports condition.
3677 condition.Trim(" ", true, true, false);
3679 // Record whether we are in a failing @supports, so that property parse
3680 // errors don't get reported.
3681 nsAutoFailingSupportsRule failing(this, conditionMet);
3683 nsRefPtr<css::GroupRule> rule = new CSSSupportsRule(conditionMet, condition);
3684 return ParseGroupRule(rule, aAppendFunc, aProcessData);
3685 }
3687 // supports_condition
3688 // : supports_condition_in_parens supports_condition_terms
3689 // | supports_condition_negation
3690 // ;
3691 bool
3692 CSSParserImpl::ParseSupportsCondition(bool& aConditionMet)
3693 {
3694 mInSupportsCondition = true;
3696 if (!GetToken(true)) {
3697 REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2);
3698 return false;
3699 }
3701 UngetToken();
3703 mScanner->ClearSeenBadToken();
3705 if (mToken.IsSymbol('(') ||
3706 mToken.mType == eCSSToken_Function ||
3707 mToken.mType == eCSSToken_URL ||
3708 mToken.mType == eCSSToken_Bad_URL) {
3709 bool result = ParseSupportsConditionInParens(aConditionMet) &&
3710 ParseSupportsConditionTerms(aConditionMet) &&
3711 !mScanner->SeenBadToken();
3712 mInSupportsCondition = false;
3713 return result;
3714 }
3716 if (mToken.mType == eCSSToken_Ident &&
3717 mToken.mIdent.LowerCaseEqualsLiteral("not")) {
3718 bool result = ParseSupportsConditionNegation(aConditionMet) &&
3719 !mScanner->SeenBadToken();
3720 mInSupportsCondition = false;
3721 return result;
3722 }
3724 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart);
3725 mInSupportsCondition = false;
3726 return false;
3727 }
3729 // supports_condition_negation
3730 // : 'not' S+ supports_condition_in_parens
3731 // ;
3732 bool
3733 CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet)
3734 {
3735 if (!GetToken(true)) {
3736 REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF);
3737 return false;
3738 }
3740 if (mToken.mType != eCSSToken_Ident ||
3741 !mToken.mIdent.LowerCaseEqualsLiteral("not")) {
3742 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot);
3743 return false;
3744 }
3746 if (!RequireWhitespace()) {
3747 REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
3748 return false;
3749 }
3751 if (ParseSupportsConditionInParens(aConditionMet)) {
3752 aConditionMet = !aConditionMet;
3753 return true;
3754 }
3756 return false;
3757 }
3759 // supports_condition_in_parens
3760 // : '(' S* supports_condition_in_parens_inside_parens ')' S*
3761 // | general_enclosed
3762 // ;
3763 bool
3764 CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet)
3765 {
3766 if (!GetToken(true)) {
3767 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF);
3768 return false;
3769 }
3771 if (mToken.mType == eCSSToken_URL) {
3772 aConditionMet = false;
3773 return true;
3774 }
3776 if (mToken.mType == eCSSToken_Function ||
3777 mToken.mType == eCSSToken_Bad_URL) {
3778 if (!SkipUntil(')')) {
3779 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
3780 return false;
3781 }
3782 aConditionMet = false;
3783 return true;
3784 }
3786 if (!mToken.IsSymbol('(')) {
3787 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction);
3788 UngetToken();
3789 return false;
3790 }
3792 if (!ParseSupportsConditionInParensInsideParens(aConditionMet)) {
3793 if (!SkipUntil(')')) {
3794 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
3795 return false;
3796 }
3797 aConditionMet = false;
3798 return true;
3799 }
3801 if (!(ExpectSymbol(')', true))) {
3802 SkipUntil(')');
3803 aConditionMet = false;
3804 return true;
3805 }
3807 return true;
3808 }
3810 // supports_condition_in_parens_inside_parens
3811 // : core_declaration
3812 // | supports_condition_negation
3813 // | supports_condition_in_parens supports_condition_terms
3814 // ;
3815 bool
3816 CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet)
3817 {
3818 if (!GetToken(true)) {
3819 return false;
3820 }
3822 if (mToken.mType == eCSSToken_Ident) {
3823 if (!mToken.mIdent.LowerCaseEqualsLiteral("not")) {
3824 nsAutoString propertyName = mToken.mIdent;
3825 if (!ExpectSymbol(':', true)) {
3826 return false;
3827 }
3829 nsCSSProperty propID = LookupEnabledProperty(propertyName);
3830 if (propID == eCSSProperty_UNKNOWN) {
3831 if (ExpectSymbol(')', true)) {
3832 UngetToken();
3833 return false;
3834 }
3835 aConditionMet = false;
3836 SkipUntil(')');
3837 UngetToken();
3838 } else if (propID == eCSSPropertyExtra_variable) {
3839 if (ExpectSymbol(')', false)) {
3840 UngetToken();
3841 return false;
3842 }
3843 CSSVariableDeclarations::Type variableType;
3844 nsString variableValue;
3845 aConditionMet =
3846 ParseVariableDeclaration(&variableType, variableValue) &&
3847 ParsePriority() != ePriority_Error;
3848 if (!aConditionMet) {
3849 SkipUntil(')');
3850 UngetToken();
3851 }
3852 } else {
3853 if (ExpectSymbol(')', true)) {
3854 UngetToken();
3855 return false;
3856 }
3857 aConditionMet = ParseProperty(propID) &&
3858 ParsePriority() != ePriority_Error;
3859 if (!aConditionMet) {
3860 SkipUntil(')');
3861 UngetToken();
3862 }
3863 mTempData.ClearProperty(propID);
3864 mTempData.AssertInitialState();
3865 }
3866 return true;
3867 }
3869 UngetToken();
3870 return ParseSupportsConditionNegation(aConditionMet);
3871 }
3873 UngetToken();
3874 return ParseSupportsConditionInParens(aConditionMet) &&
3875 ParseSupportsConditionTerms(aConditionMet);
3876 }
3878 // supports_condition_terms
3879 // : S+ 'and' supports_condition_terms_after_operator('and')
3880 // | S+ 'or' supports_condition_terms_after_operator('or')
3881 // |
3882 // ;
3883 bool
3884 CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet)
3885 {
3886 if (!RequireWhitespace() || !GetToken(false)) {
3887 return true;
3888 }
3890 if (mToken.mType != eCSSToken_Ident) {
3891 UngetToken();
3892 return true;
3893 }
3895 if (mToken.mIdent.LowerCaseEqualsLiteral("and")) {
3896 return ParseSupportsConditionTermsAfterOperator(aConditionMet, eAnd);
3897 }
3899 if (mToken.mIdent.LowerCaseEqualsLiteral("or")) {
3900 return ParseSupportsConditionTermsAfterOperator(aConditionMet, eOr);
3901 }
3903 UngetToken();
3904 return true;
3905 }
3907 // supports_condition_terms_after_operator(operator)
3908 // : S+ supports_condition_in_parens ( <operator> supports_condition_in_parens )*
3909 // ;
3910 bool
3911 CSSParserImpl::ParseSupportsConditionTermsAfterOperator(
3912 bool& aConditionMet,
3913 CSSParserImpl::SupportsConditionTermOperator aOperator)
3914 {
3915 if (!RequireWhitespace()) {
3916 REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
3917 return false;
3918 }
3920 const char* token = aOperator == eAnd ? "and" : "or";
3921 for (;;) {
3922 bool termConditionMet = false;
3923 if (!ParseSupportsConditionInParens(termConditionMet)) {
3924 return false;
3925 }
3926 aConditionMet = aOperator == eAnd ? aConditionMet && termConditionMet :
3927 aConditionMet || termConditionMet;
3929 if (!GetToken(true)) {
3930 return true;
3931 }
3933 if (mToken.mType != eCSSToken_Ident ||
3934 !mToken.mIdent.LowerCaseEqualsASCII(token)) {
3935 UngetToken();
3936 return true;
3937 }
3938 }
3939 }
3941 bool
3942 CSSParserImpl::SkipUntil(char16_t aStopSymbol)
3943 {
3944 nsCSSToken* tk = &mToken;
3945 nsAutoTArray<char16_t, 16> stack;
3946 stack.AppendElement(aStopSymbol);
3947 for (;;) {
3948 if (!GetToken(true)) {
3949 return false;
3950 }
3951 if (eCSSToken_Symbol == tk->mType) {
3952 char16_t symbol = tk->mSymbol;
3953 uint32_t stackTopIndex = stack.Length() - 1;
3954 if (symbol == stack.ElementAt(stackTopIndex)) {
3955 stack.RemoveElementAt(stackTopIndex);
3956 if (stackTopIndex == 0) {
3957 return true;
3958 }
3960 // Just handle out-of-memory by parsing incorrectly. It's
3961 // highly unlikely we're dealing with a legitimate style sheet
3962 // anyway.
3963 } else if ('{' == symbol) {
3964 stack.AppendElement('}');
3965 } else if ('[' == symbol) {
3966 stack.AppendElement(']');
3967 } else if ('(' == symbol) {
3968 stack.AppendElement(')');
3969 }
3970 } else if (eCSSToken_Function == tk->mType ||
3971 eCSSToken_Bad_URL == tk->mType) {
3972 stack.AppendElement(')');
3973 }
3974 }
3975 }
3977 bool
3978 CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol)
3979 {
3980 nsCSSToken* tk = &mToken;
3981 nsAutoTArray<char16_t, 16> stack;
3982 stack.AppendElement(aStopSymbol);
3983 for (;;) {
3984 if (!GetToken(true)) {
3985 return true;
3986 }
3987 if (eCSSToken_Symbol == tk->mType) {
3988 char16_t symbol = tk->mSymbol;
3989 uint32_t stackTopIndex = stack.Length() - 1;
3990 if (symbol == stack.ElementAt(stackTopIndex)) {
3991 stack.RemoveElementAt(stackTopIndex);
3992 if (stackTopIndex == 0) {
3993 return true;
3994 }
3996 // Just handle out-of-memory by parsing incorrectly. It's
3997 // highly unlikely we're dealing with a legitimate style sheet
3998 // anyway.
3999 } else if ('{' == symbol) {
4000 stack.AppendElement('}');
4001 } else if ('[' == symbol) {
4002 stack.AppendElement(']');
4003 } else if ('(' == symbol) {
4004 stack.AppendElement(')');
4005 } else if (')' == symbol ||
4006 ']' == symbol ||
4007 '}' == symbol) {
4008 UngetToken();
4009 return false;
4010 }
4011 } else if (eCSSToken_Function == tk->mType ||
4012 eCSSToken_Bad_URL == tk->mType) {
4013 stack.AppendElement(')');
4014 }
4015 }
4016 }
4018 void
4019 CSSParserImpl::SkipUntilOneOf(const char16_t* aStopSymbolChars)
4020 {
4021 nsCSSToken* tk = &mToken;
4022 nsDependentString stopSymbolChars(aStopSymbolChars);
4023 for (;;) {
4024 if (!GetToken(true)) {
4025 break;
4026 }
4027 if (eCSSToken_Symbol == tk->mType) {
4028 char16_t symbol = tk->mSymbol;
4029 if (stopSymbolChars.FindChar(symbol) != -1) {
4030 break;
4031 } else if ('{' == symbol) {
4032 SkipUntil('}');
4033 } else if ('[' == symbol) {
4034 SkipUntil(']');
4035 } else if ('(' == symbol) {
4036 SkipUntil(')');
4037 }
4038 } else if (eCSSToken_Function == tk->mType ||
4039 eCSSToken_Bad_URL == tk->mType) {
4040 SkipUntil(')');
4041 }
4042 }
4043 }
4045 void
4046 CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars)
4047 {
4048 uint32_t i = aStopSymbolChars.Length();
4049 while (i--) {
4050 SkipUntil(aStopSymbolChars[i]);
4051 }
4052 }
4054 bool
4055 CSSParserImpl::SkipDeclaration(bool aCheckForBraces)
4056 {
4057 nsCSSToken* tk = &mToken;
4058 for (;;) {
4059 if (!GetToken(true)) {
4060 if (aCheckForBraces) {
4061 REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
4062 }
4063 return false;
4064 }
4065 if (eCSSToken_Symbol == tk->mType) {
4066 char16_t symbol = tk->mSymbol;
4067 if (';' == symbol) {
4068 break;
4069 }
4070 if (aCheckForBraces) {
4071 if ('}' == symbol) {
4072 UngetToken();
4073 break;
4074 }
4075 }
4076 if ('{' == symbol) {
4077 SkipUntil('}');
4078 } else if ('(' == symbol) {
4079 SkipUntil(')');
4080 } else if ('[' == symbol) {
4081 SkipUntil(']');
4082 }
4083 } else if (eCSSToken_Function == tk->mType ||
4084 eCSSToken_Bad_URL == tk->mType) {
4085 SkipUntil(')');
4086 }
4087 }
4088 return true;
4089 }
4091 void
4092 CSSParserImpl::SkipRuleSet(bool aInsideBraces)
4093 {
4094 nsCSSToken* tk = &mToken;
4095 for (;;) {
4096 if (!GetToken(true)) {
4097 REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
4098 break;
4099 }
4100 if (eCSSToken_Symbol == tk->mType) {
4101 char16_t symbol = tk->mSymbol;
4102 if ('}' == symbol && aInsideBraces) {
4103 // leave block closer for higher-level grammar to consume
4104 UngetToken();
4105 break;
4106 } else if ('{' == symbol) {
4107 SkipUntil('}');
4108 break;
4109 } else if ('(' == symbol) {
4110 SkipUntil(')');
4111 } else if ('[' == symbol) {
4112 SkipUntil(']');
4113 }
4114 } else if (eCSSToken_Function == tk->mType ||
4115 eCSSToken_Bad_URL == tk->mType) {
4116 SkipUntil(')');
4117 }
4118 }
4119 }
4121 void
4122 CSSParserImpl::PushGroup(css::GroupRule* aRule)
4123 {
4124 mGroupStack.AppendElement(aRule);
4125 }
4127 void
4128 CSSParserImpl::PopGroup()
4129 {
4130 uint32_t count = mGroupStack.Length();
4131 if (0 < count) {
4132 mGroupStack.RemoveElementAt(count - 1);
4133 }
4134 }
4136 void
4137 CSSParserImpl::AppendRule(css::Rule* aRule)
4138 {
4139 uint32_t count = mGroupStack.Length();
4140 if (0 < count) {
4141 mGroupStack[count - 1]->AppendStyleRule(aRule);
4142 }
4143 else {
4144 mSheet->AppendStyleRule(aRule);
4145 }
4146 }
4148 bool
4149 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
4150 bool aInsideBraces)
4151 {
4152 // First get the list of selectors for the rule
4153 nsCSSSelectorList* slist = nullptr;
4154 uint32_t linenum, colnum;
4155 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4156 !ParseSelectorList(slist, char16_t('{'))) {
4157 REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
4158 OUTPUT_ERROR();
4159 SkipRuleSet(aInsideBraces);
4160 return false;
4161 }
4162 NS_ASSERTION(nullptr != slist, "null selector list");
4163 CLEAR_ERROR();
4165 // Next parse the declaration block
4166 uint32_t parseFlags = eParseDeclaration_InBraces |
4167 eParseDeclaration_AllowImportant;
4168 css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
4169 if (nullptr == declaration) {
4170 delete slist;
4171 return false;
4172 }
4174 #if 0
4175 slist->Dump();
4176 fputs("{\n", stdout);
4177 declaration->List();
4178 fputs("}\n", stdout);
4179 #endif
4181 // Translate the selector list and declaration block into style data
4183 nsRefPtr<css::StyleRule> rule = new css::StyleRule(slist, declaration);
4184 rule->SetLineNumberAndColumnNumber(linenum, colnum);
4185 (*aAppendFunc)(rule, aData);
4187 return true;
4188 }
4190 bool
4191 CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
4192 char16_t aStopChar)
4193 {
4194 nsCSSSelectorList* list = nullptr;
4195 if (! ParseSelectorGroup(list)) {
4196 // must have at least one selector group
4197 aListHead = nullptr;
4198 return false;
4199 }
4200 NS_ASSERTION(nullptr != list, "no selector list");
4201 aListHead = list;
4203 // After that there must either be a "," or a "{" (the latter if
4204 // StopChar is nonzero)
4205 nsCSSToken* tk = &mToken;
4206 for (;;) {
4207 if (! GetToken(true)) {
4208 if (aStopChar == char16_t(0)) {
4209 return true;
4210 }
4212 REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
4213 break;
4214 }
4216 if (eCSSToken_Symbol == tk->mType) {
4217 if (',' == tk->mSymbol) {
4218 nsCSSSelectorList* newList = nullptr;
4219 // Another selector group must follow
4220 if (! ParseSelectorGroup(newList)) {
4221 break;
4222 }
4223 // add new list to the end of the selector list
4224 list->mNext = newList;
4225 list = newList;
4226 continue;
4227 } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) {
4228 UngetToken();
4229 return true;
4230 }
4231 }
4232 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
4233 UngetToken();
4234 break;
4235 }
4237 delete aListHead;
4238 aListHead = nullptr;
4239 return false;
4240 }
4242 static bool IsUniversalSelector(const nsCSSSelector& aSelector)
4243 {
4244 return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
4245 (aSelector.mLowercaseTag == nullptr) &&
4246 (aSelector.mIDList == nullptr) &&
4247 (aSelector.mClassList == nullptr) &&
4248 (aSelector.mAttrList == nullptr) &&
4249 (aSelector.mNegations == nullptr) &&
4250 (aSelector.mPseudoClassList == nullptr));
4251 }
4253 bool
4254 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
4255 {
4256 char16_t combinator = 0;
4257 nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
4259 for (;;) {
4260 if (!ParseSelector(list, combinator)) {
4261 return false;
4262 }
4264 // Look for a combinator.
4265 if (!GetToken(false)) {
4266 break; // EOF ok here
4267 }
4269 combinator = char16_t(0);
4270 if (mToken.mType == eCSSToken_Whitespace) {
4271 if (!GetToken(true)) {
4272 break; // EOF ok here
4273 }
4274 combinator = char16_t(' ');
4275 }
4277 if (mToken.mType != eCSSToken_Symbol) {
4278 UngetToken(); // not a combinator
4279 } else {
4280 char16_t symbol = mToken.mSymbol;
4281 if (symbol == '+' || symbol == '>' || symbol == '~') {
4282 combinator = mToken.mSymbol;
4283 } else {
4284 UngetToken(); // not a combinator
4285 if (symbol == ',' || symbol == '{' || symbol == ')') {
4286 break; // end of selector group
4287 }
4288 }
4289 }
4291 if (!combinator) {
4292 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
4293 return false;
4294 }
4295 }
4297 aList = list.forget();
4298 return true;
4299 }
4301 #define SEL_MASK_NSPACE 0x01
4302 #define SEL_MASK_ELEM 0x02
4303 #define SEL_MASK_ID 0x04
4304 #define SEL_MASK_CLASS 0x08
4305 #define SEL_MASK_ATTRIB 0x10
4306 #define SEL_MASK_PCLASS 0x20
4307 #define SEL_MASK_PELEM 0x40
4309 //
4310 // Parses an ID selector #name
4311 //
4312 CSSParserImpl::nsSelectorParsingStatus
4313 CSSParserImpl::ParseIDSelector(int32_t& aDataMask,
4314 nsCSSSelector& aSelector)
4315 {
4316 NS_ASSERTION(!mToken.mIdent.IsEmpty(),
4317 "Empty mIdent in eCSSToken_ID token?");
4318 aDataMask |= SEL_MASK_ID;
4319 aSelector.AddID(mToken.mIdent);
4320 return eSelectorParsingStatus_Continue;
4321 }
4323 //
4324 // Parses a class selector .name
4325 //
4326 CSSParserImpl::nsSelectorParsingStatus
4327 CSSParserImpl::ParseClassSelector(int32_t& aDataMask,
4328 nsCSSSelector& aSelector)
4329 {
4330 if (! GetToken(false)) { // get ident
4331 REPORT_UNEXPECTED_EOF(PEClassSelEOF);
4332 return eSelectorParsingStatus_Error;
4333 }
4334 if (eCSSToken_Ident != mToken.mType) { // malformed selector
4335 REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
4336 UngetToken();
4337 return eSelectorParsingStatus_Error;
4338 }
4339 aDataMask |= SEL_MASK_CLASS;
4341 aSelector.AddClass(mToken.mIdent);
4343 return eSelectorParsingStatus_Continue;
4344 }
4346 //
4347 // Parse a type element selector or a universal selector
4348 // namespace|type or namespace|* or *|* or *
4349 //
4350 CSSParserImpl::nsSelectorParsingStatus
4351 CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
4352 nsCSSSelector& aSelector,
4353 bool aIsNegated)
4354 {
4355 nsAutoString buffer;
4356 if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
4357 if (ExpectSymbol('|', false)) { // was namespace
4358 aDataMask |= SEL_MASK_NSPACE;
4359 aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
4361 if (! GetToken(false)) {
4362 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
4363 return eSelectorParsingStatus_Error;
4364 }
4365 if (eCSSToken_Ident == mToken.mType) { // element name
4366 aDataMask |= SEL_MASK_ELEM;
4368 aSelector.SetTag(mToken.mIdent);
4369 }
4370 else if (mToken.IsSymbol('*')) { // universal selector
4371 aDataMask |= SEL_MASK_ELEM;
4372 // don't set tag
4373 }
4374 else {
4375 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
4376 UngetToken();
4377 return eSelectorParsingStatus_Error;
4378 }
4379 }
4380 else { // was universal element selector
4381 SetDefaultNamespaceOnSelector(aSelector);
4382 aDataMask |= SEL_MASK_ELEM;
4383 // don't set any tag in the selector
4384 }
4385 if (! GetToken(false)) { // premature eof is ok (here!)
4386 return eSelectorParsingStatus_Done;
4387 }
4388 }
4389 else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
4390 buffer = mToken.mIdent; // hang on to ident
4392 if (ExpectSymbol('|', false)) { // was namespace
4393 aDataMask |= SEL_MASK_NSPACE;
4394 int32_t nameSpaceID = GetNamespaceIdForPrefix(buffer);
4395 if (nameSpaceID == kNameSpaceID_Unknown) {
4396 return eSelectorParsingStatus_Error;
4397 }
4398 aSelector.SetNameSpace(nameSpaceID);
4400 if (! GetToken(false)) {
4401 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
4402 return eSelectorParsingStatus_Error;
4403 }
4404 if (eCSSToken_Ident == mToken.mType) { // element name
4405 aDataMask |= SEL_MASK_ELEM;
4406 aSelector.SetTag(mToken.mIdent);
4407 }
4408 else if (mToken.IsSymbol('*')) { // universal selector
4409 aDataMask |= SEL_MASK_ELEM;
4410 // don't set tag
4411 }
4412 else {
4413 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
4414 UngetToken();
4415 return eSelectorParsingStatus_Error;
4416 }
4417 }
4418 else { // was element name
4419 SetDefaultNamespaceOnSelector(aSelector);
4420 aSelector.SetTag(buffer);
4422 aDataMask |= SEL_MASK_ELEM;
4423 }
4424 if (! GetToken(false)) { // premature eof is ok (here!)
4425 return eSelectorParsingStatus_Done;
4426 }
4427 }
4428 else if (mToken.IsSymbol('|')) { // No namespace
4429 aDataMask |= SEL_MASK_NSPACE;
4430 aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
4432 // get mandatory tag
4433 if (! GetToken(false)) {
4434 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
4435 return eSelectorParsingStatus_Error;
4436 }
4437 if (eCSSToken_Ident == mToken.mType) { // element name
4438 aDataMask |= SEL_MASK_ELEM;
4439 aSelector.SetTag(mToken.mIdent);
4440 }
4441 else if (mToken.IsSymbol('*')) { // universal selector
4442 aDataMask |= SEL_MASK_ELEM;
4443 // don't set tag
4444 }
4445 else {
4446 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
4447 UngetToken();
4448 return eSelectorParsingStatus_Error;
4449 }
4450 if (! GetToken(false)) { // premature eof is ok (here!)
4451 return eSelectorParsingStatus_Done;
4452 }
4453 }
4454 else {
4455 SetDefaultNamespaceOnSelector(aSelector);
4456 }
4458 if (aIsNegated) {
4459 // restore last token read in case of a negated type selector
4460 UngetToken();
4461 }
4462 return eSelectorParsingStatus_Continue;
4463 }
4465 //
4466 // Parse attribute selectors [attr], [attr=value], [attr|=value],
4467 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
4468 //
4469 CSSParserImpl::nsSelectorParsingStatus
4470 CSSParserImpl::ParseAttributeSelector(int32_t& aDataMask,
4471 nsCSSSelector& aSelector)
4472 {
4473 if (! GetToken(true)) { // premature EOF
4474 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4475 return eSelectorParsingStatus_Error;
4476 }
4478 int32_t nameSpaceID = kNameSpaceID_None;
4479 nsAutoString attr;
4480 if (mToken.IsSymbol('*')) { // wildcard namespace
4481 nameSpaceID = kNameSpaceID_Unknown;
4482 if (ExpectSymbol('|', false)) {
4483 if (! GetToken(false)) { // premature EOF
4484 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4485 return eSelectorParsingStatus_Error;
4486 }
4487 if (eCSSToken_Ident == mToken.mType) { // attr name
4488 attr = mToken.mIdent;
4489 }
4490 else {
4491 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4492 UngetToken();
4493 return eSelectorParsingStatus_Error;
4494 }
4495 }
4496 else {
4497 REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
4498 return eSelectorParsingStatus_Error;
4499 }
4500 }
4501 else if (mToken.IsSymbol('|')) { // NO namespace
4502 if (! GetToken(false)) { // premature EOF
4503 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4504 return eSelectorParsingStatus_Error;
4505 }
4506 if (eCSSToken_Ident == mToken.mType) { // attr name
4507 attr = mToken.mIdent;
4508 }
4509 else {
4510 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4511 UngetToken();
4512 return eSelectorParsingStatus_Error;
4513 }
4514 }
4515 else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
4516 attr = mToken.mIdent; // hang on to it
4517 if (ExpectSymbol('|', false)) { // was a namespace
4518 nameSpaceID = GetNamespaceIdForPrefix(attr);
4519 if (nameSpaceID == kNameSpaceID_Unknown) {
4520 return eSelectorParsingStatus_Error;
4521 }
4522 if (! GetToken(false)) { // premature EOF
4523 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4524 return eSelectorParsingStatus_Error;
4525 }
4526 if (eCSSToken_Ident == mToken.mType) { // attr name
4527 attr = mToken.mIdent;
4528 }
4529 else {
4530 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4531 UngetToken();
4532 return eSelectorParsingStatus_Error;
4533 }
4534 }
4535 }
4536 else { // malformed
4537 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
4538 UngetToken();
4539 return eSelectorParsingStatus_Error;
4540 }
4542 if (! GetToken(true)) { // premature EOF
4543 REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
4544 return eSelectorParsingStatus_Error;
4545 }
4546 if ((eCSSToken_Symbol == mToken.mType) ||
4547 (eCSSToken_Includes == mToken.mType) ||
4548 (eCSSToken_Dashmatch == mToken.mType) ||
4549 (eCSSToken_Beginsmatch == mToken.mType) ||
4550 (eCSSToken_Endsmatch == mToken.mType) ||
4551 (eCSSToken_Containsmatch == mToken.mType)) {
4552 uint8_t func;
4553 if (eCSSToken_Includes == mToken.mType) {
4554 func = NS_ATTR_FUNC_INCLUDES;
4555 }
4556 else if (eCSSToken_Dashmatch == mToken.mType) {
4557 func = NS_ATTR_FUNC_DASHMATCH;
4558 }
4559 else if (eCSSToken_Beginsmatch == mToken.mType) {
4560 func = NS_ATTR_FUNC_BEGINSMATCH;
4561 }
4562 else if (eCSSToken_Endsmatch == mToken.mType) {
4563 func = NS_ATTR_FUNC_ENDSMATCH;
4564 }
4565 else if (eCSSToken_Containsmatch == mToken.mType) {
4566 func = NS_ATTR_FUNC_CONTAINSMATCH;
4567 }
4568 else if (']' == mToken.mSymbol) {
4569 aDataMask |= SEL_MASK_ATTRIB;
4570 aSelector.AddAttribute(nameSpaceID, attr);
4571 func = NS_ATTR_FUNC_SET;
4572 }
4573 else if ('=' == mToken.mSymbol) {
4574 func = NS_ATTR_FUNC_EQUALS;
4575 }
4576 else {
4577 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
4578 UngetToken(); // bad function
4579 return eSelectorParsingStatus_Error;
4580 }
4581 if (NS_ATTR_FUNC_SET != func) { // get value
4582 if (! GetToken(true)) { // premature EOF
4583 REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
4584 return eSelectorParsingStatus_Error;
4585 }
4586 if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
4587 nsAutoString value(mToken.mIdent);
4588 if (! GetToken(true)) { // premature EOF
4589 REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
4590 return eSelectorParsingStatus_Error;
4591 }
4592 if (mToken.IsSymbol(']')) {
4593 bool isCaseSensitive = true;
4595 // For cases when this style sheet is applied to an HTML
4596 // element in an HTML document, and the attribute selector is
4597 // for a non-namespaced attribute, then check to see if it's
4598 // one of the known attributes whose VALUE is
4599 // case-insensitive.
4600 if (nameSpaceID == kNameSpaceID_None) {
4601 static const char* caseInsensitiveHTMLAttribute[] = {
4602 // list based on http://www.w3.org/TR/html4/
4603 "lang",
4604 "dir",
4605 "http-equiv",
4606 "text",
4607 "link",
4608 "vlink",
4609 "alink",
4610 "compact",
4611 "align",
4612 "frame",
4613 "rules",
4614 "valign",
4615 "scope",
4616 "axis",
4617 "nowrap",
4618 "hreflang",
4619 "rel",
4620 "rev",
4621 "charset",
4622 "codetype",
4623 "declare",
4624 "valuetype",
4625 "shape",
4626 "nohref",
4627 "media",
4628 "bgcolor",
4629 "clear",
4630 "color",
4631 "face",
4632 "noshade",
4633 "noresize",
4634 "scrolling",
4635 "target",
4636 "method",
4637 "enctype",
4638 "accept-charset",
4639 "accept",
4640 "checked",
4641 "multiple",
4642 "selected",
4643 "disabled",
4644 "readonly",
4645 "language",
4646 "defer",
4647 "type",
4648 // additional attributes not in HTML4
4649 "direction", // marquee
4650 nullptr
4651 };
4652 short i = 0;
4653 const char* htmlAttr;
4654 while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
4655 if (attr.LowerCaseEqualsASCII(htmlAttr)) {
4656 isCaseSensitive = false;
4657 break;
4658 }
4659 }
4660 }
4661 aDataMask |= SEL_MASK_ATTRIB;
4662 aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
4663 }
4664 else {
4665 REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
4666 UngetToken();
4667 return eSelectorParsingStatus_Error;
4668 }
4669 }
4670 else {
4671 REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
4672 UngetToken();
4673 return eSelectorParsingStatus_Error;
4674 }
4675 }
4676 }
4677 else {
4678 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
4679 UngetToken(); // bad dog, no biscut!
4680 return eSelectorParsingStatus_Error;
4681 }
4682 return eSelectorParsingStatus_Continue;
4683 }
4685 //
4686 // Parse pseudo-classes and pseudo-elements
4687 //
4688 CSSParserImpl::nsSelectorParsingStatus
4689 CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
4690 nsCSSSelector& aSelector,
4691 bool aIsNegated,
4692 nsIAtom** aPseudoElement,
4693 nsAtomList** aPseudoElementArgs,
4694 nsCSSPseudoElements::Type* aPseudoElementType)
4695 {
4696 NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs),
4697 "expected location to store pseudo element");
4698 NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs),
4699 "negated selectors shouldn't have a place to store "
4700 "pseudo elements");
4701 if (! GetToken(false)) { // premature eof
4702 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
4703 return eSelectorParsingStatus_Error;
4704 }
4706 // First, find out whether we are parsing a CSS3 pseudo-element
4707 bool parsingPseudoElement = false;
4708 if (mToken.IsSymbol(':')) {
4709 parsingPseudoElement = true;
4710 if (! GetToken(false)) { // premature eof
4711 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
4712 return eSelectorParsingStatus_Error;
4713 }
4714 }
4716 // Do some sanity-checking on the token
4717 if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
4718 // malformed selector
4719 REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
4720 UngetToken();
4721 return eSelectorParsingStatus_Error;
4722 }
4724 // OK, now we know we have an mIdent. Atomize it. All the atoms, for
4725 // pseudo-classes as well as pseudo-elements, start with a single ':'.
4726 nsAutoString buffer;
4727 buffer.Append(char16_t(':'));
4728 buffer.Append(mToken.mIdent);
4729 nsContentUtils::ASCIIToLower(buffer);
4730 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
4731 if (!pseudo) {
4732 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
4733 }
4735 // stash away some info about this pseudo so we only have to get it once.
4736 bool isTreePseudo = false;
4737 nsCSSPseudoElements::Type pseudoElementType =
4738 nsCSSPseudoElements::GetPseudoType(pseudo);
4739 nsCSSPseudoClasses::Type pseudoClassType =
4740 nsCSSPseudoClasses::GetPseudoType(pseudo);
4741 bool pseudoClassIsUserAction =
4742 nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
4744 if (!mUnsafeRulesEnabled &&
4745 pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount &&
4746 nsCSSPseudoElements::PseudoElementIsChromeOnly(pseudoElementType)) {
4747 // This pseudo-element is not exposed to content.
4748 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
4749 UngetToken();
4750 return eSelectorParsingStatus_Error;
4751 }
4753 // We currently allow :-moz-placeholder and ::-moz-placeholder. We have to
4754 // be a bit stricter regarding the pseudo-element parsing rules.
4755 if (pseudoElementType == nsCSSPseudoElements::ePseudo_mozPlaceholder &&
4756 pseudoClassType == nsCSSPseudoClasses::ePseudoClass_mozPlaceholder) {
4757 if (parsingPseudoElement) {
4758 pseudoClassType = nsCSSPseudoClasses::ePseudoClass_NotPseudoClass;
4759 } else {
4760 pseudoElementType = nsCSSPseudoElements::ePseudo_NotPseudoElement;
4761 }
4762 }
4764 #ifdef MOZ_XUL
4765 isTreePseudo = (pseudoElementType == nsCSSPseudoElements::ePseudo_XULTree);
4766 // If a tree pseudo-element is using the function syntax, it will
4767 // get isTree set here and will pass the check below that only
4768 // allows functions if they are in our list of things allowed to be
4769 // functions. If it is _not_ using the function syntax, isTree will
4770 // be false, and it will still pass that check. So the tree
4771 // pseudo-elements are allowed to be either functions or not, as
4772 // desired.
4773 bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
4774 #endif
4775 bool isPseudoElement =
4776 (pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount);
4777 // anonymous boxes are only allowed if they're the tree boxes or we have
4778 // enabled unsafe rules
4779 bool isAnonBox = isTreePseudo ||
4780 (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox &&
4781 mUnsafeRulesEnabled);
4782 bool isPseudoClass =
4783 (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass);
4785 NS_ASSERTION(!isPseudoClass ||
4786 pseudoElementType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
4787 "Why is this atom both a pseudo-class and a pseudo-element?");
4788 NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1,
4789 "Shouldn't be more than one of these");
4791 if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
4792 // Not a pseudo-class, not a pseudo-element.... forget it
4793 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
4794 UngetToken();
4795 return eSelectorParsingStatus_Error;
4796 }
4798 // If it's a function token, it better be on our "ok" list, and if the name
4799 // is that of a function pseudo it better be a function token
4800 if ((eCSSToken_Function == mToken.mType) !=
4801 (
4802 #ifdef MOZ_XUL
4803 isTree ||
4804 #endif
4805 nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType ||
4806 nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
4807 nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
4808 nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
4809 // There are no other function pseudos
4810 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
4811 UngetToken();
4812 return eSelectorParsingStatus_Error;
4813 }
4815 // If it starts with "::", it better be a pseudo-element
4816 if (parsingPseudoElement &&
4817 !isPseudoElement &&
4818 !isAnonBox) {
4819 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
4820 UngetToken();
4821 return eSelectorParsingStatus_Error;
4822 }
4824 if (!parsingPseudoElement &&
4825 nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType) {
4826 if (aIsNegated) { // :not() can't be itself negated
4827 REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
4828 UngetToken();
4829 return eSelectorParsingStatus_Error;
4830 }
4831 // CSS 3 Negation pseudo-class takes one simple selector as argument
4832 nsSelectorParsingStatus parsingStatus =
4833 ParseNegatedSimpleSelector(aDataMask, aSelector);
4834 if (eSelectorParsingStatus_Continue != parsingStatus) {
4835 return parsingStatus;
4836 }
4837 }
4838 else if (!parsingPseudoElement && isPseudoClass) {
4839 if (aSelector.IsPseudoElement()) {
4840 nsCSSPseudoElements::Type type = aSelector.PseudoType();
4841 if (!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
4842 // We only allow user action pseudo-classes on certain pseudo-elements.
4843 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
4844 UngetToken();
4845 return eSelectorParsingStatus_Error;
4846 }
4847 if (!pseudoClassIsUserAction) {
4848 // CSS 4 Selectors says that pseudo-elements can only be followed by
4849 // a user action pseudo-class.
4850 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
4851 UngetToken();
4852 return eSelectorParsingStatus_Error;
4853 }
4854 }
4855 aDataMask |= SEL_MASK_PCLASS;
4856 if (eCSSToken_Function == mToken.mType) {
4857 nsSelectorParsingStatus parsingStatus;
4858 if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
4859 parsingStatus =
4860 ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
4861 }
4862 else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) {
4863 parsingStatus =
4864 ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType);
4865 }
4866 else {
4867 NS_ABORT_IF_FALSE(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
4868 "unexpected pseudo with function token");
4869 parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
4870 pseudoClassType);
4871 }
4872 if (eSelectorParsingStatus_Continue != parsingStatus) {
4873 if (eSelectorParsingStatus_Error == parsingStatus) {
4874 SkipUntil(')');
4875 }
4876 return parsingStatus;
4877 }
4878 }
4879 else {
4880 aSelector.AddPseudoClass(pseudoClassType);
4881 }
4882 }
4883 else if (isPseudoElement || isAnonBox) {
4884 // Pseudo-element. Make some more sanity checks.
4886 if (aIsNegated) { // pseudo-elements can't be negated
4887 REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
4888 UngetToken();
4889 return eSelectorParsingStatus_Error;
4890 }
4891 // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
4892 // to have a single ':' on them. Others (CSS3+ pseudo-elements and
4893 // various -moz-* pseudo-elements) must have |parsingPseudoElement|
4894 // set.
4895 if (!parsingPseudoElement &&
4896 !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
4897 #ifdef MOZ_XUL
4898 && !isTreePseudo
4899 #endif
4900 ) {
4901 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
4902 UngetToken();
4903 return eSelectorParsingStatus_Error;
4904 }
4906 if (0 == (aDataMask & SEL_MASK_PELEM)) {
4907 aDataMask |= SEL_MASK_PELEM;
4908 NS_ADDREF(*aPseudoElement = pseudo);
4909 *aPseudoElementType = pseudoElementType;
4911 #ifdef MOZ_XUL
4912 if (isTree) {
4913 // We have encountered a pseudoelement of the form
4914 // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each
4915 // item in the list to the pseudoclass list. They will be pulled
4916 // from the list later along with the pseudo-element.
4917 if (!ParseTreePseudoElement(aPseudoElementArgs)) {
4918 return eSelectorParsingStatus_Error;
4919 }
4920 }
4921 #endif
4923 // Pseudo-elements can only be followed by user action pseudo-classes
4924 // or be the end of the selector. So the next non-whitespace token must
4925 // be ':', '{' or ',' or EOF.
4926 if (!GetToken(true)) { // premature eof is ok (here!)
4927 return eSelectorParsingStatus_Done;
4928 }
4929 if (parsingPseudoElement && mToken.IsSymbol(':')) {
4930 UngetToken();
4931 return eSelectorParsingStatus_Continue;
4932 }
4933 if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
4934 UngetToken();
4935 return eSelectorParsingStatus_Done;
4936 }
4937 REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC);
4938 UngetToken();
4939 return eSelectorParsingStatus_Error;
4940 }
4941 else { // multiple pseudo elements, not legal
4942 REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
4943 UngetToken();
4944 return eSelectorParsingStatus_Error;
4945 }
4946 }
4947 #ifdef DEBUG
4948 else {
4949 // We should never end up here. Indeed, if we ended up here, we know (from
4950 // the current if/else cascade) that !isPseudoElement and !isAnonBox. But
4951 // then due to our earlier check we know that isPseudoClass. Since we
4952 // didn't fall into the isPseudoClass case in this cascade, we must have
4953 // parsingPseudoElement. But we've already checked the
4954 // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
4955 // it's happened.
4956 NS_NOTREACHED("How did this happen?");
4957 }
4958 #endif
4959 return eSelectorParsingStatus_Continue;
4960 }
4962 //
4963 // Parse the argument of a negation pseudo-class :not()
4964 //
4965 CSSParserImpl::nsSelectorParsingStatus
4966 CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask,
4967 nsCSSSelector& aSelector)
4968 {
4969 if (! GetToken(true)) { // premature eof
4970 REPORT_UNEXPECTED_EOF(PENegationEOF);
4971 return eSelectorParsingStatus_Error;
4972 }
4974 if (mToken.IsSymbol(')')) {
4975 REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
4976 return eSelectorParsingStatus_Error;
4977 }
4979 // Create a new nsCSSSelector and add it to the end of
4980 // aSelector.mNegations.
4981 // Given the current parsing rules, every selector in mNegations
4982 // contains only one simple selector (css3 definition) within it.
4983 // This could easily change in future versions of CSS, and the only
4984 // thing we need to change to support that is this parsing code and the
4985 // serialization code for nsCSSSelector.
4986 nsCSSSelector *newSel = new nsCSSSelector();
4987 nsCSSSelector* negations = &aSelector;
4988 while (negations->mNegations) {
4989 negations = negations->mNegations;
4990 }
4991 negations->mNegations = newSel;
4993 nsSelectorParsingStatus parsingStatus;
4994 if (eCSSToken_ID == mToken.mType) { // #id
4995 parsingStatus = ParseIDSelector(aDataMask, *newSel);
4996 }
4997 else if (mToken.IsSymbol('.')) { // .class
4998 parsingStatus = ParseClassSelector(aDataMask, *newSel);
4999 }
5000 else if (mToken.IsSymbol(':')) { // :pseudo
5001 parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true,
5002 nullptr, nullptr, nullptr);
5003 }
5004 else if (mToken.IsSymbol('[')) { // [attribute
5005 parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
5006 if (eSelectorParsingStatus_Error == parsingStatus) {
5007 // Skip forward to the matching ']'
5008 SkipUntil(']');
5009 }
5010 }
5011 else {
5012 // then it should be a type element or universal selector
5013 parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true);
5014 }
5015 if (eSelectorParsingStatus_Error == parsingStatus) {
5016 REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
5017 SkipUntil(')');
5018 return parsingStatus;
5019 }
5020 // close the parenthesis
5021 if (!ExpectSymbol(')', true)) {
5022 REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
5023 SkipUntil(')');
5024 return eSelectorParsingStatus_Error;
5025 }
5027 NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown ||
5028 (!newSel->mIDList && !newSel->mClassList &&
5029 !newSel->mPseudoClassList && !newSel->mAttrList),
5030 "Need to fix the serialization code to deal with this");
5032 return eSelectorParsingStatus_Continue;
5033 }
5035 //
5036 // Parse the argument of a pseudo-class that has an ident arg
5037 //
5038 CSSParserImpl::nsSelectorParsingStatus
5039 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
5040 nsCSSPseudoClasses::Type aType)
5041 {
5042 if (! GetToken(true)) { // premature eof
5043 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5044 return eSelectorParsingStatus_Error;
5045 }
5046 // We expect an identifier with a language abbreviation
5047 if (eCSSToken_Ident != mToken.mType) {
5048 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
5049 UngetToken();
5050 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5051 }
5053 // -moz-locale-dir and -moz-dir can only have values of 'ltr' or 'rtl'.
5054 if (aType == nsCSSPseudoClasses::ePseudoClass_mozLocaleDir ||
5055 aType == nsCSSPseudoClasses::ePseudoClass_dir) {
5056 nsContentUtils::ASCIIToLower(mToken.mIdent); // case insensitive
5057 if (!mToken.mIdent.EqualsLiteral("ltr") &&
5058 !mToken.mIdent.EqualsLiteral("rtl")) {
5059 REPORT_UNEXPECTED_TOKEN(PEBadDirValue);
5060 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5061 }
5062 }
5064 // Add the pseudo with the language parameter
5065 aSelector.AddPseudoClass(aType, mToken.mIdent.get());
5067 // close the parenthesis
5068 if (!ExpectSymbol(')', true)) {
5069 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
5070 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5071 }
5073 return eSelectorParsingStatus_Continue;
5074 }
5076 CSSParserImpl::nsSelectorParsingStatus
5077 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
5078 nsCSSPseudoClasses::Type aType)
5079 {
5080 int32_t numbers[2] = { 0, 0 };
5081 int32_t sign[2] = { 1, 1 };
5082 bool hasSign[2] = { false, false };
5083 bool lookForB = true;
5085 // Follow the whitespace rules as proposed in
5086 // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
5088 if (! GetToken(true)) {
5089 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5090 return eSelectorParsingStatus_Error;
5091 }
5093 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
5094 hasSign[0] = true;
5095 if (mToken.IsSymbol('-')) {
5096 sign[0] = -1;
5097 }
5098 if (! GetToken(false)) {
5099 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5100 return eSelectorParsingStatus_Error;
5101 }
5102 }
5104 if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
5105 // The CSS tokenization doesn't handle :nth-child() containing - well:
5106 // 2n-1 is a dimension
5107 // n-1 is an identifier
5108 // The easiest way to deal with that is to push everything from the
5109 // minus on back onto the scanner's pushback buffer.
5110 uint32_t truncAt = 0;
5111 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
5112 truncAt = 1;
5113 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-")) && !hasSign[0]) {
5114 truncAt = 2;
5115 }
5116 if (truncAt != 0) {
5117 mScanner->Backup(mToken.mIdent.Length() - truncAt);
5118 mToken.mIdent.Truncate(truncAt);
5119 }
5120 }
5122 if (eCSSToken_Ident == mToken.mType) {
5123 if (mToken.mIdent.LowerCaseEqualsLiteral("odd") && !hasSign[0]) {
5124 numbers[0] = 2;
5125 numbers[1] = 1;
5126 lookForB = false;
5127 }
5128 else if (mToken.mIdent.LowerCaseEqualsLiteral("even") && !hasSign[0]) {
5129 numbers[0] = 2;
5130 numbers[1] = 0;
5131 lookForB = false;
5132 }
5133 else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
5134 numbers[0] = sign[0];
5135 }
5136 else if (mToken.mIdent.LowerCaseEqualsLiteral("-n") && !hasSign[0]) {
5137 numbers[0] = -1;
5138 }
5139 else {
5140 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5141 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5142 }
5143 }
5144 else if (eCSSToken_Number == mToken.mType) {
5145 if (!mToken.mIntegerValid) {
5146 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5147 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5148 }
5149 // for +-an case
5150 if (mToken.mHasSign && hasSign[0]) {
5151 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5152 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5153 }
5154 int32_t intValue = mToken.mInteger * sign[0];
5155 // for -a/**/n case
5156 if (! GetToken(false)) {
5157 numbers[1] = intValue;
5158 lookForB = false;
5159 }
5160 else {
5161 if (eCSSToken_Ident == mToken.mType && mToken.mIdent.LowerCaseEqualsLiteral("n")) {
5162 numbers[0] = intValue;
5163 }
5164 else if (eCSSToken_Ident == mToken.mType && StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
5165 numbers[0] = intValue;
5166 mScanner->Backup(mToken.mIdent.Length() - 1);
5167 }
5168 else {
5169 UngetToken();
5170 numbers[1] = intValue;
5171 lookForB = false;
5172 }
5173 }
5174 }
5175 else if (eCSSToken_Dimension == mToken.mType) {
5176 if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
5177 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5178 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5179 }
5180 // for +-an case
5181 if ( mToken.mHasSign && hasSign[0] ) {
5182 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5183 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5184 }
5185 numbers[0] = mToken.mInteger * sign[0];
5186 }
5187 // XXX If it's a ')', is that valid? (as 0n+0)
5188 else {
5189 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5190 UngetToken();
5191 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5192 }
5194 if (! GetToken(true)) {
5195 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5196 return eSelectorParsingStatus_Error;
5197 }
5198 if (lookForB && !mToken.IsSymbol(')')) {
5199 // The '+' or '-' sign can optionally be separated by whitespace.
5200 // If it is separated by whitespace from what follows it, it appears
5201 // as a separate token rather than part of the number token.
5202 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
5203 hasSign[1] = true;
5204 if (mToken.IsSymbol('-')) {
5205 sign[1] = -1;
5206 }
5207 if (! GetToken(true)) {
5208 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5209 return eSelectorParsingStatus_Error;
5210 }
5211 }
5212 if (eCSSToken_Number != mToken.mType ||
5213 !mToken.mIntegerValid || mToken.mHasSign == hasSign[1]) {
5214 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
5215 UngetToken();
5216 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5217 }
5218 numbers[1] = mToken.mInteger * sign[1];
5219 if (! GetToken(true)) {
5220 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
5221 return eSelectorParsingStatus_Error;
5222 }
5223 }
5224 if (!mToken.IsSymbol(')')) {
5225 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
5226 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5227 }
5228 aSelector.AddPseudoClass(aType, numbers);
5229 return eSelectorParsingStatus_Continue;
5230 }
5232 //
5233 // Parse the argument of a pseudo-class that has a selector list argument.
5234 // Such selector lists cannot contain combinators, but can contain
5235 // anything that goes between a pair of combinators.
5236 //
5237 CSSParserImpl::nsSelectorParsingStatus
5238 CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
5239 nsCSSPseudoClasses::Type aType)
5240 {
5241 nsAutoPtr<nsCSSSelectorList> slist;
5242 if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) {
5243 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5244 }
5246 // Check that none of the selectors in the list have combinators or
5247 // pseudo-elements.
5248 for (nsCSSSelectorList *l = slist; l; l = l->mNext) {
5249 nsCSSSelector *s = l->mSelectors;
5250 if (s->mNext || s->IsPseudoElement()) {
5251 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5252 }
5253 }
5255 // Add the pseudo with the selector list parameter
5256 aSelector.AddPseudoClass(aType, slist.forget());
5258 // close the parenthesis
5259 if (!ExpectSymbol(')', true)) {
5260 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
5261 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
5262 }
5264 return eSelectorParsingStatus_Continue;
5265 }
5268 /**
5269 * This is the format for selectors:
5270 * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
5271 */
5272 bool
5273 CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
5274 char16_t aPrevCombinator)
5275 {
5276 if (! GetToken(true)) {
5277 REPORT_UNEXPECTED_EOF(PESelectorEOF);
5278 return false;
5279 }
5281 nsCSSSelector* selector = aList->AddSelector(aPrevCombinator);
5282 nsCOMPtr<nsIAtom> pseudoElement;
5283 nsAutoPtr<nsAtomList> pseudoElementArgs;
5284 nsCSSPseudoElements::Type pseudoElementType =
5285 nsCSSPseudoElements::ePseudo_NotPseudoElement;
5287 int32_t dataMask = 0;
5288 nsSelectorParsingStatus parsingStatus =
5289 ParseTypeOrUniversalSelector(dataMask, *selector, false);
5291 while (parsingStatus == eSelectorParsingStatus_Continue) {
5292 if (eCSSToken_ID == mToken.mType) { // #id
5293 parsingStatus = ParseIDSelector(dataMask, *selector);
5294 }
5295 else if (mToken.IsSymbol('.')) { // .class
5296 parsingStatus = ParseClassSelector(dataMask, *selector);
5297 }
5298 else if (mToken.IsSymbol(':')) { // :pseudo
5299 parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
5300 getter_AddRefs(pseudoElement),
5301 getter_Transfers(pseudoElementArgs),
5302 &pseudoElementType);
5303 if (pseudoElement &&
5304 pseudoElementType != nsCSSPseudoElements::ePseudo_AnonBox) {
5305 // Pseudo-elements other than anonymous boxes are represented with
5306 // a special ':' combinator.
5308 aList->mWeight += selector->CalcWeight();
5310 selector = aList->AddSelector(':');
5312 selector->mLowercaseTag.swap(pseudoElement);
5313 selector->mClassList = pseudoElementArgs.forget();
5314 selector->SetPseudoType(pseudoElementType);
5315 }
5316 }
5317 else if (mToken.IsSymbol('[')) { // [attribute
5318 parsingStatus = ParseAttributeSelector(dataMask, *selector);
5319 if (eSelectorParsingStatus_Error == parsingStatus) {
5320 SkipUntil(']');
5321 }
5322 }
5323 else { // not a selector token, we're done
5324 parsingStatus = eSelectorParsingStatus_Done;
5325 UngetToken();
5326 break;
5327 }
5329 if (parsingStatus != eSelectorParsingStatus_Continue) {
5330 break;
5331 }
5333 if (! GetToken(false)) { // premature eof is ok (here!)
5334 parsingStatus = eSelectorParsingStatus_Done;
5335 break;
5336 }
5337 }
5339 if (parsingStatus == eSelectorParsingStatus_Error) {
5340 return false;
5341 }
5343 if (!dataMask) {
5344 if (selector->mNext) {
5345 REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
5346 } else {
5347 REPORT_UNEXPECTED(PESelectorGroupNoSelector);
5348 }
5349 return false;
5350 }
5352 if (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox) {
5353 // We got an anonymous box pseudo-element; it must be the only
5354 // thing in this selector group.
5355 if (selector->mNext || !IsUniversalSelector(*selector)) {
5356 REPORT_UNEXPECTED(PEAnonBoxNotAlone);
5357 return false;
5358 }
5360 // Rewrite the current selector as this pseudo-element.
5361 // It does not contribute to selector weight.
5362 selector->mLowercaseTag.swap(pseudoElement);
5363 selector->mClassList = pseudoElementArgs.forget();
5364 selector->SetPseudoType(pseudoElementType);
5365 return true;
5366 }
5368 aList->mWeight += selector->CalcWeight();
5370 return true;
5371 }
5373 css::Declaration*
5374 CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext)
5375 {
5376 bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
5378 if (checkForBraces) {
5379 if (!ExpectSymbol('{', true)) {
5380 REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
5381 OUTPUT_ERROR();
5382 return nullptr;
5383 }
5384 }
5385 css::Declaration* declaration = new css::Declaration();
5386 mData.AssertInitialState();
5387 if (declaration) {
5388 for (;;) {
5389 bool changed;
5390 if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) {
5391 if (!SkipDeclaration(checkForBraces)) {
5392 break;
5393 }
5394 if (checkForBraces) {
5395 if (ExpectSymbol('}', true)) {
5396 break;
5397 }
5398 }
5399 // Since the skipped declaration didn't end the block we parse
5400 // the next declaration.
5401 }
5402 }
5403 declaration->CompressFrom(&mData);
5404 }
5405 return declaration;
5406 }
5408 bool
5409 CSSParserImpl::ParseColor(nsCSSValue& aValue)
5410 {
5411 if (!GetToken(true)) {
5412 REPORT_UNEXPECTED_EOF(PEColorEOF);
5413 return false;
5414 }
5416 nsCSSToken* tk = &mToken;
5417 nscolor rgba;
5418 switch (tk->mType) {
5419 case eCSSToken_ID:
5420 case eCSSToken_Hash:
5421 // #xxyyzz
5422 if (NS_HexToRGB(tk->mIdent, &rgba)) {
5423 MOZ_ASSERT(tk->mIdent.Length() == 3 || tk->mIdent.Length() == 6,
5424 "unexpected hex color length");
5425 nsCSSUnit unit = tk->mIdent.Length() == 3 ?
5426 eCSSUnit_ShortHexColor :
5427 eCSSUnit_HexColor;
5428 aValue.SetIntegerColorValue(rgba, unit);
5429 return true;
5430 }
5431 break;
5433 case eCSSToken_Ident:
5434 if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
5435 aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
5436 return true;
5437 }
5438 else {
5439 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
5440 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
5441 int32_t value;
5442 if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
5443 aValue.SetIntValue(value, eCSSUnit_EnumColor);
5444 return true;
5445 }
5446 }
5447 }
5448 break;
5449 case eCSSToken_Function:
5450 if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
5451 // rgb ( component , component , component )
5452 if (GetToken(true)) {
5453 UngetToken();
5454 }
5455 if (mToken.mType == eCSSToken_Number) {
5456 uint8_t r, g, b;
5457 if (ParseNumberColorComponent(r, ',') &&
5458 ParseNumberColorComponent(g, ',') &&
5459 ParseNumberColorComponent(b, ')')) {
5460 aValue.SetIntegerColorValue(NS_RGB(r, g, b), eCSSUnit_RGBColor);
5461 return true;
5462 }
5463 } else {
5464 float r, g, b;
5465 if (ParsePercentageColorComponent(r, ',') &&
5466 ParsePercentageColorComponent(g, ',') &&
5467 ParsePercentageColorComponent(b, ')')) {
5468 aValue.SetFloatColorValue(r, g, b, 1.0f,
5469 eCSSUnit_PercentageRGBColor);
5470 return true;
5471 }
5472 }
5473 SkipUntil(')');
5474 return false;
5475 }
5476 else if (mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
5477 // rgba ( component , component , component , opacity )
5478 if (GetToken(true)) {
5479 UngetToken();
5480 }
5481 if (mToken.mType == eCSSToken_Number) {
5482 uint8_t r, g, b, a;
5483 if (ParseNumberColorComponent(r, ',') &&
5484 ParseNumberColorComponent(g, ',') &&
5485 ParseNumberColorComponent(b, ',') &&
5486 ParseColorOpacity(a)) {
5487 aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a),
5488 eCSSUnit_RGBAColor);
5489 return true;
5490 }
5491 } else {
5492 float r, g, b, a;
5493 if (ParsePercentageColorComponent(r, ',') &&
5494 ParsePercentageColorComponent(g, ',') &&
5495 ParsePercentageColorComponent(b, ',') &&
5496 ParseColorOpacity(a)) {
5497 aValue.SetFloatColorValue(r, g, b, a, eCSSUnit_PercentageRGBAColor);
5498 return true;
5499 }
5500 }
5501 SkipUntil(')');
5502 return false;
5503 }
5504 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
5505 // hsl ( hue , saturation , lightness )
5506 // "hue" is a number, "saturation" and "lightness" are percentages.
5507 float h, s, l;
5508 if (ParseHSLColor(h, s, l, ')')) {
5509 aValue.SetFloatColorValue(h, s, l, 1.0f, eCSSUnit_HSLColor);
5510 return true;
5511 }
5512 SkipUntil(')');
5513 return false;
5514 }
5515 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
5516 // hsla ( hue , saturation , lightness , opacity )
5517 // "hue" is a number, "saturation" and "lightness" are percentages,
5518 // "opacity" is a number.
5519 float h, s, l, a;
5520 if (ParseHSLColor(h, s, l, ',') &&
5521 ParseColorOpacity(a)) {
5522 aValue.SetFloatColorValue(h, s, l, a, eCSSUnit_HSLAColor);
5523 return true;
5524 }
5525 SkipUntil(')');
5526 return false;
5527 }
5528 break;
5529 default:
5530 break;
5531 }
5533 // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
5534 if (mHashlessColorQuirk) {
5535 // - If the string starts with 'a-f', the nsCSSScanner builds the
5536 // token as a eCSSToken_Ident and we can parse the string as a
5537 // 'xxyyzz' RGB color.
5538 // - If it only contains '0-9' digits, the token is a
5539 // eCSSToken_Number and it must be converted back to a 6
5540 // characters string to be parsed as a RGB color.
5541 // - If it starts with '0-9' and contains any 'a-f', the token is a
5542 // eCSSToken_Dimension, the mNumber part must be converted back to
5543 // a string and the mIdent part must be appended to that string so
5544 // that the resulting string has 6 characters.
5545 // Note: This is a hack for Nav compatibility. Do not attempt to
5546 // simplify it by hacking into the ncCSSScanner. This would be very
5547 // bad.
5548 nsAutoString str;
5549 char buffer[20];
5550 switch (tk->mType) {
5551 case eCSSToken_Ident:
5552 str.Assign(tk->mIdent);
5553 break;
5555 case eCSSToken_Number:
5556 if (tk->mIntegerValid) {
5557 PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
5558 str.AssignWithConversion(buffer);
5559 }
5560 break;
5562 case eCSSToken_Dimension:
5563 if (tk->mIdent.Length() <= 6) {
5564 PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
5565 nsAutoString temp;
5566 temp.AssignWithConversion(buffer);
5567 temp.Right(str, 6 - tk->mIdent.Length());
5568 str.Append(tk->mIdent);
5569 }
5570 break;
5571 default:
5572 // There is a whole bunch of cases that are
5573 // not handled by this switch. Ignore them.
5574 break;
5575 }
5576 if (NS_HexToRGB(str, &rgba)) {
5577 aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor);
5578 return true;
5579 }
5580 }
5582 // It's not a color
5583 REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
5584 UngetToken();
5585 return false;
5586 }
5588 bool
5589 CSSParserImpl::ParseNumberColorComponent(uint8_t& aComponent, char aStop)
5590 {
5591 if (!GetToken(true)) {
5592 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
5593 return false;
5594 }
5596 if (mToken.mType != eCSSToken_Number || !mToken.mIntegerValid) {
5597 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
5598 UngetToken();
5599 return false;
5600 }
5602 float value = mToken.mNumber;
5603 if (value < 0.0f) value = 0.0f;
5604 if (value > 255.0f) value = 255.0f;
5606 if (ExpectSymbol(aStop, true)) {
5607 aComponent = NSToIntRound(value);
5608 return true;
5609 }
5610 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
5611 return false;
5612 }
5614 bool
5615 CSSParserImpl::ParsePercentageColorComponent(float& aComponent, char aStop)
5616 {
5617 if (!GetToken(true)) {
5618 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
5619 return false;
5620 }
5622 if (mToken.mType != eCSSToken_Percentage) {
5623 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
5624 UngetToken();
5625 return false;
5626 }
5628 float value = mToken.mNumber;
5629 if (value < 0.0f) value = 0.0f;
5630 if (value > 1.0f) value = 1.0f;
5632 if (ExpectSymbol(aStop, true)) {
5633 aComponent = value;
5634 return true;
5635 }
5636 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
5637 return false;
5638 }
5641 bool
5642 CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
5643 char aStop)
5644 {
5645 float h, s, l;
5647 // Get the hue
5648 if (!GetToken(true)) {
5649 REPORT_UNEXPECTED_EOF(PEColorHueEOF);
5650 return false;
5651 }
5652 if (mToken.mType != eCSSToken_Number) {
5653 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
5654 UngetToken();
5655 return false;
5656 }
5657 h = mToken.mNumber;
5658 h /= 360.0f;
5659 // hue values are wraparound
5660 h = h - floor(h);
5662 if (!ExpectSymbol(',', true)) {
5663 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
5664 return false;
5665 }
5667 // Get the saturation
5668 if (!GetToken(true)) {
5669 REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
5670 return false;
5671 }
5672 if (mToken.mType != eCSSToken_Percentage) {
5673 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
5674 UngetToken();
5675 return false;
5676 }
5677 s = mToken.mNumber;
5678 if (s < 0.0f) s = 0.0f;
5679 if (s > 1.0f) s = 1.0f;
5681 if (!ExpectSymbol(',', true)) {
5682 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
5683 return false;
5684 }
5686 // Get the lightness
5687 if (!GetToken(true)) {
5688 REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
5689 return false;
5690 }
5691 if (mToken.mType != eCSSToken_Percentage) {
5692 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
5693 UngetToken();
5694 return false;
5695 }
5696 l = mToken.mNumber;
5697 if (l < 0.0f) l = 0.0f;
5698 if (l > 1.0f) l = 1.0f;
5700 if (ExpectSymbol(aStop, true)) {
5701 aHue = h;
5702 aSaturation = s;
5703 aLightness = l;
5704 return true;
5705 }
5707 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
5708 return false;
5709 }
5712 bool
5713 CSSParserImpl::ParseColorOpacity(uint8_t& aOpacity)
5714 {
5715 float floatOpacity;
5716 if (!ParseColorOpacity(floatOpacity)) {
5717 return false;
5718 }
5720 uint8_t value = nsStyleUtil::FloatToColorComponent(floatOpacity);
5721 // Need to compare to something slightly larger
5722 // than 0.5 due to floating point inaccuracies.
5723 NS_ASSERTION(fabs(255.0f*mToken.mNumber - value) <= 0.51f,
5724 "FloatToColorComponent did something weird");
5726 aOpacity = value;
5727 return true;
5728 }
5730 bool
5731 CSSParserImpl::ParseColorOpacity(float& aOpacity)
5732 {
5733 if (!GetToken(true)) {
5734 REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
5735 return false;
5736 }
5738 if (mToken.mType != eCSSToken_Number) {
5739 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
5740 UngetToken();
5741 return false;
5742 }
5744 if (!ExpectSymbol(')', true)) {
5745 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
5746 return false;
5747 }
5749 if (mToken.mNumber < 0.0f) {
5750 mToken.mNumber = 0.0f;
5751 } else if (mToken.mNumber > 1.0f) {
5752 mToken.mNumber = 1.0f;
5753 }
5755 aOpacity = mToken.mNumber;
5756 return true;
5757 }
5759 #ifdef MOZ_XUL
5760 bool
5761 CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs)
5762 {
5763 // The argument to a tree pseudo-element is a sequence of identifiers
5764 // that are either space- or comma-separated. (Was the intent to
5765 // allow only comma-separated? That's not what was done.)
5766 nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass
5768 while (!ExpectSymbol(')', true)) {
5769 if (!GetToken(true)) {
5770 return false;
5771 }
5772 if (eCSSToken_Ident == mToken.mType) {
5773 fakeSelector.AddClass(mToken.mIdent);
5774 }
5775 else if (!mToken.IsSymbol(',')) {
5776 UngetToken();
5777 SkipUntil(')');
5778 return false;
5779 }
5780 }
5781 *aPseudoElementArgs = fakeSelector.mClassList;
5782 fakeSelector.mClassList = nullptr;
5783 return true;
5784 }
5785 #endif
5787 //----------------------------------------------------------------------
5789 bool
5790 CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
5791 uint32_t aFlags,
5792 bool aMustCallValueAppended,
5793 bool* aChanged,
5794 nsCSSContextType aContext)
5795 {
5796 NS_PRECONDITION(aContext == eCSSContext_General ||
5797 aContext == eCSSContext_Page,
5798 "Must be page or general context");
5800 bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
5802 mTempData.AssertInitialState();
5804 // Get property name
5805 nsCSSToken* tk = &mToken;
5806 nsAutoString propertyName;
5807 for (;;) {
5808 if (!GetToken(true)) {
5809 if (checkForBraces) {
5810 REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
5811 }
5812 return false;
5813 }
5814 if (eCSSToken_Ident == tk->mType) {
5815 propertyName = tk->mIdent;
5816 // grab the ident before the ExpectSymbol trashes the token
5817 if (!ExpectSymbol(':', true)) {
5818 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
5819 REPORT_UNEXPECTED(PEDeclDropped);
5820 OUTPUT_ERROR();
5821 return false;
5822 }
5823 break;
5824 }
5825 if (tk->IsSymbol(';')) {
5826 // dangling semicolons are skipped
5827 continue;
5828 }
5830 if (!tk->IsSymbol('}')) {
5831 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
5832 REPORT_UNEXPECTED(PEDeclSkipped);
5833 OUTPUT_ERROR();
5835 if (eCSSToken_AtKeyword == tk->mType) {
5836 SkipAtRule(checkForBraces);
5837 return true; // Not a declaration, but don't skip until ';'
5838 }
5839 }
5840 // Not a declaration...
5841 UngetToken();
5842 return false;
5843 }
5845 // Don't report property parse errors if we're inside a failing @supports
5846 // rule.
5847 nsAutoSuppressErrors suppressErrors(this, mInFailingSupportsRule);
5849 // Information about a parsed non-custom property.
5850 nsCSSProperty propID;
5852 // Information about a parsed custom property.
5853 CSSVariableDeclarations::Type variableType;
5854 nsString variableValue;
5856 // Check if the property name is a custom property.
5857 bool customProperty = nsLayoutUtils::CSSVariablesEnabled() &&
5858 nsCSSProps::IsCustomPropertyName(propertyName) &&
5859 aContext == eCSSContext_General;
5861 if (customProperty) {
5862 if (!ParseVariableDeclaration(&variableType, variableValue)) {
5863 REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
5864 REPORT_UNEXPECTED(PEDeclDropped);
5865 OUTPUT_ERROR();
5866 return false;
5867 }
5868 } else {
5869 // Map property name to its ID.
5870 propID = LookupEnabledProperty(propertyName);
5871 if (eCSSProperty_UNKNOWN == propID ||
5872 (aContext == eCSSContext_Page &&
5873 !nsCSSProps::PropHasFlags(propID,
5874 CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property
5875 if (!NonMozillaVendorIdentifier(propertyName)) {
5876 REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName);
5877 REPORT_UNEXPECTED(PEDeclDropped);
5878 OUTPUT_ERROR();
5879 }
5880 return false;
5881 }
5882 // Then parse the property.
5883 if (!ParseProperty(propID)) {
5884 // XXX Much better to put stuff in the value parsers instead...
5885 REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
5886 REPORT_UNEXPECTED(PEDeclDropped);
5887 OUTPUT_ERROR();
5888 mTempData.ClearProperty(propID);
5889 mTempData.AssertInitialState();
5890 return false;
5891 }
5892 }
5894 CLEAR_ERROR();
5896 // Look for "!important".
5897 PriorityParsingStatus status;
5898 if ((aFlags & eParseDeclaration_AllowImportant) != 0) {
5899 status = ParsePriority();
5900 } else {
5901 status = ePriority_None;
5902 }
5904 // Look for a semicolon or close brace.
5905 if (status != ePriority_Error) {
5906 if (!GetToken(true)) {
5907 // EOF is always ok
5908 } else if (mToken.IsSymbol(';')) {
5909 // semicolon is always ok
5910 } else if (mToken.IsSymbol('}')) {
5911 // brace is ok if checkForBraces, but don't eat it
5912 UngetToken();
5913 if (!checkForBraces) {
5914 status = ePriority_Error;
5915 }
5916 } else {
5917 UngetToken();
5918 status = ePriority_Error;
5919 }
5920 }
5922 if (status == ePriority_Error) {
5923 if (checkForBraces) {
5924 REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
5925 } else {
5926 REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
5927 }
5928 REPORT_UNEXPECTED(PEDeclDropped);
5929 OUTPUT_ERROR();
5930 if (!customProperty) {
5931 mTempData.ClearProperty(propID);
5932 }
5933 mTempData.AssertInitialState();
5934 return false;
5935 }
5937 if (customProperty) {
5938 MOZ_ASSERT(Substring(propertyName, 0,
5939 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
5940 // remove '--'
5941 nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
5942 aDeclaration->AddVariableDeclaration(varName, variableType, variableValue,
5943 status == ePriority_Important, false);
5944 } else {
5945 *aChanged |= mData.TransferFromBlock(mTempData, propID,
5946 status == ePriority_Important,
5947 false, aMustCallValueAppended,
5948 aDeclaration);
5949 }
5951 return true;
5952 }
5954 static const nsCSSProperty kBorderTopIDs[] = {
5955 eCSSProperty_border_top_width,
5956 eCSSProperty_border_top_style,
5957 eCSSProperty_border_top_color
5958 };
5959 static const nsCSSProperty kBorderRightIDs[] = {
5960 eCSSProperty_border_right_width_value,
5961 eCSSProperty_border_right_style_value,
5962 eCSSProperty_border_right_color_value,
5963 eCSSProperty_border_right_width,
5964 eCSSProperty_border_right_style,
5965 eCSSProperty_border_right_color
5966 };
5967 static const nsCSSProperty kBorderBottomIDs[] = {
5968 eCSSProperty_border_bottom_width,
5969 eCSSProperty_border_bottom_style,
5970 eCSSProperty_border_bottom_color
5971 };
5972 static const nsCSSProperty kBorderLeftIDs[] = {
5973 eCSSProperty_border_left_width_value,
5974 eCSSProperty_border_left_style_value,
5975 eCSSProperty_border_left_color_value,
5976 eCSSProperty_border_left_width,
5977 eCSSProperty_border_left_style,
5978 eCSSProperty_border_left_color
5979 };
5980 static const nsCSSProperty kBorderStartIDs[] = {
5981 eCSSProperty_border_start_width_value,
5982 eCSSProperty_border_start_style_value,
5983 eCSSProperty_border_start_color_value,
5984 eCSSProperty_border_start_width,
5985 eCSSProperty_border_start_style,
5986 eCSSProperty_border_start_color
5987 };
5988 static const nsCSSProperty kBorderEndIDs[] = {
5989 eCSSProperty_border_end_width_value,
5990 eCSSProperty_border_end_style_value,
5991 eCSSProperty_border_end_color_value,
5992 eCSSProperty_border_end_width,
5993 eCSSProperty_border_end_style,
5994 eCSSProperty_border_end_color
5995 };
5996 static const nsCSSProperty kColumnRuleIDs[] = {
5997 eCSSProperty__moz_column_rule_width,
5998 eCSSProperty__moz_column_rule_style,
5999 eCSSProperty__moz_column_rule_color
6000 };
6002 bool
6003 CSSParserImpl::ParseEnum(nsCSSValue& aValue,
6004 const KTableValue aKeywordTable[])
6005 {
6006 nsSubstring* ident = NextIdent();
6007 if (nullptr == ident) {
6008 return false;
6009 }
6010 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
6011 if (eCSSKeyword_UNKNOWN < keyword) {
6012 int32_t value;
6013 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
6014 aValue.SetIntValue(value, eCSSUnit_Enumerated);
6015 return true;
6016 }
6017 }
6019 // Put the unknown identifier back and return
6020 UngetToken();
6021 return false;
6022 }
6025 struct UnitInfo {
6026 char name[6]; // needs to be long enough for the longest unit, with
6027 // terminating null.
6028 uint32_t length;
6029 nsCSSUnit unit;
6030 int32_t type;
6031 };
6033 #define STR_WITH_LEN(_str) \
6034 _str, sizeof(_str) - 1
6036 const UnitInfo UnitData[] = {
6037 { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
6038 { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
6039 { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
6040 { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
6041 { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
6042 { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
6043 { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
6044 { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH },
6045 { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
6046 { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH },
6047 { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH },
6048 { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH },
6049 { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH },
6050 { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH },
6051 { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
6052 { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
6053 { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
6054 { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
6055 { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE },
6056 { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
6057 { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
6058 { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
6059 { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
6060 };
6062 #undef STR_WITH_LEN
6064 bool
6065 CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
6066 int32_t aVariantMask,
6067 float aNumber,
6068 const nsString& aUnit)
6069 {
6070 nsCSSUnit units;
6071 int32_t type = 0;
6072 if (!aUnit.IsEmpty()) {
6073 uint32_t i;
6074 for (i = 0; i < ArrayLength(UnitData); ++i) {
6075 if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
6076 UnitData[i].length)) {
6077 units = UnitData[i].unit;
6078 type = UnitData[i].type;
6079 break;
6080 }
6081 }
6083 if (i == ArrayLength(UnitData)) {
6084 // Unknown unit
6085 return false;
6086 }
6088 if (!mViewportUnitsEnabled &&
6089 (eCSSUnit_ViewportWidth == units ||
6090 eCSSUnit_ViewportHeight == units ||
6091 eCSSUnit_ViewportMin == units ||
6092 eCSSUnit_ViewportMax == units)) {
6093 // Viewport units aren't allowed right now, probably because we're
6094 // inside an @page declaration. Fail.
6095 return false;
6096 }
6097 } else {
6098 // Must be a zero number...
6099 NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
6100 if ((VARIANT_LENGTH & aVariantMask) != 0) {
6101 units = eCSSUnit_Pixel;
6102 type = VARIANT_LENGTH;
6103 }
6104 else if ((VARIANT_ANGLE & aVariantMask) != 0) {
6105 NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE,
6106 "must have allowed zero angle");
6107 units = eCSSUnit_Degree;
6108 type = VARIANT_ANGLE;
6109 }
6110 else {
6111 NS_ERROR("Variant mask does not include dimension; why were we called?");
6112 return false;
6113 }
6114 }
6115 if ((type & aVariantMask) != 0) {
6116 aValue.SetFloatValue(aNumber, units);
6117 return true;
6118 }
6119 return false;
6120 }
6122 // Note that this does include VARIANT_CALC, which is numeric. This is
6123 // because calc() parsing, as proposed, drops range restrictions inside
6124 // the calc() expression and clamps the result of the calculation to the
6125 // range.
6126 #define VARIANT_ALL_NONNUMERIC \
6127 VARIANT_KEYWORD | \
6128 VARIANT_COLOR | \
6129 VARIANT_URL | \
6130 VARIANT_STRING | \
6131 VARIANT_COUNTER | \
6132 VARIANT_ATTR | \
6133 VARIANT_IDENTIFIER | \
6134 VARIANT_IDENTIFIER_NO_INHERIT | \
6135 VARIANT_AUTO | \
6136 VARIANT_INHERIT | \
6137 VARIANT_NONE | \
6138 VARIANT_NORMAL | \
6139 VARIANT_SYSFONT | \
6140 VARIANT_GRADIENT | \
6141 VARIANT_TIMING_FUNCTION | \
6142 VARIANT_ALL | \
6143 VARIANT_CALC | \
6144 VARIANT_OPENTYPE_SVG_KEYWORD
6146 // Note that callers passing VARIANT_CALC in aVariantMask will get
6147 // full-range parsing inside the calc() expression, and the code that
6148 // computes the calc will be required to clamp the resulting value to an
6149 // appropriate range.
6150 bool
6151 CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue,
6152 int32_t aVariantMask,
6153 const KTableValue aKeywordTable[])
6154 {
6155 // The variant mask must only contain non-numeric variants or the ones
6156 // that we specifically handle.
6157 NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
6158 VARIANT_NUMBER |
6159 VARIANT_LENGTH |
6160 VARIANT_PERCENT |
6161 VARIANT_INTEGER)) == 0,
6162 "need to update code below to handle additional variants");
6164 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
6165 if (eCSSUnit_Number == aValue.GetUnit() ||
6166 aValue.IsLengthUnit()){
6167 if (aValue.GetFloatValue() < 0) {
6168 UngetToken();
6169 return false;
6170 }
6171 }
6172 else if (aValue.GetUnit() == eCSSUnit_Percent) {
6173 if (aValue.GetPercentValue() < 0) {
6174 UngetToken();
6175 return false;
6176 }
6177 } else if (aValue.GetUnit() == eCSSUnit_Integer) {
6178 if (aValue.GetIntValue() < 0) {
6179 UngetToken();
6180 return false;
6181 }
6182 }
6183 return true;
6184 }
6185 return false;
6186 }
6188 // Note that callers passing VARIANT_CALC in aVariantMask will get
6189 // full-range parsing inside the calc() expression, and the code that
6190 // computes the calc will be required to clamp the resulting value to an
6191 // appropriate range.
6192 bool
6193 CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue,
6194 int32_t aVariantMask,
6195 const KTableValue aKeywordTable[])
6196 {
6197 // The variant mask must only contain non-numeric variants or the ones
6198 // that we specifically handle.
6199 NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
6200 VARIANT_NUMBER |
6201 VARIANT_INTEGER)) == 0,
6202 "need to update code below to handle additional variants");
6204 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
6205 if (aValue.GetUnit() == eCSSUnit_Integer) {
6206 if (aValue.GetIntValue() < 1) {
6207 UngetToken();
6208 return false;
6209 }
6210 } else if (eCSSUnit_Number == aValue.GetUnit()) {
6211 if (aValue.GetFloatValue() < 1.0f) {
6212 UngetToken();
6213 return false;
6214 }
6215 }
6216 return true;
6217 }
6218 return false;
6219 }
6221 // Assigns to aValue iff it returns true.
6222 bool
6223 CSSParserImpl::ParseVariant(nsCSSValue& aValue,
6224 int32_t aVariantMask,
6225 const KTableValue aKeywordTable[])
6226 {
6227 NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
6228 !(aVariantMask & VARIANT_NUMBER),
6229 "can't distinguish colors from numbers");
6230 NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
6231 !(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)),
6232 "can't distinguish colors from lengths");
6233 NS_ASSERTION(!(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)) ||
6234 !(aVariantMask & VARIANT_NUMBER),
6235 "can't distinguish lengths from numbers");
6236 NS_ABORT_IF_FALSE(!(aVariantMask & VARIANT_IDENTIFIER) ||
6237 !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT),
6238 "must not set both VARIANT_IDENTIFIER and "
6239 "VARIANT_IDENTIFIER_NO_INHERIT");
6241 if (!GetToken(true)) {
6242 return false;
6243 }
6244 nsCSSToken* tk = &mToken;
6245 if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
6246 (eCSSToken_Ident == tk->mType)) {
6247 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
6248 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
6249 if ((aVariantMask & VARIANT_AUTO) != 0) {
6250 if (eCSSKeyword_auto == keyword) {
6251 aValue.SetAutoValue();
6252 return true;
6253 }
6254 }
6255 if ((aVariantMask & VARIANT_INHERIT) != 0) {
6256 // XXX Should we check IsParsingCompoundProperty, or do all
6257 // callers handle it? (Not all callers set it, though, since
6258 // they want the quirks that are disabled by setting it.)
6260 // IMPORTANT: If new keywords are added here,
6261 // they probably need to be added in ParseCustomIdent as well.
6262 if (eCSSKeyword_inherit == keyword) {
6263 aValue.SetInheritValue();
6264 return true;
6265 }
6266 else if (eCSSKeyword_initial == keyword) {
6267 aValue.SetInitialValue();
6268 return true;
6269 }
6270 else if (eCSSKeyword_unset == keyword &&
6271 nsLayoutUtils::UnsetValueEnabled()) {
6272 aValue.SetUnsetValue();
6273 return true;
6274 }
6275 }
6276 if ((aVariantMask & VARIANT_NONE) != 0) {
6277 if (eCSSKeyword_none == keyword) {
6278 aValue.SetNoneValue();
6279 return true;
6280 }
6281 }
6282 if ((aVariantMask & VARIANT_ALL) != 0) {
6283 if (eCSSKeyword_all == keyword) {
6284 aValue.SetAllValue();
6285 return true;
6286 }
6287 }
6288 if ((aVariantMask & VARIANT_NORMAL) != 0) {
6289 if (eCSSKeyword_normal == keyword) {
6290 aValue.SetNormalValue();
6291 return true;
6292 }
6293 }
6294 if ((aVariantMask & VARIANT_SYSFONT) != 0) {
6295 if (eCSSKeyword__moz_use_system_font == keyword &&
6296 !IsParsingCompoundProperty()) {
6297 aValue.SetSystemFontValue();
6298 return true;
6299 }
6300 }
6301 if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) {
6302 static bool sOpentypeSVGEnabled;
6303 static bool sOpentypeSVGEnabledCached = false;
6304 if (!sOpentypeSVGEnabledCached) {
6305 sOpentypeSVGEnabledCached = true;
6306 Preferences::AddBoolVarCache(&sOpentypeSVGEnabled,
6307 "gfx.font_rendering.opentype_svg.enabled");
6308 }
6309 if (sOpentypeSVGEnabled) {
6310 aVariantMask |= VARIANT_KEYWORD;
6311 }
6312 }
6313 if ((aVariantMask & VARIANT_KEYWORD) != 0) {
6314 int32_t value;
6315 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
6316 aValue.SetIntValue(value, eCSSUnit_Enumerated);
6317 return true;
6318 }
6319 }
6320 }
6321 }
6322 // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or
6323 // VARIANT_ZERO_ANGLE.
6324 if (((aVariantMask & VARIANT_NUMBER) != 0) &&
6325 (eCSSToken_Number == tk->mType)) {
6326 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
6327 return true;
6328 }
6329 if (((aVariantMask & VARIANT_INTEGER) != 0) &&
6330 (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
6331 aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
6332 return true;
6333 }
6334 if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
6335 VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
6336 eCSSToken_Dimension == tk->mType) ||
6337 ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
6338 eCSSToken_Number == tk->mType &&
6339 tk->mNumber == 0.0f)) {
6340 if (((aVariantMask & VARIANT_POSITIVE_DIMENSION) != 0 &&
6341 tk->mNumber <= 0.0) ||
6342 ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 &&
6343 tk->mNumber < 0.0)) {
6344 UngetToken();
6345 return false;
6346 }
6347 if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
6348 return true;
6349 }
6350 // Put the token back; we didn't parse it, so we shouldn't consume it
6351 UngetToken();
6352 return false;
6353 }
6354 if (((aVariantMask & VARIANT_PERCENT) != 0) &&
6355 (eCSSToken_Percentage == tk->mType)) {
6356 aValue.SetPercentValue(tk->mNumber);
6357 return true;
6358 }
6359 if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px
6360 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
6361 (eCSSToken_Number == tk->mType)) {
6362 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
6363 return true;
6364 }
6365 }
6367 if (IsSVGMode() && !IsParsingCompoundProperty()) {
6368 // STANDARD: SVG Spec states that lengths and coordinates can be unitless
6369 // in which case they default to user-units (1 px = 1 user unit)
6370 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
6371 (eCSSToken_Number == tk->mType)) {
6372 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
6373 return true;
6374 }
6375 }
6377 if (((aVariantMask & VARIANT_URL) != 0) &&
6378 eCSSToken_URL == tk->mType) {
6379 SetValueToURL(aValue, tk->mIdent);
6380 return true;
6381 }
6382 if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
6383 eCSSToken_Function == tk->mType) {
6384 // a generated gradient
6385 nsDependentString tmp(tk->mIdent, 0);
6386 bool isLegacy = false;
6387 if (StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) {
6388 tmp.Rebind(tmp, 5);
6389 isLegacy = true;
6390 }
6391 bool isRepeating = false;
6392 if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) {
6393 tmp.Rebind(tmp, 10);
6394 isRepeating = true;
6395 }
6397 if (tmp.LowerCaseEqualsLiteral("linear-gradient")) {
6398 return ParseLinearGradient(aValue, isRepeating, isLegacy);
6399 }
6400 if (tmp.LowerCaseEqualsLiteral("radial-gradient")) {
6401 return ParseRadialGradient(aValue, isRepeating, isLegacy);
6402 }
6403 }
6404 if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
6405 eCSSToken_Function == tk->mType &&
6406 tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
6407 return ParseImageRect(aValue);
6408 }
6409 if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
6410 eCSSToken_Function == tk->mType &&
6411 tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) {
6412 return ParseElement(aValue);
6413 }
6414 if ((aVariantMask & VARIANT_COLOR) != 0) {
6415 if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
6416 (eCSSToken_ID == tk->mType) ||
6417 (eCSSToken_Hash == tk->mType) ||
6418 (eCSSToken_Ident == tk->mType) ||
6419 ((eCSSToken_Function == tk->mType) &&
6420 (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
6421 tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
6422 tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
6423 tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
6424 {
6425 // Put token back so that parse color can get it
6426 UngetToken();
6427 if (ParseColor(aValue)) {
6428 return true;
6429 }
6430 return false;
6431 }
6432 }
6433 if (((aVariantMask & VARIANT_STRING) != 0) &&
6434 (eCSSToken_String == tk->mType)) {
6435 nsAutoString buffer;
6436 buffer.Append(tk->mIdent);
6437 aValue.SetStringValue(buffer, eCSSUnit_String);
6438 return true;
6439 }
6440 if (((aVariantMask &
6441 (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
6442 (eCSSToken_Ident == tk->mType) &&
6443 ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
6444 !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
6445 tk->mIdent.LowerCaseEqualsLiteral("initial") ||
6446 (tk->mIdent.LowerCaseEqualsLiteral("unset") &&
6447 nsLayoutUtils::UnsetValueEnabled())))) {
6448 aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
6449 return true;
6450 }
6451 if (((aVariantMask & VARIANT_COUNTER) != 0) &&
6452 (eCSSToken_Function == tk->mType) &&
6453 (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
6454 tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
6455 return ParseCounter(aValue);
6456 }
6457 if (((aVariantMask & VARIANT_ATTR) != 0) &&
6458 (eCSSToken_Function == tk->mType) &&
6459 tk->mIdent.LowerCaseEqualsLiteral("attr")) {
6460 if (!ParseAttr(aValue)) {
6461 SkipUntil(')');
6462 return false;
6463 }
6464 return true;
6465 }
6466 if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
6467 (eCSSToken_Function == tk->mType)) {
6468 if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
6469 if (!ParseTransitionTimingFunctionValues(aValue)) {
6470 SkipUntil(')');
6471 return false;
6472 }
6473 return true;
6474 }
6475 if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
6476 if (!ParseTransitionStepTimingFunctionValues(aValue)) {
6477 SkipUntil(')');
6478 return false;
6479 }
6480 return true;
6481 }
6482 }
6483 if ((aVariantMask & VARIANT_CALC) &&
6484 (eCSSToken_Function == tk->mType) &&
6485 (tk->mIdent.LowerCaseEqualsLiteral("calc") ||
6486 tk->mIdent.LowerCaseEqualsLiteral("-moz-calc"))) {
6487 // calc() currently allows only lengths and percents inside it.
6488 return ParseCalc(aValue, aVariantMask & VARIANT_LP);
6489 }
6491 UngetToken();
6492 return false;
6493 }
6495 bool
6496 CSSParserImpl::ParseCustomIdent(nsCSSValue& aValue,
6497 const nsAutoString& aIdentValue,
6498 const nsCSSKeyword aExcludedKeywords[],
6499 const nsCSSProps::KTableValue aPropertyKTable[])
6500 {
6501 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aIdentValue);
6502 if (keyword == eCSSKeyword_UNKNOWN) {
6503 // Fast path for identifiers that are not known CSS keywords:
6504 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
6505 return true;
6506 }
6507 if (keyword == eCSSKeyword_inherit ||
6508 keyword == eCSSKeyword_initial ||
6509 keyword == eCSSKeyword_unset ||
6510 keyword == eCSSKeyword_default ||
6511 (aPropertyKTable &&
6512 nsCSSProps::FindIndexOfKeyword(keyword, aPropertyKTable) >= 0)) {
6513 return false;
6514 }
6515 if (aExcludedKeywords) {
6516 for (uint32_t i = 0;; i++) {
6517 nsCSSKeyword excludedKeyword = aExcludedKeywords[i];
6518 if (excludedKeyword == eCSSKeyword_UNKNOWN) {
6519 break;
6520 }
6521 if (excludedKeyword == keyword) {
6522 return false;
6523 }
6524 }
6525 }
6526 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
6527 return true;
6528 }
6530 bool
6531 CSSParserImpl::ParseCounter(nsCSSValue& aValue)
6532 {
6533 nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
6534 eCSSUnit_Counter : eCSSUnit_Counters);
6536 // A non-iterative for loop to break out when an error occurs.
6537 for (;;) {
6538 if (!GetToken(true)) {
6539 break;
6540 }
6541 if (eCSSToken_Ident != mToken.mType) {
6542 UngetToken();
6543 break;
6544 }
6546 nsRefPtr<nsCSSValue::Array> val =
6547 nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
6549 val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
6551 if (eCSSUnit_Counters == unit) {
6552 // must have a comma and then a separator string
6553 if (!ExpectSymbol(',', true) || !GetToken(true)) {
6554 break;
6555 }
6556 if (eCSSToken_String != mToken.mType) {
6557 UngetToken();
6558 break;
6559 }
6560 val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
6561 }
6563 // get optional type
6564 int32_t type = NS_STYLE_LIST_STYLE_DECIMAL;
6565 if (ExpectSymbol(',', true)) {
6566 if (!GetToken(true)) {
6567 break;
6568 }
6569 nsCSSKeyword keyword;
6570 if (eCSSToken_Ident != mToken.mType ||
6571 (keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) ==
6572 eCSSKeyword_UNKNOWN ||
6573 !nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable,
6574 type)) {
6575 UngetToken();
6576 break;
6577 }
6578 }
6580 int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1;
6581 val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
6583 if (!ExpectSymbol(')', true)) {
6584 break;
6585 }
6587 aValue.SetArrayValue(val, unit);
6588 return true;
6589 }
6591 SkipUntil(')');
6592 return false;
6593 }
6595 bool
6596 CSSParserImpl::ParseAttr(nsCSSValue& aValue)
6597 {
6598 if (!GetToken(true)) {
6599 return false;
6600 }
6602 nsAutoString attr;
6603 if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
6604 nsAutoString holdIdent(mToken.mIdent);
6605 if (ExpectSymbol('|', false)) { // namespace
6606 int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent);
6607 if (nameSpaceID == kNameSpaceID_Unknown) {
6608 return false;
6609 }
6610 attr.AppendInt(nameSpaceID, 10);
6611 attr.Append(char16_t('|'));
6612 if (! GetToken(false)) {
6613 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
6614 return false;
6615 }
6616 if (eCSSToken_Ident == mToken.mType) {
6617 attr.Append(mToken.mIdent);
6618 }
6619 else {
6620 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
6621 UngetToken();
6622 return false;
6623 }
6624 }
6625 else { // no namespace
6626 attr = holdIdent;
6627 }
6628 }
6629 else if (mToken.IsSymbol('*')) { // namespace wildcard
6630 // Wildcard namespace makes no sense here and is not allowed
6631 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
6632 UngetToken();
6633 return false;
6634 }
6635 else if (mToken.IsSymbol('|')) { // explicit NO namespace
6636 if (! GetToken(false)) {
6637 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
6638 return false;
6639 }
6640 if (eCSSToken_Ident == mToken.mType) {
6641 attr.Append(mToken.mIdent);
6642 }
6643 else {
6644 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
6645 UngetToken();
6646 return false;
6647 }
6648 }
6649 else {
6650 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
6651 UngetToken();
6652 return false;
6653 }
6654 if (!ExpectSymbol(')', true)) {
6655 return false;
6656 }
6657 aValue.SetStringValue(attr, eCSSUnit_Attr);
6658 return true;
6659 }
6661 bool
6662 CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL)
6663 {
6664 if (!mSheetPrincipal) {
6665 NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
6666 "origin principal");
6667 return false;
6668 }
6670 nsRefPtr<nsStringBuffer> buffer(nsCSSValue::BufferFromString(aURL));
6672 // Note: urlVal retains its own reference to |buffer|.
6673 mozilla::css::URLValue *urlVal =
6674 new mozilla::css::URLValue(buffer, mBaseURI, mSheetURI, mSheetPrincipal);
6675 aValue.SetURLValue(urlVal);
6676 return true;
6677 }
6679 /**
6680 * Parse the image-orientation property, which has the grammar:
6681 * <angle> flip? | flip | from-image
6682 */
6683 bool
6684 CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue)
6685 {
6686 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
6687 // 'inherit', 'initial' and 'unset' must be alone
6688 return true;
6689 }
6691 // Check for an angle with optional 'flip'.
6692 nsCSSValue angle;
6693 if (ParseVariant(angle, VARIANT_ANGLE, nullptr)) {
6694 nsCSSValue flip;
6696 if (ParseVariant(flip, VARIANT_KEYWORD, nsCSSProps::kImageOrientationFlipKTable)) {
6697 nsRefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(2);
6698 array->Item(0) = angle;
6699 array->Item(1) = flip;
6700 aValue.SetArrayValue(array, eCSSUnit_Array);
6701 } else {
6702 aValue = angle;
6703 }
6705 return true;
6706 }
6708 // The remaining possibilities (bare 'flip' and 'from-image') are both
6709 // keywords, so we can handle them at the same time.
6710 nsCSSValue keyword;
6711 if (ParseVariant(keyword, VARIANT_KEYWORD, nsCSSProps::kImageOrientationKTable)) {
6712 aValue = keyword;
6713 return true;
6714 }
6716 // All possibilities failed.
6717 return false;
6718 }
6720 /**
6721 * Parse the arguments of -moz-image-rect() function.
6722 * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
6723 */
6724 bool
6725 CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
6726 {
6727 // A non-iterative for loop to break out when an error occurs.
6728 for (;;) {
6729 nsCSSValue newFunction;
6730 static const uint32_t kNumArgs = 5;
6731 nsCSSValue::Array* func =
6732 newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs);
6734 // func->Item(0) is reserved for the function name.
6735 nsCSSValue& url = func->Item(1);
6736 nsCSSValue& top = func->Item(2);
6737 nsCSSValue& right = func->Item(3);
6738 nsCSSValue& bottom = func->Item(4);
6739 nsCSSValue& left = func->Item(5);
6741 nsAutoString urlString;
6742 if (!ParseURLOrString(urlString) ||
6743 !SetValueToURL(url, urlString) ||
6744 !ExpectSymbol(',', true)) {
6745 break;
6746 }
6748 static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT;
6749 if (!ParseNonNegativeVariant(top, VARIANT_SIDE, nullptr) ||
6750 !ExpectSymbol(',', true) ||
6751 !ParseNonNegativeVariant(right, VARIANT_SIDE, nullptr) ||
6752 !ExpectSymbol(',', true) ||
6753 !ParseNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) ||
6754 !ExpectSymbol(',', true) ||
6755 !ParseNonNegativeVariant(left, VARIANT_SIDE, nullptr) ||
6756 !ExpectSymbol(')', true))
6757 break;
6759 aImage = newFunction;
6760 return true;
6761 }
6763 SkipUntil(')');
6764 return false;
6765 }
6767 // <element>: -moz-element(# <element_id> )
6768 bool
6769 CSSParserImpl::ParseElement(nsCSSValue& aValue)
6770 {
6771 // A non-iterative for loop to break out when an error occurs.
6772 for (;;) {
6773 if (!GetToken(true))
6774 break;
6776 if (mToken.mType == eCSSToken_ID) {
6777 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element);
6778 } else {
6779 UngetToken();
6780 break;
6781 }
6783 if (!ExpectSymbol(')', true))
6784 break;
6786 return true;
6787 }
6789 // If we detect a syntax error, we must match the opening parenthesis of the
6790 // function with the closing parenthesis and skip all the tokens in between.
6791 SkipUntil(')');
6792 return false;
6793 }
6795 // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
6796 bool
6797 CSSParserImpl::ParseFlex()
6798 {
6799 // First check for inherit / initial / unset
6800 nsCSSValue tmpVal;
6801 if (ParseVariant(tmpVal, VARIANT_INHERIT, nullptr)) {
6802 AppendValue(eCSSProperty_flex_grow, tmpVal);
6803 AppendValue(eCSSProperty_flex_shrink, tmpVal);
6804 AppendValue(eCSSProperty_flex_basis, tmpVal);
6805 return true;
6806 }
6808 // Next, check for 'none' == '0 0 auto'
6809 if (ParseVariant(tmpVal, VARIANT_NONE, nullptr)) {
6810 AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number));
6811 AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number));
6812 AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto));
6813 return true;
6814 }
6816 // OK, try parsing our value as individual per-subproperty components:
6817 // [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
6819 // Each subproperty has a default value that it takes when it's omitted in a
6820 // "flex" shorthand value. These default values are *only* for the shorthand
6821 // syntax -- they're distinct from the subproperties' own initial values. We
6822 // start with each subproperty at its default, as if we had "flex: 1 1 0%".
6823 nsCSSValue flexGrow(1.0f, eCSSUnit_Number);
6824 nsCSSValue flexShrink(1.0f, eCSSUnit_Number);
6825 nsCSSValue flexBasis(0.0f, eCSSUnit_Percent);
6827 // OVERVIEW OF PARSING STRATEGY:
6828 // =============================
6829 // a) Parse the first component as either flex-basis or flex-grow.
6830 // b) If it wasn't flex-grow, parse the _next_ component as flex-grow.
6831 // c) Now we've just parsed flex-grow -- so try parsing the next thing as
6832 // flex-shrink.
6833 // d) Finally: If we didn't get flex-basis at the beginning, try to parse
6834 // it now, at the end.
6835 //
6836 // More details in each section below.
6838 uint32_t flexBasisVariantMask =
6839 (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT));
6841 // (a) Parse first component. It can be either be a 'flex-basis' value or a
6842 // 'flex-grow' value, so we use the flex-basis-specific variant mask, along
6843 // with VARIANT_NUMBER to accept 'flex-grow' values.
6844 //
6845 // NOTE: if we encounter unitless 0 here, we *must* interpret it as a
6846 // 'flex-grow' value (a number), *not* as a 'flex-basis' value (a length).
6847 // Conveniently, that's the behavior this combined variant-mask gives us --
6848 // it'll treat unitless 0 as a number. The flexbox spec requires this:
6849 // "a unitless zero that is not already preceded by two flex factors must be
6850 // interpreted as a flex factor.
6851 if (!ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER,
6852 nsCSSProps::kWidthKTable)) {
6853 // First component was not a valid flex-basis or flex-grow value. Fail.
6854 return false;
6855 }
6857 // Record what we just parsed as either flex-basis or flex-grow:
6858 bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number);
6859 (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal;
6861 // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow.
6862 bool doneParsing = false;
6863 if (wasFirstComponentFlexBasis) {
6864 if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) {
6865 flexGrow = tmpVal;
6866 } else {
6867 // Failed to parse anything after our flex-basis -- that's fine. We can
6868 // skip the remaining parsing.
6869 doneParsing = true;
6870 }
6871 }
6873 if (!doneParsing) {
6874 // (c) OK -- the last thing we parsed was flex-grow, so look for a
6875 // flex-shrink in the next position.
6876 if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) {
6877 flexShrink = tmpVal;
6878 }
6880 // d) Finally: If we didn't get flex-basis at the beginning, try to parse
6881 // it now, at the end.
6882 //
6883 // NOTE: If we encounter unitless 0 in this final position, we'll parse it
6884 // as a 'flex-basis' value. That's OK, because we know it must have
6885 // been "preceded by 2 flex factors" (justification below), which gets us
6886 // out of the spec's requirement of otherwise having to treat unitless 0
6887 // as a flex factor.
6888 //
6889 // JUSTIFICATION: How do we know that a unitless 0 here must have been
6890 // preceded by 2 flex factors? Well, suppose we had a unitless 0 that
6891 // was preceded by only 1 flex factor. Then, we would have already
6892 // accepted this unitless 0 as the 'flex-shrink' value, up above (since
6893 // it's a valid flex-shrink value), and we'd have moved on to the next
6894 // token (if any). And of course, if we instead had a unitless 0 preceded
6895 // by *no* flex factors (if it were the first token), we would've already
6896 // parsed it in our very first call to ParseNonNegativeVariant(). So, any
6897 // unitless 0 encountered here *must* have been preceded by 2 flex factors.
6898 if (!wasFirstComponentFlexBasis &&
6899 ParseNonNegativeVariant(tmpVal, flexBasisVariantMask,
6900 nsCSSProps::kWidthKTable)) {
6901 flexBasis = tmpVal;
6902 }
6903 }
6905 AppendValue(eCSSProperty_flex_grow, flexGrow);
6906 AppendValue(eCSSProperty_flex_shrink, flexShrink);
6907 AppendValue(eCSSProperty_flex_basis, flexBasis);
6909 return true;
6910 }
6912 // flex-flow: <flex-direction> || <flex-wrap>
6913 bool
6914 CSSParserImpl::ParseFlexFlow()
6915 {
6916 static const nsCSSProperty kFlexFlowSubprops[] = {
6917 eCSSProperty_flex_direction,
6918 eCSSProperty_flex_wrap
6919 };
6920 const size_t numProps = MOZ_ARRAY_LENGTH(kFlexFlowSubprops);
6921 nsCSSValue values[numProps];
6923 int32_t found = ParseChoice(values, kFlexFlowSubprops, numProps);
6925 // Bail if we didn't successfully parse anything
6926 if (found < 1) {
6927 return false;
6928 }
6930 // If either property didn't get an explicit value, use its initial value.
6931 if ((found & 1) == 0) {
6932 values[0].SetIntValue(NS_STYLE_FLEX_DIRECTION_ROW, eCSSUnit_Enumerated);
6933 }
6934 if ((found & 2) == 0) {
6935 values[1].SetIntValue(NS_STYLE_FLEX_WRAP_NOWRAP, eCSSUnit_Enumerated);
6936 }
6938 // Store these values and declare success!
6939 for (size_t i = 0; i < numProps; i++) {
6940 AppendValue(kFlexFlowSubprops[i], values[i]);
6941 }
6942 return true;
6943 }
6945 bool
6946 CSSParserImpl::ParseGridAutoFlow()
6947 {
6948 nsCSSValue value;
6949 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
6950 AppendValue(eCSSProperty_grid_auto_flow, value);
6951 return true;
6952 }
6954 static const int32_t mask[] = {
6955 NS_STYLE_GRID_AUTO_FLOW_COLUMN | NS_STYLE_GRID_AUTO_FLOW_ROW,
6956 MASK_END_VALUE
6957 };
6958 if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) {
6959 return false;
6960 }
6961 int32_t bitField = value.GetIntValue();
6963 // Requires one of these
6964 if (!(bitField & NS_STYLE_GRID_AUTO_FLOW_NONE ||
6965 bitField & NS_STYLE_GRID_AUTO_FLOW_COLUMN ||
6966 bitField & NS_STYLE_GRID_AUTO_FLOW_ROW)) {
6967 return false;
6968 }
6970 // 'none' is only valid if it occurs alone:
6971 if (bitField & NS_STYLE_GRID_AUTO_FLOW_NONE &&
6972 bitField != NS_STYLE_GRID_AUTO_FLOW_NONE) {
6973 return false;
6974 }
6976 AppendValue(eCSSProperty_grid_auto_flow, value);
6977 return true;
6978 }
6980 CSSParseResult
6981 CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue)
6982 {
6983 if (!ExpectSymbol('(', true)) {
6984 return CSSParseResult::NotFound;
6985 }
6986 if (!GetToken(true) || mToken.IsSymbol(')')) {
6987 return CSSParseResult::Ok;
6988 }
6989 // 'return' so far leaves aValue untouched, to represent an empty list.
6991 nsCSSValueList* item;
6992 if (aValue.GetUnit() == eCSSUnit_List) {
6993 // Find the end of an existing list.
6994 // The grid-template shorthand uses this, at most once for a given list.
6996 // NOTE: we could avoid this traversal by somehow keeping around
6997 // a pointer to the last item from the previous call.
6998 // It's not yet clear if this is worth the additional code complexity.
6999 item = aValue.GetListValue();
7000 while (item->mNext) {
7001 item = item->mNext;
7002 }
7003 item->mNext = new nsCSSValueList;
7004 item = item->mNext;
7005 } else {
7006 MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "Unexpected unit");
7007 item = aValue.SetListValue();
7008 }
7009 for (;;) {
7010 if (!(eCSSToken_Ident == mToken.mType &&
7011 ParseCustomIdent(item->mValue, mToken.mIdent))) {
7012 UngetToken();
7013 SkipUntil(')');
7014 return CSSParseResult::Error;
7015 }
7016 if (!GetToken(true) || mToken.IsSymbol(')')) {
7017 return CSSParseResult::Ok;
7018 }
7019 item->mNext = new nsCSSValueList;
7020 item = item->mNext;
7021 }
7022 }
7024 // Assuming the 'repeat(' function token has already been consumed,
7025 // parse the rest of repeat(<positive-integer>, <line-names>+)
7026 // Append to the linked list whose end is given by |aTailPtr|,
7027 // and updated |aTailPtr| to point to the new end of the list.
7028 bool
7029 CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr)
7030 {
7031 if (!(GetToken(true) &&
7032 mToken.mType == eCSSToken_Number &&
7033 mToken.mIntegerValid &&
7034 mToken.mInteger > 0)) {
7035 SkipUntil(')');
7036 return false;
7037 }
7038 int32_t repetitions = std::min(mToken.mInteger,
7039 GRID_TEMPLATE_MAX_REPETITIONS);
7040 if (!ExpectSymbol(',', true)) {
7041 SkipUntil(')');
7042 return false;
7043 }
7045 // Parse at least one <line-names>
7046 nsCSSValueList* tail = *aTailPtr;
7047 do {
7048 tail->mNext = new nsCSSValueList;
7049 tail = tail->mNext;
7050 if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) {
7051 SkipUntil(')');
7052 return false;
7053 }
7054 } while (!ExpectSymbol(')', true));
7055 nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext;
7056 nsCSSValueList* lastRepeatedItem = tail;
7058 // Our repeated items are already in the target list once,
7059 // so they need to be repeated |repetitions - 1| more times.
7060 MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
7061 while (--repetitions) {
7062 nsCSSValueList* repeatedItem = firstRepeatedItem;
7063 for (;;) {
7064 tail->mNext = new nsCSSValueList;
7065 tail = tail->mNext;
7066 tail->mValue = repeatedItem->mValue;
7067 if (repeatedItem == lastRepeatedItem) {
7068 break;
7069 }
7070 repeatedItem = repeatedItem->mNext;
7071 }
7072 }
7073 *aTailPtr = tail;
7074 return true;
7075 }
7077 // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>?
7078 bool
7079 CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
7080 {
7081 nsCSSValueList* item = aValue.SetListValue();
7082 // This marker distinguishes the value from a <track-list>.
7083 item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID,
7084 eCSSUnit_Enumerated);
7085 for (;;) {
7086 // First try to parse repeat(<positive-integer>, <line-names>+)
7087 if (!GetToken(true)) {
7088 return true;
7089 }
7090 if (mToken.mType == eCSSToken_Function &&
7091 mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
7092 if (!ParseGridLineNameListRepeat(&item)) {
7093 return false;
7094 }
7095 } else {
7096 UngetToken();
7098 // This was not a repeat() function. Try to parse <line-names>.
7099 nsCSSValue lineNames;
7100 CSSParseResult result = ParseGridLineNames(lineNames);
7101 if (result == CSSParseResult::NotFound) {
7102 return true;
7103 }
7104 if (result == CSSParseResult::Error) {
7105 return false;
7106 }
7107 item->mNext = new nsCSSValueList;
7108 item = item->mNext;
7109 item->mValue = lineNames;
7110 }
7111 }
7112 }
7114 // Parse a <track-breadth>
7115 bool
7116 CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
7117 {
7118 if (ParseNonNegativeVariant(aValue,
7119 VARIANT_LPCALC | VARIANT_KEYWORD,
7120 nsCSSProps::kGridTrackBreadthKTable)) {
7121 return true;
7122 }
7124 // Attempt to parse <flex> (a dimension with the "fr" unit)
7125 if (!GetToken(true)) {
7126 return false;
7127 }
7128 if (!(eCSSToken_Dimension == mToken.mType &&
7129 mToken.mIdent.LowerCaseEqualsLiteral("fr") &&
7130 mToken.mNumber >= 0)) {
7131 UngetToken();
7132 return false;
7133 }
7134 aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction);
7135 return true;
7136 }
7138 // Parse a <track-size>
7139 CSSParseResult
7140 CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue)
7141 {
7142 // Attempt to parse 'auto' or a single <track-breadth>
7143 if (ParseGridTrackBreadth(aValue) ||
7144 ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
7145 return CSSParseResult::Ok;
7146 }
7148 // Attempt to parse a minmax() function
7149 if (!GetToken(true)) {
7150 return CSSParseResult::NotFound;
7151 }
7152 if (!(eCSSToken_Function == mToken.mType &&
7153 mToken.mIdent.LowerCaseEqualsLiteral("minmax"))) {
7154 UngetToken();
7155 return CSSParseResult::NotFound;
7156 }
7157 nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2);
7158 if (ParseGridTrackBreadth(func->Item(1)) &&
7159 ExpectSymbol(',', true) &&
7160 ParseGridTrackBreadth(func->Item(2)) &&
7161 ExpectSymbol(')', true)) {
7162 return CSSParseResult::Ok;
7163 }
7164 SkipUntil(')');
7165 return CSSParseResult::Error;
7166 }
7168 bool
7169 CSSParserImpl::ParseGridAutoColumnsRows(nsCSSProperty aPropID)
7170 {
7171 nsCSSValue value;
7172 if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
7173 ParseGridTrackSize(value) == CSSParseResult::Ok) {
7174 AppendValue(aPropID, value);
7175 return true;
7176 }
7177 return false;
7178 }
7180 bool
7181 CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
7182 const nsCSSValue& aFirstLineNames)
7183 {
7184 nsCSSValueList* firstLineNamesItem = aValue.SetListValue();
7185 firstLineNamesItem->mValue = aFirstLineNames;
7187 // This function is trying to parse <track-list>, which is
7188 // [ <line-names>? [ <track-size> | <repeat()> ] ]+ <line-names>?
7189 // and we're already past the first "<line-names>?".
7190 //
7191 // Each iteration of the following loop attempts to parse either a
7192 // repeat() or a <track-size> expression, and then an (optional)
7193 // <line-names> expression.
7194 //
7195 // The only successful exit point from this loop is the ::NotFound
7196 // case after ParseGridTrackSize(); i.e. we'll greedily parse
7197 // repeat()/<track-size> until we can't find one.
7198 nsCSSValueList* item = firstLineNamesItem;
7199 for (;;) {
7200 // First try to parse repeat()
7201 if (!GetToken(true)) {
7202 break;
7203 }
7204 if (mToken.mType == eCSSToken_Function &&
7205 mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
7206 if (!ParseGridTrackListRepeat(&item)) {
7207 return false;
7208 }
7209 } else {
7210 UngetToken();
7212 // This was not a repeat() function. Try to parse <track-size>.
7213 nsCSSValue trackSize;
7214 CSSParseResult result = ParseGridTrackSize(trackSize);
7215 if (result == CSSParseResult::Error) {
7216 return false;
7217 }
7218 if (result == CSSParseResult::NotFound) {
7219 // What we've parsed so far is a valid <track-list>
7220 // (modulo the "at least one <track-size>" check below.)
7221 // Stop here.
7222 break;
7223 }
7224 item->mNext = new nsCSSValueList;
7225 item = item->mNext;
7226 item->mValue = trackSize;
7228 item->mNext = new nsCSSValueList;
7229 item = item->mNext;
7230 }
7231 if (ParseGridLineNames(item->mValue) == CSSParseResult::Error) {
7232 return false;
7233 }
7234 }
7236 // Require at least one <track-size>.
7237 if (item == firstLineNamesItem) {
7238 return false;
7239 }
7241 MOZ_ASSERT(aValue.GetListValue() &&
7242 aValue.GetListValue()->mNext &&
7243 aValue.GetListValue()->mNext->mNext,
7244 "<track-list> should have a minimum length of 3");
7245 return true;
7246 }
7248 // Takes ownership of |aSecond|
7249 static void
7250 ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond)
7251 {
7252 if (aSecond.GetUnit() == eCSSUnit_Null) {
7253 // Nothing to do.
7254 return;
7255 }
7256 if (aFirst.GetUnit() == eCSSUnit_Null) {
7257 // Empty or omitted <line-names>. Replace it.
7258 aFirst = aSecond;
7259 return;
7260 }
7262 // Join the two <line-names> lists.
7263 nsCSSValueList* source = aSecond.GetListValue();
7264 nsCSSValueList* target = aFirst.GetListValue();
7265 // Find the end:
7266 while (target->mNext) {
7267 target = target->mNext;
7268 }
7269 // Copy the first name. We can't take ownership of it
7270 // as it'll be destroyed when |aSecond| goes out of scope.
7271 target->mNext = new nsCSSValueList;
7272 target = target->mNext;
7273 target->mValue = source->mValue;
7274 // Move the rest of the linked list.
7275 target->mNext = source->mNext;
7276 source->mNext = nullptr;
7277 }
7279 // Assuming the 'repeat(' function token has already been consumed,
7280 // parse the rest of
7281 // repeat( <positive-integer> ,
7282 // [ <line-names>? <track-size> ]+ <line-names>? )
7283 // Append to the linked list whose end is given by |aTailPtr|,
7284 // and updated |aTailPtr| to point to the new end of the list.
7285 bool
7286 CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
7287 {
7288 if (!(GetToken(true) &&
7289 mToken.mType == eCSSToken_Number &&
7290 mToken.mIntegerValid &&
7291 mToken.mInteger > 0)) {
7292 SkipUntil(')');
7293 return false;
7294 }
7295 int32_t repetitions = std::min(mToken.mInteger,
7296 GRID_TEMPLATE_MAX_REPETITIONS);
7297 if (!ExpectSymbol(',', true)) {
7298 SkipUntil(')');
7299 return false;
7300 }
7302 // Parse [ <line-names>? <track-size> ]+ <line-names>?
7303 // but keep the first and last <line-names> separate
7304 // because they'll need to be joined.
7305 // http://dev.w3.org/csswg/css-grid/#repeat-notation
7306 nsCSSValue firstLineNames;
7307 nsCSSValue trackSize;
7308 nsCSSValue lastLineNames;
7309 // Optional
7310 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) {
7311 SkipUntil(')');
7312 return false;
7313 }
7314 // Required
7315 if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
7316 SkipUntil(')');
7317 return false;
7318 }
7319 // Use nsAutoPtr to free the list in case of early return.
7320 nsAutoPtr<nsCSSValueList> firstTrackSizeItemAuto(new nsCSSValueList);
7321 firstTrackSizeItemAuto->mValue = trackSize;
7323 nsCSSValueList* item = firstTrackSizeItemAuto;
7324 for (;;) {
7325 // Optional
7326 if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) {
7327 SkipUntil(')');
7328 return false;
7329 }
7331 if (ExpectSymbol(')', true)) {
7332 break;
7333 }
7335 // Required
7336 if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
7337 SkipUntil(')');
7338 return false;
7339 }
7341 item->mNext = new nsCSSValueList;
7342 item = item->mNext;
7343 item->mValue = lastLineNames;
7344 // Do not append to this list at the next iteration.
7345 lastLineNames.Reset();
7347 item->mNext = new nsCSSValueList;
7348 item = item->mNext;
7349 item->mValue = trackSize;
7350 }
7351 nsCSSValueList* lastTrackSizeItem = item;
7353 // [ <line-names>? <track-size> ]+ <line-names>? is now parsed into:
7354 // * firstLineNames: the first <line-names>
7355 // * a linked list of odd length >= 1, from firstTrackSizeItem
7356 // (the first <track-size>) to lastTrackSizeItem (the last),
7357 // with the <line-names> sublists in between
7358 // * lastLineNames: the last <line-names>
7361 // Join the last and first <line-names> (in that order.)
7362 // For example, repeat(3, (a) 100px (b) 200px (c)) results in
7363 // (a) 100px (b) 200px (c a) 100px (b) 200px (c a) 100px (b) 200px (c)
7364 // This is (c a).
7365 // Make deep copies: the originals will be moved.
7366 nsCSSValue joinerLineNames;
7367 {
7368 nsCSSValueList* target = nullptr;
7369 if (lastLineNames.GetUnit() != eCSSUnit_Null) {
7370 target = joinerLineNames.SetListValue();
7371 nsCSSValueList* source = lastLineNames.GetListValue();
7372 for (;;) {
7373 target->mValue = source->mValue;
7374 source = source->mNext;
7375 if (!source) {
7376 break;
7377 }
7378 target->mNext = new nsCSSValueList;
7379 target = target->mNext;
7380 }
7381 }
7383 if (firstLineNames.GetUnit() != eCSSUnit_Null) {
7384 if (target) {
7385 target->mNext = new nsCSSValueList;
7386 target = target->mNext;
7387 } else {
7388 target = joinerLineNames.SetListValue();
7389 }
7390 nsCSSValueList* source = firstLineNames.GetListValue();
7391 for (;;) {
7392 target->mValue = source->mValue;
7393 source = source->mNext;
7394 if (!source) {
7395 break;
7396 }
7397 target->mNext = new nsCSSValueList;
7398 target = target->mNext;
7399 }
7400 }
7401 }
7403 // Join our first <line-names> with the one before repeat().
7404 // (a) repeat(1, (b) 20px) expands to (a b) 20px
7405 nsCSSValueList* previousItemBeforeRepeat = *aTailPtr;
7406 ConcatLineNames(previousItemBeforeRepeat->mValue, firstLineNames);
7408 // Move our linked list
7409 // (first to last <track-size>, with the <line-names> sublists in between).
7410 // This is the first repetition.
7411 NS_ASSERTION(previousItemBeforeRepeat->mNext == nullptr,
7412 "Expected the end of a linked list");
7413 previousItemBeforeRepeat->mNext = firstTrackSizeItemAuto.forget();
7414 nsCSSValueList* firstTrackSizeItem = previousItemBeforeRepeat->mNext;
7415 nsCSSValueList* tail = lastTrackSizeItem;
7417 // Repeat |repetitions - 1| more times:
7418 // * the joiner <line-names>
7419 // * the linked list
7420 // (first to last <track-size>, with the <line-names> sublists in between)
7421 MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
7422 while (--repetitions) {
7423 tail->mNext = new nsCSSValueList;
7424 tail = tail->mNext;
7425 tail->mValue = joinerLineNames;
7427 nsCSSValueList* repeatedItem = firstTrackSizeItem;
7428 for (;;) {
7429 tail->mNext = new nsCSSValueList;
7430 tail = tail->mNext;
7431 tail->mValue = repeatedItem->mValue;
7432 if (repeatedItem == lastTrackSizeItem) {
7433 break;
7434 }
7435 repeatedItem = repeatedItem->mNext;
7436 }
7437 }
7439 // Finally, move our last <line-names>.
7440 // Any <line-names> immediately after repeat() will append to it.
7441 tail->mNext = new nsCSSValueList;
7442 tail = tail->mNext;
7443 tail->mValue = lastLineNames;
7445 *aTailPtr = tail;
7446 return true;
7447 }
7449 bool
7450 CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSProperty aPropID)
7451 {
7452 nsCSSValue value;
7453 if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
7454 AppendValue(aPropID, value);
7455 return true;
7456 }
7458 nsSubstring* ident = NextIdent();
7459 if (ident) {
7460 if (ident->LowerCaseEqualsLiteral("subgrid")) {
7461 if (!ParseOptionalLineNameListAfterSubgrid(value)) {
7462 return false;
7463 }
7464 AppendValue(aPropID, value);
7465 return true;
7466 }
7467 UngetToken();
7468 }
7470 nsCSSValue firstLineNames;
7471 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
7472 !ParseGridTrackListWithFirstLineNames(value, firstLineNames)) {
7473 return false;
7474 }
7475 AppendValue(aPropID, value);
7476 return true;
7477 }
7479 bool
7480 CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput,
7481 css::GridTemplateAreasValue* aAreas,
7482 nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices)
7483 {
7484 aAreas->mTemplates.AppendElement(mToken.mIdent);
7486 nsCSSGridTemplateAreaScanner scanner(aInput);
7487 nsCSSGridTemplateAreaToken token;
7488 css::GridNamedArea* currentArea = nullptr;
7489 uint32_t row = aAreas->NRows();
7490 uint32_t column;
7491 for (column = 1; scanner.Next(token); column++) {
7492 if (token.isTrash) {
7493 return false;
7494 }
7495 if (currentArea) {
7496 if (token.mName == currentArea->mName) {
7497 if (currentArea->mRowStart == row) {
7498 // Next column in the first row of this named area.
7499 currentArea->mColumnEnd++;
7500 }
7501 continue;
7502 }
7503 // We're exiting |currentArea|, so currentArea is ending at |column|.
7504 // Make sure that this is consistent with currentArea on previous rows:
7505 if (currentArea->mColumnEnd != column) {
7506 NS_ASSERTION(currentArea->mRowStart != row,
7507 "Inconsistent column end for the first row of a named area.");
7508 // Not a rectangle
7509 return false;
7510 }
7511 currentArea = nullptr;
7512 }
7513 if (!token.mName.IsEmpty()) {
7514 // Named cell that doesn't have a cell with the same name on its left.
7516 // Check if this is the continuation of an existing named area:
7517 uint32_t index;
7518 if (aAreaIndices.Get(token.mName, &index)) {
7519 MOZ_ASSERT(index < aAreas->mNamedAreas.Length(),
7520 "Invalid aAreaIndices hash table");
7521 currentArea = &aAreas->mNamedAreas[index];
7522 if (currentArea->mColumnStart != column ||
7523 currentArea->mRowEnd != row) {
7524 // Existing named area, but not forming a rectangle
7525 return false;
7526 }
7527 // Next row of an existing named area
7528 currentArea->mRowEnd++;
7529 } else {
7530 // New named area
7531 aAreaIndices.Put(token.mName, aAreas->mNamedAreas.Length());
7532 currentArea = aAreas->mNamedAreas.AppendElement();
7533 currentArea->mName = token.mName;
7534 // For column or row N (starting at 1),
7535 // the start line is N, the end line is N + 1
7536 currentArea->mColumnStart = column;
7537 currentArea->mColumnEnd = column + 1;
7538 currentArea->mRowStart = row;
7539 currentArea->mRowEnd = row + 1;
7540 }
7541 }
7542 }
7543 if (currentArea && currentArea->mColumnEnd != column) {
7544 NS_ASSERTION(currentArea->mRowStart != row,
7545 "Inconsistent column end for the first row of a named area.");
7546 // Not a rectangle
7547 return false;
7548 }
7550 // On the first row, set the number of columns
7551 // that grid-template-areas contributes to the explicit grid.
7552 // On other rows, check that the number of columns is consistent
7553 // between rows.
7554 if (row == 1) {
7555 aAreas->mNColumns = column;
7556 } else if (aAreas->mNColumns != column) {
7557 return false;
7558 }
7559 return true;
7560 }
7562 bool
7563 CSSParserImpl::ParseGridTemplateAreas()
7564 {
7565 nsCSSValue value;
7566 if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
7567 AppendValue(eCSSProperty_grid_template_areas, value);
7568 return true;
7569 }
7571 nsRefPtr<css::GridTemplateAreasValue> areas =
7572 new css::GridTemplateAreasValue();
7573 nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
7574 for (;;) {
7575 if (!GetToken(true)) {
7576 break;
7577 }
7578 if (eCSSToken_String != mToken.mType) {
7579 UngetToken();
7580 break;
7581 }
7582 if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
7583 return false;
7584 }
7585 }
7587 if (areas->NRows() == 0) {
7588 return false;
7589 }
7591 AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
7592 return true;
7593 }
7595 bool
7596 CSSParserImpl::ParseGridTemplate()
7597 {
7598 // none |
7599 // subgrid |
7600 // <'grid-template-columns'> / <'grid-template-rows'> |
7601 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
7602 nsCSSValue value;
7603 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
7604 AppendValue(eCSSProperty_grid_template_areas, value);
7605 AppendValue(eCSSProperty_grid_template_columns, value);
7606 AppendValue(eCSSProperty_grid_template_rows, value);
7607 return true;
7608 }
7610 // TODO (bug 983175): add parsing for 'subgrid' by itself
7612 // 'none' can appear either by itself,
7613 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
7614 if (ParseVariant(value, VARIANT_NONE, nullptr)) {
7615 AppendValue(eCSSProperty_grid_template_columns, value);
7616 if (ExpectSymbol('/', true)) {
7617 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
7618 }
7619 AppendValue(eCSSProperty_grid_template_areas, value);
7620 AppendValue(eCSSProperty_grid_template_rows, value);
7621 return true;
7622 }
7624 // 'subgrid' can appear either by itself,
7625 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
7626 nsSubstring* ident = NextIdent();
7627 if (ident) {
7628 if (ident->LowerCaseEqualsLiteral("subgrid")) {
7629 if (!ParseOptionalLineNameListAfterSubgrid(value)) {
7630 return false;
7631 }
7632 AppendValue(eCSSProperty_grid_template_columns, value);
7633 if (ExpectSymbol('/', true)) {
7634 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
7635 }
7636 if (value.GetListValue()->mNext) {
7637 // Non-empty <line-name-list> after 'subgrid'.
7638 // This is only valid as part of <'grid-template-columns'>,
7639 // which must be followed by a slash.
7640 return false;
7641 }
7642 // 'subgrid' by itself sets both grid-template-columns
7643 // and grid-template-rows.
7644 AppendValue(eCSSProperty_grid_template_rows, value);
7645 value.SetNoneValue();
7646 AppendValue(eCSSProperty_grid_template_areas, value);
7647 return true;
7648 }
7649 UngetToken();
7650 }
7652 // [ <line-names>? ] here is ambiguous:
7653 // it can be either the start of a <track-list>,
7654 // or the start of [ <line-names>? <string> <track-size>? <line-names>? ]+
7655 nsCSSValue firstLineNames;
7656 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
7657 !GetToken(true)) {
7658 return false;
7659 }
7660 if (mToken.mType == eCSSToken_String) {
7661 // [ <track-list> / ]? was omitted
7662 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
7663 value.SetNoneValue();
7664 AppendValue(eCSSProperty_grid_template_columns, value);
7665 return ParseGridTemplateAfterString(firstLineNames);
7666 }
7667 UngetToken();
7669 if (!(ParseGridTrackListWithFirstLineNames(value, firstLineNames) &&
7670 ExpectSymbol('/', true))) {
7671 return false;
7672 }
7673 AppendValue(eCSSProperty_grid_template_columns, value);
7674 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ true);
7675 }
7677 // Helper for parsing the 'grid-template' shorthand
7678 //
7679 // NOTE: This parses the portion after the slash, for *one* of the
7680 // following types of expressions:
7681 // - <'grid-template-columns'> / <'grid-template-rows'>
7682 // - <track-list> / [ <line-names>? <string> <track-size>? <line-names>? ]+
7683 //
7684 // We don't know which type of expression we've got until we've parsed the
7685 // second half, since the pre-slash part is ambiguous. The various return
7686 // clauses below are labeled with the type of expression they're completing.
7687 bool
7688 CSSParserImpl::ParseGridTemplateAfterSlash(bool aColumnsIsTrackList)
7689 {
7690 nsCSSValue rowsValue;
7691 if (ParseVariant(rowsValue, VARIANT_NONE, nullptr)) {
7692 // <'grid-template-columns'> / <'grid-template-rows'>
7693 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
7694 nsCSSValue areasValue(eCSSUnit_None); // implied
7695 AppendValue(eCSSProperty_grid_template_areas, areasValue);
7696 return true;
7697 }
7699 nsSubstring* ident = NextIdent();
7700 if (ident) {
7701 if (ident->LowerCaseEqualsLiteral("subgrid")) {
7702 if (!ParseOptionalLineNameListAfterSubgrid(rowsValue)) {
7703 return false;
7704 }
7705 // <'grid-template-columns'> / <'grid-template-rows'>
7706 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
7707 nsCSSValue areasValue(eCSSUnit_None); // implied
7708 AppendValue(eCSSProperty_grid_template_areas, areasValue);
7709 return true;
7710 }
7711 UngetToken();
7712 }
7714 nsCSSValue firstLineNames;
7715 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
7716 !GetToken(true)) {
7717 return false;
7718 }
7719 if (aColumnsIsTrackList && mToken.mType == eCSSToken_String) {
7720 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
7721 return ParseGridTemplateAfterString(firstLineNames);
7722 }
7723 UngetToken();
7725 if (!ParseGridTrackListWithFirstLineNames(rowsValue, firstLineNames)) {
7726 return false;
7727 }
7729 // <'grid-template-columns'> / <'grid-template-rows'>
7730 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
7731 nsCSSValue areasValue(eCSSUnit_None); // implied
7732 AppendValue(eCSSProperty_grid_template_areas, areasValue);
7733 return true;
7734 }
7736 // Helper for parsing the 'grid-template' shorthand:
7737 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
7738 // with a <line-names>? already consumed, stored in |aFirstLineNames|,
7739 // and the current token a <string>
7740 bool
7741 CSSParserImpl::ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames)
7742 {
7743 MOZ_ASSERT(mToken.mType == eCSSToken_String,
7744 "ParseGridTemplateAfterString called with a non-string token");
7746 nsCSSValue rowsValue;
7747 nsRefPtr<css::GridTemplateAreasValue> areas =
7748 new css::GridTemplateAreasValue();
7749 nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
7750 nsCSSValueList* rowsItem = rowsValue.SetListValue();
7751 rowsItem->mValue = aFirstLineNames;
7753 for (;;) {
7754 if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
7755 return false;
7756 }
7758 rowsItem->mNext = new nsCSSValueList;
7759 rowsItem = rowsItem->mNext;
7760 CSSParseResult result = ParseGridTrackSize(rowsItem->mValue);
7761 if (result == CSSParseResult::Error) {
7762 return false;
7763 }
7764 if (result == CSSParseResult::NotFound) {
7765 rowsItem->mValue.SetAutoValue();
7766 }
7768 rowsItem->mNext = new nsCSSValueList;
7769 rowsItem = rowsItem->mNext;
7770 result = ParseGridLineNames(rowsItem->mValue);
7771 if (result == CSSParseResult::Error) {
7772 return false;
7773 }
7774 if (result == CSSParseResult::Ok) {
7775 // Append to the same list as the previous call to ParseGridLineNames.
7776 result = ParseGridLineNames(rowsItem->mValue);
7777 if (result == CSSParseResult::Error) {
7778 return false;
7779 }
7780 if (result == CSSParseResult::Ok) {
7781 // Parsed <line-name> twice.
7782 // The property value can not end here, we expect a string next.
7783 if (!GetToken(true)) {
7784 return false;
7785 }
7786 if (eCSSToken_String != mToken.mType) {
7787 UngetToken();
7788 return false;
7789 }
7790 continue;
7791 }
7792 }
7794 // Did not find a <line-names>.
7795 // Next, we expect either a string or the end of the property value.
7796 if (!GetToken(true)) {
7797 break;
7798 }
7799 if (eCSSToken_String != mToken.mType) {
7800 UngetToken();
7801 break;
7802 }
7803 }
7805 AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
7806 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
7807 return true;
7808 }
7810 // <'grid-template'> |
7811 // [ <'grid-auto-flow'> [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]? ]
7812 bool
7813 CSSParserImpl::ParseGrid()
7814 {
7815 nsCSSValue value;
7816 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
7817 for (const nsCSSProperty* subprops =
7818 nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid);
7819 *subprops != eCSSProperty_UNKNOWN; ++subprops) {
7820 AppendValue(*subprops, value);
7821 }
7822 return true;
7823 }
7825 // 'none' at the beginning could be a <'grid-auto-flow'>
7826 // (which also covers 'none' by itself)
7827 // or a <'grid-template-columns'> (as part of <'grid-template'>)
7828 if (ParseVariant(value, VARIANT_NONE, nullptr)) {
7829 if (ExpectSymbol('/', true)) {
7830 AppendValue(eCSSProperty_grid_template_columns, value);
7832 // Set grid-auto-* subproperties to their initial values.
7833 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated);
7834 AppendValue(eCSSProperty_grid_auto_flow, value);
7835 value.SetAutoValue();
7836 AppendValue(eCSSProperty_grid_auto_columns, value);
7837 AppendValue(eCSSProperty_grid_auto_rows, value);
7839 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
7840 }
7841 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated);
7842 AppendValue(eCSSProperty_grid_auto_flow, value);
7843 return ParseGridShorthandAutoProps();
7844 }
7846 // An empty value is always invalid.
7847 if (!GetToken(true)) {
7848 return false;
7849 }
7851 // If the value starts with a 'dense', 'column' or 'row' keyword,
7852 // it can only start with a <'grid-auto-flow'>
7853 if (mToken.mType == eCSSToken_Ident) {
7854 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
7855 if (keyword == eCSSKeyword_dense ||
7856 keyword == eCSSKeyword_column ||
7857 keyword == eCSSKeyword_row) {
7858 UngetToken();
7859 return ParseGridAutoFlow() && ParseGridShorthandAutoProps();
7860 }
7861 }
7862 UngetToken();
7864 // Set other subproperties to their initial values
7865 // and parse <'grid-template'>.
7866 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated);
7867 AppendValue(eCSSProperty_grid_auto_flow, value);
7868 value.SetAutoValue();
7869 AppendValue(eCSSProperty_grid_auto_columns, value);
7870 AppendValue(eCSSProperty_grid_auto_rows, value);
7871 return ParseGridTemplate();
7872 }
7874 // Parse [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]?
7875 // for the 'grid' shorthand.
7876 // Assumes that <'grid-auto-flow'> was already parsed by the caller.
7877 bool
7878 CSSParserImpl::ParseGridShorthandAutoProps()
7879 {
7880 nsCSSValue autoColumnsValue;
7881 nsCSSValue autoRowsValue;
7882 CSSParseResult result = ParseGridTrackSize(autoColumnsValue);
7883 if (result == CSSParseResult::Error) {
7884 return false;
7885 }
7886 if (result == CSSParseResult::NotFound) {
7887 autoColumnsValue.SetAutoValue();
7888 autoRowsValue.SetAutoValue();
7889 } else {
7890 if (!ExpectSymbol('/', true)) {
7891 autoRowsValue.SetAutoValue();
7892 } else if (ParseGridTrackSize(autoRowsValue) != CSSParseResult::Ok) {
7893 return false;
7894 }
7895 }
7896 AppendValue(eCSSProperty_grid_auto_columns, autoColumnsValue);
7897 AppendValue(eCSSProperty_grid_auto_rows, autoRowsValue);
7898 nsCSSValue templateValue(eCSSUnit_None); // Initial values
7899 AppendValue(eCSSProperty_grid_template_areas, templateValue);
7900 AppendValue(eCSSProperty_grid_template_columns, templateValue);
7901 AppendValue(eCSSProperty_grid_template_rows, templateValue);
7902 return true;
7903 }
7905 // Parse a <grid-line>.
7906 // If successful, set aValue to eCSSUnit_Auto,
7907 // or a eCSSUnit_List containing, in that order:
7908 //
7909 // * An optional eCSSUnit_Enumerated marking a "span" keyword.
7910 // * An optional eCSSUnit_Integer
7911 // * An optional eCSSUnit_Ident
7912 //
7913 // At least one of eCSSUnit_Integer or eCSSUnit_Ident is present.
7914 bool
7915 CSSParserImpl::ParseGridLine(nsCSSValue& aValue)
7916 {
7917 // <grid-line> =
7918 // auto |
7919 // <custom-ident> |
7920 // [ <integer> && <custom-ident>? ] |
7921 // [ span && [ <integer> || <custom-ident> ] ]
7922 //
7923 // Syntactically, this simplifies to:
7924 //
7925 // <grid-line> =
7926 // auto |
7927 // [ span? && [ <integer> || <custom-ident> ] ]
7929 if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
7930 return true;
7931 }
7933 static const nsCSSKeyword kGridLineKeywords[] = {
7934 eCSSKeyword_span,
7935 eCSSKeyword_UNKNOWN // End-of-array marker
7936 };
7937 bool hasSpan = false;
7938 bool hasInteger = false;
7939 bool hasIdent = false;
7940 int32_t integer;
7941 nsCSSValue ident;
7943 if (!GetToken(true)) {
7944 return false;
7945 }
7946 if (mToken.mType == eCSSToken_Ident &&
7947 mToken.mIdent.LowerCaseEqualsLiteral("span")) {
7948 hasSpan = true;
7949 if (!GetToken(true)) {
7950 return false;
7951 }
7952 }
7954 do {
7955 if (!hasIdent &&
7956 mToken.mType == eCSSToken_Ident &&
7957 ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) {
7958 hasIdent = true;
7959 } else if (!hasInteger &&
7960 mToken.mType == eCSSToken_Number &&
7961 mToken.mIntegerValid &&
7962 mToken.mInteger != 0) {
7963 hasInteger = true;
7964 integer = mToken.mInteger;
7965 } else {
7966 UngetToken();
7967 break;
7968 }
7969 } while (!(hasInteger && hasIdent) && GetToken(true));
7971 // Require at least one of <integer> or <custom-ident>
7972 if (!(hasInteger || hasIdent)) {
7973 return false;
7974 }
7976 if (!hasSpan && GetToken(true)) {
7977 if (mToken.mType == eCSSToken_Ident &&
7978 mToken.mIdent.LowerCaseEqualsLiteral("span")) {
7979 hasSpan = true;
7980 } else {
7981 UngetToken();
7982 }
7983 }
7985 nsCSSValueList* item = aValue.SetListValue();
7986 if (hasSpan) {
7987 // Given "span", a negative <integer> is invalid.
7988 if (hasInteger && integer < 0) {
7989 return false;
7990 }
7991 // '1' here is a dummy value.
7992 // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword.
7993 item->mValue.SetIntValue(1, eCSSUnit_Enumerated);
7994 item->mNext = new nsCSSValueList;
7995 item = item->mNext;
7996 }
7997 if (hasInteger) {
7998 item->mValue.SetIntValue(integer, eCSSUnit_Integer);
7999 if (hasIdent) {
8000 item->mNext = new nsCSSValueList;
8001 item = item->mNext;
8002 }
8003 }
8004 if (hasIdent) {
8005 item->mValue = ident;
8006 }
8007 return true;
8008 }
8010 bool
8011 CSSParserImpl::ParseGridAutoPosition()
8012 {
8013 nsCSSValue value;
8014 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
8015 AppendValue(eCSSProperty_grid_auto_position, value);
8016 return true;
8017 }
8018 nsCSSValue columnStartValue;
8019 nsCSSValue rowStartValue;
8020 if (ParseGridLine(columnStartValue) &&
8021 ExpectSymbol('/', true) &&
8022 ParseGridLine(rowStartValue)) {
8023 value.SetPairValue(columnStartValue, rowStartValue);
8024 AppendValue(eCSSProperty_grid_auto_position, value);
8025 return true;
8026 }
8027 return false;
8028 }
8030 bool
8031 CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSProperty aPropID)
8032 {
8033 nsCSSValue value;
8034 if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
8035 ParseGridLine(value)) {
8036 AppendValue(aPropID, value);
8037 return true;
8038 }
8039 return false;
8040 }
8042 // If |aFallback| is a List containing a single Ident, set |aValue| to that.
8043 // Otherwise, set |aValue| to Auto.
8044 // Used with |aFallback| from ParseGridLine()
8045 static void
8046 HandleGridLineFallback(const nsCSSValue& aFallback, nsCSSValue& aValue)
8047 {
8048 if (aFallback.GetUnit() == eCSSUnit_List &&
8049 aFallback.GetListValue()->mValue.GetUnit() == eCSSUnit_Ident &&
8050 !aFallback.GetListValue()->mNext) {
8051 aValue = aFallback;
8052 } else {
8053 aValue.SetAutoValue();
8054 }
8055 }
8057 bool
8058 CSSParserImpl::ParseGridColumnRow(nsCSSProperty aStartPropID,
8059 nsCSSProperty aEndPropID)
8060 {
8061 nsCSSValue value;
8062 nsCSSValue secondValue;
8063 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
8064 AppendValue(aStartPropID, value);
8065 AppendValue(aEndPropID, value);
8066 return true;
8067 }
8069 if (!ParseGridLine(value)) {
8070 return false;
8071 }
8072 if (GetToken(true)) {
8073 if (mToken.IsSymbol('/')) {
8074 if (ParseGridLine(secondValue)) {
8075 AppendValue(aStartPropID, value);
8076 AppendValue(aEndPropID, secondValue);
8077 return true;
8078 } else {
8079 return false;
8080 }
8081 }
8082 UngetToken();
8083 }
8085 // A single <custom-ident> is repeated to both properties,
8086 // anything else sets the grid-{column,row}-end property to 'auto'.
8087 HandleGridLineFallback(value, secondValue);
8089 AppendValue(aStartPropID, value);
8090 AppendValue(aEndPropID, secondValue);
8091 return true;
8092 }
8094 bool
8095 CSSParserImpl::ParseGridArea()
8096 {
8097 nsCSSValue values[4];
8098 if (ParseVariant(values[0], VARIANT_INHERIT, nullptr)) {
8099 AppendValue(eCSSProperty_grid_row_start, values[0]);
8100 AppendValue(eCSSProperty_grid_column_start, values[0]);
8101 AppendValue(eCSSProperty_grid_row_end, values[0]);
8102 AppendValue(eCSSProperty_grid_column_end, values[0]);
8103 return true;
8104 }
8106 int32_t i = 0;
8107 for (;;) {
8108 if (!ParseGridLine(values[i])) {
8109 return false;
8110 }
8111 if (++i == 4 || !GetToken(true)) {
8112 break;
8113 }
8114 if (!mToken.IsSymbol('/')) {
8115 UngetToken();
8116 break;
8117 }
8118 }
8120 MOZ_ASSERT(i >= 1, "should have parsed at least one grid-line (or returned)");
8121 if (i < 2) {
8122 HandleGridLineFallback(values[0], values[1]);
8123 }
8124 if (i < 3) {
8125 HandleGridLineFallback(values[0], values[2]);
8126 }
8127 if (i < 4) {
8128 HandleGridLineFallback(values[1], values[3]);
8129 }
8131 AppendValue(eCSSProperty_grid_row_start, values[0]);
8132 AppendValue(eCSSProperty_grid_column_start, values[1]);
8133 AppendValue(eCSSProperty_grid_row_end, values[2]);
8134 AppendValue(eCSSProperty_grid_column_end, values[3]);
8135 return true;
8136 }
8138 // <color-stop> : <color> [ <percentage> | <length> ]?
8139 bool
8140 CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
8141 {
8142 nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
8143 if (!ParseVariant(stop->mColor, VARIANT_COLOR, nullptr)) {
8144 return false;
8145 }
8147 // Stop positions do not have to fall between the starting-point and
8148 // ending-point, so we don't use ParseNonNegativeVariant.
8149 if (!ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr)) {
8150 stop->mLocation.SetNoneValue();
8151 }
8152 return true;
8153 }
8155 // <gradient>
8156 // : linear-gradient( <linear-gradient-line>? <color-stops> ')'
8157 // | radial-gradient( <radial-gradient-line>? <color-stops> ')'
8158 //
8159 // <linear-gradient-line> : [ to [left | right] || [top | bottom] ] ,
8160 // | <legacy-gradient-line>
8161 // <radial-gradient-line> : [ <shape> || <size> ] [ at <position> ]? ,
8162 // | [ at <position> ] ,
8163 // | <legacy-gradient-line>? <legacy-shape-size>?
8164 // <shape> : circle | ellipse
8165 // <size> : closest-side | closest-corner | farthest-side | farthest-corner
8166 // | <length> | [<length> | <percentage>]{2}
8167 //
8168 // <legacy-gradient-line> : [ <position> || <angle>] ,
8169 //
8170 // <legacy-shape-size> : [ <shape> || <legacy-size> ] ,
8171 // <legacy-size> : closest-side | closest-corner | farthest-side
8172 // | farthest-corner | contain | cover
8173 //
8174 // <color-stops> : <color-stop> , <color-stop> [, <color-stop>]*
8175 bool
8176 CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating,
8177 bool aIsLegacy)
8178 {
8179 nsRefPtr<nsCSSValueGradient> cssGradient
8180 = new nsCSSValueGradient(false, aIsRepeating);
8182 if (!GetToken(true)) {
8183 return false;
8184 }
8186 if (mToken.mType == eCSSToken_Ident &&
8187 mToken.mIdent.LowerCaseEqualsLiteral("to")) {
8189 // "to" syntax doesn't allow explicit "center"
8190 if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) {
8191 SkipUntil(')');
8192 return false;
8193 }
8195 // [ to [left | right] || [top | bottom] ] ,
8196 const nsCSSValue& xValue = cssGradient->mBgPos.mXValue;
8197 const nsCSSValue& yValue = cssGradient->mBgPos.mYValue;
8198 if (xValue.GetUnit() != eCSSUnit_Enumerated ||
8199 !(xValue.GetIntValue() & (NS_STYLE_BG_POSITION_LEFT |
8200 NS_STYLE_BG_POSITION_CENTER |
8201 NS_STYLE_BG_POSITION_RIGHT)) ||
8202 yValue.GetUnit() != eCSSUnit_Enumerated ||
8203 !(yValue.GetIntValue() & (NS_STYLE_BG_POSITION_TOP |
8204 NS_STYLE_BG_POSITION_CENTER |
8205 NS_STYLE_BG_POSITION_BOTTOM))) {
8206 SkipUntil(')');
8207 return false;
8208 }
8210 if (!ExpectSymbol(',', true)) {
8211 SkipUntil(')');
8212 return false;
8213 }
8215 return ParseGradientColorStops(cssGradient, aValue);
8216 }
8218 if (!aIsLegacy) {
8219 UngetToken();
8221 // <angle> ,
8222 if (ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) &&
8223 !ExpectSymbol(',', true)) {
8224 SkipUntil(')');
8225 return false;
8226 }
8228 return ParseGradientColorStops(cssGradient, aValue);
8229 }
8231 nsCSSTokenType ty = mToken.mType;
8232 nsString id = mToken.mIdent;
8233 UngetToken();
8235 // <legacy-gradient-line>
8236 bool haveGradientLine = IsLegacyGradientLine(ty, id);
8237 if (haveGradientLine) {
8238 cssGradient->mIsLegacySyntax = true;
8239 bool haveAngle =
8240 ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
8242 // if we got an angle, we might now have a comma, ending the gradient-line
8243 if (!haveAngle || !ExpectSymbol(',', true)) {
8244 if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
8245 SkipUntil(')');
8246 return false;
8247 }
8249 if (!ExpectSymbol(',', true) &&
8250 // if we didn't already get an angle, we might have one now,
8251 // otherwise it's an error
8252 (haveAngle ||
8253 !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) ||
8254 // now we better have a comma
8255 !ExpectSymbol(',', true))) {
8256 SkipUntil(')');
8257 return false;
8258 }
8259 }
8260 }
8262 return ParseGradientColorStops(cssGradient, aValue);
8263 }
8265 bool
8266 CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating,
8267 bool aIsLegacy)
8268 {
8269 nsRefPtr<nsCSSValueGradient> cssGradient
8270 = new nsCSSValueGradient(true, aIsRepeating);
8272 // [ <shape> || <size> ]
8273 bool haveShape =
8274 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
8275 nsCSSProps::kRadialGradientShapeKTable);
8277 bool haveSize = ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
8278 aIsLegacy ?
8279 nsCSSProps::kRadialGradientLegacySizeKTable :
8280 nsCSSProps::kRadialGradientSizeKTable);
8281 if (haveSize) {
8282 if (!haveShape) {
8283 // <size> <shape>
8284 haveShape = ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
8285 nsCSSProps::kRadialGradientShapeKTable);
8286 }
8287 } else if (!aIsLegacy) {
8288 // Save RadialShape before parsing RadiusX because RadialShape and
8289 // RadiusX share the storage.
8290 int32_t shape =
8291 cssGradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated ?
8292 cssGradient->GetRadialShape().GetIntValue() : -1;
8293 // <length> | [<length> | <percentage>]{2}
8294 cssGradient->mIsExplicitSize = true;
8295 haveSize =
8296 ParseNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP, nullptr);
8297 if (!haveSize) {
8298 // It was not an explicit size after all.
8299 // Note that ParseNonNegativeVariant may have put something
8300 // invalid into our storage, but only in the case where it was
8301 // rejected only for being negative. Since this means the token
8302 // was a length or a percentage, we know it's not valid syntax
8303 // (which must be a comma, the 'at' keyword, or a color), so we
8304 // know this value will be dropped. This means it doesn't matter
8305 // that we have something invalid in our storage.
8306 cssGradient->mIsExplicitSize = false;
8307 } else {
8308 // vertical extent is optional
8309 bool haveYSize =
8310 ParseNonNegativeVariant(cssGradient->GetRadiusY(), VARIANT_LP, nullptr);
8311 if (!haveShape) {
8312 nsCSSValue shapeValue;
8313 haveShape = ParseVariant(shapeValue, VARIANT_KEYWORD,
8314 nsCSSProps::kRadialGradientShapeKTable);
8315 if (haveShape) {
8316 shape = shapeValue.GetIntValue();
8317 }
8318 }
8319 if (haveYSize
8320 ? shape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR
8321 : cssGradient->GetRadiusX().GetUnit() == eCSSUnit_Percent ||
8322 shape == NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL) {
8323 SkipUntil(')');
8324 return false;
8325 }
8326 }
8327 }
8329 if ((haveShape || haveSize) && ExpectSymbol(',', true)) {
8330 // [ <shape> || <size> ] ,
8331 return ParseGradientColorStops(cssGradient, aValue);
8332 }
8334 if (!GetToken(true)) {
8335 return false;
8336 }
8338 if (!aIsLegacy) {
8339 if (mToken.mType == eCSSToken_Ident &&
8340 mToken.mIdent.LowerCaseEqualsLiteral("at")) {
8341 // [ <shape> || <size> ]? at <position> ,
8342 if (!ParseBoxPositionValues(cssGradient->mBgPos, false) ||
8343 !ExpectSymbol(',', true)) {
8344 SkipUntil(')');
8345 return false;
8346 }
8348 return ParseGradientColorStops(cssGradient, aValue);
8349 }
8351 // <color-stops> only
8352 UngetToken();
8353 return ParseGradientColorStops(cssGradient, aValue);
8354 }
8355 MOZ_ASSERT(!cssGradient->mIsExplicitSize);
8357 nsCSSTokenType ty = mToken.mType;
8358 nsString id = mToken.mIdent;
8359 UngetToken();
8361 // <legacy-gradient-line>
8362 bool haveGradientLine = false;
8363 // if we already encountered a shape or size,
8364 // we can not have a gradient-line in legacy syntax
8365 if (!haveShape && !haveSize) {
8366 haveGradientLine = IsLegacyGradientLine(ty, id);
8367 }
8368 if (haveGradientLine) {
8369 bool haveAngle =
8370 ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
8372 // if we got an angle, we might now have a comma, ending the gradient-line
8373 if (!haveAngle || !ExpectSymbol(',', true)) {
8374 if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
8375 SkipUntil(')');
8376 return false;
8377 }
8379 if (!ExpectSymbol(',', true) &&
8380 // if we didn't already get an angle, we might have one now,
8381 // otherwise it's an error
8382 (haveAngle ||
8383 !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) ||
8384 // now we better have a comma
8385 !ExpectSymbol(',', true))) {
8386 SkipUntil(')');
8387 return false;
8388 }
8389 }
8391 if (cssGradient->mAngle.GetUnit() != eCSSUnit_None) {
8392 cssGradient->mIsLegacySyntax = true;
8393 }
8394 }
8396 // radial gradients might have a shape and size here for legacy syntax
8397 if (!haveShape && !haveSize) {
8398 haveShape =
8399 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
8400 nsCSSProps::kRadialGradientShapeKTable);
8401 haveSize =
8402 ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
8403 nsCSSProps::kRadialGradientLegacySizeKTable);
8405 // could be in either order
8406 if (!haveShape) {
8407 haveShape =
8408 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
8409 nsCSSProps::kRadialGradientShapeKTable);
8410 }
8411 }
8413 if ((haveShape || haveSize) && !ExpectSymbol(',', true)) {
8414 SkipUntil(')');
8415 return false;
8416 }
8418 return ParseGradientColorStops(cssGradient, aValue);
8419 }
8421 bool
8422 CSSParserImpl::IsLegacyGradientLine(const nsCSSTokenType& aType,
8423 const nsString& aId)
8424 {
8425 // N.B. ParseBoxPositionValues is not guaranteed to put back
8426 // everything it scanned if it fails, so we must only call it
8427 // if there is no alternative to consuming a <box-position>.
8428 // ParseVariant, as used here, will either succeed and consume
8429 // a single token, or fail and consume none, so we can be more
8430 // cavalier about calling it.
8432 bool haveGradientLine = false;
8433 switch (aType) {
8434 case eCSSToken_Percentage:
8435 case eCSSToken_Number:
8436 case eCSSToken_Dimension:
8437 haveGradientLine = true;
8438 break;
8440 case eCSSToken_Function:
8441 if (aId.LowerCaseEqualsLiteral("calc") ||
8442 aId.LowerCaseEqualsLiteral("-moz-calc")) {
8443 haveGradientLine = true;
8444 break;
8445 }
8446 // fall through
8447 case eCSSToken_ID:
8448 case eCSSToken_Hash:
8449 // this is a color
8450 break;
8452 case eCSSToken_Ident: {
8453 // This is only a gradient line if it's a box position keyword.
8454 nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(aId);
8455 int32_t junk;
8456 if (kw != eCSSKeyword_UNKNOWN &&
8457 nsCSSProps::FindKeyword(kw, nsCSSProps::kBackgroundPositionKTable,
8458 junk)) {
8459 haveGradientLine = true;
8460 }
8461 break;
8462 }
8464 default:
8465 // error
8466 break;
8467 }
8469 return haveGradientLine;
8470 }
8472 bool
8473 CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient,
8474 nsCSSValue& aValue)
8475 {
8476 // At least two color stops are required
8477 if (!ParseColorStop(aGradient) ||
8478 !ExpectSymbol(',', true) ||
8479 !ParseColorStop(aGradient)) {
8480 SkipUntil(')');
8481 return false;
8482 }
8484 // Additional color stops
8485 while (ExpectSymbol(',', true)) {
8486 if (!ParseColorStop(aGradient)) {
8487 SkipUntil(')');
8488 return false;
8489 }
8490 }
8492 if (!ExpectSymbol(')', true)) {
8493 SkipUntil(')');
8494 return false;
8495 }
8497 aValue.SetGradientValue(aGradient);
8498 return true;
8499 }
8501 int32_t
8502 CSSParserImpl::ParseChoice(nsCSSValue aValues[],
8503 const nsCSSProperty aPropIDs[], int32_t aNumIDs)
8504 {
8505 int32_t found = 0;
8506 nsAutoParseCompoundProperty compound(this);
8508 int32_t loop;
8509 for (loop = 0; loop < aNumIDs; loop++) {
8510 // Try each property parser in order
8511 int32_t hadFound = found;
8512 int32_t index;
8513 for (index = 0; index < aNumIDs; index++) {
8514 int32_t bit = 1 << index;
8515 if ((found & bit) == 0) {
8516 if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) {
8517 found |= bit;
8518 // It's more efficient to break since it will reset |hadFound|
8519 // to |found|. Furthermore, ParseListStyle depends on our going
8520 // through the properties in order for each value..
8521 break;
8522 }
8523 }
8524 }
8525 if (found == hadFound) { // found nothing new
8526 break;
8527 }
8528 }
8529 if (0 < found) {
8530 if (1 == found) { // only first property
8531 if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
8532 for (loop = 1; loop < aNumIDs; loop++) {
8533 aValues[loop].SetInheritValue();
8534 }
8535 found = ((1 << aNumIDs) - 1);
8536 }
8537 else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
8538 for (loop = 1; loop < aNumIDs; loop++) {
8539 aValues[loop].SetInitialValue();
8540 }
8541 found = ((1 << aNumIDs) - 1);
8542 }
8543 else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset
8544 for (loop = 1; loop < aNumIDs; loop++) {
8545 aValues[loop].SetUnsetValue();
8546 }
8547 found = ((1 << aNumIDs) - 1);
8548 }
8549 }
8550 else { // more than one value, verify no inherits, initials or unsets
8551 for (loop = 0; loop < aNumIDs; loop++) {
8552 if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
8553 found = -1;
8554 break;
8555 }
8556 else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
8557 found = -1;
8558 break;
8559 }
8560 else if (eCSSUnit_Unset == aValues[loop].GetUnit()) {
8561 found = -1;
8562 break;
8563 }
8564 }
8565 }
8566 }
8567 return found;
8568 }
8570 void
8571 CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue)
8572 {
8573 mTempData.AddLonghandProperty(aPropID, aValue);
8574 }
8576 /**
8577 * Parse a "box" property. Box properties have 1 to 4 values. When less
8578 * than 4 values are provided a standard mapping is used to replicate
8579 * existing values.
8580 */
8581 bool
8582 CSSParserImpl::ParseBoxProperties(const nsCSSProperty aPropIDs[])
8583 {
8584 // Get up to four values for the property
8585 int32_t count = 0;
8586 nsCSSRect result;
8587 NS_FOR_CSS_SIDES (index) {
8588 if (! ParseSingleValueProperty(result.*(nsCSSRect::sides[index]),
8589 aPropIDs[index])) {
8590 break;
8591 }
8592 count++;
8593 }
8594 if (count == 0) {
8595 return false;
8596 }
8598 if (1 < count) { // verify no more than single inherit, initial or unset
8599 NS_FOR_CSS_SIDES (index) {
8600 nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
8601 if (eCSSUnit_Inherit == unit ||
8602 eCSSUnit_Initial == unit ||
8603 eCSSUnit_Unset == unit) {
8604 return false;
8605 }
8606 }
8607 }
8609 // Provide missing values by replicating some of the values found
8610 switch (count) {
8611 case 1: // Make right == top
8612 result.mRight = result.mTop;
8613 case 2: // Make bottom == top
8614 result.mBottom = result.mTop;
8615 case 3: // Make left == right
8616 result.mLeft = result.mRight;
8617 }
8619 NS_FOR_CSS_SIDES (index) {
8620 AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index]));
8621 }
8622 return true;
8623 }
8625 // Similar to ParseBoxProperties, except there is only one property
8626 // with the result as its value, not four. Requires values be nonnegative.
8627 bool
8628 CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask,
8629 /** outparam */ nsCSSValue& aValue)
8630 {
8631 nsCSSRect& result = aValue.SetRectValue();
8633 int32_t count = 0;
8634 NS_FOR_CSS_SIDES (index) {
8635 if (!ParseNonNegativeVariant(result.*(nsCSSRect::sides[index]),
8636 aVariantMask, nullptr)) {
8637 break;
8638 }
8639 count++;
8640 }
8642 if (count == 0) {
8643 return false;
8644 }
8646 // Provide missing values by replicating some of the values found
8647 switch (count) {
8648 case 1: // Make right == top
8649 result.mRight = result.mTop;
8650 case 2: // Make bottom == top
8651 result.mBottom = result.mTop;
8652 case 3: // Make left == right
8653 result.mLeft = result.mRight;
8654 }
8656 return true;
8657 }
8659 bool
8660 CSSParserImpl::ParseDirectionalBoxProperty(nsCSSProperty aProperty,
8661 int32_t aSourceType)
8662 {
8663 const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty);
8664 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
8665 "not box property with physical vs. logical cascading");
8666 nsCSSValue value;
8667 if (!ParseSingleValueProperty(value, subprops[0])) {
8668 return false;
8669 }
8671 AppendValue(subprops[0], value);
8672 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
8673 AppendValue(subprops[1], typeVal);
8674 AppendValue(subprops[2], typeVal);
8675 return true;
8676 }
8678 bool
8679 CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID)
8680 {
8681 nsCSSValue dimenX, dimenY;
8682 // required first value
8683 if (! ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr))
8684 return false;
8686 // optional second value (forbidden if first value is inherit/initial/unset)
8687 if (dimenX.GetUnit() != eCSSUnit_Inherit &&
8688 dimenX.GetUnit() != eCSSUnit_Initial &&
8689 dimenX.GetUnit() != eCSSUnit_Unset) {
8690 ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr);
8691 }
8693 if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) {
8694 AppendValue(aPropID, dimenX);
8695 } else {
8696 nsCSSValue value;
8697 value.SetPairValue(dimenX, dimenY);
8698 AppendValue(aPropID, value);
8699 }
8700 return true;
8701 }
8703 bool
8704 CSSParserImpl::ParseBoxCornerRadii(const nsCSSProperty aPropIDs[])
8705 {
8706 // Rectangles are used as scratch storage.
8707 // top => top-left, right => top-right,
8708 // bottom => bottom-right, left => bottom-left.
8709 nsCSSRect dimenX, dimenY;
8710 int32_t countX = 0, countY = 0;
8712 NS_FOR_CSS_SIDES (side) {
8713 if (! ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side],
8714 (side > 0 ? 0 : VARIANT_INHERIT) |
8715 VARIANT_LP | VARIANT_CALC,
8716 nullptr))
8717 break;
8718 countX++;
8719 }
8720 if (countX == 0)
8721 return false;
8723 if (ExpectSymbol('/', true)) {
8724 NS_FOR_CSS_SIDES (side) {
8725 if (! ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side],
8726 VARIANT_LP | VARIANT_CALC, nullptr))
8727 break;
8728 countY++;
8729 }
8730 if (countY == 0)
8731 return false;
8732 }
8734 // if 'initial', 'inherit' or 'unset' was used, it must be the only value
8735 if (countX > 1 || countY > 0) {
8736 nsCSSUnit unit = dimenX.mTop.GetUnit();
8737 if (eCSSUnit_Inherit == unit ||
8738 eCSSUnit_Initial == unit ||
8739 eCSSUnit_Unset == unit)
8740 return false;
8741 }
8743 // if we have no Y-values, use the X-values
8744 if (countY == 0) {
8745 dimenY = dimenX;
8746 countY = countX;
8747 }
8749 // Provide missing values by replicating some of the values found
8750 switch (countX) {
8751 case 1: dimenX.mRight = dimenX.mTop; // top-right same as top-left, and
8752 case 2: dimenX.mBottom = dimenX.mTop; // bottom-right same as top-left, and
8753 case 3: dimenX.mLeft = dimenX.mRight; // bottom-left same as top-right
8754 }
8756 switch (countY) {
8757 case 1: dimenY.mRight = dimenY.mTop; // top-right same as top-left, and
8758 case 2: dimenY.mBottom = dimenY.mTop; // bottom-right same as top-left, and
8759 case 3: dimenY.mLeft = dimenY.mRight; // bottom-left same as top-right
8760 }
8762 NS_FOR_CSS_SIDES(side) {
8763 nsCSSValue& x = dimenX.*nsCSSRect::sides[side];
8764 nsCSSValue& y = dimenY.*nsCSSRect::sides[side];
8766 if (x == y) {
8767 AppendValue(aPropIDs[side], x);
8768 } else {
8769 nsCSSValue pair;
8770 pair.SetPairValue(x, y);
8771 AppendValue(aPropIDs[side], pair);
8772 }
8773 }
8774 return true;
8775 }
8777 // These must be in CSS order (top,right,bottom,left) for indexing to work
8778 static const nsCSSProperty kBorderStyleIDs[] = {
8779 eCSSProperty_border_top_style,
8780 eCSSProperty_border_right_style_value,
8781 eCSSProperty_border_bottom_style,
8782 eCSSProperty_border_left_style_value
8783 };
8784 static const nsCSSProperty kBorderWidthIDs[] = {
8785 eCSSProperty_border_top_width,
8786 eCSSProperty_border_right_width_value,
8787 eCSSProperty_border_bottom_width,
8788 eCSSProperty_border_left_width_value
8789 };
8790 static const nsCSSProperty kBorderColorIDs[] = {
8791 eCSSProperty_border_top_color,
8792 eCSSProperty_border_right_color_value,
8793 eCSSProperty_border_bottom_color,
8794 eCSSProperty_border_left_color_value
8795 };
8796 static const nsCSSProperty kBorderRadiusIDs[] = {
8797 eCSSProperty_border_top_left_radius,
8798 eCSSProperty_border_top_right_radius,
8799 eCSSProperty_border_bottom_right_radius,
8800 eCSSProperty_border_bottom_left_radius
8801 };
8802 static const nsCSSProperty kOutlineRadiusIDs[] = {
8803 eCSSProperty__moz_outline_radius_topLeft,
8804 eCSSProperty__moz_outline_radius_topRight,
8805 eCSSProperty__moz_outline_radius_bottomRight,
8806 eCSSProperty__moz_outline_radius_bottomLeft
8807 };
8809 void
8810 CSSParserImpl::SaveInputState(CSSParserInputState& aState)
8811 {
8812 aState.mToken = mToken;
8813 aState.mHavePushBack = mHavePushBack;
8814 mScanner->SavePosition(aState.mPosition);
8815 }
8817 void
8818 CSSParserImpl::RestoreSavedInputState(const CSSParserInputState& aState)
8819 {
8820 mToken = aState.mToken;
8821 mHavePushBack = aState.mHavePushBack;
8822 mScanner->RestoreSavedPosition(aState.mPosition);
8823 }
8825 bool
8826 CSSParserImpl::ParseProperty(nsCSSProperty aPropID)
8827 {
8828 // Can't use AutoRestore<bool> because it's a bitfield.
8829 NS_ABORT_IF_FALSE(!mHashlessColorQuirk,
8830 "hashless color quirk should not be set");
8831 NS_ABORT_IF_FALSE(!mUnitlessLengthQuirk,
8832 "unitless length quirk should not be set");
8834 if (mNavQuirkMode) {
8835 mHashlessColorQuirk =
8836 nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_HASHLESS_COLOR_QUIRK);
8837 mUnitlessLengthQuirk =
8838 nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_UNITLESS_LENGTH_QUIRK);
8839 }
8841 // Save the current input state so that we can restore it later if we
8842 // have to re-parse the property value as a variable-reference-containing
8843 // token stream.
8844 CSSParserInputState stateBeforeProperty;
8845 SaveInputState(stateBeforeProperty);
8846 mScanner->ClearSeenVariableReference();
8848 NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
8849 bool allowVariables = true;
8850 bool result;
8851 switch (nsCSSProps::PropertyParseType(aPropID)) {
8852 case CSS_PROPERTY_PARSE_INACCESSIBLE: {
8853 // The user can't use these
8854 REPORT_UNEXPECTED(PEInaccessibleProperty2);
8855 allowVariables = false;
8856 result = false;
8857 break;
8858 }
8859 case CSS_PROPERTY_PARSE_FUNCTION: {
8860 result = ParsePropertyByFunction(aPropID);
8861 break;
8862 }
8863 case CSS_PROPERTY_PARSE_VALUE: {
8864 result = false;
8865 nsCSSValue value;
8866 if (ParseSingleValueProperty(value, aPropID)) {
8867 AppendValue(aPropID, value);
8868 result = true;
8869 }
8870 // XXX Report errors?
8871 break;
8872 }
8873 case CSS_PROPERTY_PARSE_VALUE_LIST: {
8874 result = ParseValueList(aPropID);
8875 break;
8876 }
8877 default: {
8878 result = false;
8879 allowVariables = false;
8880 NS_ABORT_IF_FALSE(false,
8881 "Property's flags field in nsCSSPropList.h is missing "
8882 "one of the CSS_PROPERTY_PARSE_* constants");
8883 break;
8884 }
8885 }
8887 if (result) {
8888 // We need to call ExpectEndProperty() to decide whether to reparse
8889 // with variables. This is needed because the property parsing may
8890 // have stopped upon finding a variable (e.g., 'margin: 1px var(a)')
8891 // in a way that future variable substitutions will be valid, or
8892 // because it parsed everything that's possible but we still want to
8893 // act as though the property contains variables even though we know
8894 // the substitution will never work (e.g., for 'margin: 1px 2px 3px
8895 // 4px 5px var(a)').
8896 //
8897 // It would be nice to find a better solution here
8898 // (and for the SkipUntilOneOf below), though, that doesn't depend
8899 // on using what we don't accept for doing parsing correctly.
8900 if (!ExpectEndProperty()) {
8901 result = false;
8902 }
8903 }
8905 bool seenVariable = mScanner->SeenVariableReference() ||
8906 (stateBeforeProperty.mHavePushBack &&
8907 stateBeforeProperty.mToken.mType == eCSSToken_Function &&
8908 stateBeforeProperty.mToken.mIdent.LowerCaseEqualsLiteral("var"));
8909 bool parseAsTokenStream;
8911 if (!result && allowVariables) {
8912 parseAsTokenStream = true;
8913 if (!seenVariable) {
8914 // We might have stopped parsing the property before its end and before
8915 // finding a variable reference. Keep checking until the end of the
8916 // property.
8917 CSSParserInputState stateAtError;
8918 SaveInputState(stateAtError);
8920 const char16_t stopChars[] = { ';', '!', '}', ')', 0 };
8921 SkipUntilOneOf(stopChars);
8922 UngetToken();
8923 parseAsTokenStream = mScanner->SeenVariableReference();
8925 if (!parseAsTokenStream) {
8926 // If we parsed to the end of the propery and didn't find any variable
8927 // references, then the real position we want to report the error at
8928 // is |stateAtError|.
8929 RestoreSavedInputState(stateAtError);
8930 }
8931 }
8932 } else {
8933 parseAsTokenStream = false;
8934 }
8936 if (parseAsTokenStream) {
8937 // Go back to the start of the property value and parse it to make sure
8938 // its variable references are syntactically valid and is otherwise
8939 // balanced.
8940 RestoreSavedInputState(stateBeforeProperty);
8942 if (!mInSupportsCondition) {
8943 mScanner->StartRecording();
8944 }
8946 CSSVariableDeclarations::Type type;
8947 bool dropBackslash;
8948 nsString impliedCharacters;
8949 nsCSSValue value;
8950 if (ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
8951 nullptr, nullptr)) {
8952 MOZ_ASSERT(type == CSSVariableDeclarations::eTokenStream,
8953 "a non-custom property reparsed since it contained variable "
8954 "references should not have been 'initial' or 'inherit'");
8956 nsString propertyValue;
8958 if (!mInSupportsCondition) {
8959 // If we are in an @supports condition, we don't need to store the
8960 // actual token stream on the nsCSSValue.
8961 mScanner->StopRecording(propertyValue);
8962 if (dropBackslash) {
8963 MOZ_ASSERT(!propertyValue.IsEmpty() &&
8964 propertyValue[propertyValue.Length() - 1] == '\\');
8965 propertyValue.Truncate(propertyValue.Length() - 1);
8966 }
8967 propertyValue.Append(impliedCharacters);
8968 }
8970 if (mHavePushBack) {
8971 // If we came to the end of a property value that had a variable
8972 // reference and a token was pushed back, then it would have been
8973 // ended by '!', ')', ';', ']' or '}'. We should remove it from the
8974 // recorded property value.
8975 MOZ_ASSERT(mToken.IsSymbol('!') ||
8976 mToken.IsSymbol(')') ||
8977 mToken.IsSymbol(';') ||
8978 mToken.IsSymbol(']') ||
8979 mToken.IsSymbol('}'));
8980 if (!mInSupportsCondition) {
8981 MOZ_ASSERT(!propertyValue.IsEmpty());
8982 MOZ_ASSERT(propertyValue[propertyValue.Length() - 1] ==
8983 mToken.mSymbol);
8984 propertyValue.Truncate(propertyValue.Length() - 1);
8985 }
8986 }
8988 if (!mInSupportsCondition) {
8989 if (nsCSSProps::IsShorthand(aPropID)) {
8990 // If this is a shorthand property, we store the token stream on each
8991 // of its corresponding longhand properties.
8992 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
8993 nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
8994 tokenStream->mPropertyID = *p;
8995 tokenStream->mShorthandPropertyID = aPropID;
8996 tokenStream->mTokenStream = propertyValue;
8997 tokenStream->mBaseURI = mBaseURI;
8998 tokenStream->mSheetURI = mSheetURI;
8999 tokenStream->mSheetPrincipal = mSheetPrincipal;
9000 tokenStream->mSheet = mSheet;
9001 tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
9002 tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
9003 value.SetTokenStreamValue(tokenStream);
9004 AppendValue(*p, value);
9005 }
9006 } else {
9007 nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
9008 tokenStream->mPropertyID = aPropID;
9009 tokenStream->mTokenStream = propertyValue;
9010 tokenStream->mBaseURI = mBaseURI;
9011 tokenStream->mSheetURI = mSheetURI;
9012 tokenStream->mSheetPrincipal = mSheetPrincipal;
9013 tokenStream->mSheet = mSheet;
9014 tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
9015 tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
9016 value.SetTokenStreamValue(tokenStream);
9017 AppendValue(aPropID, value);
9018 }
9019 }
9020 result = true;
9021 } else {
9022 if (!mInSupportsCondition) {
9023 mScanner->StopRecording();
9024 }
9025 }
9026 }
9028 if (mNavQuirkMode) {
9029 mHashlessColorQuirk = false;
9030 mUnitlessLengthQuirk = false;
9031 }
9033 return result;
9034 }
9036 bool
9037 CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID)
9038 {
9039 switch (aPropID) { // handle shorthand or multiple properties
9040 case eCSSProperty_background:
9041 return ParseBackground();
9042 case eCSSProperty_background_repeat:
9043 return ParseBackgroundRepeat();
9044 case eCSSProperty_background_position:
9045 return ParseBackgroundPosition();
9046 case eCSSProperty_background_size:
9047 return ParseBackgroundSize();
9048 case eCSSProperty_border:
9049 return ParseBorderSide(kBorderTopIDs, true);
9050 case eCSSProperty_border_color:
9051 return ParseBorderColor();
9052 case eCSSProperty_border_spacing:
9053 return ParseBorderSpacing();
9054 case eCSSProperty_border_style:
9055 return ParseBorderStyle();
9056 case eCSSProperty_border_bottom:
9057 return ParseBorderSide(kBorderBottomIDs, false);
9058 case eCSSProperty_border_end:
9059 return ParseDirectionalBorderSide(kBorderEndIDs,
9060 NS_BOXPROP_SOURCE_LOGICAL);
9061 case eCSSProperty_border_left:
9062 return ParseDirectionalBorderSide(kBorderLeftIDs,
9063 NS_BOXPROP_SOURCE_PHYSICAL);
9064 case eCSSProperty_border_right:
9065 return ParseDirectionalBorderSide(kBorderRightIDs,
9066 NS_BOXPROP_SOURCE_PHYSICAL);
9067 case eCSSProperty_border_start:
9068 return ParseDirectionalBorderSide(kBorderStartIDs,
9069 NS_BOXPROP_SOURCE_LOGICAL);
9070 case eCSSProperty_border_top:
9071 return ParseBorderSide(kBorderTopIDs, false);
9072 case eCSSProperty_border_bottom_colors:
9073 case eCSSProperty_border_left_colors:
9074 case eCSSProperty_border_right_colors:
9075 case eCSSProperty_border_top_colors:
9076 return ParseBorderColors(aPropID);
9077 case eCSSProperty_border_image_slice:
9078 return ParseBorderImageSlice(true, nullptr);
9079 case eCSSProperty_border_image_width:
9080 return ParseBorderImageWidth(true);
9081 case eCSSProperty_border_image_outset:
9082 return ParseBorderImageOutset(true);
9083 case eCSSProperty_border_image_repeat:
9084 return ParseBorderImageRepeat(true);
9085 case eCSSProperty_border_image:
9086 return ParseBorderImage();
9087 case eCSSProperty_border_width:
9088 return ParseBorderWidth();
9089 case eCSSProperty_border_end_color:
9090 return ParseDirectionalBoxProperty(eCSSProperty_border_end_color,
9091 NS_BOXPROP_SOURCE_LOGICAL);
9092 case eCSSProperty_border_left_color:
9093 return ParseDirectionalBoxProperty(eCSSProperty_border_left_color,
9094 NS_BOXPROP_SOURCE_PHYSICAL);
9095 case eCSSProperty_border_right_color:
9096 return ParseDirectionalBoxProperty(eCSSProperty_border_right_color,
9097 NS_BOXPROP_SOURCE_PHYSICAL);
9098 case eCSSProperty_border_start_color:
9099 return ParseDirectionalBoxProperty(eCSSProperty_border_start_color,
9100 NS_BOXPROP_SOURCE_LOGICAL);
9101 case eCSSProperty_border_end_width:
9102 return ParseDirectionalBoxProperty(eCSSProperty_border_end_width,
9103 NS_BOXPROP_SOURCE_LOGICAL);
9104 case eCSSProperty_border_left_width:
9105 return ParseDirectionalBoxProperty(eCSSProperty_border_left_width,
9106 NS_BOXPROP_SOURCE_PHYSICAL);
9107 case eCSSProperty_border_right_width:
9108 return ParseDirectionalBoxProperty(eCSSProperty_border_right_width,
9109 NS_BOXPROP_SOURCE_PHYSICAL);
9110 case eCSSProperty_border_start_width:
9111 return ParseDirectionalBoxProperty(eCSSProperty_border_start_width,
9112 NS_BOXPROP_SOURCE_LOGICAL);
9113 case eCSSProperty_border_end_style:
9114 return ParseDirectionalBoxProperty(eCSSProperty_border_end_style,
9115 NS_BOXPROP_SOURCE_LOGICAL);
9116 case eCSSProperty_border_left_style:
9117 return ParseDirectionalBoxProperty(eCSSProperty_border_left_style,
9118 NS_BOXPROP_SOURCE_PHYSICAL);
9119 case eCSSProperty_border_right_style:
9120 return ParseDirectionalBoxProperty(eCSSProperty_border_right_style,
9121 NS_BOXPROP_SOURCE_PHYSICAL);
9122 case eCSSProperty_border_start_style:
9123 return ParseDirectionalBoxProperty(eCSSProperty_border_start_style,
9124 NS_BOXPROP_SOURCE_LOGICAL);
9125 case eCSSProperty_border_radius:
9126 return ParseBoxCornerRadii(kBorderRadiusIDs);
9127 case eCSSProperty__moz_outline_radius:
9128 return ParseBoxCornerRadii(kOutlineRadiusIDs);
9130 case eCSSProperty_border_top_left_radius:
9131 case eCSSProperty_border_top_right_radius:
9132 case eCSSProperty_border_bottom_right_radius:
9133 case eCSSProperty_border_bottom_left_radius:
9134 case eCSSProperty__moz_outline_radius_topLeft:
9135 case eCSSProperty__moz_outline_radius_topRight:
9136 case eCSSProperty__moz_outline_radius_bottomRight:
9137 case eCSSProperty__moz_outline_radius_bottomLeft:
9138 return ParseBoxCornerRadius(aPropID);
9140 case eCSSProperty_box_shadow:
9141 case eCSSProperty_text_shadow:
9142 return ParseShadowList(aPropID);
9144 case eCSSProperty_clip:
9145 return ParseRect(eCSSProperty_clip);
9146 case eCSSProperty__moz_columns:
9147 return ParseColumns();
9148 case eCSSProperty__moz_column_rule:
9149 return ParseBorderSide(kColumnRuleIDs, false);
9150 case eCSSProperty_content:
9151 return ParseContent();
9152 case eCSSProperty_counter_increment:
9153 case eCSSProperty_counter_reset:
9154 return ParseCounterData(aPropID);
9155 case eCSSProperty_cursor:
9156 return ParseCursor();
9157 case eCSSProperty_filter:
9158 return ParseFilter();
9159 case eCSSProperty_flex:
9160 return ParseFlex();
9161 case eCSSProperty_flex_flow:
9162 return ParseFlexFlow();
9163 case eCSSProperty_font:
9164 return ParseFont();
9165 case eCSSProperty_grid_auto_flow:
9166 return ParseGridAutoFlow();
9167 case eCSSProperty_grid_auto_columns:
9168 case eCSSProperty_grid_auto_rows:
9169 return ParseGridAutoColumnsRows(aPropID);
9170 case eCSSProperty_grid_template_areas:
9171 return ParseGridTemplateAreas();
9172 case eCSSProperty_grid_template_columns:
9173 case eCSSProperty_grid_template_rows:
9174 return ParseGridTemplateColumnsRows(aPropID);
9175 case eCSSProperty_grid_template:
9176 return ParseGridTemplate();
9177 case eCSSProperty_grid:
9178 return ParseGrid();
9179 case eCSSProperty_grid_auto_position:
9180 return ParseGridAutoPosition();
9181 case eCSSProperty_grid_column_start:
9182 case eCSSProperty_grid_column_end:
9183 case eCSSProperty_grid_row_start:
9184 case eCSSProperty_grid_row_end:
9185 return ParseGridColumnRowStartEnd(aPropID);
9186 case eCSSProperty_grid_column:
9187 return ParseGridColumnRow(eCSSProperty_grid_column_start,
9188 eCSSProperty_grid_column_end);
9189 case eCSSProperty_grid_row:
9190 return ParseGridColumnRow(eCSSProperty_grid_row_start,
9191 eCSSProperty_grid_row_end);
9192 case eCSSProperty_grid_area:
9193 return ParseGridArea();
9194 case eCSSProperty_image_region:
9195 return ParseRect(eCSSProperty_image_region);
9196 case eCSSProperty_list_style:
9197 return ParseListStyle();
9198 case eCSSProperty_margin:
9199 return ParseMargin();
9200 case eCSSProperty_margin_end:
9201 return ParseDirectionalBoxProperty(eCSSProperty_margin_end,
9202 NS_BOXPROP_SOURCE_LOGICAL);
9203 case eCSSProperty_margin_left:
9204 return ParseDirectionalBoxProperty(eCSSProperty_margin_left,
9205 NS_BOXPROP_SOURCE_PHYSICAL);
9206 case eCSSProperty_margin_right:
9207 return ParseDirectionalBoxProperty(eCSSProperty_margin_right,
9208 NS_BOXPROP_SOURCE_PHYSICAL);
9209 case eCSSProperty_margin_start:
9210 return ParseDirectionalBoxProperty(eCSSProperty_margin_start,
9211 NS_BOXPROP_SOURCE_LOGICAL);
9212 case eCSSProperty_outline:
9213 return ParseOutline();
9214 case eCSSProperty_overflow:
9215 return ParseOverflow();
9216 case eCSSProperty_padding:
9217 return ParsePadding();
9218 case eCSSProperty_padding_end:
9219 return ParseDirectionalBoxProperty(eCSSProperty_padding_end,
9220 NS_BOXPROP_SOURCE_LOGICAL);
9221 case eCSSProperty_padding_left:
9222 return ParseDirectionalBoxProperty(eCSSProperty_padding_left,
9223 NS_BOXPROP_SOURCE_PHYSICAL);
9224 case eCSSProperty_padding_right:
9225 return ParseDirectionalBoxProperty(eCSSProperty_padding_right,
9226 NS_BOXPROP_SOURCE_PHYSICAL);
9227 case eCSSProperty_padding_start:
9228 return ParseDirectionalBoxProperty(eCSSProperty_padding_start,
9229 NS_BOXPROP_SOURCE_LOGICAL);
9230 case eCSSProperty_quotes:
9231 return ParseQuotes();
9232 case eCSSProperty_size:
9233 return ParseSize();
9234 case eCSSProperty_text_decoration:
9235 return ParseTextDecoration();
9236 case eCSSProperty_will_change:
9237 return ParseWillChange();
9238 case eCSSProperty_transform:
9239 return ParseTransform(false);
9240 case eCSSProperty__moz_transform:
9241 return ParseTransform(true);
9242 case eCSSProperty_transform_origin:
9243 return ParseTransformOrigin(false);
9244 case eCSSProperty_perspective_origin:
9245 return ParseTransformOrigin(true);
9246 case eCSSProperty_transition:
9247 return ParseTransition();
9248 case eCSSProperty_animation:
9249 return ParseAnimation();
9250 case eCSSProperty_transition_property:
9251 return ParseTransitionProperty();
9252 case eCSSProperty_fill:
9253 case eCSSProperty_stroke:
9254 return ParsePaint(aPropID);
9255 case eCSSProperty_stroke_dasharray:
9256 return ParseDasharray();
9257 case eCSSProperty_marker:
9258 return ParseMarker();
9259 case eCSSProperty_paint_order:
9260 return ParsePaintOrder();
9261 case eCSSProperty_all:
9262 return ParseAll();
9263 default:
9264 NS_ABORT_IF_FALSE(false, "should not be called");
9265 return false;
9266 }
9267 }
9269 // Bits used in determining which background position info we have
9270 #define BG_CENTER NS_STYLE_BG_POSITION_CENTER
9271 #define BG_TOP NS_STYLE_BG_POSITION_TOP
9272 #define BG_BOTTOM NS_STYLE_BG_POSITION_BOTTOM
9273 #define BG_LEFT NS_STYLE_BG_POSITION_LEFT
9274 #define BG_RIGHT NS_STYLE_BG_POSITION_RIGHT
9275 #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM)
9276 #define BG_TB (BG_TOP | BG_BOTTOM)
9277 #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT)
9278 #define BG_LR (BG_LEFT | BG_RIGHT)
9280 bool
9281 CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
9282 nsCSSProperty aPropID)
9283 {
9284 if (aPropID == eCSSPropertyExtra_x_none_value) {
9285 return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nullptr);
9286 }
9288 if (aPropID == eCSSPropertyExtra_x_auto_value) {
9289 return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nullptr);
9290 }
9292 if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
9293 NS_ABORT_IF_FALSE(false, "not a single value property");
9294 return false;
9295 }
9297 if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) {
9298 switch (aPropID) {
9299 case eCSSProperty_font_family:
9300 return ParseFamily(aValue);
9301 case eCSSProperty_font_synthesis:
9302 return ParseFontSynthesis(aValue);
9303 case eCSSProperty_font_variant_alternates:
9304 return ParseFontVariantAlternates(aValue);
9305 case eCSSProperty_font_variant_east_asian:
9306 return ParseFontVariantEastAsian(aValue);
9307 case eCSSProperty_font_variant_ligatures:
9308 return ParseFontVariantLigatures(aValue);
9309 case eCSSProperty_font_variant_numeric:
9310 return ParseFontVariantNumeric(aValue);
9311 case eCSSProperty_font_feature_settings:
9312 return ParseFontFeatureSettings(aValue);
9313 case eCSSProperty_font_weight:
9314 return ParseFontWeight(aValue);
9315 case eCSSProperty_image_orientation:
9316 return ParseImageOrientation(aValue);
9317 case eCSSProperty_marks:
9318 return ParseMarks(aValue);
9319 case eCSSProperty_text_align:
9320 return ParseTextAlign(aValue);
9321 case eCSSProperty_text_align_last:
9322 return ParseTextAlignLast(aValue);
9323 case eCSSProperty_text_decoration_line:
9324 return ParseTextDecorationLine(aValue);
9325 case eCSSProperty_text_combine_upright:
9326 return ParseTextCombineUpright(aValue);
9327 case eCSSProperty_text_overflow:
9328 return ParseTextOverflow(aValue);
9329 case eCSSProperty_touch_action:
9330 return ParseTouchAction(aValue);
9331 default:
9332 NS_ABORT_IF_FALSE(false, "should not reach here");
9333 return false;
9334 }
9335 }
9337 uint32_t variant = nsCSSProps::ParserVariant(aPropID);
9338 if (variant == 0) {
9339 NS_ABORT_IF_FALSE(false, "not a single value property");
9340 return false;
9341 }
9343 // We only allow 'script-level' when unsafe rules are enabled, because
9344 // otherwise it could interfere with rulenode optimizations if used in
9345 // a non-MathML-enabled document. We also only allow math-display when
9346 // unsafe rules are enabled.
9347 if (!mUnsafeRulesEnabled &&
9348 (aPropID == eCSSProperty_script_level ||
9349 aPropID == eCSSProperty_math_display))
9350 return false;
9352 const KTableValue *kwtable = nsCSSProps::kKeywordTableTable[aPropID];
9353 switch (nsCSSProps::ValueRestrictions(aPropID)) {
9354 default:
9355 NS_ABORT_IF_FALSE(false, "should not be reached");
9356 case 0:
9357 return ParseVariant(aValue, variant, kwtable);
9358 case CSS_PROPERTY_VALUE_NONNEGATIVE:
9359 return ParseNonNegativeVariant(aValue, variant, kwtable);
9360 case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
9361 return ParseOneOrLargerVariant(aValue, variant, kwtable);
9362 }
9363 }
9365 // nsFont::EnumerateFamilies callback for ParseFontDescriptorValue
9366 struct MOZ_STACK_CLASS ExtractFirstFamilyData {
9367 nsAutoString mFamilyName;
9368 bool mGood;
9369 ExtractFirstFamilyData() : mFamilyName(), mGood(false) {}
9370 };
9372 static bool
9373 ExtractFirstFamily(const nsString& aFamily,
9374 bool aGeneric,
9375 void* aData)
9376 {
9377 ExtractFirstFamilyData* realData = (ExtractFirstFamilyData*) aData;
9378 if (aGeneric || realData->mFamilyName.Length() > 0) {
9379 realData->mGood = false;
9380 return false;
9381 }
9382 realData->mFamilyName.Assign(aFamily);
9383 realData->mGood = true;
9384 return true;
9385 }
9387 // font-descriptor: descriptor ':' value ';'
9388 // caller has advanced mToken to point at the descriptor
9389 bool
9390 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
9391 nsCSSValue& aValue)
9392 {
9393 switch (aDescID) {
9394 // These four are similar to the properties of the same name,
9395 // possibly with more restrictions on the values they can take.
9396 case eCSSFontDesc_Family: {
9397 if (!ParseFamily(aValue) ||
9398 aValue.GetUnit() != eCSSUnit_Families)
9399 return false;
9401 // the style parameters to the nsFont constructor are ignored,
9402 // because it's only being used to call EnumerateFamilies
9403 nsAutoString valueStr;
9404 aValue.GetStringValue(valueStr);
9405 nsFont font(valueStr, 0, 0, 0, 0, 0, 0);
9406 ExtractFirstFamilyData dat;
9408 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
9409 if (!dat.mGood)
9410 return false;
9412 aValue.SetStringValue(dat.mFamilyName, eCSSUnit_String);
9413 return true;
9414 }
9416 case eCSSFontDesc_Style:
9417 // property is VARIANT_HMK|VARIANT_SYSFONT
9418 return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
9419 nsCSSProps::kFontStyleKTable);
9421 case eCSSFontDesc_Weight:
9422 return (ParseFontWeight(aValue) &&
9423 aValue.GetUnit() != eCSSUnit_Inherit &&
9424 aValue.GetUnit() != eCSSUnit_Initial &&
9425 aValue.GetUnit() != eCSSUnit_Unset &&
9426 (aValue.GetUnit() != eCSSUnit_Enumerated ||
9427 (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
9428 aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
9430 case eCSSFontDesc_Stretch:
9431 // property is VARIANT_HK|VARIANT_SYSFONT
9432 return ParseVariant(aValue, VARIANT_KEYWORD,
9433 nsCSSProps::kFontStretchKTable);
9435 // These two are unique to @font-face and have their own special grammar.
9436 case eCSSFontDesc_Src:
9437 return ParseFontSrc(aValue);
9439 case eCSSFontDesc_UnicodeRange:
9440 return ParseFontRanges(aValue);
9442 case eCSSFontDesc_FontFeatureSettings:
9443 return ParseFontFeatureSettings(aValue);
9445 case eCSSFontDesc_FontLanguageOverride:
9446 return ParseVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, nullptr);
9448 case eCSSFontDesc_UNKNOWN:
9449 case eCSSFontDesc_COUNT:
9450 NS_NOTREACHED("bad nsCSSFontDesc code");
9451 }
9452 // explicitly do NOT have a default case to let the compiler
9453 // help find missing descriptors
9454 return false;
9455 }
9457 void
9458 CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties)
9459 {
9460 nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
9461 for (const nsCSSProperty *prop = aSourceProperties;
9462 *prop != eCSSProperty_UNKNOWN; ++prop) {
9463 AppendValue(*prop, physical);
9464 }
9465 }
9467 static nsCSSValue
9468 BoxPositionMaskToCSSValue(int32_t aMask, bool isX)
9469 {
9470 int32_t val = NS_STYLE_BG_POSITION_CENTER;
9471 if (isX) {
9472 if (aMask & BG_LEFT) {
9473 val = NS_STYLE_BG_POSITION_LEFT;
9474 }
9475 else if (aMask & BG_RIGHT) {
9476 val = NS_STYLE_BG_POSITION_RIGHT;
9477 }
9478 }
9479 else {
9480 if (aMask & BG_TOP) {
9481 val = NS_STYLE_BG_POSITION_TOP;
9482 }
9483 else if (aMask & BG_BOTTOM) {
9484 val = NS_STYLE_BG_POSITION_BOTTOM;
9485 }
9486 }
9488 return nsCSSValue(val, eCSSUnit_Enumerated);
9489 }
9491 bool
9492 CSSParserImpl::ParseBackground()
9493 {
9494 nsAutoParseCompoundProperty compound(this);
9496 // background-color can only be set once, so it's not a list.
9497 nsCSSValue color;
9499 // Check first for inherit/initial/unset.
9500 if (ParseVariant(color, VARIANT_INHERIT, nullptr)) {
9501 // must be alone
9502 for (const nsCSSProperty* subprops =
9503 nsCSSProps::SubpropertyEntryFor(eCSSProperty_background);
9504 *subprops != eCSSProperty_UNKNOWN; ++subprops) {
9505 AppendValue(*subprops, color);
9506 }
9507 return true;
9508 }
9510 nsCSSValue image, repeat, attachment, clip, origin, position, size;
9511 BackgroundParseState state(color, image.SetListValue(),
9512 repeat.SetPairListValue(),
9513 attachment.SetListValue(), clip.SetListValue(),
9514 origin.SetListValue(), position.SetListValue(),
9515 size.SetPairListValue());
9517 for (;;) {
9518 if (!ParseBackgroundItem(state)) {
9519 return false;
9520 }
9521 // If we saw a color, this must be the last item.
9522 if (color.GetUnit() != eCSSUnit_Null) {
9523 break;
9524 }
9525 // If there's a comma, expect another item.
9526 if (!ExpectSymbol(',', true)) {
9527 break;
9528 }
9529 // Chain another entry on all the lists.
9530 state.mImage->mNext = new nsCSSValueList;
9531 state.mImage = state.mImage->mNext;
9532 state.mRepeat->mNext = new nsCSSValuePairList;
9533 state.mRepeat = state.mRepeat->mNext;
9534 state.mAttachment->mNext = new nsCSSValueList;
9535 state.mAttachment = state.mAttachment->mNext;
9536 state.mClip->mNext = new nsCSSValueList;
9537 state.mClip = state.mClip->mNext;
9538 state.mOrigin->mNext = new nsCSSValueList;
9539 state.mOrigin = state.mOrigin->mNext;
9540 state.mPosition->mNext = new nsCSSValueList;
9541 state.mPosition = state.mPosition->mNext;
9542 state.mSize->mNext = new nsCSSValuePairList;
9543 state.mSize = state.mSize->mNext;
9544 }
9546 // If we get to this point without seeing a color, provide a default.
9547 if (color.GetUnit() == eCSSUnit_Null) {
9548 color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor);
9549 }
9551 AppendValue(eCSSProperty_background_image, image);
9552 AppendValue(eCSSProperty_background_repeat, repeat);
9553 AppendValue(eCSSProperty_background_attachment, attachment);
9554 AppendValue(eCSSProperty_background_clip, clip);
9555 AppendValue(eCSSProperty_background_origin, origin);
9556 AppendValue(eCSSProperty_background_position, position);
9557 AppendValue(eCSSProperty_background_size, size);
9558 AppendValue(eCSSProperty_background_color, color);
9559 return true;
9560 }
9562 // Parse one item of the background shorthand property.
9563 bool
9564 CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState)
9566 {
9567 // Fill in the values that the shorthand will set if we don't find
9568 // other values.
9569 aState.mImage->mValue.SetNoneValue();
9570 aState.mRepeat->mXValue.SetIntValue(NS_STYLE_BG_REPEAT_REPEAT,
9571 eCSSUnit_Enumerated);
9572 aState.mRepeat->mYValue.Reset();
9573 aState.mAttachment->mValue.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL,
9574 eCSSUnit_Enumerated);
9575 aState.mClip->mValue.SetIntValue(NS_STYLE_BG_CLIP_BORDER,
9576 eCSSUnit_Enumerated);
9577 aState.mOrigin->mValue.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING,
9578 eCSSUnit_Enumerated);
9579 nsRefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4);
9580 aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array);
9581 positionArr->Item(1).SetPercentValue(0.0f);
9582 positionArr->Item(3).SetPercentValue(0.0f);
9583 aState.mSize->mXValue.SetAutoValue();
9584 aState.mSize->mYValue.SetAutoValue();
9586 bool haveColor = false,
9587 haveImage = false,
9588 haveRepeat = false,
9589 haveAttach = false,
9590 havePositionAndSize = false,
9591 haveOrigin = false,
9592 haveSomething = false;
9594 while (GetToken(true)) {
9595 nsCSSTokenType tt = mToken.mType;
9596 UngetToken(); // ...but we'll still cheat and use mToken
9597 if (tt == eCSSToken_Symbol) {
9598 // ExpectEndProperty only looks for symbols, and nothing else will
9599 // show up as one.
9600 break;
9601 }
9603 if (tt == eCSSToken_Ident) {
9604 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
9605 int32_t dummy;
9606 if (keyword == eCSSKeyword_inherit ||
9607 keyword == eCSSKeyword_initial ||
9608 keyword == eCSSKeyword_unset) {
9609 return false;
9610 } else if (keyword == eCSSKeyword_none) {
9611 if (haveImage)
9612 return false;
9613 haveImage = true;
9614 if (!ParseSingleValueProperty(aState.mImage->mValue,
9615 eCSSProperty_background_image)) {
9616 NS_NOTREACHED("should be able to parse");
9617 return false;
9618 }
9619 } else if (nsCSSProps::FindKeyword(keyword,
9620 nsCSSProps::kBackgroundAttachmentKTable, dummy)) {
9621 if (haveAttach)
9622 return false;
9623 haveAttach = true;
9624 if (!ParseSingleValueProperty(aState.mAttachment->mValue,
9625 eCSSProperty_background_attachment)) {
9626 NS_NOTREACHED("should be able to parse");
9627 return false;
9628 }
9629 } else if (nsCSSProps::FindKeyword(keyword,
9630 nsCSSProps::kBackgroundRepeatKTable, dummy)) {
9631 if (haveRepeat)
9632 return false;
9633 haveRepeat = true;
9634 nsCSSValuePair scratch;
9635 if (!ParseBackgroundRepeatValues(scratch)) {
9636 NS_NOTREACHED("should be able to parse");
9637 return false;
9638 }
9639 aState.mRepeat->mXValue = scratch.mXValue;
9640 aState.mRepeat->mYValue = scratch.mYValue;
9641 } else if (nsCSSProps::FindKeyword(keyword,
9642 nsCSSProps::kBackgroundPositionKTable, dummy)) {
9643 if (havePositionAndSize)
9644 return false;
9645 havePositionAndSize = true;
9646 if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) {
9647 return false;
9648 }
9649 if (ExpectSymbol('/', true)) {
9650 nsCSSValuePair scratch;
9651 if (!ParseBackgroundSizeValues(scratch)) {
9652 return false;
9653 }
9654 aState.mSize->mXValue = scratch.mXValue;
9655 aState.mSize->mYValue = scratch.mYValue;
9656 }
9657 } else if (nsCSSProps::FindKeyword(keyword,
9658 nsCSSProps::kBackgroundOriginKTable, dummy)) {
9659 if (haveOrigin)
9660 return false;
9661 haveOrigin = true;
9662 if (!ParseSingleValueProperty(aState.mOrigin->mValue,
9663 eCSSProperty_background_origin)) {
9664 NS_NOTREACHED("should be able to parse");
9665 return false;
9666 }
9668 // The spec allows a second box value (for background-clip),
9669 // immediately following the first one (for background-origin).
9671 // 'background-clip' and 'background-origin' use the same keyword table
9672 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
9673 eCSSProperty_background_origin] ==
9674 nsCSSProps::kBackgroundOriginKTable);
9675 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
9676 eCSSProperty_background_clip] ==
9677 nsCSSProps::kBackgroundOriginKTable);
9678 static_assert(NS_STYLE_BG_CLIP_BORDER ==
9679 NS_STYLE_BG_ORIGIN_BORDER &&
9680 NS_STYLE_BG_CLIP_PADDING ==
9681 NS_STYLE_BG_ORIGIN_PADDING &&
9682 NS_STYLE_BG_CLIP_CONTENT ==
9683 NS_STYLE_BG_ORIGIN_CONTENT,
9684 "bg-clip and bg-origin style constants must agree");
9686 if (!ParseSingleValueProperty(aState.mClip->mValue,
9687 eCSSProperty_background_clip)) {
9688 // When exactly one <box> value is set, it is used for both
9689 // 'background-origin' and 'background-clip'.
9690 // See assertions above showing these values are compatible.
9691 aState.mClip->mValue = aState.mOrigin->mValue;
9692 }
9693 } else {
9694 if (haveColor)
9695 return false;
9696 haveColor = true;
9697 if (!ParseSingleValueProperty(aState.mColor,
9698 eCSSProperty_background_color)) {
9699 return false;
9700 }
9701 }
9702 } else if (tt == eCSSToken_URL ||
9703 (tt == eCSSToken_Function &&
9704 (mToken.mIdent.LowerCaseEqualsLiteral("linear-gradient") ||
9705 mToken.mIdent.LowerCaseEqualsLiteral("radial-gradient") ||
9706 mToken.mIdent.LowerCaseEqualsLiteral("repeating-linear-gradient") ||
9707 mToken.mIdent.LowerCaseEqualsLiteral("repeating-radial-gradient") ||
9708 mToken.mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
9709 mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
9710 mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
9711 mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
9712 mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect") ||
9713 mToken.mIdent.LowerCaseEqualsLiteral("-moz-element")))) {
9714 if (haveImage)
9715 return false;
9716 haveImage = true;
9717 if (!ParseSingleValueProperty(aState.mImage->mValue,
9718 eCSSProperty_background_image)) {
9719 return false;
9720 }
9721 } else if (tt == eCSSToken_Dimension ||
9722 tt == eCSSToken_Number ||
9723 tt == eCSSToken_Percentage ||
9724 (tt == eCSSToken_Function &&
9725 (mToken.mIdent.LowerCaseEqualsLiteral("calc") ||
9726 mToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")))) {
9727 if (havePositionAndSize)
9728 return false;
9729 havePositionAndSize = true;
9730 if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) {
9731 return false;
9732 }
9733 if (ExpectSymbol('/', true)) {
9734 nsCSSValuePair scratch;
9735 if (!ParseBackgroundSizeValues(scratch)) {
9736 return false;
9737 }
9738 aState.mSize->mXValue = scratch.mXValue;
9739 aState.mSize->mYValue = scratch.mYValue;
9740 }
9741 } else {
9742 if (haveColor)
9743 return false;
9744 haveColor = true;
9745 // Note: This parses 'inherit', 'initial' and 'unset', but
9746 // we've already checked for them, so it's ok.
9747 if (!ParseSingleValueProperty(aState.mColor,
9748 eCSSProperty_background_color)) {
9749 return false;
9750 }
9751 }
9752 haveSomething = true;
9753 }
9755 return haveSomething;
9756 }
9758 // This function is very similar to ParseBackgroundPosition and
9759 // ParseBackgroundSize.
9760 bool
9761 CSSParserImpl::ParseValueList(nsCSSProperty aPropID)
9762 {
9763 // aPropID is a single value prop-id
9764 nsCSSValue value;
9765 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
9766 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
9767 nsCSSValueList* item = value.SetListValue();
9768 for (;;) {
9769 if (!ParseSingleValueProperty(item->mValue, aPropID)) {
9770 return false;
9771 }
9772 if (!ExpectSymbol(',', true)) {
9773 break;
9774 }
9775 item->mNext = new nsCSSValueList;
9776 item = item->mNext;
9777 }
9778 }
9779 AppendValue(aPropID, value);
9780 return true;
9781 }
9783 bool
9784 CSSParserImpl::ParseBackgroundRepeat()
9785 {
9786 nsCSSValue value;
9787 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
9788 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
9789 nsCSSValuePair valuePair;
9790 if (!ParseBackgroundRepeatValues(valuePair)) {
9791 return false;
9792 }
9793 nsCSSValuePairList* item = value.SetPairListValue();
9794 for (;;) {
9795 item->mXValue = valuePair.mXValue;
9796 item->mYValue = valuePair.mYValue;
9797 if (!ExpectSymbol(',', true)) {
9798 break;
9799 }
9800 if (!ParseBackgroundRepeatValues(valuePair)) {
9801 return false;
9802 }
9803 item->mNext = new nsCSSValuePairList;
9804 item = item->mNext;
9805 }
9806 }
9808 AppendValue(eCSSProperty_background_repeat, value);
9809 return true;
9810 }
9812 bool
9813 CSSParserImpl::ParseBackgroundRepeatValues(nsCSSValuePair& aValue)
9814 {
9815 nsCSSValue& xValue = aValue.mXValue;
9816 nsCSSValue& yValue = aValue.mYValue;
9818 if (ParseEnum(xValue, nsCSSProps::kBackgroundRepeatKTable)) {
9819 int32_t value = xValue.GetIntValue();
9820 // For single values set yValue as eCSSUnit_Null.
9821 if (value == NS_STYLE_BG_REPEAT_REPEAT_X ||
9822 value == NS_STYLE_BG_REPEAT_REPEAT_Y ||
9823 !ParseEnum(yValue, nsCSSProps::kBackgroundRepeatPartKTable)) {
9824 // the caller will fail cases like "repeat-x no-repeat"
9825 // by expecting a list separator or an end property.
9826 yValue.Reset();
9827 }
9828 return true;
9829 }
9831 return false;
9832 }
9834 // This function is very similar to ParseBackgroundList and ParseBackgroundSize.
9835 bool
9836 CSSParserImpl::ParseBackgroundPosition()
9837 {
9838 nsCSSValue value;
9839 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
9840 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
9841 nsCSSValue itemValue;
9842 if (!ParseBackgroundPositionValues(itemValue, false)) {
9843 return false;
9844 }
9845 nsCSSValueList* item = value.SetListValue();
9846 for (;;) {
9847 item->mValue = itemValue;
9848 if (!ExpectSymbol(',', true)) {
9849 break;
9850 }
9851 if (!ParseBackgroundPositionValues(itemValue, false)) {
9852 return false;
9853 }
9854 item->mNext = new nsCSSValueList;
9855 item = item->mNext;
9856 }
9857 }
9858 AppendValue(eCSSProperty_background_position, value);
9859 return true;
9860 }
9862 /**
9863 * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used
9864 * for parsing the CSS 2.1 background-position syntax (which has at
9865 * most two values). (Compare to the css3-background syntax which
9866 * takes up to four values.) Some current CSS specifications that
9867 * use background-position-like syntax still use this old syntax.
9868 **
9869 * Parses two values that correspond to positions in a box. These can be
9870 * values corresponding to percentages of the box, raw offsets, or keywords
9871 * like "top," "left center," etc.
9872 *
9873 * @param aOut The nsCSSValuePair in which to place the result.
9874 * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are
9875 * legal values
9876 * @param aAllowExplicitCenter If true, 'center' is a legal value
9877 * @return Whether or not the operation succeeded.
9878 */
9879 bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
9880 bool aAcceptsInherit,
9881 bool aAllowExplicitCenter)
9882 {
9883 // First try a percentage or a length value
9884 nsCSSValue &xValue = aOut.mXValue,
9885 &yValue = aOut.mYValue;
9886 int32_t variantMask =
9887 (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC;
9888 if (ParseVariant(xValue, variantMask, nullptr)) {
9889 if (eCSSUnit_Inherit == xValue.GetUnit() ||
9890 eCSSUnit_Initial == xValue.GetUnit() ||
9891 eCSSUnit_Unset == xValue.GetUnit()) { // both are inherit, initial or unset
9892 yValue = xValue;
9893 return true;
9894 }
9895 // We have one percentage/length/calc. Get the optional second
9896 // percentage/length/calc/keyword.
9897 if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
9898 // We have two numbers
9899 return true;
9900 }
9902 if (ParseEnum(yValue, nsCSSProps::kBackgroundPositionKTable)) {
9903 int32_t yVal = yValue.GetIntValue();
9904 if (!(yVal & BG_CTB)) {
9905 // The second keyword can only be 'center', 'top', or 'bottom'
9906 return false;
9907 }
9908 yValue = BoxPositionMaskToCSSValue(yVal, false);
9909 return true;
9910 }
9912 // If only one percentage or length value is given, it sets the
9913 // horizontal position only, and the vertical position will be 50%.
9914 yValue.SetPercentValue(0.5f);
9915 return true;
9916 }
9918 // Now try keywords. We do this manually to allow for the first
9919 // appearance of "center" to apply to the either the x or y
9920 // position (it's ambiguous so we have to disambiguate). Each
9921 // allowed keyword value is assigned it's own bit. We don't allow
9922 // any duplicate keywords other than center. We try to get two
9923 // keywords but it's okay if there is only one.
9924 int32_t mask = 0;
9925 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
9926 int32_t bit = xValue.GetIntValue();
9927 mask |= bit;
9928 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
9929 bit = xValue.GetIntValue();
9930 if (mask & (bit & ~BG_CENTER)) {
9931 // Only the 'center' keyword can be duplicated.
9932 return false;
9933 }
9934 mask |= bit;
9935 }
9936 else {
9937 // Only one keyword. See if we have a length, percentage, or calc.
9938 if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
9939 if (!(mask & BG_CLR)) {
9940 // The first keyword can only be 'center', 'left', or 'right'
9941 return false;
9942 }
9944 xValue = BoxPositionMaskToCSSValue(mask, true);
9945 return true;
9946 }
9947 }
9948 }
9950 // Check for bad input. Bad input consists of no matching keywords,
9951 // or pairs of x keywords or pairs of y keywords.
9952 if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
9953 (mask == (BG_LEFT | BG_RIGHT)) ||
9954 (!aAllowExplicitCenter && (mask & BG_CENTER))) {
9955 return false;
9956 }
9958 // Create style values
9959 xValue = BoxPositionMaskToCSSValue(mask, true);
9960 yValue = BoxPositionMaskToCSSValue(mask, false);
9961 return true;
9962 }
9964 bool CSSParserImpl::ParseBackgroundPositionValues(nsCSSValue& aOut,
9965 bool aAcceptsInherit)
9966 {
9967 // css3-background allows positions to be defined as offsets
9968 // from an edge. There can be 2 keywords and 2 offsets given. These
9969 // four 'values' are stored in an array in the following order:
9970 // [keyword offset keyword offset]. If a keyword or offset isn't
9971 // parsed the value of the corresponding array element is set
9972 // to eCSSUnit_Null by a call to nsCSSValue::Reset().
9973 if (aAcceptsInherit && ParseVariant(aOut, VARIANT_INHERIT, nullptr)) {
9974 return true;
9975 }
9977 nsRefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(4);
9978 aOut.SetArrayValue(value, eCSSUnit_Array);
9980 // The following clarifies organisation of the array.
9981 nsCSSValue &xEdge = value->Item(0),
9982 &xOffset = value->Item(1),
9983 &yEdge = value->Item(2),
9984 &yOffset = value->Item(3);
9986 // Parse all the values into the array.
9987 uint32_t valueCount = 0;
9988 for (int32_t i = 0; i < 4; i++) {
9989 if (!ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD,
9990 nsCSSProps::kBackgroundPositionKTable)) {
9991 break;
9992 }
9993 ++valueCount;
9994 }
9996 switch (valueCount) {
9997 case 4:
9998 // "If three or four values are given, then each <percentage> or <length>
9999 // represents an offset and must be preceded by a keyword, which specifies
10000 // from which edge the offset is given."
10001 if (eCSSUnit_Enumerated != xEdge.GetUnit() ||
10002 BG_CENTER == xEdge.GetIntValue() ||
10003 eCSSUnit_Enumerated == xOffset.GetUnit() ||
10004 eCSSUnit_Enumerated != yEdge.GetUnit() ||
10005 BG_CENTER == yEdge.GetIntValue() ||
10006 eCSSUnit_Enumerated == yOffset.GetUnit()) {
10007 return false;
10008 }
10009 break;
10010 case 3:
10011 // "If three or four values are given, then each <percentage> or<length>
10012 // represents an offset and must be preceded by a keyword, which specifies
10013 // from which edge the offset is given." ... "If three values are given,
10014 // the missing offset is assumed to be zero."
10015 if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) {
10016 // keyword offset keyword
10017 // Second value is non-keyword, thus first value must be a non-center
10018 // keyword.
10019 if (eCSSUnit_Enumerated != value->Item(0).GetUnit() ||
10020 BG_CENTER == value->Item(0).GetIntValue()) {
10021 return false;
10022 }
10024 // Remaining value must be a keyword.
10025 if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
10026 return false;
10027 }
10029 yOffset.Reset(); // Everything else is in the correct position.
10030 } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
10031 // keyword keyword offset
10032 // Third value is non-keyword, thus second value must be non-center
10033 // keyword.
10034 if (BG_CENTER == value->Item(1).GetIntValue()) {
10035 return false;
10036 }
10038 // Remaining value must be a keyword.
10039 if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) {
10040 return false;
10041 }
10043 // Move the values to the correct position in the array.
10044 value->Item(3) = value->Item(2); // yOffset
10045 value->Item(2) = value->Item(1); // yEdge
10046 value->Item(1).Reset(); // xOffset
10047 } else {
10048 return false;
10049 }
10050 break;
10051 case 2:
10052 // "If two values are given and at least one value is not a keyword, then
10053 // the first value represents the horizontal position (or offset) and the
10054 // second represents the vertical position (or offset)"
10055 if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
10056 if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
10057 // keyword keyword
10058 value->Item(2) = value->Item(1); // move yEdge to correct position
10059 xOffset.Reset();
10060 yOffset.Reset();
10061 } else {
10062 // keyword offset
10063 // First value must represent horizontal position.
10064 if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) {
10065 return false;
10066 }
10067 value->Item(3) = value->Item(1); // move yOffset to correct position
10068 xOffset.Reset();
10069 yEdge.Reset();
10070 }
10071 } else {
10072 if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
10073 // offset keyword
10074 // Second value must represent vertical position.
10075 if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) {
10076 return false;
10077 }
10078 value->Item(2) = value->Item(1); // move yEdge to correct position
10079 value->Item(1) = value->Item(0); // move xOffset to correct position
10080 xEdge.Reset();
10081 yOffset.Reset();
10082 } else {
10083 // offset offset
10084 value->Item(3) = value->Item(1); // move yOffset to correct position
10085 value->Item(1) = value->Item(0); // move xOffset to correct position
10086 xEdge.Reset();
10087 yEdge.Reset();
10088 }
10089 }
10090 break;
10091 case 1:
10092 // "If only one value is specified, the second value is assumed to be
10093 // center."
10094 if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
10095 xOffset.Reset();
10096 } else {
10097 value->Item(1) = value->Item(0); // move xOffset to correct position
10098 xEdge.Reset();
10099 }
10100 yEdge.SetIntValue(NS_STYLE_BG_POSITION_CENTER, eCSSUnit_Enumerated);
10101 yOffset.Reset();
10102 break;
10103 default:
10104 return false;
10105 }
10107 // For compatibility with CSS2.1 code the edges can be unspecified.
10108 // Unspecified edges are recorded as nullptr.
10109 NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() ||
10110 eCSSUnit_Null == xEdge.GetUnit()) &&
10111 (eCSSUnit_Enumerated == yEdge.GetUnit() ||
10112 eCSSUnit_Null == yEdge.GetUnit()) &&
10113 eCSSUnit_Enumerated != xOffset.GetUnit() &&
10114 eCSSUnit_Enumerated != yOffset.GetUnit(),
10115 "Unexpected units");
10117 // Keywords in first and second pairs can not both be vertical or
10118 // horizontal keywords. (eg. left right, bottom top). Additionally,
10119 // non-center keyword can not be duplicated (eg. left left).
10120 int32_t xEdgeEnum =
10121 xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0;
10122 int32_t yEdgeEnum =
10123 yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0;
10124 if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) ||
10125 (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) ||
10126 (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) {
10127 return false;
10128 }
10130 // The values could be in an order that is different than expected.
10131 // eg. x contains vertical information, y contains horizontal information.
10132 // Swap if incorrect order.
10133 if (xEdgeEnum & (BG_TOP | BG_BOTTOM) ||
10134 yEdgeEnum & (BG_LEFT | BG_RIGHT)) {
10135 nsCSSValue swapEdge = xEdge;
10136 nsCSSValue swapOffset = xOffset;
10137 xEdge = yEdge;
10138 xOffset = yOffset;
10139 yEdge = swapEdge;
10140 yOffset = swapOffset;
10141 }
10143 return true;
10144 }
10146 // This function is very similar to ParseBackgroundList and
10147 // ParseBackgroundPosition.
10148 bool
10149 CSSParserImpl::ParseBackgroundSize()
10150 {
10151 nsCSSValue value;
10152 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
10153 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10154 nsCSSValuePair valuePair;
10155 if (!ParseBackgroundSizeValues(valuePair)) {
10156 return false;
10157 }
10158 nsCSSValuePairList* item = value.SetPairListValue();
10159 for (;;) {
10160 item->mXValue = valuePair.mXValue;
10161 item->mYValue = valuePair.mYValue;
10162 if (!ExpectSymbol(',', true)) {
10163 break;
10164 }
10165 if (!ParseBackgroundSizeValues(valuePair)) {
10166 return false;
10167 }
10168 item->mNext = new nsCSSValuePairList;
10169 item = item->mNext;
10170 }
10171 }
10172 AppendValue(eCSSProperty_background_size, value);
10173 return true;
10174 }
10176 /**
10177 * Parses two values that correspond to lengths for the background-size
10178 * property. These can be one or two lengths (or the 'auto' keyword) or
10179 * percentages corresponding to the element's dimensions or the single keywords
10180 * 'contain' or 'cover'. 'initial', 'inherit' and 'unset' must be handled by
10181 * the caller if desired.
10182 *
10183 * @param aOut The nsCSSValuePair in which to place the result.
10184 * @return Whether or not the operation succeeded.
10185 */
10186 #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC)
10187 bool CSSParserImpl::ParseBackgroundSizeValues(nsCSSValuePair &aOut)
10188 {
10189 // First try a percentage or a length value
10190 nsCSSValue &xValue = aOut.mXValue,
10191 &yValue = aOut.mYValue;
10192 if (ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr)) {
10193 // We have one percentage/length/calc/auto. Get the optional second
10194 // percentage/length/calc/keyword.
10195 if (ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr)) {
10196 // We have a second percentage/length/calc/auto.
10197 return true;
10198 }
10200 // If only one percentage or length value is given, it sets the
10201 // horizontal size only, and the vertical size will be as if by 'auto'.
10202 yValue.SetAutoValue();
10203 return true;
10204 }
10206 // Now address 'contain' and 'cover'.
10207 if (!ParseEnum(xValue, nsCSSProps::kBackgroundSizeKTable))
10208 return false;
10209 yValue.Reset();
10210 return true;
10211 }
10212 #undef BG_SIZE_VARIANT
10214 bool
10215 CSSParserImpl::ParseBorderColor()
10216 {
10217 static const nsCSSProperty kBorderColorSources[] = {
10218 eCSSProperty_border_left_color_ltr_source,
10219 eCSSProperty_border_left_color_rtl_source,
10220 eCSSProperty_border_right_color_ltr_source,
10221 eCSSProperty_border_right_color_rtl_source,
10222 eCSSProperty_UNKNOWN
10223 };
10225 // do this now, in case 4 values weren't specified
10226 InitBoxPropsAsPhysical(kBorderColorSources);
10227 return ParseBoxProperties(kBorderColorIDs);
10228 }
10230 void
10231 CSSParserImpl::SetBorderImageInitialValues()
10232 {
10233 // border-image-source: none
10234 nsCSSValue source;
10235 source.SetNoneValue();
10236 AppendValue(eCSSProperty_border_image_source, source);
10238 // border-image-slice: 100%
10239 nsCSSValue sliceBoxValue;
10240 nsCSSRect& sliceBox = sliceBoxValue.SetRectValue();
10241 sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent));
10242 nsCSSValue slice;
10243 nsCSSValueList* sliceList = slice.SetListValue();
10244 sliceList->mValue = sliceBoxValue;
10245 AppendValue(eCSSProperty_border_image_slice, slice);
10247 // border-image-width: 1
10248 nsCSSValue width;
10249 nsCSSRect& widthBox = width.SetRectValue();
10250 widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number));
10251 AppendValue(eCSSProperty_border_image_width, width);
10253 // border-image-outset: 0
10254 nsCSSValue outset;
10255 nsCSSRect& outsetBox = outset.SetRectValue();
10256 outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number));
10257 AppendValue(eCSSProperty_border_image_outset, outset);
10259 // border-image-repeat: repeat
10260 nsCSSValue repeat;
10261 nsCSSValuePair repeatPair;
10262 repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
10263 eCSSUnit_Enumerated));
10264 repeat.SetPairValue(&repeatPair);
10265 AppendValue(eCSSProperty_border_image_repeat, repeat);
10266 }
10268 bool
10269 CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
10270 bool* aConsumedTokens)
10271 {
10272 // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
10273 nsCSSValue value;
10275 if (aConsumedTokens) {
10276 *aConsumedTokens = true;
10277 }
10279 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10280 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10281 // are done.
10282 AppendValue(eCSSProperty_border_image_slice, value);
10283 return true;
10284 }
10286 // Try parsing "fill" value.
10287 nsCSSValue imageSliceFillValue;
10288 bool hasFill = ParseEnum(imageSliceFillValue,
10289 nsCSSProps::kBorderImageSliceKTable);
10291 // Parse the box dimensions.
10292 nsCSSValue imageSliceBoxValue;
10293 if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue)) {
10294 if (!hasFill && aConsumedTokens) {
10295 *aConsumedTokens = false;
10296 }
10298 return false;
10299 }
10301 // Try parsing "fill" keyword again if the first time failed because keyword
10302 // and slice dimensions can be in any order.
10303 if (!hasFill) {
10304 hasFill = ParseEnum(imageSliceFillValue,
10305 nsCSSProps::kBorderImageSliceKTable);
10306 }
10308 nsCSSValueList* borderImageSlice = value.SetListValue();
10309 // Put the box value into the list.
10310 borderImageSlice->mValue = imageSliceBoxValue;
10312 if (hasFill) {
10313 // Put the "fill" value into the list.
10314 borderImageSlice->mNext = new nsCSSValueList;
10315 borderImageSlice->mNext->mValue = imageSliceFillValue;
10316 }
10318 AppendValue(eCSSProperty_border_image_slice, value);
10319 return true;
10320 }
10322 bool
10323 CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
10324 {
10325 // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
10326 nsCSSValue value;
10328 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10329 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10330 // are done.
10331 AppendValue(eCSSProperty_border_image_width, value);
10332 return true;
10333 }
10335 // Parse the box dimensions.
10336 if (!ParseGroupedBoxProperty(VARIANT_ALPN, value)) {
10337 return false;
10338 }
10340 AppendValue(eCSSProperty_border_image_width, value);
10341 return true;
10342 }
10344 bool
10345 CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
10346 {
10347 // border-image-outset: initial | [<length>|<number>]{1,4}
10348 nsCSSValue value;
10350 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10351 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10352 // are done.
10353 AppendValue(eCSSProperty_border_image_outset, value);
10354 return true;
10355 }
10357 // Parse the box dimensions.
10358 if (!ParseGroupedBoxProperty(VARIANT_LN, value)) {
10359 return false;
10360 }
10362 AppendValue(eCSSProperty_border_image_outset, value);
10363 return true;
10364 }
10366 bool
10367 CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit)
10368 {
10369 nsCSSValue value;
10370 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10371 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
10372 // are done.
10373 AppendValue(eCSSProperty_border_image_repeat, value);
10374 return true;
10375 }
10377 nsCSSValuePair result;
10378 if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) {
10379 return false;
10380 }
10382 // optional second keyword, defaults to first
10383 if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) {
10384 result.mYValue = result.mXValue;
10385 }
10387 value.SetPairValue(&result);
10388 AppendValue(eCSSProperty_border_image_repeat, value);
10389 return true;
10390 }
10392 bool
10393 CSSParserImpl::ParseBorderImage()
10394 {
10395 nsAutoParseCompoundProperty compound(this);
10397 // border-image: inherit | initial |
10398 // <border-image-source> ||
10399 // <border-image-slice>
10400 // [ / <border-image-width> |
10401 // / <border-image-width>? / <border-image-outset> ]? ||
10402 // <border-image-repeat>
10404 nsCSSValue value;
10405 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
10406 AppendValue(eCSSProperty_border_image_source, value);
10407 AppendValue(eCSSProperty_border_image_slice, value);
10408 AppendValue(eCSSProperty_border_image_width, value);
10409 AppendValue(eCSSProperty_border_image_outset, value);
10410 AppendValue(eCSSProperty_border_image_repeat, value);
10411 // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done.
10412 return true;
10413 }
10415 // No empty property.
10416 if (CheckEndProperty()) {
10417 return false;
10418 }
10420 // Shorthand properties are required to set everything they can.
10421 SetBorderImageInitialValues();
10423 bool foundSource = false;
10424 bool foundSliceWidthOutset = false;
10425 bool foundRepeat = false;
10427 // This loop is used to handle the parsing of border-image properties which
10428 // can appear in any order.
10429 nsCSSValue imageSourceValue;
10430 while (!CheckEndProperty()) {
10431 // <border-image-source>
10432 if (!foundSource && ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) {
10433 AppendValue(eCSSProperty_border_image_source, imageSourceValue);
10434 foundSource = true;
10435 continue;
10436 }
10438 // <border-image-slice>
10439 // ParseBorderImageSlice is weird. It may consume tokens and then return
10440 // false, because it parses a property with two required components that
10441 // can appear in either order. Since the tokens that were consumed cannot
10442 // parse as anything else we care about, this isn't a problem.
10443 if (!foundSliceWidthOutset) {
10444 bool sliceConsumedTokens = false;
10445 if (ParseBorderImageSlice(false, &sliceConsumedTokens)) {
10446 foundSliceWidthOutset = true;
10448 // [ / <border-image-width>?
10449 if (ExpectSymbol('/', true)) {
10450 bool foundBorderImageWidth = ParseBorderImageWidth(false);
10452 // [ / <border-image-outset>
10453 if (ExpectSymbol('/', true)) {
10454 if (!ParseBorderImageOutset(false)) {
10455 return false;
10456 }
10457 } else if (!foundBorderImageWidth) {
10458 // If this part has an trailing slash, the whole declaration is
10459 // invalid.
10460 return false;
10461 }
10462 }
10464 continue;
10465 } else {
10466 // If we consumed some tokens for <border-image-slice> but did not
10467 // successfully parse it, we have an error.
10468 if (sliceConsumedTokens) {
10469 return false;
10470 }
10471 }
10472 }
10474 // <border-image-repeat>
10475 if (!foundRepeat && ParseBorderImageRepeat(false)) {
10476 foundRepeat = true;
10477 continue;
10478 }
10480 return false;
10481 }
10483 return true;
10484 }
10486 bool
10487 CSSParserImpl::ParseBorderSpacing()
10488 {
10489 nsCSSValue xValue, yValue;
10490 if (!ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr)) {
10491 return false;
10492 }
10494 // If we have one length, get the optional second length.
10495 // set the second value equal to the first.
10496 if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) {
10497 ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC, nullptr);
10498 }
10500 if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) {
10501 AppendValue(eCSSProperty_border_spacing, xValue);
10502 } else {
10503 nsCSSValue pair;
10504 pair.SetPairValue(xValue, yValue);
10505 AppendValue(eCSSProperty_border_spacing, pair);
10506 }
10507 return true;
10508 }
10510 bool
10511 CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[],
10512 bool aSetAllSides)
10513 {
10514 const int32_t numProps = 3;
10515 nsCSSValue values[numProps];
10517 int32_t found = ParseChoice(values, aPropIDs, numProps);
10518 if (found < 1) {
10519 return false;
10520 }
10522 if ((found & 1) == 0) { // Provide default border-width
10523 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
10524 }
10525 if ((found & 2) == 0) { // Provide default border-style
10526 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
10527 }
10528 if ((found & 4) == 0) { // text color will be used
10529 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
10530 }
10532 if (aSetAllSides) {
10533 static const nsCSSProperty kBorderSources[] = {
10534 eCSSProperty_border_left_color_ltr_source,
10535 eCSSProperty_border_left_color_rtl_source,
10536 eCSSProperty_border_right_color_ltr_source,
10537 eCSSProperty_border_right_color_rtl_source,
10538 eCSSProperty_border_left_style_ltr_source,
10539 eCSSProperty_border_left_style_rtl_source,
10540 eCSSProperty_border_right_style_ltr_source,
10541 eCSSProperty_border_right_style_rtl_source,
10542 eCSSProperty_border_left_width_ltr_source,
10543 eCSSProperty_border_left_width_rtl_source,
10544 eCSSProperty_border_right_width_ltr_source,
10545 eCSSProperty_border_right_width_rtl_source,
10546 eCSSProperty_UNKNOWN
10547 };
10549 InitBoxPropsAsPhysical(kBorderSources);
10551 // Parsing "border" shorthand; set all four sides to the same thing
10552 for (int32_t index = 0; index < 4; index++) {
10553 NS_ASSERTION(numProps == 3, "This code needs updating");
10554 AppendValue(kBorderWidthIDs[index], values[0]);
10555 AppendValue(kBorderStyleIDs[index], values[1]);
10556 AppendValue(kBorderColorIDs[index], values[2]);
10557 }
10559 static const nsCSSProperty kBorderColorsProps[] = {
10560 eCSSProperty_border_top_colors,
10561 eCSSProperty_border_right_colors,
10562 eCSSProperty_border_bottom_colors,
10563 eCSSProperty_border_left_colors
10564 };
10566 // Set the other properties that the border shorthand sets to their
10567 // initial values.
10568 nsCSSValue extraValue;
10569 switch (values[0].GetUnit()) {
10570 case eCSSUnit_Inherit:
10571 case eCSSUnit_Initial:
10572 case eCSSUnit_Unset:
10573 extraValue = values[0];
10574 // Set value of border-image properties to initial/inherit/unset
10575 AppendValue(eCSSProperty_border_image_source, extraValue);
10576 AppendValue(eCSSProperty_border_image_slice, extraValue);
10577 AppendValue(eCSSProperty_border_image_width, extraValue);
10578 AppendValue(eCSSProperty_border_image_outset, extraValue);
10579 AppendValue(eCSSProperty_border_image_repeat, extraValue);
10580 break;
10581 default:
10582 extraValue.SetNoneValue();
10583 SetBorderImageInitialValues();
10584 break;
10585 }
10586 NS_FOR_CSS_SIDES(side) {
10587 AppendValue(kBorderColorsProps[side], extraValue);
10588 }
10589 }
10590 else {
10591 // Just set our one side
10592 for (int32_t index = 0; index < numProps; index++) {
10593 AppendValue(aPropIDs[index], values[index]);
10594 }
10595 }
10596 return true;
10597 }
10599 bool
10600 CSSParserImpl::ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
10601 int32_t aSourceType)
10602 {
10603 const int32_t numProps = 3;
10604 nsCSSValue values[numProps];
10606 int32_t found = ParseChoice(values, aPropIDs, numProps);
10607 if (found < 1) {
10608 return false;
10609 }
10611 if ((found & 1) == 0) { // Provide default border-width
10612 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
10613 }
10614 if ((found & 2) == 0) { // Provide default border-style
10615 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
10616 }
10617 if ((found & 4) == 0) { // text color will be used
10618 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
10619 }
10620 for (int32_t index = 0; index < numProps; index++) {
10621 const nsCSSProperty* subprops =
10622 nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]);
10623 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
10624 "not box property with physical vs. logical cascading");
10625 AppendValue(subprops[0], values[index]);
10626 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
10627 AppendValue(subprops[1], typeVal);
10628 AppendValue(subprops[2], typeVal);
10629 }
10630 return true;
10631 }
10633 bool
10634 CSSParserImpl::ParseBorderStyle()
10635 {
10636 static const nsCSSProperty kBorderStyleSources[] = {
10637 eCSSProperty_border_left_style_ltr_source,
10638 eCSSProperty_border_left_style_rtl_source,
10639 eCSSProperty_border_right_style_ltr_source,
10640 eCSSProperty_border_right_style_rtl_source,
10641 eCSSProperty_UNKNOWN
10642 };
10644 // do this now, in case 4 values weren't specified
10645 InitBoxPropsAsPhysical(kBorderStyleSources);
10646 return ParseBoxProperties(kBorderStyleIDs);
10647 }
10649 bool
10650 CSSParserImpl::ParseBorderWidth()
10651 {
10652 static const nsCSSProperty kBorderWidthSources[] = {
10653 eCSSProperty_border_left_width_ltr_source,
10654 eCSSProperty_border_left_width_rtl_source,
10655 eCSSProperty_border_right_width_ltr_source,
10656 eCSSProperty_border_right_width_rtl_source,
10657 eCSSProperty_UNKNOWN
10658 };
10660 // do this now, in case 4 values weren't specified
10661 InitBoxPropsAsPhysical(kBorderWidthSources);
10662 return ParseBoxProperties(kBorderWidthIDs);
10663 }
10665 bool
10666 CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty)
10667 {
10668 nsCSSValue value;
10669 // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
10670 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
10671 nsCSSValueList *cur = value.SetListValue();
10672 for (;;) {
10673 if (!ParseVariant(cur->mValue, VARIANT_COLOR | VARIANT_KEYWORD,
10674 nsCSSProps::kBorderColorKTable)) {
10675 return false;
10676 }
10677 if (CheckEndProperty()) {
10678 break;
10679 }
10680 cur->mNext = new nsCSSValueList;
10681 cur = cur->mNext;
10682 }
10683 }
10684 AppendValue(aProperty, value);
10685 return true;
10686 }
10688 // Parse the top level of a calc() expression.
10689 bool
10690 CSSParserImpl::ParseCalc(nsCSSValue &aValue, int32_t aVariantMask)
10691 {
10692 // Parsing calc expressions requires, in a number of cases, looking
10693 // for a token that is *either* a value of the property or a number.
10694 // This can be done without lookahead when we assume that the property
10695 // values cannot themselves be numbers.
10696 NS_ASSERTION(!(aVariantMask & VARIANT_NUMBER), "unexpected variant mask");
10697 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
10699 bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk;
10700 mUnitlessLengthQuirk = false;
10702 // One-iteration loop so we can break to the error-handling case.
10703 do {
10704 // The toplevel of a calc() is always an nsCSSValue::Array of length 1.
10705 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
10707 if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask))
10708 break;
10710 if (!ExpectSymbol(')', true))
10711 break;
10713 aValue.SetArrayValue(arr, eCSSUnit_Calc);
10714 mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
10715 return true;
10716 } while (false);
10718 SkipUntil(')');
10719 mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
10720 return false;
10721 }
10723 // We optimize away the <value-expression> production given that
10724 // ParseVariant consumes initial whitespace and we call
10725 // ExpectSymbol(')') with true for aSkipWS.
10726 // * If aVariantMask is VARIANT_NUMBER, this function parses the
10727 // <number-additive-expression> production.
10728 // * If aVariantMask does not contain VARIANT_NUMBER, this function
10729 // parses the <value-additive-expression> production.
10730 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
10731 // whichever one of the productions matches ***and modifies
10732 // aVariantMask*** to reflect which one it has parsed by either
10733 // removing VARIANT_NUMBER or removing all other bits.
10734 // It does so iteratively, but builds the correct recursive
10735 // data structure.
10736 bool
10737 CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue,
10738 int32_t& aVariantMask)
10739 {
10740 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
10741 nsCSSValue *storage = &aValue;
10742 for (;;) {
10743 bool haveWS;
10744 if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS))
10745 return false;
10747 if (!haveWS || !GetToken(false))
10748 return true;
10749 nsCSSUnit unit;
10750 if (mToken.IsSymbol('+')) {
10751 unit = eCSSUnit_Calc_Plus;
10752 } else if (mToken.IsSymbol('-')) {
10753 unit = eCSSUnit_Calc_Minus;
10754 } else {
10755 UngetToken();
10756 return true;
10757 }
10758 if (!RequireWhitespace())
10759 return false;
10761 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
10762 arr->Item(0) = aValue;
10763 storage = &arr->Item(1);
10764 aValue.SetArrayValue(arr, unit);
10765 }
10766 }
10768 struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
10769 public mozilla::css::CSSValueInputCalcOps
10770 {
10771 result_type ComputeLeafValue(const nsCSSValue& aValue)
10772 {
10773 NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
10774 return aValue.GetFloatValue();
10775 }
10777 float ComputeNumber(const nsCSSValue& aValue)
10778 {
10779 return mozilla::css::ComputeCalc(aValue, *this);
10780 }
10781 };
10783 // * If aVariantMask is VARIANT_NUMBER, this function parses the
10784 // <number-multiplicative-expression> production.
10785 // * If aVariantMask does not contain VARIANT_NUMBER, this function
10786 // parses the <value-multiplicative-expression> production.
10787 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
10788 // whichever one of the productions matches ***and modifies
10789 // aVariantMask*** to reflect which one it has parsed by either
10790 // removing VARIANT_NUMBER or removing all other bits.
10791 // It does so iteratively, but builds the correct recursive data
10792 // structure.
10793 // This function always consumes *trailing* whitespace when it returns
10794 // true; whether there was any such whitespace is returned in the
10795 // aHadFinalWS parameter.
10796 bool
10797 CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
10798 int32_t& aVariantMask,
10799 bool *aHadFinalWS)
10800 {
10801 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
10802 bool gotValue = false; // already got the part with the unit
10803 bool afterDivision = false;
10805 nsCSSValue *storage = &aValue;
10806 for (;;) {
10807 int32_t variantMask;
10808 if (afterDivision || gotValue) {
10809 variantMask = VARIANT_NUMBER;
10810 } else {
10811 variantMask = aVariantMask | VARIANT_NUMBER;
10812 }
10813 if (!ParseCalcTerm(*storage, variantMask))
10814 return false;
10815 NS_ABORT_IF_FALSE(variantMask != 0,
10816 "ParseCalcTerm did not set variantMask appropriately");
10817 NS_ABORT_IF_FALSE(!(variantMask & VARIANT_NUMBER) ||
10818 !(variantMask & ~int32_t(VARIANT_NUMBER)),
10819 "ParseCalcTerm did not set variantMask appropriately");
10821 if (variantMask & VARIANT_NUMBER) {
10822 // Simplify the value immediately so we can check for division by
10823 // zero.
10824 ReduceNumberCalcOps ops;
10825 float number = mozilla::css::ComputeCalc(*storage, ops);
10826 if (number == 0.0 && afterDivision)
10827 return false;
10828 storage->SetFloatValue(number, eCSSUnit_Number);
10829 } else {
10830 gotValue = true;
10832 if (storage != &aValue) {
10833 // Simplify any numbers in the Times_L position (which are
10834 // not simplified by the check above).
10835 NS_ABORT_IF_FALSE(storage == &aValue.GetArrayValue()->Item(1),
10836 "unexpected relationship to current storage");
10837 nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
10838 ReduceNumberCalcOps ops;
10839 float number = mozilla::css::ComputeCalc(leftValue, ops);
10840 leftValue.SetFloatValue(number, eCSSUnit_Number);
10841 }
10842 }
10844 bool hadWS = RequireWhitespace();
10845 if (!GetToken(false)) {
10846 *aHadFinalWS = hadWS;
10847 break;
10848 }
10849 nsCSSUnit unit;
10850 if (mToken.IsSymbol('*')) {
10851 unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
10852 afterDivision = false;
10853 } else if (mToken.IsSymbol('/')) {
10854 unit = eCSSUnit_Calc_Divided;
10855 afterDivision = true;
10856 } else {
10857 UngetToken();
10858 *aHadFinalWS = hadWS;
10859 break;
10860 }
10862 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
10863 arr->Item(0) = aValue;
10864 storage = &arr->Item(1);
10865 aValue.SetArrayValue(arr, unit);
10866 }
10868 // Adjust aVariantMask (see comments above function) to reflect which
10869 // option we took.
10870 if (aVariantMask & VARIANT_NUMBER) {
10871 if (gotValue) {
10872 aVariantMask &= ~int32_t(VARIANT_NUMBER);
10873 } else {
10874 aVariantMask = VARIANT_NUMBER;
10875 }
10876 } else {
10877 if (!gotValue) {
10878 // We had to find a value, but we didn't.
10879 return false;
10880 }
10881 }
10883 return true;
10884 }
10886 // * If aVariantMask is VARIANT_NUMBER, this function parses the
10887 // <number-term> production.
10888 // * If aVariantMask does not contain VARIANT_NUMBER, this function
10889 // parses the <value-term> production.
10890 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
10891 // whichever one of the productions matches ***and modifies
10892 // aVariantMask*** to reflect which one it has parsed by either
10893 // removing VARIANT_NUMBER or removing all other bits.
10894 bool
10895 CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask)
10896 {
10897 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
10898 if (!GetToken(true))
10899 return false;
10900 // Either an additive expression in parentheses...
10901 if (mToken.IsSymbol('(')) {
10902 if (!ParseCalcAdditiveExpression(aValue, aVariantMask) ||
10903 !ExpectSymbol(')', true)) {
10904 SkipUntil(')');
10905 return false;
10906 }
10907 return true;
10908 }
10909 // ... or just a value
10910 UngetToken();
10911 // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
10912 // always gets picked up
10913 if (!ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr)) {
10914 return false;
10915 }
10916 // ...and do the VARIANT_NUMBER check ourselves.
10917 if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
10918 return false;
10919 }
10920 // If we did the value parsing, we need to adjust aVariantMask to
10921 // reflect which option we took (see above).
10922 if (aVariantMask & VARIANT_NUMBER) {
10923 if (aValue.GetUnit() == eCSSUnit_Number) {
10924 aVariantMask = VARIANT_NUMBER;
10925 } else {
10926 aVariantMask &= ~int32_t(VARIANT_NUMBER);
10927 }
10928 }
10929 return true;
10930 }
10932 // This function consumes all consecutive whitespace and returns whether
10933 // there was any.
10934 bool
10935 CSSParserImpl::RequireWhitespace()
10936 {
10937 if (!GetToken(false))
10938 return false;
10939 if (mToken.mType != eCSSToken_Whitespace) {
10940 UngetToken();
10941 return false;
10942 }
10943 // Skip any additional whitespace tokens.
10944 if (GetToken(true)) {
10945 UngetToken();
10946 }
10947 return true;
10948 }
10950 bool
10951 CSSParserImpl::ParseRect(nsCSSProperty aPropID)
10952 {
10953 nsCSSValue val;
10954 if (ParseVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) {
10955 AppendValue(aPropID, val);
10956 return true;
10957 }
10959 if (! GetToken(true)) {
10960 return false;
10961 }
10963 if (mToken.mType == eCSSToken_Function &&
10964 mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
10965 nsCSSRect& rect = val.SetRectValue();
10966 bool useCommas;
10967 NS_FOR_CSS_SIDES(side) {
10968 if (! ParseVariant(rect.*(nsCSSRect::sides[side]),
10969 VARIANT_AL, nullptr)) {
10970 return false;
10971 }
10972 if (side == 0) {
10973 useCommas = ExpectSymbol(',', true);
10974 } else if (useCommas && side < 3) {
10975 // Skip optional commas between elements, but only if the first
10976 // separator was a comma.
10977 if (!ExpectSymbol(',', true)) {
10978 return false;
10979 }
10980 }
10981 }
10982 if (!ExpectSymbol(')', true)) {
10983 return false;
10984 }
10985 } else {
10986 UngetToken();
10987 return false;
10988 }
10990 AppendValue(aPropID, val);
10991 return true;
10992 }
10994 bool
10995 CSSParserImpl::ParseColumns()
10996 {
10997 // We use a similar "fake value" hack to ParseListStyle, because
10998 // "auto" is acceptable for both column-count and column-width.
10999 // If the fake "auto" value is found, and one of the real values isn't,
11000 // that means the fake auto value is meant for the real value we didn't
11001 // find.
11002 static const nsCSSProperty columnIDs[] = {
11003 eCSSPropertyExtra_x_auto_value,
11004 eCSSProperty__moz_column_count,
11005 eCSSProperty__moz_column_width
11006 };
11007 const int32_t numProps = MOZ_ARRAY_LENGTH(columnIDs);
11009 nsCSSValue values[numProps];
11010 int32_t found = ParseChoice(values, columnIDs, numProps);
11011 if (found < 1) {
11012 return false;
11013 }
11014 if ((found & (1|2|4)) == (1|2|4) &&
11015 values[0].GetUnit() == eCSSUnit_Auto) {
11016 // We filled all 3 values, which is invalid
11017 return false;
11018 }
11020 if ((found & 2) == 0) {
11021 // Provide auto column-count
11022 values[1].SetAutoValue();
11023 }
11024 if ((found & 4) == 0) {
11025 // Provide auto column-width
11026 values[2].SetAutoValue();
11027 }
11029 // Start at index 1 to skip the fake auto value.
11030 for (int32_t index = 1; index < numProps; index++) {
11031 AppendValue(columnIDs[index], values[index]);
11032 }
11033 return true;
11034 }
11036 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
11037 VARIANT_KEYWORD)
11038 bool
11039 CSSParserImpl::ParseContent()
11040 {
11041 // We need to divide the 'content' keywords into two classes for
11042 // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable.
11043 static const KTableValue kContentListKWs[] = {
11044 eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE,
11045 eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE,
11046 eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE,
11047 eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE,
11048 eCSSKeyword_UNKNOWN,-1
11049 };
11051 static const KTableValue kContentSolitaryKWs[] = {
11052 eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT,
11053 eCSSKeyword_UNKNOWN,-1
11054 };
11056 // Verify that these two lists add up to the size of
11057 // nsCSSProps::kContentKTable.
11058 NS_ABORT_IF_FALSE(nsCSSProps::kContentKTable[
11059 ArrayLength(kContentListKWs) +
11060 ArrayLength(kContentSolitaryKWs) - 4] ==
11061 eCSSKeyword_UNKNOWN &&
11062 nsCSSProps::kContentKTable[
11063 ArrayLength(kContentListKWs) +
11064 ArrayLength(kContentSolitaryKWs) - 3] == -1,
11065 "content keyword tables out of sync");
11067 nsCSSValue value;
11068 // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must
11069 // be alone
11070 if (!ParseVariant(value, VARIANT_HMK | VARIANT_NONE,
11071 kContentSolitaryKWs)) {
11072 nsCSSValueList* cur = value.SetListValue();
11073 for (;;) {
11074 if (!ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs)) {
11075 return false;
11076 }
11077 if (CheckEndProperty()) {
11078 break;
11079 }
11080 cur->mNext = new nsCSSValueList;
11081 cur = cur->mNext;
11082 }
11083 }
11084 AppendValue(eCSSProperty_content, value);
11085 return true;
11086 }
11088 bool
11089 CSSParserImpl::ParseCounterData(nsCSSProperty aPropID)
11090 {
11091 static const nsCSSKeyword kCounterDataKTable[] = {
11092 eCSSKeyword_none,
11093 eCSSKeyword_UNKNOWN
11094 };
11095 nsCSSValue value;
11096 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
11097 if (!GetToken(true)) {
11098 return false;
11099 }
11100 if (mToken.mType != eCSSToken_Ident) {
11101 UngetToken();
11102 return false;
11103 }
11105 nsCSSValuePairList *cur = value.SetPairListValue();
11106 for (;;) {
11107 if (!ParseCustomIdent(cur->mXValue, mToken.mIdent, kCounterDataKTable)) {
11108 return false;
11109 }
11110 if (!GetToken(true)) {
11111 break;
11112 }
11113 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
11114 cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
11115 } else {
11116 UngetToken();
11117 }
11118 if (!GetToken(true)) {
11119 break;
11120 }
11121 if (mToken.mType != eCSSToken_Ident) {
11122 UngetToken();
11123 break;
11124 }
11125 cur->mNext = new nsCSSValuePairList;
11126 cur = cur->mNext;
11127 }
11128 }
11129 AppendValue(aPropID, value);
11130 return true;
11131 }
11133 bool
11134 CSSParserImpl::ParseCursor()
11135 {
11136 nsCSSValue value;
11137 // 'inherit', 'initial' and 'unset' must be alone
11138 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
11139 nsCSSValueList* cur = value.SetListValue();
11140 for (;;) {
11141 if (!ParseVariant(cur->mValue, VARIANT_UK, nsCSSProps::kCursorKTable)) {
11142 return false;
11143 }
11144 if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last
11145 break;
11146 }
11148 // We have a URL, so make a value array with three values.
11149 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
11150 val->Item(0) = cur->mValue;
11152 // Parse optional x and y position of cursor hotspot (css3-ui).
11153 if (ParseVariant(val->Item(1), VARIANT_NUMBER, nullptr)) {
11154 // If we have one number, we must have two.
11155 if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nullptr)) {
11156 return false;
11157 }
11158 }
11159 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
11161 if (!ExpectSymbol(',', true)) { // url must not be last
11162 return false;
11163 }
11164 cur->mNext = new nsCSSValueList;
11165 cur = cur->mNext;
11166 }
11167 }
11168 AppendValue(eCSSProperty_cursor, value);
11169 return true;
11170 }
11173 bool
11174 CSSParserImpl::ParseFont()
11175 {
11176 static const nsCSSProperty fontIDs[] = {
11177 eCSSProperty_font_style,
11178 eCSSProperty_font_variant,
11179 eCSSProperty_font_weight
11180 };
11182 // font-variant-alternates enabled ==> layout.css.font-features.enabled is true
11183 bool featuresEnabled =
11184 nsCSSProps::IsEnabled(eCSSProperty_font_variant_alternates);
11185 nsCSSValue family;
11186 if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
11187 if (eCSSUnit_Inherit == family.GetUnit() ||
11188 eCSSUnit_Initial == family.GetUnit() ||
11189 eCSSUnit_Unset == family.GetUnit()) {
11190 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
11191 AppendValue(eCSSProperty_font_family, family);
11192 AppendValue(eCSSProperty_font_style, family);
11193 AppendValue(eCSSProperty_font_variant, family);
11194 AppendValue(eCSSProperty_font_weight, family);
11195 AppendValue(eCSSProperty_font_size, family);
11196 AppendValue(eCSSProperty_line_height, family);
11197 AppendValue(eCSSProperty_font_stretch, family);
11198 AppendValue(eCSSProperty_font_size_adjust, family);
11199 AppendValue(eCSSProperty_font_feature_settings, family);
11200 AppendValue(eCSSProperty_font_language_override, family);
11201 if (featuresEnabled) {
11202 AppendValue(eCSSProperty_font_kerning, family);
11203 AppendValue(eCSSProperty_font_synthesis, family);
11204 AppendValue(eCSSProperty_font_variant_alternates, family);
11205 AppendValue(eCSSProperty_font_variant_caps, family);
11206 AppendValue(eCSSProperty_font_variant_east_asian, family);
11207 AppendValue(eCSSProperty_font_variant_ligatures, family);
11208 AppendValue(eCSSProperty_font_variant_numeric, family);
11209 AppendValue(eCSSProperty_font_variant_position, family);
11210 }
11211 }
11212 else {
11213 AppendValue(eCSSProperty__x_system_font, family);
11214 nsCSSValue systemFont(eCSSUnit_System_Font);
11215 AppendValue(eCSSProperty_font_family, systemFont);
11216 AppendValue(eCSSProperty_font_style, systemFont);
11217 AppendValue(eCSSProperty_font_variant, systemFont);
11218 AppendValue(eCSSProperty_font_weight, systemFont);
11219 AppendValue(eCSSProperty_font_size, systemFont);
11220 AppendValue(eCSSProperty_line_height, systemFont);
11221 AppendValue(eCSSProperty_font_stretch, systemFont);
11222 AppendValue(eCSSProperty_font_size_adjust, systemFont);
11223 AppendValue(eCSSProperty_font_feature_settings, systemFont);
11224 AppendValue(eCSSProperty_font_language_override, systemFont);
11225 if (featuresEnabled) {
11226 AppendValue(eCSSProperty_font_kerning, systemFont);
11227 AppendValue(eCSSProperty_font_synthesis, systemFont);
11228 AppendValue(eCSSProperty_font_variant_alternates, systemFont);
11229 AppendValue(eCSSProperty_font_variant_caps, systemFont);
11230 AppendValue(eCSSProperty_font_variant_east_asian, systemFont);
11231 AppendValue(eCSSProperty_font_variant_ligatures, systemFont);
11232 AppendValue(eCSSProperty_font_variant_numeric, systemFont);
11233 AppendValue(eCSSProperty_font_variant_position, systemFont);
11234 }
11235 }
11236 return true;
11237 }
11239 // Get optional font-style, font-variant and font-weight (in any order)
11240 const int32_t numProps = 3;
11241 nsCSSValue values[numProps];
11242 int32_t found = ParseChoice(values, fontIDs, numProps);
11243 if (found < 0 ||
11244 eCSSUnit_Inherit == values[0].GetUnit() ||
11245 eCSSUnit_Initial == values[0].GetUnit() ||
11246 eCSSUnit_Unset == values[0].GetUnit()) { // illegal data
11247 return false;
11248 }
11249 if ((found & 1) == 0) {
11250 // Provide default font-style
11251 values[0].SetIntValue(NS_FONT_STYLE_NORMAL, eCSSUnit_Enumerated);
11252 }
11253 if ((found & 2) == 0) {
11254 // Provide default font-variant
11255 values[1].SetIntValue(NS_FONT_VARIANT_NORMAL, eCSSUnit_Enumerated);
11256 }
11257 if ((found & 4) == 0) {
11258 // Provide default font-weight
11259 values[2].SetIntValue(NS_FONT_WEIGHT_NORMAL, eCSSUnit_Enumerated);
11260 }
11262 // Get mandatory font-size
11263 nsCSSValue size;
11264 if (! ParseNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP,
11265 nsCSSProps::kFontSizeKTable)) {
11266 return false;
11267 }
11269 // Get optional "/" line-height
11270 nsCSSValue lineHeight;
11271 if (ExpectSymbol('/', true)) {
11272 if (! ParseNonNegativeVariant(lineHeight,
11273 VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
11274 nullptr)) {
11275 return false;
11276 }
11277 }
11278 else {
11279 lineHeight.SetNormalValue();
11280 }
11282 // Get final mandatory font-family
11283 nsAutoParseCompoundProperty compound(this);
11284 if (ParseFamily(family)) {
11285 if (eCSSUnit_Inherit != family.GetUnit() &&
11286 eCSSUnit_Initial != family.GetUnit() &&
11287 eCSSUnit_Unset != family.GetUnit()) {
11288 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
11289 AppendValue(eCSSProperty_font_family, family);
11290 AppendValue(eCSSProperty_font_style, values[0]);
11291 AppendValue(eCSSProperty_font_variant, values[1]);
11292 AppendValue(eCSSProperty_font_weight, values[2]);
11293 AppendValue(eCSSProperty_font_size, size);
11294 AppendValue(eCSSProperty_line_height, lineHeight);
11295 AppendValue(eCSSProperty_font_stretch,
11296 nsCSSValue(NS_FONT_STRETCH_NORMAL, eCSSUnit_Enumerated));
11297 AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
11298 AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal));
11299 AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal));
11300 if (featuresEnabled) {
11301 AppendValue(eCSSProperty_font_kerning,
11302 nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated));
11303 AppendValue(eCSSProperty_font_synthesis,
11304 nsCSSValue(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE,
11305 eCSSUnit_Enumerated));
11306 AppendValue(eCSSProperty_font_variant_alternates,
11307 nsCSSValue(eCSSUnit_Normal));
11308 AppendValue(eCSSProperty_font_variant_caps, nsCSSValue(eCSSUnit_Normal));
11309 AppendValue(eCSSProperty_font_variant_east_asian,
11310 nsCSSValue(eCSSUnit_Normal));
11311 AppendValue(eCSSProperty_font_variant_ligatures,
11312 nsCSSValue(eCSSUnit_Normal));
11313 AppendValue(eCSSProperty_font_variant_numeric,
11314 nsCSSValue(eCSSUnit_Normal));
11315 AppendValue(eCSSProperty_font_variant_position,
11316 nsCSSValue(eCSSUnit_Normal));
11317 }
11318 return true;
11319 }
11320 }
11321 return false;
11322 }
11324 bool
11325 CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue)
11326 {
11327 if (!ParseVariant(aValue, VARIANT_HK | VARIANT_NONE,
11328 nsCSSProps::kFontSynthesisKTable)) {
11329 return false;
11330 }
11332 // first value 'none' ==> done
11333 if (eCSSUnit_None == aValue.GetUnit() ||
11334 eCSSUnit_Initial == aValue.GetUnit() ||
11335 eCSSUnit_Inherit == aValue.GetUnit() ||
11336 eCSSUnit_Unset == aValue.GetUnit())
11337 {
11338 return true;
11339 }
11341 // look for a second value
11342 int32_t intValue = aValue.GetIntValue();
11343 nsCSSValue nextValue;
11345 if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) {
11346 int32_t nextIntValue = nextValue.GetIntValue();
11347 if (nextIntValue & intValue) {
11348 return false;
11349 }
11350 aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
11351 }
11353 return true;
11354 }
11356 // font-variant-alternates allows for a combination of multiple
11357 // simple enumerated values and functional values. Functional values have
11358 // parameter lists with one or more idents which are later resolved
11359 // based on values defined in @font-feature-value rules.
11360 //
11361 // font-variant-alternates: swash(flowing), historical-forms, styleset(alt-g, alt-m);
11362 //
11363 // So for this the nsCSSValue is set to a pair value, with one
11364 // value for a bitmask of both simple and functional property values
11365 // and another value containing a ValuePairList with lists of idents
11366 // for each functional property value.
11367 //
11368 // pairValue
11369 // o intValue
11370 // NS_FONT_VARIANT_ALTERNATES_SWASH |
11371 // NS_FONT_VARIANT_ALTERNATES_STYLESET
11372 // o valuePairList, each element with
11373 // - intValue - indicates which alternate
11374 // - string or valueList of strings
11375 //
11376 // Note: when only 'historical-forms' is specified, there are no
11377 // functional values to store, in which case the valuePairList is a
11378 // single element dummy list. In all other cases, the length of the
11379 // list will match the number of functional values.
11381 #define MAX_ALLOWED_FEATURES 512
11383 bool
11384 CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature,
11385 nsCSSValue& aValue)
11386 {
11387 if (!GetToken(true)) {
11388 return false;
11389 }
11391 bool isIdent = (mToken.mType == eCSSToken_Ident);
11392 if (mToken.mType != eCSSToken_Function && !isIdent) {
11393 UngetToken();
11394 return false;
11395 }
11397 // ident ==> simple enumerated prop val (e.g. historical-forms)
11398 // function ==> e.g. swash(flowing) styleset(alt-g, alt-m)
11400 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
11401 if (!(eCSSKeyword_UNKNOWN < keyword &&
11402 nsCSSProps::FindKeyword(keyword,
11403 (isIdent ?
11404 nsCSSProps::kFontVariantAlternatesKTable :
11405 nsCSSProps::kFontVariantAlternatesFuncsKTable),
11406 aWhichFeature)))
11407 {
11408 // failed, pop token
11409 UngetToken();
11410 return false;
11411 }
11413 if (isIdent) {
11414 aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated);
11415 return true;
11416 }
11418 uint16_t maxElems = 1;
11419 if (keyword == eCSSKeyword_styleset ||
11420 keyword == eCSSKeyword_character_variant) {
11421 maxElems = MAX_ALLOWED_FEATURES;
11422 }
11423 return ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER,
11424 1, maxElems, aValue);
11425 }
11427 bool
11428 CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue)
11429 {
11430 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
11431 return true;
11432 }
11434 // iterate through parameters
11435 nsCSSValue listValue;
11436 int32_t feature, featureFlags = 0;
11438 // if no functional values, this may be a list with a single, unused element
11439 listValue.SetListValue();
11441 nsCSSValueList* list = nullptr;
11442 nsCSSValue value;
11443 while (ParseSingleAlternate(feature, value)) {
11445 // check to make sure value not already set
11446 if (feature == 0 ||
11447 feature & featureFlags) {
11448 return false;
11449 }
11451 featureFlags |= feature;
11453 // if function, need to add to the list of functions
11454 if (value.GetUnit() == eCSSUnit_Function) {
11455 if (!list) {
11456 list = listValue.GetListValue();
11457 } else {
11458 list->mNext = new nsCSSValueList;
11459 list = list->mNext;
11460 }
11461 list->mValue = value;
11462 }
11463 }
11465 if (featureFlags == 0) {
11466 // ParseSingleAlternate failed the first time through the loop.
11467 return false;
11468 }
11470 nsCSSValue featureValue;
11471 featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated);
11472 aValue.SetPairValue(featureValue, listValue);
11474 return true;
11475 }
11477 // aMasks - array of masks for mutually-exclusive property values,
11478 // e.g. proportial-nums, tabular-nums
11480 bool
11481 CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue,
11482 const KTableValue aKeywordTable[],
11483 const int32_t aMasks[])
11484 {
11485 // Parse at least one keyword
11486 if (!ParseEnum(aValue, aKeywordTable)) {
11487 return false;
11488 }
11490 // look for more values
11491 nsCSSValue nextValue;
11492 int32_t mergedValue = aValue.GetIntValue();
11494 while (ParseEnum(nextValue, aKeywordTable))
11495 {
11496 int32_t nextIntValue = nextValue.GetIntValue();
11498 // check to make sure value not already set
11499 if (nextIntValue & mergedValue) {
11500 return false;
11501 }
11503 const int32_t *m = aMasks;
11504 int32_t c = 0;
11506 while (*m != MASK_END_VALUE) {
11507 if (*m & nextIntValue) {
11508 c = mergedValue & *m;
11509 break;
11510 }
11511 m++;
11512 }
11514 if (c) {
11515 return false;
11516 }
11518 mergedValue |= nextIntValue;
11519 }
11521 aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated);
11523 return true;
11524 }
11526 static const int32_t maskEastAsian[] = {
11527 NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK,
11528 NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK,
11529 MASK_END_VALUE
11530 };
11532 bool
11533 CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue)
11534 {
11535 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
11536 return true;
11537 }
11539 NS_ASSERTION(maskEastAsian[ArrayLength(maskEastAsian) - 1] ==
11540 MASK_END_VALUE,
11541 "incorrectly terminated array");
11543 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable,
11544 maskEastAsian);
11545 }
11547 static const int32_t maskLigatures[] = {
11548 NS_FONT_VARIANT_LIGATURES_COMMON_MASK,
11549 NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK,
11550 NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK,
11551 NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK,
11552 MASK_END_VALUE
11553 };
11555 bool
11556 CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue)
11557 {
11558 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
11559 return true;
11560 }
11562 NS_ASSERTION(maskLigatures[ArrayLength(maskLigatures) - 1] ==
11563 MASK_END_VALUE,
11564 "incorrectly terminated array");
11566 bool parsed =
11567 ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable,
11568 maskLigatures);
11570 // if none value included, no other values are possible
11571 if (parsed && eCSSUnit_Enumerated == aValue.GetUnit()) {
11572 int32_t val = aValue.GetIntValue();
11573 if ((val & NS_FONT_VARIANT_LIGATURES_NONE) &&
11574 (val & ~int32_t(NS_FONT_VARIANT_LIGATURES_NONE))) {
11575 parsed = false;
11576 }
11577 }
11579 return parsed;
11580 }
11582 static const int32_t maskNumeric[] = {
11583 NS_FONT_VARIANT_NUMERIC_FIGURE_MASK,
11584 NS_FONT_VARIANT_NUMERIC_SPACING_MASK,
11585 NS_FONT_VARIANT_NUMERIC_FRACTION_MASK,
11586 MASK_END_VALUE
11587 };
11589 bool
11590 CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue)
11591 {
11592 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
11593 return true;
11594 }
11596 NS_ASSERTION(maskNumeric[ArrayLength(maskNumeric) - 1] ==
11597 MASK_END_VALUE,
11598 "incorrectly terminated array");
11600 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable,
11601 maskNumeric);
11602 }
11604 bool
11605 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
11606 {
11607 if (ParseVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT,
11608 nsCSSProps::kFontWeightKTable)) {
11609 if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
11610 int32_t intValue = aValue.GetIntValue();
11611 if ((100 <= intValue) &&
11612 (intValue <= 900) &&
11613 (0 == (intValue % 100))) {
11614 return true;
11615 } else {
11616 UngetToken();
11617 return false;
11618 }
11619 }
11620 return true;
11621 }
11622 return false;
11623 }
11625 bool
11626 CSSParserImpl::ParseOneFamily(nsAString& aFamily, bool& aOneKeyword)
11627 {
11628 if (!GetToken(true))
11629 return false;
11631 nsCSSToken* tk = &mToken;
11633 aOneKeyword = false;
11634 if (eCSSToken_Ident == tk->mType) {
11635 aOneKeyword = true;
11636 aFamily.Append(tk->mIdent);
11637 for (;;) {
11638 if (!GetToken(false))
11639 break;
11641 if (eCSSToken_Ident == tk->mType) {
11642 aOneKeyword = false;
11643 // We had at least another keyword before.
11644 // "If a sequence of identifiers is given as a font family name,
11645 // the computed value is the name converted to a string by joining
11646 // all the identifiers in the sequence by single spaces."
11647 // -- CSS 2.1, section 15.3
11648 // Whitespace tokens do not actually matter,
11649 // identifier tokens can be separated by comments.
11650 aFamily.Append(char16_t(' '));
11651 aFamily.Append(tk->mIdent);
11652 } else if (eCSSToken_Whitespace != tk->mType) {
11653 UngetToken();
11654 break;
11655 }
11656 }
11657 return true;
11659 } else if (eCSSToken_String == tk->mType) {
11660 aFamily.Append(tk->mSymbol); // replace the quotes
11661 aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
11662 aFamily.Append(tk->mSymbol);
11663 return true;
11665 } else {
11666 UngetToken();
11667 return false;
11668 }
11669 }
11671 bool
11672 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
11673 {
11674 nsAutoString family;
11675 bool single;
11677 // keywords only have meaning in the first position
11678 if (!ParseOneFamily(family, single))
11679 return false;
11681 // check for keywords, but only when keywords appear by themselves
11682 // i.e. not in compounds such as font-family: default blah;
11683 if (single) {
11684 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
11685 if (keyword == eCSSKeyword_inherit) {
11686 aValue.SetInheritValue();
11687 return true;
11688 }
11689 // 605231 - don't parse unquoted 'default' reserved keyword
11690 if (keyword == eCSSKeyword_default) {
11691 return false;
11692 }
11693 if (keyword == eCSSKeyword_initial) {
11694 aValue.SetInitialValue();
11695 return true;
11696 }
11697 if (keyword == eCSSKeyword_unset &&
11698 nsLayoutUtils::UnsetValueEnabled()) {
11699 aValue.SetUnsetValue();
11700 return true;
11701 }
11702 if (keyword == eCSSKeyword__moz_use_system_font &&
11703 !IsParsingCompoundProperty()) {
11704 aValue.SetSystemFontValue();
11705 return true;
11706 }
11707 }
11709 for (;;) {
11710 if (!ExpectSymbol(',', true))
11711 break;
11713 family.Append(char16_t(','));
11715 nsAutoString nextFamily;
11716 if (!ParseOneFamily(nextFamily, single))
11717 return false;
11719 // at this point unquoted keywords are not allowed
11720 // as font family names but can appear within names
11721 if (single) {
11722 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily);
11723 switch (keyword) {
11724 case eCSSKeyword_inherit:
11725 case eCSSKeyword_initial:
11726 case eCSSKeyword_default:
11727 case eCSSKeyword__moz_use_system_font:
11728 return false;
11729 case eCSSKeyword_unset:
11730 if (nsLayoutUtils::UnsetValueEnabled()) {
11731 return false;
11732 }
11733 // fall through
11734 default:
11735 break;
11736 }
11737 }
11739 family.Append(nextFamily);
11740 }
11742 if (family.IsEmpty()) {
11743 return false;
11744 }
11745 aValue.SetStringValue(family, eCSSUnit_Families);
11746 return true;
11747 }
11749 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
11750 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
11751 // local-src: 'local(' ( string | ident ) ')'
11753 bool
11754 CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
11755 {
11756 // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>?
11757 InfallibleTArray<nsCSSValue> values;
11758 nsCSSValue cur;
11759 for (;;) {
11760 if (!GetToken(true))
11761 break;
11763 if (mToken.mType == eCSSToken_URL) {
11764 SetValueToURL(cur, mToken.mIdent);
11765 values.AppendElement(cur);
11766 if (!ParseFontSrcFormat(values))
11767 return false;
11769 } else if (mToken.mType == eCSSToken_Function &&
11770 mToken.mIdent.LowerCaseEqualsLiteral("local")) {
11771 // css3-fonts does not specify a formal grammar for local().
11772 // The text permits both unquoted identifiers and quoted
11773 // strings. We resolve this ambiguity in the spec by
11774 // assuming that the appropriate production is a single
11775 // <family-name>, possibly surrounded by whitespace.
11777 nsAutoString family;
11778 bool single;
11779 if (!ParseOneFamily(family, single)) {
11780 SkipUntil(')');
11781 return false;
11782 }
11783 if (!ExpectSymbol(')', true)) {
11784 SkipUntil(')');
11785 return false;
11786 }
11788 // XXX: Getting closer...
11789 // the style parameters to the nsFont constructor are ignored,
11790 // because it's only being used to call EnumerateFamilies
11791 nsFont font(family, 0, 0, 0, 0, 0, 0);
11792 ExtractFirstFamilyData dat;
11794 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
11795 if (!dat.mGood)
11796 return false;
11798 cur.SetStringValue(dat.mFamilyName, eCSSUnit_Local_Font);
11799 values.AppendElement(cur);
11800 } else {
11801 // We don't know what to do with this token; unget it and error out
11802 UngetToken();
11803 return false;
11804 }
11806 if (!ExpectSymbol(',', true))
11807 break;
11808 }
11810 if (values.Length() == 0)
11811 return false;
11813 nsRefPtr<nsCSSValue::Array> srcVals
11814 = nsCSSValue::Array::Create(values.Length());
11816 uint32_t i;
11817 for (i = 0; i < values.Length(); i++)
11818 srcVals->Item(i) = values[i];
11819 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
11820 return true;
11821 }
11823 bool
11824 CSSParserImpl::ParseFontSrcFormat(InfallibleTArray<nsCSSValue> & values)
11825 {
11826 if (!GetToken(true))
11827 return true; // EOF harmless here
11828 if (mToken.mType != eCSSToken_Function ||
11829 !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
11830 UngetToken();
11831 return true;
11832 }
11834 do {
11835 if (!GetToken(true))
11836 return false; // EOF - no need for SkipUntil
11838 if (mToken.mType != eCSSToken_String) {
11839 UngetToken();
11840 SkipUntil(')');
11841 return false;
11842 }
11844 nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
11845 values.AppendElement(cur);
11846 } while (ExpectSymbol(',', true));
11848 if (!ExpectSymbol(')', true)) {
11849 SkipUntil(')');
11850 return false;
11851 }
11853 return true;
11854 }
11856 // font-ranges: urange ( ',' urange )*
11857 bool
11858 CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
11859 {
11860 InfallibleTArray<uint32_t> ranges;
11861 for (;;) {
11862 if (!GetToken(true))
11863 break;
11865 if (mToken.mType != eCSSToken_URange) {
11866 UngetToken();
11867 break;
11868 }
11870 // An invalid range token is a parsing error, causing the entire
11871 // descriptor to be ignored.
11872 if (!mToken.mIntegerValid)
11873 return false;
11875 uint32_t low = mToken.mInteger;
11876 uint32_t high = mToken.mInteger2;
11878 // A range that descends, or a range that is entirely outside the
11879 // current range of Unicode (U+0-10FFFF) is ignored, but does not
11880 // invalidate the descriptor. A range that straddles the high end
11881 // is clipped.
11882 if (low <= 0x10FFFF && low <= high) {
11883 if (high > 0x10FFFF)
11884 high = 0x10FFFF;
11886 ranges.AppendElement(low);
11887 ranges.AppendElement(high);
11888 }
11889 if (!ExpectSymbol(',', true))
11890 break;
11891 }
11893 if (ranges.Length() == 0)
11894 return false;
11896 nsRefPtr<nsCSSValue::Array> srcVals
11897 = nsCSSValue::Array::Create(ranges.Length());
11899 for (uint32_t i = 0; i < ranges.Length(); i++)
11900 srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer);
11901 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
11902 return true;
11903 }
11905 // font-feature-settings: normal | <feature-tag-value> [, <feature-tag-value>]*
11906 // <feature-tag-value> = <string> [ <integer> | on | off ]?
11908 // minimum - "tagx", "tagy", "tagz"
11909 // edge error case - "tagx" on 1, "tagx" "tagy", "tagx" -1, "tagx" big
11911 // pair value is always x = string, y = int
11913 // font feature tags must be four ASCII characters
11914 #define FEATURE_TAG_LENGTH 4
11916 static bool
11917 ValidFontFeatureTag(const nsString& aTag)
11918 {
11919 if (aTag.Length() != FEATURE_TAG_LENGTH) {
11920 return false;
11921 }
11922 uint32_t i;
11923 for (i = 0; i < FEATURE_TAG_LENGTH; i++) {
11924 uint32_t ch = aTag[i];
11925 if (ch < 0x20 || ch > 0x7e) {
11926 return false;
11927 }
11928 }
11929 return true;
11930 }
11932 bool
11933 CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue)
11934 {
11935 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
11936 return true;
11937 }
11939 nsCSSValuePairList *cur = aValue.SetPairListValue();
11940 for (;;) {
11941 // feature tag
11942 if (!GetToken(true)) {
11943 return false;
11944 }
11946 if (mToken.mType != eCSSToken_String ||
11947 !ValidFontFeatureTag(mToken.mIdent)) {
11948 UngetToken();
11949 return false;
11950 }
11951 cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
11953 if (!GetToken(true)) {
11954 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
11955 break;
11956 }
11958 // optional value or on/off keyword
11959 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid &&
11960 mToken.mInteger >= 0) {
11961 cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
11962 } else if (mToken.mType == eCSSToken_Ident &&
11963 mToken.mIdent.LowerCaseEqualsLiteral("on")) {
11964 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
11965 } else if (mToken.mType == eCSSToken_Ident &&
11966 mToken.mIdent.LowerCaseEqualsLiteral("off")) {
11967 cur->mYValue.SetIntValue(0, eCSSUnit_Integer);
11968 } else {
11969 // something other than value/on/off, set default value
11970 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
11971 UngetToken();
11972 }
11974 if (!ExpectSymbol(',', true)) {
11975 break;
11976 }
11978 cur->mNext = new nsCSSValuePairList;
11979 cur = cur->mNext;
11980 }
11982 return true;
11983 }
11985 bool
11986 CSSParserImpl::ParseListStyle()
11987 {
11988 // 'list-style' can accept 'none' for two different subproperties,
11989 // 'list-style-type' and 'list-style-position'. In order to accept
11990 // 'none' as the value of either but still allow another value for
11991 // either, we need to ensure that the first 'none' we find gets
11992 // allocated to a dummy property instead.
11993 static const nsCSSProperty listStyleIDs[] = {
11994 eCSSPropertyExtra_x_none_value,
11995 eCSSProperty_list_style_type,
11996 eCSSProperty_list_style_position,
11997 eCSSProperty_list_style_image
11998 };
12000 nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)];
12001 int32_t found =
12002 ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs));
12003 if (found < 1) {
12004 return false;
12005 }
12007 if ((found & (1|2|8)) == (1|2|8)) {
12008 if (values[0].GetUnit() == eCSSUnit_None) {
12009 // We found a 'none' plus another value for both of
12010 // 'list-style-type' and 'list-style-image'. This is a parse
12011 // error, since the 'none' has to count for at least one of them.
12012 return false;
12013 } else {
12014 NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] &&
12015 values[0] == values[2] && values[0] == values[3],
12016 "should be a special value");
12017 }
12018 }
12020 // Provide default values
12021 if ((found & 2) == 0) {
12022 if (found & 1) {
12023 values[1].SetIntValue(NS_STYLE_LIST_STYLE_NONE, eCSSUnit_Enumerated);
12024 } else {
12025 values[1].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
12026 }
12027 }
12028 if ((found & 4) == 0) {
12029 values[2].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
12030 eCSSUnit_Enumerated);
12031 }
12032 if ((found & 8) == 0) {
12033 values[3].SetNoneValue();
12034 }
12036 // Start at 1 to avoid appending fake value.
12037 for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) {
12038 AppendValue(listStyleIDs[index], values[index]);
12039 }
12040 return true;
12041 }
12043 bool
12044 CSSParserImpl::ParseMargin()
12045 {
12046 static const nsCSSProperty kMarginSideIDs[] = {
12047 eCSSProperty_margin_top,
12048 eCSSProperty_margin_right_value,
12049 eCSSProperty_margin_bottom,
12050 eCSSProperty_margin_left_value
12051 };
12052 static const nsCSSProperty kMarginSources[] = {
12053 eCSSProperty_margin_left_ltr_source,
12054 eCSSProperty_margin_left_rtl_source,
12055 eCSSProperty_margin_right_ltr_source,
12056 eCSSProperty_margin_right_rtl_source,
12057 eCSSProperty_UNKNOWN
12058 };
12060 // do this now, in case 4 values weren't specified
12061 InitBoxPropsAsPhysical(kMarginSources);
12062 return ParseBoxProperties(kMarginSideIDs);
12063 }
12065 bool
12066 CSSParserImpl::ParseMarks(nsCSSValue& aValue)
12067 {
12068 if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPageMarksKTable)) {
12069 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
12070 if (NS_STYLE_PAGE_MARKS_NONE != aValue.GetIntValue() &&
12071 false == CheckEndProperty()) {
12072 nsCSSValue second;
12073 if (ParseEnum(second, nsCSSProps::kPageMarksKTable)) {
12074 // 'none' keyword in conjuction with others is not allowed
12075 if (NS_STYLE_PAGE_MARKS_NONE != second.GetIntValue()) {
12076 aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(),
12077 eCSSUnit_Enumerated);
12078 return true;
12079 }
12080 }
12081 return false;
12082 }
12083 }
12084 return true;
12085 }
12086 return false;
12087 }
12089 bool
12090 CSSParserImpl::ParseOutline()
12091 {
12092 const int32_t numProps = 3;
12093 static const nsCSSProperty kOutlineIDs[] = {
12094 eCSSProperty_outline_color,
12095 eCSSProperty_outline_style,
12096 eCSSProperty_outline_width
12097 };
12099 nsCSSValue values[numProps];
12100 int32_t found = ParseChoice(values, kOutlineIDs, numProps);
12101 if (found < 1) {
12102 return false;
12103 }
12105 // Provide default values
12106 if ((found & 1) == 0) { // Provide default outline-color
12107 values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
12108 }
12109 if ((found & 2) == 0) { // Provide default outline-style
12110 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
12111 }
12112 if ((found & 4) == 0) { // Provide default outline-width
12113 values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
12114 }
12116 int32_t index;
12117 for (index = 0; index < numProps; index++) {
12118 AppendValue(kOutlineIDs[index], values[index]);
12119 }
12120 return true;
12121 }
12123 bool
12124 CSSParserImpl::ParseOverflow()
12125 {
12126 nsCSSValue overflow;
12127 if (!ParseVariant(overflow, VARIANT_HK, nsCSSProps::kOverflowKTable)) {
12128 return false;
12129 }
12131 nsCSSValue overflowX(overflow);
12132 nsCSSValue overflowY(overflow);
12133 if (eCSSUnit_Enumerated == overflow.GetUnit())
12134 switch(overflow.GetIntValue()) {
12135 case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
12136 overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
12137 overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
12138 break;
12139 case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
12140 overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
12141 overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
12142 break;
12143 }
12144 AppendValue(eCSSProperty_overflow_x, overflowX);
12145 AppendValue(eCSSProperty_overflow_y, overflowY);
12146 return true;
12147 }
12149 bool
12150 CSSParserImpl::ParsePadding()
12151 {
12152 static const nsCSSProperty kPaddingSideIDs[] = {
12153 eCSSProperty_padding_top,
12154 eCSSProperty_padding_right_value,
12155 eCSSProperty_padding_bottom,
12156 eCSSProperty_padding_left_value
12157 };
12158 static const nsCSSProperty kPaddingSources[] = {
12159 eCSSProperty_padding_left_ltr_source,
12160 eCSSProperty_padding_left_rtl_source,
12161 eCSSProperty_padding_right_ltr_source,
12162 eCSSProperty_padding_right_rtl_source,
12163 eCSSProperty_UNKNOWN
12164 };
12166 // do this now, in case 4 values weren't specified
12167 InitBoxPropsAsPhysical(kPaddingSources);
12168 return ParseBoxProperties(kPaddingSideIDs);
12169 }
12171 bool
12172 CSSParserImpl::ParseQuotes()
12173 {
12174 nsCSSValue value;
12175 if (!ParseVariant(value, VARIANT_HOS, nullptr)) {
12176 return false;
12177 }
12178 if (value.GetUnit() == eCSSUnit_String) {
12179 nsCSSValue open = value;
12180 nsCSSValuePairList* quotes = value.SetPairListValue();
12181 for (;;) {
12182 quotes->mXValue = open;
12183 // get mandatory close
12184 if (!ParseVariant(quotes->mYValue, VARIANT_STRING, nullptr)) {
12185 return false;
12186 }
12187 // look for another open
12188 if (!ParseVariant(open, VARIANT_STRING, nullptr)) {
12189 break;
12190 }
12191 quotes->mNext = new nsCSSValuePairList;
12192 quotes = quotes->mNext;
12193 }
12194 }
12195 AppendValue(eCSSProperty_quotes, value);
12196 return true;
12197 }
12199 bool
12200 CSSParserImpl::ParseSize()
12201 {
12202 nsCSSValue width, height;
12203 if (!ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
12204 return false;
12205 }
12206 if (width.IsLengthUnit()) {
12207 ParseVariant(height, VARIANT_LENGTH, nullptr);
12208 }
12210 if (width == height || height.GetUnit() == eCSSUnit_Null) {
12211 AppendValue(eCSSProperty_size, width);
12212 } else {
12213 nsCSSValue pair;
12214 pair.SetPairValue(width, height);
12215 AppendValue(eCSSProperty_size, pair);
12216 }
12217 return true;
12218 }
12220 bool
12221 CSSParserImpl::ParseTextDecoration()
12222 {
12223 enum {
12224 eDecorationNone = NS_STYLE_TEXT_DECORATION_LINE_NONE,
12225 eDecorationUnderline = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
12226 eDecorationOverline = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE,
12227 eDecorationLineThrough = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
12228 eDecorationBlink = NS_STYLE_TEXT_DECORATION_LINE_BLINK,
12229 eDecorationPrefAnchors = NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS
12230 };
12231 static_assert((eDecorationNone ^ eDecorationUnderline ^
12232 eDecorationOverline ^ eDecorationLineThrough ^
12233 eDecorationBlink ^ eDecorationPrefAnchors) ==
12234 (eDecorationNone | eDecorationUnderline |
12235 eDecorationOverline | eDecorationLineThrough |
12236 eDecorationBlink | eDecorationPrefAnchors),
12237 "text decoration constants need to be bitmasks");
12239 static const KTableValue kTextDecorationKTable[] = {
12240 eCSSKeyword_none, eDecorationNone,
12241 eCSSKeyword_underline, eDecorationUnderline,
12242 eCSSKeyword_overline, eDecorationOverline,
12243 eCSSKeyword_line_through, eDecorationLineThrough,
12244 eCSSKeyword_blink, eDecorationBlink,
12245 eCSSKeyword__moz_anchor_decoration, eDecorationPrefAnchors,
12246 eCSSKeyword_UNKNOWN,-1
12247 };
12249 nsCSSValue value;
12250 if (!ParseVariant(value, VARIANT_HK, kTextDecorationKTable)) {
12251 return false;
12252 }
12254 nsCSSValue line, style, color;
12255 switch (value.GetUnit()) {
12256 case eCSSUnit_Enumerated: {
12257 // We shouldn't accept decoration line style and color via
12258 // text-decoration.
12259 color.SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR,
12260 eCSSUnit_Enumerated);
12261 style.SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
12262 eCSSUnit_Enumerated);
12264 int32_t intValue = value.GetIntValue();
12265 if (intValue == eDecorationNone) {
12266 line.SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE,
12267 eCSSUnit_Enumerated);
12268 break;
12269 }
12271 // look for more keywords
12272 nsCSSValue keyword;
12273 int32_t index;
12274 for (index = 0; index < 3; index++) {
12275 if (!ParseEnum(keyword, kTextDecorationKTable)) {
12276 break;
12277 }
12278 int32_t newValue = keyword.GetIntValue();
12279 if (newValue == eDecorationNone || newValue & intValue) {
12280 // 'none' keyword in conjuction with others is not allowed, and
12281 // duplicate keyword is not allowed.
12282 return false;
12283 }
12284 intValue |= newValue;
12285 }
12287 line.SetIntValue(intValue, eCSSUnit_Enumerated);
12288 break;
12289 }
12290 default:
12291 line = color = style = value;
12292 break;
12293 }
12295 AppendValue(eCSSProperty_text_decoration_line, line);
12296 AppendValue(eCSSProperty_text_decoration_color, color);
12297 AppendValue(eCSSProperty_text_decoration_style, style);
12299 return true;
12300 }
12302 bool
12303 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableValue aTable[])
12304 {
12305 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
12306 // 'inherit', 'initial' and 'unset' must be alone
12307 return true;
12308 }
12310 nsCSSValue left;
12311 if (!ParseVariant(left, VARIANT_KEYWORD, aTable)) {
12312 return false;
12313 }
12315 if (!nsLayoutUtils::IsTextAlignTrueValueEnabled()) {
12316 aValue = left;
12317 return true;
12318 }
12320 nsCSSValue right;
12321 if (ParseVariant(right, VARIANT_KEYWORD, aTable)) {
12322 // 'true' must be combined with some other value than 'true'.
12323 if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE &&
12324 right.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
12325 return false;
12326 }
12327 aValue.SetPairValue(left, right);
12328 } else {
12329 // Single value 'true' is not allowed.
12330 if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
12331 return false;
12332 }
12333 aValue = left;
12334 }
12335 return true;
12336 }
12338 bool
12339 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue)
12340 {
12341 return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable);
12342 }
12344 bool
12345 CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue)
12346 {
12347 return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable);
12348 }
12350 bool
12351 CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
12352 {
12353 if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTextDecorationLineKTable)) {
12354 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
12355 int32_t intValue = aValue.GetIntValue();
12356 if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
12357 // look for more keywords
12358 nsCSSValue keyword;
12359 int32_t index;
12360 for (index = 0; index < 3; index++) {
12361 if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) {
12362 int32_t newValue = keyword.GetIntValue();
12363 if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE ||
12364 newValue & intValue) {
12365 // 'none' keyword in conjuction with others is not allowed, and
12366 // duplicate keyword is not allowed.
12367 return false;
12368 }
12369 intValue |= newValue;
12370 }
12371 else {
12372 break;
12373 }
12374 }
12375 aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
12376 }
12377 }
12378 return true;
12379 }
12380 return false;
12381 }
12383 bool
12384 CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
12385 {
12386 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
12387 // 'inherit', 'initial' and 'unset' must be alone
12388 return true;
12389 }
12391 nsCSSValue left;
12392 if (!ParseVariant(left, VARIANT_KEYWORD | VARIANT_STRING,
12393 nsCSSProps::kTextOverflowKTable))
12394 return false;
12396 nsCSSValue right;
12397 if (ParseVariant(right, VARIANT_KEYWORD | VARIANT_STRING,
12398 nsCSSProps::kTextOverflowKTable))
12399 aValue.SetPairValue(left, right);
12400 else {
12401 aValue = left;
12402 }
12403 return true;
12404 }
12406 bool
12407 CSSParserImpl::ParseTouchAction(nsCSSValue& aValue)
12408 {
12409 // Avaliable values of property touch-action:
12410 // auto | none | [pan-x || pan-y] | manipulation
12412 if (!ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTouchActionKTable)) {
12413 return false;
12414 }
12416 // Auto and None keywords aren't allowed in conjunction with others.
12417 // Also inherit, initial and unset values are available.
12418 if (eCSSUnit_Enumerated != aValue.GetUnit()) {
12419 return true;
12420 }
12422 int32_t intValue = aValue.GetIntValue();
12423 nsCSSValue nextValue;
12424 if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) {
12425 int32_t nextIntValue = nextValue.GetIntValue();
12427 // duplicates aren't allowed.
12428 if (nextIntValue & intValue) {
12429 return false;
12430 }
12432 // Auto and None and Manipulation is not allowed in conjunction with others.
12433 if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE |
12434 NS_STYLE_TOUCH_ACTION_AUTO |
12435 NS_STYLE_TOUCH_ACTION_MANIPULATION)) {
12436 return false;
12437 }
12439 aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
12440 }
12442 return true;
12443 }
12445 bool
12446 CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue)
12447 {
12448 if (!ParseVariant(aValue, VARIANT_HK,
12449 nsCSSProps::kTextCombineUprightKTable)) {
12450 return false;
12451 }
12453 // if 'digits', need to check for an explicit number [2, 3, 4]
12454 if (eCSSUnit_Enumerated == aValue.GetUnit() &&
12455 aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) {
12456 if (!GetToken(true)) {
12457 return true;
12458 }
12459 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
12460 switch (mToken.mInteger) {
12461 case 2: // already set, nothing to do
12462 break;
12463 case 3:
12464 aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3,
12465 eCSSUnit_Enumerated);
12466 break;
12467 case 4:
12468 aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4,
12469 eCSSUnit_Enumerated);
12470 break;
12471 default:
12472 // invalid digits value
12473 return false;
12474 }
12475 } else {
12476 UngetToken();
12477 }
12478 }
12479 return true;
12480 }
12482 ///////////////////////////////////////////////////////
12483 // transform Parsing Implementation
12485 /* Reads a function list of arguments and consumes the closing parenthesis.
12486 * Do not call this function directly; it's meant to be called from
12487 * ParseFunction.
12488 */
12489 bool
12490 CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[],
12491 int32_t aVariantMaskAll,
12492 uint16_t aMinElems,
12493 uint16_t aMaxElems,
12494 InfallibleTArray<nsCSSValue> &aOutput)
12495 {
12496 NS_ASSERTION((aVariantMask && !aVariantMaskAll) ||
12497 (!aVariantMask && aVariantMaskAll),
12498 "only one of the two variant mask parameters can be set");
12500 for (uint16_t index = 0; index < aMaxElems; ++index) {
12501 nsCSSValue newValue;
12502 int32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index];
12503 if (!ParseVariant(newValue, m, nullptr)) {
12504 break;
12505 }
12507 aOutput.AppendElement(newValue);
12509 if (ExpectSymbol(',', true)) {
12510 // Move on to the next argument if we see a comma.
12511 continue;
12512 }
12514 if (ExpectSymbol(')', true)) {
12515 // Make sure we've read enough symbols if we see a closing parenthesis.
12516 return (index + 1) >= aMinElems;
12517 }
12519 // Only a comma or a closing parenthesis is valid after an argument.
12520 break;
12521 }
12523 // If we're here, we've hit an error without seeing a closing parenthesis or
12524 // we've read too many elements without seeing a closing parenthesis.
12525 SkipUntil(')');
12526 return false;
12527 }
12529 /* Parses a function [ input of the form (a [, b]*) ] and stores it
12530 * as an nsCSSValue that holds a function of the form
12531 * function-name arg1 arg2 ... argN
12532 *
12533 * On error, the return value is false.
12534 *
12535 * @param aFunction The name of the function that we're reading.
12536 * @param aAllowedTypes An array of values corresponding to the legal
12537 * types for each element in the function. The zeroth element in the
12538 * array corresponds to the first function parameter, etc. The length
12539 * of this array _must_ be greater than or equal to aMaxElems or the
12540 * behavior is undefined. If not null, aAllowTypesAll must be 0.
12541 * @param aAllowedTypesAll If set, every element tested for these types
12542 * @param aMinElems Minimum number of elements to read. Reading fewer than
12543 * this many elements will result in the function failing.
12544 * @param aMaxElems Maximum number of elements to read. Reading more than
12545 * this many elements will result in the function failing.
12546 * @param aValue (out) The value that was parsed.
12547 */
12548 bool
12549 CSSParserImpl::ParseFunction(nsCSSKeyword aFunction,
12550 const int32_t aAllowedTypes[],
12551 int32_t aAllowedTypesAll,
12552 uint16_t aMinElems, uint16_t aMaxElems,
12553 nsCSSValue &aValue)
12554 {
12555 NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) ||
12556 (!aAllowedTypes && aAllowedTypesAll),
12557 "only one of the two allowed type parameter can be set");
12558 typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
12560 /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
12561 * elements stored in the the nsCSSValue::Array.
12562 */
12563 static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
12565 /* Read in a list of values as an array, failing if we can't or if
12566 * it's out of bounds.
12567 *
12568 * We reserve 16 entries in the foundValues array in order to avoid
12569 * having to resize the array dynamically when parsing some well-formed
12570 * functions. The number 16 is coming from the number of arguments that
12571 * matrix3d() accepts.
12572 */
12573 AutoInfallibleTArray<nsCSSValue, 16> foundValues;
12574 if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems,
12575 aMaxElems, foundValues)) {
12576 return false;
12577 }
12579 /*
12580 * In case the user has given us more than 2^16 - 2 arguments,
12581 * we'll truncate them at 2^16 - 2 arguments.
12582 */
12583 uint16_t numArgs = std::min(foundValues.Length(), MAX_ALLOWED_ELEMS);
12584 nsRefPtr<nsCSSValue::Array> convertedArray =
12585 aValue.InitFunction(aFunction, numArgs);
12587 /* Copy things over. */
12588 for (uint16_t index = 0; index < numArgs; ++index)
12589 convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
12591 /* Return it! */
12592 return true;
12593 }
12595 /**
12596 * Given a token, determines the minimum and maximum number of function
12597 * parameters to read, along with the mask that should be used to read
12598 * those function parameters. If the token isn't a transform function,
12599 * returns an error.
12600 *
12601 * @param aToken The token identifying the function.
12602 * @param aMinElems [out] The minimum number of elements to read.
12603 * @param aMaxElems [out] The maximum number of elements to read
12604 * @param aVariantMask [out] The variant mask to use during parsing
12605 * @return Whether the information was loaded successfully.
12606 */
12607 static bool GetFunctionParseInformation(nsCSSKeyword aToken,
12608 bool aIsPrefixed,
12609 uint16_t &aMinElems,
12610 uint16_t &aMaxElems,
12611 const int32_t *& aVariantMask)
12612 {
12613 /* These types represent the common variant masks that will be used to
12614 * parse out the individual functions. The order in the enumeration
12615 * must match the order in which the masks are declared.
12616 */
12617 enum { eLengthPercentCalc,
12618 eLengthCalc,
12619 eTwoLengthPercentCalcs,
12620 eTwoLengthPercentCalcsOneLengthCalc,
12621 eAngle,
12622 eTwoAngles,
12623 eNumber,
12624 ePositiveLength,
12625 eTwoNumbers,
12626 eThreeNumbers,
12627 eThreeNumbersOneAngle,
12628 eMatrix,
12629 eMatrixPrefixed,
12630 eMatrix3d,
12631 eMatrix3dPrefixed,
12632 eNumVariantMasks };
12633 static const int32_t kMaxElemsPerFunction = 16;
12634 static const int32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
12635 {VARIANT_LPCALC},
12636 {VARIANT_LENGTH|VARIANT_CALC},
12637 {VARIANT_LPCALC, VARIANT_LPCALC},
12638 {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LENGTH|VARIANT_CALC},
12639 {VARIANT_ANGLE_OR_ZERO},
12640 {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
12641 {VARIANT_NUMBER},
12642 {VARIANT_LENGTH|VARIANT_POSITIVE_DIMENSION},
12643 {VARIANT_NUMBER, VARIANT_NUMBER},
12644 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
12645 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
12646 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
12647 VARIANT_NUMBER, VARIANT_NUMBER},
12648 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
12649 VARIANT_LPNCALC, VARIANT_LPNCALC},
12650 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
12651 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
12652 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
12653 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
12654 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
12655 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
12656 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
12657 VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
12659 #ifdef DEBUG
12660 static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
12661 {1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 6, 16, 16};
12662 #endif
12664 int32_t variantIndex = eNumVariantMasks;
12666 switch (aToken) {
12667 case eCSSKeyword_translatex:
12668 case eCSSKeyword_translatey:
12669 /* Exactly one length or percent. */
12670 variantIndex = eLengthPercentCalc;
12671 aMinElems = 1U;
12672 aMaxElems = 1U;
12673 break;
12674 case eCSSKeyword_translatez:
12675 /* Exactly one length */
12676 variantIndex = eLengthCalc;
12677 aMinElems = 1U;
12678 aMaxElems = 1U;
12679 break;
12680 case eCSSKeyword_translate3d:
12681 /* Exactly two lengthds or percents and a number */
12682 variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
12683 aMinElems = 3U;
12684 aMaxElems = 3U;
12685 break;
12686 case eCSSKeyword_scalez:
12687 case eCSSKeyword_scalex:
12688 case eCSSKeyword_scaley:
12689 /* Exactly one scale factor. */
12690 variantIndex = eNumber;
12691 aMinElems = 1U;
12692 aMaxElems = 1U;
12693 break;
12694 case eCSSKeyword_scale3d:
12695 /* Exactly three scale factors. */
12696 variantIndex = eThreeNumbers;
12697 aMinElems = 3U;
12698 aMaxElems = 3U;
12699 break;
12700 case eCSSKeyword_rotatex:
12701 case eCSSKeyword_rotatey:
12702 case eCSSKeyword_rotate:
12703 case eCSSKeyword_rotatez:
12704 /* Exactly one angle. */
12705 variantIndex = eAngle;
12706 aMinElems = 1U;
12707 aMaxElems = 1U;
12708 break;
12709 case eCSSKeyword_rotate3d:
12710 variantIndex = eThreeNumbersOneAngle;
12711 aMinElems = 4U;
12712 aMaxElems = 4U;
12713 break;
12714 case eCSSKeyword_translate:
12715 /* One or two lengths or percents. */
12716 variantIndex = eTwoLengthPercentCalcs;
12717 aMinElems = 1U;
12718 aMaxElems = 2U;
12719 break;
12720 case eCSSKeyword_skew:
12721 /* Exactly one or two angles. */
12722 variantIndex = eTwoAngles;
12723 aMinElems = 1U;
12724 aMaxElems = 2U;
12725 break;
12726 case eCSSKeyword_scale:
12727 /* One or two scale factors. */
12728 variantIndex = eTwoNumbers;
12729 aMinElems = 1U;
12730 aMaxElems = 2U;
12731 break;
12732 case eCSSKeyword_skewx:
12733 /* Exactly one angle. */
12734 variantIndex = eAngle;
12735 aMinElems = 1U;
12736 aMaxElems = 1U;
12737 break;
12738 case eCSSKeyword_skewy:
12739 /* Exactly one angle. */
12740 variantIndex = eAngle;
12741 aMinElems = 1U;
12742 aMaxElems = 1U;
12743 break;
12744 case eCSSKeyword_matrix:
12745 /* Six values, all numbers. */
12746 variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
12747 aMinElems = 6U;
12748 aMaxElems = 6U;
12749 break;
12750 case eCSSKeyword_matrix3d:
12751 /* 16 matrix values, all numbers */
12752 variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
12753 aMinElems = 16U;
12754 aMaxElems = 16U;
12755 break;
12756 case eCSSKeyword_perspective:
12757 /* Exactly one scale number. */
12758 variantIndex = ePositiveLength;
12759 aMinElems = 1U;
12760 aMaxElems = 1U;
12761 break;
12762 default:
12763 /* Oh dear, we didn't match. Report an error. */
12764 return false;
12765 }
12767 NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
12768 NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
12769 NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
12770 NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
12771 NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
12772 #ifdef DEBUG
12773 NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
12774 "Invalid aMaxElems for this variant mask.");
12775 #endif
12777 // Convert the index into a mask.
12778 aVariantMask = kVariantMasks[variantIndex];
12780 return true;
12781 }
12783 bool CSSParserImpl::ParseWillChange()
12784 {
12785 nsCSSValue listValue;
12786 nsCSSValueList* currentListValue = listValue.SetListValue();
12787 bool first = true;
12788 for (;;) {
12789 const uint32_t variantMask = VARIANT_IDENTIFIER |
12790 VARIANT_INHERIT |
12791 VARIANT_NONE |
12792 VARIANT_ALL |
12793 VARIANT_AUTO;
12794 nsCSSValue value;
12795 if (!ParseVariant(value, variantMask, nullptr)) {
12796 return false;
12797 }
12799 if (value.GetUnit() == eCSSUnit_None ||
12800 value.GetUnit() == eCSSUnit_All)
12801 {
12802 return false;
12803 }
12805 if (value.GetUnit() != eCSSUnit_Ident) {
12806 if (first) {
12807 AppendValue(eCSSProperty_will_change, value);
12808 return true;
12809 } else {
12810 return false;
12811 }
12812 }
12814 nsString str;
12815 value.GetStringValue(str);
12816 if (str.LowerCaseEqualsLiteral("default")) {
12817 return false;
12818 }
12820 currentListValue->mValue = value;
12822 if (!ExpectSymbol(',', true)) {
12823 break;
12824 }
12825 currentListValue->mNext = new nsCSSValueList;
12826 currentListValue = currentListValue->mNext;
12827 first = false;
12828 }
12830 AppendValue(eCSSProperty_will_change, listValue);
12831 return true;
12832 }
12834 /* Reads a single transform function from the tokenizer stream, reporting an
12835 * error if something goes wrong.
12836 */
12837 bool
12838 CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue)
12839 {
12840 if (!GetToken(true))
12841 return false;
12843 if (mToken.mType != eCSSToken_Function) {
12844 UngetToken();
12845 return false;
12846 }
12848 const int32_t* variantMask;
12849 uint16_t minElems, maxElems;
12850 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
12852 if (!GetFunctionParseInformation(keyword, aIsPrefixed,
12853 minElems, maxElems, variantMask))
12854 return false;
12856 return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue);
12857 }
12859 /* Parses a transform property list by continuously reading in properties
12860 * and constructing a matrix from it.
12861 */
12862 bool CSSParserImpl::ParseTransform(bool aIsPrefixed)
12863 {
12864 nsCSSValue value;
12865 // 'inherit', 'initial', 'unset' and 'none' must be alone
12866 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
12867 nsCSSValueSharedList* list = new nsCSSValueSharedList;
12868 value.SetSharedListValue(list);
12869 list->mHead = new nsCSSValueList;
12870 nsCSSValueList* cur = list->mHead;
12871 for (;;) {
12872 if (!ParseSingleTransform(aIsPrefixed, cur->mValue)) {
12873 return false;
12874 }
12875 if (CheckEndProperty()) {
12876 break;
12877 }
12878 cur->mNext = new nsCSSValueList;
12879 cur = cur->mNext;
12880 }
12881 }
12882 AppendValue(eCSSProperty_transform, value);
12883 return true;
12884 }
12886 bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
12887 {
12888 nsCSSValuePair position;
12889 if (!ParseBoxPositionValues(position, true))
12890 return false;
12892 nsCSSProperty prop = eCSSProperty_transform_origin;
12893 if (aPerspective) {
12894 prop = eCSSProperty_perspective_origin;
12895 }
12897 // Unlike many other uses of pairs, this position should always be stored
12898 // as a pair, even if the values are the same, so it always serializes as
12899 // a pair, and to keep the computation code simple.
12900 if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
12901 position.mXValue.GetUnit() == eCSSUnit_Initial ||
12902 position.mXValue.GetUnit() == eCSSUnit_Unset) {
12903 NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
12904 "inherit/initial/unset only half?");
12905 AppendValue(prop, position.mXValue);
12906 } else {
12907 nsCSSValue value;
12908 if (aPerspective) {
12909 value.SetPairValue(position.mXValue, position.mYValue);
12910 } else {
12911 nsCSSValue depth;
12912 if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr)) {
12913 depth.SetFloatValue(0.0f, eCSSUnit_Pixel);
12914 }
12915 value.SetTripletValue(position.mXValue, position.mYValue, depth);
12916 }
12918 AppendValue(prop, value);
12919 }
12920 return true;
12921 }
12923 /**
12924 * Reads a drop-shadow value. At the moment the Filter Effects specification
12925 * just expects one shadow item. Should this ever change to a list of shadow
12926 * items, use ParseShadowList instead.
12927 */
12928 bool
12929 CSSParserImpl::ParseDropShadow(nsCSSValue* aValue)
12930 {
12931 // Use nsCSSValueList to reuse the shadow resolving code in
12932 // nsRuleNode and nsComputedDOMStyle.
12933 nsCSSValue shadow;
12934 nsCSSValueList* cur = shadow.SetListValue();
12935 if (!ParseShadowItem(cur->mValue, false))
12936 return false;
12938 if (!ExpectSymbol(')', true))
12939 return false;
12941 nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1);
12943 // Copy things over.
12944 dropShadow->Item(1) = shadow;
12946 return true;
12947 }
12949 /**
12950 * Reads a single url or filter function from the tokenizer stream, reporting an
12951 * error if something goes wrong.
12952 */
12953 bool
12954 CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
12955 {
12956 if (ParseVariant(*aValue, VARIANT_URL, nullptr)) {
12957 return true;
12958 }
12960 if (!nsLayoutUtils::CSSFiltersEnabled()) {
12961 // With CSS Filters disabled, we should only accept an SVG reference filter.
12962 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
12963 return false;
12964 }
12966 if (!GetToken(true)) {
12967 REPORT_UNEXPECTED_EOF(PEFilterEOF);
12968 return false;
12969 }
12971 if (mToken.mType != eCSSToken_Function) {
12972 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
12973 return false;
12974 }
12976 nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
12977 // Parse drop-shadow independently of the other filter functions
12978 // because of its more complex characteristics.
12979 if (functionName == eCSSKeyword_drop_shadow) {
12980 if (ParseDropShadow(aValue)) {
12981 return true;
12982 } else {
12983 // Unrecognized filter function.
12984 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
12985 SkipUntil(')');
12986 return false;
12987 }
12988 }
12990 // Set up the parsing rules based on the filter function.
12991 int32_t variantMask = VARIANT_PN;
12992 bool rejectNegativeArgument = true;
12993 bool clampArgumentToOne = false;
12994 switch (functionName) {
12995 case eCSSKeyword_blur:
12996 variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
12997 // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
12998 rejectNegativeArgument = false;
12999 break;
13000 case eCSSKeyword_brightness:
13001 case eCSSKeyword_contrast:
13002 case eCSSKeyword_saturate:
13003 break;
13004 case eCSSKeyword_grayscale:
13005 case eCSSKeyword_invert:
13006 case eCSSKeyword_sepia:
13007 case eCSSKeyword_opacity:
13008 clampArgumentToOne = true;
13009 break;
13010 case eCSSKeyword_hue_rotate:
13011 variantMask = VARIANT_ANGLE;
13012 rejectNegativeArgument = false;
13013 break;
13014 default:
13015 // Unrecognized filter function.
13016 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
13017 SkipUntil(')');
13018 return false;
13019 }
13021 // Parse the function.
13022 uint16_t minElems = 1U;
13023 uint16_t maxElems = 1U;
13024 uint32_t allVariants = 0;
13025 if (!ParseFunction(functionName, &variantMask, allVariants,
13026 minElems, maxElems, *aValue)) {
13027 REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError);
13028 return false;
13029 }
13031 // Get the first and only argument to the filter function.
13032 NS_ABORT_IF_FALSE(aValue->GetUnit() == eCSSUnit_Function,
13033 "expected a filter function");
13034 NS_ABORT_IF_FALSE(aValue->UnitHasArrayValue(),
13035 "filter function should be an array");
13036 NS_ABORT_IF_FALSE(aValue->GetArrayValue()->Count() == 2,
13037 "filter function should have exactly one argument");
13038 nsCSSValue& arg = aValue->GetArrayValue()->Item(1);
13040 if (rejectNegativeArgument &&
13041 ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) ||
13042 (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) {
13043 REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
13044 return false;
13045 }
13047 if (clampArgumentToOne) {
13048 if (arg.GetUnit() == eCSSUnit_Number &&
13049 arg.GetFloatValue() > 1.0f) {
13050 arg.SetFloatValue(1.0f, arg.GetUnit());
13051 } else if (arg.GetUnit() == eCSSUnit_Percent &&
13052 arg.GetPercentValue() > 1.0f) {
13053 arg.SetPercentValue(1.0f);
13054 }
13055 }
13057 return true;
13058 }
13060 /**
13061 * Parses a filter property value by continuously reading in urls and/or filter
13062 * functions and constructing a list.
13063 *
13064 * When CSS Filters are enabled, the filter property accepts one or more SVG
13065 * reference filters and/or CSS filter functions.
13066 * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%);
13067 *
13068 * When CSS Filters are disabled, the filter property only accepts one SVG
13069 * reference filter.
13070 * e.g. filter: url(#my-filter);
13071 */
13072 bool
13073 CSSParserImpl::ParseFilter()
13074 {
13075 nsCSSValue value;
13076 // 'inherit', 'initial', 'unset' and 'none' must be alone
13077 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
13078 nsCSSValueList* cur = value.SetListValue();
13079 while (cur) {
13080 if (!ParseSingleFilter(&cur->mValue)) {
13081 return false;
13082 }
13083 if (CheckEndProperty()) {
13084 break;
13085 }
13086 if (!nsLayoutUtils::CSSFiltersEnabled()) {
13087 // With CSS Filters disabled, we should only accept one SVG reference
13088 // filter.
13089 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
13090 return false;
13091 }
13092 cur->mNext = new nsCSSValueList;
13093 cur = cur->mNext;
13094 }
13095 }
13096 AppendValue(eCSSProperty_filter, value);
13097 return true;
13098 }
13100 bool
13101 CSSParserImpl::ParseTransitionProperty()
13102 {
13103 nsCSSValue value;
13104 // 'inherit', 'initial', 'unset' and 'none' must be alone
13105 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
13106 // Accept a list of arbitrary identifiers. They should be
13107 // CSS properties, but we want to accept any so that we
13108 // accept properties that we don't know about yet, e.g.
13109 // transition-property: invalid-property, left, opacity;
13110 nsCSSValueList* cur = value.SetListValue();
13111 for (;;) {
13112 if (!ParseVariant(cur->mValue, VARIANT_IDENTIFIER | VARIANT_ALL, nullptr)) {
13113 return false;
13114 }
13115 if (cur->mValue.GetUnit() == eCSSUnit_Ident) {
13116 nsDependentString str(cur->mValue.GetStringBufferValue());
13117 // Exclude 'none', 'inherit', 'initial' and 'unset' according to the
13118 // same rules as for 'counter-reset' in CSS 2.1.
13119 if (str.LowerCaseEqualsLiteral("none") ||
13120 str.LowerCaseEqualsLiteral("inherit") ||
13121 str.LowerCaseEqualsLiteral("initial") ||
13122 (str.LowerCaseEqualsLiteral("unset") &&
13123 nsLayoutUtils::UnsetValueEnabled())) {
13124 return false;
13125 }
13126 }
13127 if (!ExpectSymbol(',', true)) {
13128 break;
13129 }
13130 cur->mNext = new nsCSSValueList;
13131 cur = cur->mNext;
13132 }
13133 }
13134 AppendValue(eCSSProperty_transition_property, value);
13135 return true;
13136 }
13138 bool
13139 CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue)
13140 {
13141 NS_ASSERTION(!mHavePushBack &&
13142 mToken.mType == eCSSToken_Function &&
13143 mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"),
13144 "unexpected initial state");
13146 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(4);
13148 float x1, x2, y1, y2;
13149 if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) ||
13150 !ParseTransitionTimingFunctionValueComponent(y1, ',', false) ||
13151 !ParseTransitionTimingFunctionValueComponent(x2, ',', true) ||
13152 !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) {
13153 return false;
13154 }
13156 val->Item(0).SetFloatValue(x1, eCSSUnit_Number);
13157 val->Item(1).SetFloatValue(y1, eCSSUnit_Number);
13158 val->Item(2).SetFloatValue(x2, eCSSUnit_Number);
13159 val->Item(3).SetFloatValue(y2, eCSSUnit_Number);
13161 aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier);
13163 return true;
13164 }
13166 bool
13167 CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent,
13168 char aStop,
13169 bool aCheckRange)
13170 {
13171 if (!GetToken(true)) {
13172 return false;
13173 }
13174 nsCSSToken* tk = &mToken;
13175 if (tk->mType == eCSSToken_Number) {
13176 float num = tk->mNumber;
13177 if (aCheckRange && (num < 0.0 || num > 1.0)) {
13178 return false;
13179 }
13180 aComponent = num;
13181 if (ExpectSymbol(aStop, true)) {
13182 return true;
13183 }
13184 }
13185 return false;
13186 }
13188 bool
13189 CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
13190 {
13191 NS_ASSERTION(!mHavePushBack &&
13192 mToken.mType == eCSSToken_Function &&
13193 mToken.mIdent.LowerCaseEqualsLiteral("steps"),
13194 "unexpected initial state");
13196 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
13198 if (!ParseOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, nullptr)) {
13199 return false;
13200 }
13202 int32_t type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
13203 if (ExpectSymbol(',', true)) {
13204 if (!GetToken(true)) {
13205 return false;
13206 }
13207 type = -1;
13208 if (mToken.mType == eCSSToken_Ident) {
13209 if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
13210 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
13211 } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) {
13212 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
13213 }
13214 }
13215 if (type == -1) {
13216 UngetToken();
13217 return false;
13218 }
13219 }
13220 val->Item(1).SetIntValue(type, eCSSUnit_Enumerated);
13222 if (!ExpectSymbol(')', true)) {
13223 return false;
13224 }
13226 aValue.SetArrayValue(val, eCSSUnit_Steps);
13227 return true;
13228 }
13230 static nsCSSValueList*
13231 AppendValueToList(nsCSSValue& aContainer,
13232 nsCSSValueList* aTail,
13233 const nsCSSValue& aValue)
13234 {
13235 nsCSSValueList* entry;
13236 if (aContainer.GetUnit() == eCSSUnit_Null) {
13237 NS_ABORT_IF_FALSE(!aTail, "should not have an entry");
13238 entry = aContainer.SetListValue();
13239 } else {
13240 NS_ABORT_IF_FALSE(!aTail->mNext, "should not have a next entry");
13241 NS_ABORT_IF_FALSE(aContainer.GetUnit() == eCSSUnit_List, "not a list");
13242 entry = new nsCSSValueList;
13243 aTail->mNext = entry;
13244 }
13245 entry->mValue = aValue;
13246 return entry;
13247 }
13249 CSSParserImpl::ParseAnimationOrTransitionShorthandResult
13250 CSSParserImpl::ParseAnimationOrTransitionShorthand(
13251 const nsCSSProperty* aProperties,
13252 const nsCSSValue* aInitialValues,
13253 nsCSSValue* aValues,
13254 size_t aNumProperties)
13255 {
13256 nsCSSValue tempValue;
13257 // first see if 'inherit', 'initial' or 'unset' is specified. If one is,
13258 // it can be the only thing specified, so don't attempt to parse any
13259 // additional properties
13260 if (ParseVariant(tempValue, VARIANT_INHERIT, nullptr)) {
13261 for (uint32_t i = 0; i < aNumProperties; ++i) {
13262 AppendValue(aProperties[i], tempValue);
13263 }
13264 return eParseAnimationOrTransitionShorthand_Inherit;
13265 }
13267 static const size_t maxNumProperties = 7;
13268 NS_ABORT_IF_FALSE(aNumProperties <= maxNumProperties,
13269 "can't handle this many properties");
13270 nsCSSValueList *cur[maxNumProperties];
13271 bool parsedProperty[maxNumProperties];
13273 for (size_t i = 0; i < aNumProperties; ++i) {
13274 cur[i] = nullptr;
13275 }
13276 bool atEOP = false; // at end of property?
13277 for (;;) { // loop over comma-separated transitions or animations
13278 // whether a particular subproperty was specified for this
13279 // transition or animation
13280 bool haveAnyProperty = false;
13281 for (size_t i = 0; i < aNumProperties; ++i) {
13282 parsedProperty[i] = false;
13283 }
13284 for (;;) { // loop over values within a transition or animation
13285 bool foundProperty = false;
13286 // check to see if we're at the end of one full transition or
13287 // animation definition (either because we hit a comma or because
13288 // we hit the end of the property definition)
13289 if (ExpectSymbol(',', true))
13290 break;
13291 if (CheckEndProperty()) {
13292 atEOP = true;
13293 break;
13294 }
13296 // else, try to parse the next transition or animation sub-property
13297 for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) {
13298 if (!parsedProperty[i]) {
13299 // if we haven't found this property yet, try to parse it
13300 if (ParseSingleValueProperty(tempValue, aProperties[i])) {
13301 parsedProperty[i] = true;
13302 cur[i] = AppendValueToList(aValues[i], cur[i], tempValue);
13303 foundProperty = true;
13304 haveAnyProperty = true;
13305 break; // out of inner loop; continue looking for next sub-property
13306 }
13307 }
13308 }
13309 if (!foundProperty) {
13310 // We're not at a ',' or at the end of the property, but we couldn't
13311 // parse any of the sub-properties, so the declaration is invalid.
13312 return eParseAnimationOrTransitionShorthand_Error;
13313 }
13314 }
13316 if (!haveAnyProperty) {
13317 // Got an empty item.
13318 return eParseAnimationOrTransitionShorthand_Error;
13319 }
13321 // We hit the end of the property or the end of one transition
13322 // or animation definition, add its components to the list.
13323 for (uint32_t i = 0; i < aNumProperties; ++i) {
13324 // If all of the subproperties were not explicitly specified, fill
13325 // in the missing ones with initial values.
13326 if (!parsedProperty[i]) {
13327 cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]);
13328 }
13329 }
13331 if (atEOP)
13332 break;
13333 // else we just hit a ',' so continue parsing the next compound transition
13334 }
13336 return eParseAnimationOrTransitionShorthand_Values;
13337 }
13339 bool
13340 CSSParserImpl::ParseTransition()
13341 {
13342 static const nsCSSProperty kTransitionProperties[] = {
13343 eCSSProperty_transition_duration,
13344 eCSSProperty_transition_timing_function,
13345 // Must check 'transition-delay' after 'transition-duration', since
13346 // that's our assumption about what the spec means for the shorthand
13347 // syntax (the first time given is the duration, and the second
13348 // given is the delay).
13349 eCSSProperty_transition_delay,
13350 // Must check 'transition-property' after
13351 // 'transition-timing-function' since 'transition-property' accepts
13352 // any keyword.
13353 eCSSProperty_transition_property
13354 };
13355 static const uint32_t numProps = MOZ_ARRAY_LENGTH(kTransitionProperties);
13356 // this is a shorthand property that accepts -property, -delay,
13357 // -duration, and -timing-function with some components missing.
13358 // there can be multiple transitions, separated with commas
13360 nsCSSValue initialValues[numProps];
13361 initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
13362 initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
13363 eCSSUnit_Enumerated);
13364 initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
13365 initialValues[3].SetAllValue();
13367 nsCSSValue values[numProps];
13369 ParseAnimationOrTransitionShorthandResult spres =
13370 ParseAnimationOrTransitionShorthand(kTransitionProperties,
13371 initialValues, values, numProps);
13372 if (spres != eParseAnimationOrTransitionShorthand_Values) {
13373 return spres != eParseAnimationOrTransitionShorthand_Error;
13374 }
13376 // Make two checks on the list for 'transition-property':
13377 // + If there is more than one item, then none of the items can be
13378 // 'none'.
13379 // + None of the items can be 'inherit', 'initial' or 'unset'.
13380 {
13381 NS_ABORT_IF_FALSE(kTransitionProperties[3] ==
13382 eCSSProperty_transition_property,
13383 "array index mismatch");
13384 nsCSSValueList *l = values[3].GetListValue();
13385 bool multipleItems = !!l->mNext;
13386 do {
13387 const nsCSSValue& val = l->mValue;
13388 if (val.GetUnit() == eCSSUnit_None) {
13389 if (multipleItems) {
13390 // This is a syntax error.
13391 return false;
13392 }
13394 // Unbox a solitary 'none'.
13395 values[3].SetNoneValue();
13396 break;
13397 }
13398 if (val.GetUnit() == eCSSUnit_Ident) {
13399 nsDependentString str(val.GetStringBufferValue());
13400 if (str.EqualsLiteral("inherit") ||
13401 str.EqualsLiteral("initial") ||
13402 (str.EqualsLiteral("unset") &&
13403 nsLayoutUtils::UnsetValueEnabled())) {
13404 return false;
13405 }
13406 }
13407 } while ((l = l->mNext));
13408 }
13410 // Save all parsed transition sub-properties in mTempData
13411 for (uint32_t i = 0; i < numProps; ++i) {
13412 AppendValue(kTransitionProperties[i], values[i]);
13413 }
13414 return true;
13415 }
13417 bool
13418 CSSParserImpl::ParseAnimation()
13419 {
13420 static const nsCSSProperty kAnimationProperties[] = {
13421 eCSSProperty_animation_duration,
13422 eCSSProperty_animation_timing_function,
13423 // Must check 'animation-delay' after 'animation-duration', since
13424 // that's our assumption about what the spec means for the shorthand
13425 // syntax (the first time given is the duration, and the second
13426 // given is the delay).
13427 eCSSProperty_animation_delay,
13428 eCSSProperty_animation_direction,
13429 eCSSProperty_animation_fill_mode,
13430 eCSSProperty_animation_iteration_count,
13431 // Must check 'animation-name' after 'animation-timing-function',
13432 // 'animation-direction', 'animation-fill-mode',
13433 // 'animation-iteration-count', and 'animation-play-state' since
13434 // 'animation-name' accepts any keyword.
13435 eCSSProperty_animation_name
13436 };
13437 static const uint32_t numProps = MOZ_ARRAY_LENGTH(kAnimationProperties);
13438 // this is a shorthand property that accepts -property, -delay,
13439 // -duration, and -timing-function with some components missing.
13440 // there can be multiple animations, separated with commas
13442 nsCSSValue initialValues[numProps];
13443 initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
13444 initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
13445 eCSSUnit_Enumerated);
13446 initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
13447 initialValues[3].SetIntValue(NS_STYLE_ANIMATION_DIRECTION_NORMAL, eCSSUnit_Enumerated);
13448 initialValues[4].SetIntValue(NS_STYLE_ANIMATION_FILL_MODE_NONE, eCSSUnit_Enumerated);
13449 initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
13450 initialValues[6].SetNoneValue();
13452 nsCSSValue values[numProps];
13454 ParseAnimationOrTransitionShorthandResult spres =
13455 ParseAnimationOrTransitionShorthand(kAnimationProperties,
13456 initialValues, values, numProps);
13457 if (spres != eParseAnimationOrTransitionShorthand_Values) {
13458 return spres != eParseAnimationOrTransitionShorthand_Error;
13459 }
13461 // Save all parsed animation sub-properties in mTempData
13462 for (uint32_t i = 0; i < numProps; ++i) {
13463 AppendValue(kAnimationProperties[i], values[i]);
13464 }
13465 return true;
13466 }
13468 bool
13469 CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow)
13470 {
13471 // A shadow list item is an array, with entries in this sequence:
13472 enum {
13473 IndexX,
13474 IndexY,
13475 IndexRadius,
13476 IndexSpread, // only for box-shadow
13477 IndexColor,
13478 IndexInset // only for box-shadow
13479 };
13481 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6);
13483 if (aIsBoxShadow) {
13484 // Optional inset keyword (ignore errors)
13485 ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
13486 nsCSSProps::kBoxShadowTypeKTable);
13487 }
13489 nsCSSValue xOrColor;
13490 bool haveColor = false;
13491 if (!ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC,
13492 nullptr)) {
13493 return false;
13494 }
13495 if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) {
13496 val->Item(IndexX) = xOrColor;
13497 } else {
13498 // Must be a color (as string or color value)
13499 NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident ||
13500 xOrColor.GetUnit() == eCSSUnit_EnumColor ||
13501 xOrColor.IsNumericColorUnit(),
13502 "Must be a color value");
13503 val->Item(IndexColor) = xOrColor;
13504 haveColor = true;
13506 // X coordinate mandatory after color
13507 if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC,
13508 nullptr)) {
13509 return false;
13510 }
13511 }
13513 // Y coordinate; mandatory
13514 if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC,
13515 nullptr)) {
13516 return false;
13517 }
13519 // Optional radius. Ignore errors except if they pass a negative
13520 // value which we must reject. If we use ParseNonNegativeVariant
13521 // we can't tell the difference between an unspecified radius
13522 // and a negative radius.
13523 if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC,
13524 nullptr) &&
13525 val->Item(IndexRadius).IsLengthUnit() &&
13526 val->Item(IndexRadius).GetFloatValue() < 0) {
13527 return false;
13528 }
13530 if (aIsBoxShadow) {
13531 // Optional spread
13532 ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC, nullptr);
13533 }
13535 if (!haveColor) {
13536 // Optional color
13537 ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr);
13538 }
13540 if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) {
13541 // Optional inset keyword
13542 ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
13543 nsCSSProps::kBoxShadowTypeKTable);
13544 }
13546 aValue.SetArrayValue(val, eCSSUnit_Array);
13547 return true;
13548 }
13550 bool
13551 CSSParserImpl::ParseShadowList(nsCSSProperty aProperty)
13552 {
13553 nsAutoParseCompoundProperty compound(this);
13554 bool isBoxShadow = aProperty == eCSSProperty_box_shadow;
13556 nsCSSValue value;
13557 // 'inherit', 'initial', 'unset' and 'none' must be alone
13558 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
13559 nsCSSValueList* cur = value.SetListValue();
13560 for (;;) {
13561 if (!ParseShadowItem(cur->mValue, isBoxShadow)) {
13562 return false;
13563 }
13564 if (!ExpectSymbol(',', true)) {
13565 break;
13566 }
13567 cur->mNext = new nsCSSValueList;
13568 cur = cur->mNext;
13569 }
13570 }
13571 AppendValue(aProperty, value);
13572 return true;
13573 }
13575 int32_t
13576 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix)
13577 {
13578 NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
13580 int32_t nameSpaceID = kNameSpaceID_Unknown;
13581 if (mNameSpaceMap) {
13582 // user-specified identifiers are case-sensitive (bug 416106)
13583 nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
13584 if (!prefix) {
13585 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
13586 }
13587 nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
13588 }
13589 // else no declared namespaces
13591 if (nameSpaceID == kNameSpaceID_Unknown) { // unknown prefix, dump it
13592 REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, aPrefix);
13593 }
13595 return nameSpaceID;
13596 }
13598 void
13599 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
13600 {
13601 if (mNameSpaceMap) {
13602 aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nullptr));
13603 } else {
13604 aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
13605 }
13606 }
13608 bool
13609 CSSParserImpl::ParsePaint(nsCSSProperty aPropID)
13610 {
13611 nsCSSValue x, y;
13613 if (!ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL |
13614 VARIANT_OPENTYPE_SVG_KEYWORD,
13615 nsCSSProps::kContextPatternKTable)) {
13616 return false;
13617 }
13619 bool canHaveFallback = x.GetUnit() == eCSSUnit_URL ||
13620 x.GetUnit() == eCSSUnit_Enumerated;
13621 if (canHaveFallback) {
13622 if (!ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr))
13623 y.SetNoneValue();
13624 }
13626 if (!canHaveFallback) {
13627 AppendValue(aPropID, x);
13628 } else {
13629 nsCSSValue val;
13630 val.SetPairValue(x, y);
13631 AppendValue(aPropID, val);
13632 }
13633 return true;
13634 }
13636 bool
13637 CSSParserImpl::ParseDasharray()
13638 {
13639 nsCSSValue value;
13641 // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
13642 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE |
13643 VARIANT_OPENTYPE_SVG_KEYWORD,
13644 nsCSSProps::kStrokeContextValueKTable)) {
13645 nsCSSValueList *cur = value.SetListValue();
13646 for (;;) {
13647 if (!ParseNonNegativeVariant(cur->mValue, VARIANT_LPN, nullptr)) {
13648 return false;
13649 }
13650 if (CheckEndProperty()) {
13651 break;
13652 }
13653 // skip optional commas between elements
13654 (void)ExpectSymbol(',', true);
13656 cur->mNext = new nsCSSValueList;
13657 cur = cur->mNext;
13658 }
13659 }
13660 AppendValue(eCSSProperty_stroke_dasharray, value);
13661 return true;
13662 }
13664 bool
13665 CSSParserImpl::ParseMarker()
13666 {
13667 nsCSSValue marker;
13668 if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) {
13669 AppendValue(eCSSProperty_marker_end, marker);
13670 AppendValue(eCSSProperty_marker_mid, marker);
13671 AppendValue(eCSSProperty_marker_start, marker);
13672 return true;
13673 }
13674 return false;
13675 }
13677 bool
13678 CSSParserImpl::ParsePaintOrder()
13679 {
13680 static_assert
13681 ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) > NS_STYLE_PAINT_ORDER_LAST_VALUE,
13682 "bitfield width insufficient for paint-order constants");
13684 static const KTableValue kPaintOrderKTable[] = {
13685 eCSSKeyword_normal, NS_STYLE_PAINT_ORDER_NORMAL,
13686 eCSSKeyword_fill, NS_STYLE_PAINT_ORDER_FILL,
13687 eCSSKeyword_stroke, NS_STYLE_PAINT_ORDER_STROKE,
13688 eCSSKeyword_markers, NS_STYLE_PAINT_ORDER_MARKERS,
13689 eCSSKeyword_UNKNOWN,-1
13690 };
13692 static_assert(MOZ_ARRAY_LENGTH(kPaintOrderKTable) ==
13693 2 * (NS_STYLE_PAINT_ORDER_LAST_VALUE + 2),
13694 "missing paint-order values in kPaintOrderKTable");
13696 nsCSSValue value;
13697 if (!ParseVariant(value, VARIANT_HK, kPaintOrderKTable)) {
13698 return false;
13699 }
13701 uint32_t seen = 0;
13702 uint32_t order = 0;
13703 uint32_t position = 0;
13705 // Ensure that even cast to a signed int32_t when stored in CSSValue,
13706 // we have enough space for the entire paint-order value.
13707 static_assert
13708 (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE < 32,
13709 "seen and order not big enough");
13711 if (value.GetUnit() == eCSSUnit_Enumerated) {
13712 uint32_t component = static_cast<uint32_t>(value.GetIntValue());
13713 if (component != NS_STYLE_PAINT_ORDER_NORMAL) {
13714 bool parsedOK = true;
13715 for (;;) {
13716 if (seen & (1 << component)) {
13717 // Already seen this component.
13718 UngetToken();
13719 parsedOK = false;
13720 break;
13721 }
13722 seen |= (1 << component);
13723 order |= (component << position);
13724 position += NS_STYLE_PAINT_ORDER_BITWIDTH;
13725 if (!ParseEnum(value, kPaintOrderKTable)) {
13726 break;
13727 }
13728 component = value.GetIntValue();
13729 if (component == NS_STYLE_PAINT_ORDER_NORMAL) {
13730 // Can't have "normal" in the middle of the list of paint components.
13731 UngetToken();
13732 parsedOK = false;
13733 break;
13734 }
13735 }
13737 // Fill in the remaining paint-order components in the order of their
13738 // constant values.
13739 if (parsedOK) {
13740 for (component = 1;
13741 component <= NS_STYLE_PAINT_ORDER_LAST_VALUE;
13742 component++) {
13743 if (!(seen & (1 << component))) {
13744 order |= (component << position);
13745 position += NS_STYLE_PAINT_ORDER_BITWIDTH;
13746 }
13747 }
13748 }
13749 }
13751 static_assert(NS_STYLE_PAINT_ORDER_NORMAL == 0,
13752 "unexpected value for NS_STYLE_PAINT_ORDER_NORMAL");
13753 value.SetIntValue(static_cast<int32_t>(order), eCSSUnit_Enumerated);
13754 }
13756 AppendValue(eCSSProperty_paint_order, value);
13757 return true;
13758 }
13760 bool
13761 CSSParserImpl::BackslashDropped()
13762 {
13763 return mScanner->GetEOFCharacters() &
13764 nsCSSScanner::eEOFCharacters_DropBackslash;
13765 }
13767 void
13768 CSSParserImpl::AppendImpliedEOFCharacters(nsAString& aResult)
13769 {
13770 nsCSSScanner::AppendImpliedEOFCharacters(mScanner->GetEOFCharacters(),
13771 aResult);
13772 }
13774 bool
13775 CSSParserImpl::ParseAll()
13776 {
13777 nsCSSValue value;
13778 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
13779 return false;
13780 }
13782 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all) {
13783 AppendValue(*p, value);
13784 }
13785 return true;
13786 }
13788 bool
13789 CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
13790 nsString& aValue)
13791 {
13792 CSSVariableDeclarations::Type type;
13793 nsString variableValue;
13794 bool dropBackslash;
13795 nsString impliedCharacters;
13797 // Record the token stream while parsing a variable value.
13798 if (!mInSupportsCondition) {
13799 mScanner->StartRecording();
13800 }
13801 if (!ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
13802 nullptr, nullptr)) {
13803 if (!mInSupportsCondition) {
13804 mScanner->StopRecording();
13805 }
13806 return false;
13807 }
13809 if (!mInSupportsCondition) {
13810 if (type == CSSVariableDeclarations::eTokenStream) {
13811 // This was indeed a token stream value, so store it in variableValue.
13812 mScanner->StopRecording(variableValue);
13813 if (dropBackslash) {
13814 MOZ_ASSERT(!variableValue.IsEmpty() &&
13815 variableValue[variableValue.Length() - 1] == '\\');
13816 variableValue.Truncate(variableValue.Length() - 1);
13817 }
13818 variableValue.Append(impliedCharacters);
13819 } else {
13820 // This was either 'inherit' or 'initial'; we don't need the recorded
13821 // input.
13822 mScanner->StopRecording();
13823 }
13824 }
13826 if (mHavePushBack && type == CSSVariableDeclarations::eTokenStream) {
13827 // If we came to the end of a valid variable declaration and a token was
13828 // pushed back, then it would have been ended by '!', ')', ';', ']' or '}'.
13829 // We need to remove it from the recorded variable value.
13830 MOZ_ASSERT(mToken.IsSymbol('!') ||
13831 mToken.IsSymbol(')') ||
13832 mToken.IsSymbol(';') ||
13833 mToken.IsSymbol(']') ||
13834 mToken.IsSymbol('}'));
13835 if (!mInSupportsCondition) {
13836 MOZ_ASSERT(!variableValue.IsEmpty());
13837 MOZ_ASSERT(variableValue[variableValue.Length() - 1] == mToken.mSymbol);
13838 variableValue.Truncate(variableValue.Length() - 1);
13839 }
13840 }
13842 *aType = type;
13843 aValue = variableValue;
13844 return true;
13845 }
13847 bool
13848 CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
13849 bool* aDropBackslash,
13850 nsString& aImpliedCharacters,
13851 void (*aFunc)(const nsAString&, void*),
13852 void* aData)
13853 {
13854 // A property value is invalid if it contains variable references and also:
13855 //
13856 // * has unbalanced parens, brackets or braces
13857 // * has any BAD_STRING or BAD_URL tokens
13858 // * has any ';' or '!' tokens at the top level of a variable reference's
13859 // fallback
13860 //
13861 // If the property is a custom property (i.e. a variable declaration), then
13862 // it is also invalid if it consists of no tokens, such as:
13863 //
13864 // --invalid:;
13865 //
13866 // Note that is valid for a custom property to have a value that consists
13867 // solely of white space, such as:
13868 //
13869 // --valid: ;
13871 // Stack of closing characters for currently open constructs.
13872 StopSymbolCharStack stack;
13874 // Indexes into ')' characters in |stack| that correspond to "var(". This
13875 // is used to stop parsing when we encounter a '!' or ';' at the top level
13876 // of a variable reference's fallback.
13877 nsAutoTArray<uint32_t, 16> references;
13879 if (!GetToken(false)) {
13880 // Variable value was empty since we reached EOF.
13881 REPORT_UNEXPECTED_EOF(PEVariableEOF);
13882 return false;
13883 }
13885 if (mToken.mType == eCSSToken_Symbol &&
13886 (mToken.mSymbol == '!' ||
13887 mToken.mSymbol == ')' ||
13888 mToken.mSymbol == ';' ||
13889 mToken.mSymbol == ']' ||
13890 mToken.mSymbol == '}')) {
13891 // Variable value was empty since we reached the end of the construct.
13892 UngetToken();
13893 REPORT_UNEXPECTED_TOKEN(PEVariableEmpty);
13894 return false;
13895 }
13897 if (mToken.mType == eCSSToken_Whitespace) {
13898 if (!GetToken(true)) {
13899 // Variable value was white space only. This is valid.
13900 MOZ_ASSERT(!BackslashDropped());
13901 *aType = CSSVariableDeclarations::eTokenStream;
13902 *aDropBackslash = false;
13903 AppendImpliedEOFCharacters(aImpliedCharacters);
13904 return true;
13905 }
13906 }
13908 // Look for 'initial', 'inherit' or 'unset' as the first non-white space
13909 // token.
13910 CSSVariableDeclarations::Type type = CSSVariableDeclarations::eTokenStream;
13911 if (mToken.mType == eCSSToken_Ident) {
13912 if (mToken.mIdent.LowerCaseEqualsLiteral("initial")) {
13913 type = CSSVariableDeclarations::eInitial;
13914 } else if (mToken.mIdent.LowerCaseEqualsLiteral("inherit")) {
13915 type = CSSVariableDeclarations::eInherit;
13916 } else if (mToken.mIdent.LowerCaseEqualsLiteral("unset")) {
13917 type = CSSVariableDeclarations::eUnset;
13918 }
13919 }
13921 if (type != CSSVariableDeclarations::eTokenStream) {
13922 if (!GetToken(true)) {
13923 // Variable value was 'initial' or 'inherit' followed by EOF.
13924 MOZ_ASSERT(!BackslashDropped());
13925 *aType = type;
13926 *aDropBackslash = false;
13927 AppendImpliedEOFCharacters(aImpliedCharacters);
13928 return true;
13929 }
13930 UngetToken();
13931 if (mToken.mType == eCSSToken_Symbol &&
13932 (mToken.mSymbol == '!' ||
13933 mToken.mSymbol == ')' ||
13934 mToken.mSymbol == ';' ||
13935 mToken.mSymbol == ']' ||
13936 mToken.mSymbol == '}')) {
13937 // Variable value was 'initial' or 'inherit' followed by the end
13938 // of the declaration.
13939 MOZ_ASSERT(!BackslashDropped());
13940 *aType = type;
13941 *aDropBackslash = false;
13942 return true;
13943 }
13944 }
13946 do {
13947 switch (mToken.mType) {
13948 case eCSSToken_Symbol:
13949 if (mToken.mSymbol == '(') {
13950 stack.AppendElement(')');
13951 } else if (mToken.mSymbol == '[') {
13952 stack.AppendElement(']');
13953 } else if (mToken.mSymbol == '{') {
13954 stack.AppendElement('}');
13955 } else if (mToken.mSymbol == ';' ||
13956 mToken.mSymbol == '!') {
13957 if (stack.IsEmpty()) {
13958 UngetToken();
13959 MOZ_ASSERT(!BackslashDropped());
13960 *aType = CSSVariableDeclarations::eTokenStream;
13961 *aDropBackslash = false;
13962 return true;
13963 } else if (!references.IsEmpty() &&
13964 references.LastElement() == stack.Length() - 1) {
13965 REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback);
13966 SkipUntilAllOf(stack);
13967 return false;
13968 }
13969 } else if (mToken.mSymbol == ')' ||
13970 mToken.mSymbol == ']' ||
13971 mToken.mSymbol == '}') {
13972 for (;;) {
13973 if (stack.IsEmpty()) {
13974 UngetToken();
13975 MOZ_ASSERT(!BackslashDropped());
13976 *aType = CSSVariableDeclarations::eTokenStream;
13977 *aDropBackslash = false;
13978 return true;
13979 }
13980 char16_t c = stack.LastElement();
13981 stack.TruncateLength(stack.Length() - 1);
13982 if (!references.IsEmpty() &&
13983 references.LastElement() == stack.Length()) {
13984 references.TruncateLength(references.Length() - 1);
13985 }
13986 if (mToken.mSymbol == c) {
13987 break;
13988 }
13989 }
13990 }
13991 break;
13993 case eCSSToken_Function:
13994 if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
13995 if (!GetToken(true)) {
13996 // EOF directly after "var(".
13997 REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF);
13998 return false;
13999 }
14000 if (mToken.mType != eCSSToken_Ident ||
14001 !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
14002 // There must be an identifier directly after the "var(" and
14003 // it must be a custom property name.
14004 UngetToken();
14005 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName);
14006 SkipUntil(')');
14007 SkipUntilAllOf(stack);
14008 return false;
14009 }
14010 if (aFunc) {
14011 MOZ_ASSERT(Substring(mToken.mIdent, 0,
14012 CSS_CUSTOM_NAME_PREFIX_LENGTH).
14013 EqualsLiteral("--"));
14014 // remove '--'
14015 const nsDependentSubstring varName =
14016 Substring(mToken.mIdent, CSS_CUSTOM_NAME_PREFIX_LENGTH);
14017 aFunc(varName, aData);
14018 }
14019 if (!GetToken(true)) {
14020 // EOF right after "var(<ident>".
14021 stack.AppendElement(')');
14022 } else if (mToken.IsSymbol(',')) {
14023 // Variable reference with fallback.
14024 if (!GetToken(false) || mToken.IsSymbol(')')) {
14025 // Comma must be followed by at least one fallback token.
14026 REPORT_UNEXPECTED(PEExpectedVariableFallback);
14027 SkipUntilAllOf(stack);
14028 return false;
14029 }
14030 UngetToken();
14031 references.AppendElement(stack.Length());
14032 stack.AppendElement(')');
14033 } else if (mToken.IsSymbol(')')) {
14034 // Correctly closed variable reference.
14035 } else {
14036 // Malformed variable reference.
14037 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen);
14038 SkipUntil(')');
14039 SkipUntilAllOf(stack);
14040 return false;
14041 }
14042 } else {
14043 stack.AppendElement(')');
14044 }
14045 break;
14047 case eCSSToken_Bad_String:
14048 SkipUntilAllOf(stack);
14049 return false;
14051 case eCSSToken_Bad_URL:
14052 SkipUntil(')');
14053 SkipUntilAllOf(stack);
14054 return false;
14056 default:
14057 break;
14058 }
14059 } while (GetToken(true));
14061 // Append any implied closing characters.
14062 *aDropBackslash = BackslashDropped();
14063 AppendImpliedEOFCharacters(aImpliedCharacters);
14064 uint32_t i = stack.Length();
14065 while (i--) {
14066 aImpliedCharacters.Append(stack[i]);
14067 }
14069 *aType = type;
14070 return true;
14071 }
14073 } // anonymous namespace
14075 // Recycling of parser implementation objects
14077 static CSSParserImpl* gFreeList = nullptr;
14079 nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
14080 nsCSSStyleSheet* aSheet)
14081 {
14082 CSSParserImpl *impl = gFreeList;
14083 if (impl) {
14084 gFreeList = impl->mNextFree;
14085 impl->mNextFree = nullptr;
14086 } else {
14087 impl = new CSSParserImpl();
14088 }
14090 if (aLoader) {
14091 impl->SetChildLoader(aLoader);
14092 impl->SetQuirkMode(aLoader->GetCompatibilityMode() ==
14093 eCompatibility_NavQuirks);
14094 }
14095 if (aSheet) {
14096 impl->SetStyleSheet(aSheet);
14097 }
14099 mImpl = static_cast<void*>(impl);
14100 }
14102 nsCSSParser::~nsCSSParser()
14103 {
14104 CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl);
14105 impl->Reset();
14106 impl->mNextFree = gFreeList;
14107 gFreeList = impl;
14108 }
14110 /* static */ void
14111 nsCSSParser::Shutdown()
14112 {
14113 CSSParserImpl *tofree = gFreeList;
14114 CSSParserImpl *next;
14115 while (tofree)
14116 {
14117 next = tofree->mNextFree;
14118 delete tofree;
14119 tofree = next;
14120 }
14121 }
14123 // Wrapper methods
14125 nsresult
14126 nsCSSParser::SetStyleSheet(nsCSSStyleSheet* aSheet)
14127 {
14128 return static_cast<CSSParserImpl*>(mImpl)->
14129 SetStyleSheet(aSheet);
14130 }
14132 nsresult
14133 nsCSSParser::SetQuirkMode(bool aQuirkMode)
14134 {
14135 return static_cast<CSSParserImpl*>(mImpl)->
14136 SetQuirkMode(aQuirkMode);
14137 }
14139 nsresult
14140 nsCSSParser::SetChildLoader(mozilla::css::Loader* aChildLoader)
14141 {
14142 return static_cast<CSSParserImpl*>(mImpl)->
14143 SetChildLoader(aChildLoader);
14144 }
14146 nsresult
14147 nsCSSParser::ParseSheet(const nsAString& aInput,
14148 nsIURI* aSheetURI,
14149 nsIURI* aBaseURI,
14150 nsIPrincipal* aSheetPrincipal,
14151 uint32_t aLineNumber,
14152 bool aAllowUnsafeRules)
14153 {
14154 return static_cast<CSSParserImpl*>(mImpl)->
14155 ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
14156 aAllowUnsafeRules);
14157 }
14159 nsresult
14160 nsCSSParser::ParseStyleAttribute(const nsAString& aAttributeValue,
14161 nsIURI* aDocURI,
14162 nsIURI* aBaseURI,
14163 nsIPrincipal* aNodePrincipal,
14164 css::StyleRule** aResult)
14165 {
14166 return static_cast<CSSParserImpl*>(mImpl)->
14167 ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI,
14168 aNodePrincipal, aResult);
14169 }
14171 nsresult
14172 nsCSSParser::ParseDeclarations(const nsAString& aBuffer,
14173 nsIURI* aSheetURI,
14174 nsIURI* aBaseURI,
14175 nsIPrincipal* aSheetPrincipal,
14176 css::Declaration* aDeclaration,
14177 bool* aChanged)
14178 {
14179 return static_cast<CSSParserImpl*>(mImpl)->
14180 ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal,
14181 aDeclaration, aChanged);
14182 }
14184 nsresult
14185 nsCSSParser::ParseRule(const nsAString& aRule,
14186 nsIURI* aSheetURI,
14187 nsIURI* aBaseURI,
14188 nsIPrincipal* aSheetPrincipal,
14189 css::Rule** aResult)
14190 {
14191 return static_cast<CSSParserImpl*>(mImpl)->
14192 ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult);
14193 }
14195 nsresult
14196 nsCSSParser::ParseProperty(const nsCSSProperty aPropID,
14197 const nsAString& aPropValue,
14198 nsIURI* aSheetURI,
14199 nsIURI* aBaseURI,
14200 nsIPrincipal* aSheetPrincipal,
14201 css::Declaration* aDeclaration,
14202 bool* aChanged,
14203 bool aIsImportant,
14204 bool aIsSVGMode)
14205 {
14206 return static_cast<CSSParserImpl*>(mImpl)->
14207 ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
14208 aSheetPrincipal, aDeclaration, aChanged,
14209 aIsImportant, aIsSVGMode);
14210 }
14212 nsresult
14213 nsCSSParser::ParseVariable(const nsAString& aVariableName,
14214 const nsAString& aPropValue,
14215 nsIURI* aSheetURI,
14216 nsIURI* aBaseURI,
14217 nsIPrincipal* aSheetPrincipal,
14218 css::Declaration* aDeclaration,
14219 bool* aChanged,
14220 bool aIsImportant)
14221 {
14222 return static_cast<CSSParserImpl*>(mImpl)->
14223 ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI,
14224 aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
14225 }
14227 void
14228 nsCSSParser::ParseMediaList(const nsSubstring& aBuffer,
14229 nsIURI* aURI,
14230 uint32_t aLineNumber,
14231 nsMediaList* aMediaList,
14232 bool aHTMLMode)
14233 {
14234 static_cast<CSSParserImpl*>(mImpl)->
14235 ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode);
14236 }
14238 bool
14239 nsCSSParser::ParseColorString(const nsSubstring& aBuffer,
14240 nsIURI* aURI,
14241 uint32_t aLineNumber,
14242 nsCSSValue& aValue)
14243 {
14244 return static_cast<CSSParserImpl*>(mImpl)->
14245 ParseColorString(aBuffer, aURI, aLineNumber, aValue);
14246 }
14248 nsresult
14249 nsCSSParser::ParseSelectorString(const nsSubstring& aSelectorString,
14250 nsIURI* aURI,
14251 uint32_t aLineNumber,
14252 nsCSSSelectorList** aSelectorList)
14253 {
14254 return static_cast<CSSParserImpl*>(mImpl)->
14255 ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList);
14256 }
14258 already_AddRefed<nsCSSKeyframeRule>
14259 nsCSSParser::ParseKeyframeRule(const nsSubstring& aBuffer,
14260 nsIURI* aURI,
14261 uint32_t aLineNumber)
14262 {
14263 return static_cast<CSSParserImpl*>(mImpl)->
14264 ParseKeyframeRule(aBuffer, aURI, aLineNumber);
14265 }
14267 bool
14268 nsCSSParser::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
14269 nsIURI* aURI,
14270 uint32_t aLineNumber,
14271 InfallibleTArray<float>& aSelectorList)
14272 {
14273 return static_cast<CSSParserImpl*>(mImpl)->
14274 ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber,
14275 aSelectorList);
14276 }
14278 bool
14279 nsCSSParser::EvaluateSupportsDeclaration(const nsAString& aProperty,
14280 const nsAString& aValue,
14281 nsIURI* aDocURL,
14282 nsIURI* aBaseURL,
14283 nsIPrincipal* aDocPrincipal)
14284 {
14285 return static_cast<CSSParserImpl*>(mImpl)->
14286 EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL,
14287 aDocPrincipal);
14288 }
14290 bool
14291 nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition,
14292 nsIURI* aDocURL,
14293 nsIURI* aBaseURL,
14294 nsIPrincipal* aDocPrincipal)
14295 {
14296 return static_cast<CSSParserImpl*>(mImpl)->
14297 EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL, aDocPrincipal);
14298 }
14300 bool
14301 nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue,
14302 VariableEnumFunc aFunc,
14303 void* aData)
14304 {
14305 return static_cast<CSSParserImpl*>(mImpl)->
14306 EnumerateVariableReferences(aPropertyValue, aFunc, aData);
14307 }
14309 bool
14310 nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue,
14311 const CSSVariableValues* aVariables,
14312 nsString& aResult,
14313 nsCSSTokenSerializationType& aFirstToken,
14314 nsCSSTokenSerializationType& aLastToken)
14315 {
14316 return static_cast<CSSParserImpl*>(mImpl)->
14317 ResolveVariableValue(aPropertyValue, aVariables,
14318 aResult, aFirstToken, aLastToken);
14319 }
14321 void
14322 nsCSSParser::ParsePropertyWithVariableReferences(
14323 nsCSSProperty aPropertyID,
14324 nsCSSProperty aShorthandPropertyID,
14325 const nsAString& aValue,
14326 const CSSVariableValues* aVariables,
14327 nsRuleData* aRuleData,
14328 nsIURI* aDocURL,
14329 nsIURI* aBaseURL,
14330 nsIPrincipal* aDocPrincipal,
14331 nsCSSStyleSheet* aSheet,
14332 uint32_t aLineNumber,
14333 uint32_t aLineOffset)
14334 {
14335 static_cast<CSSParserImpl*>(mImpl)->
14336 ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID,
14337 aValue, aVariables, aRuleData, aDocURL,
14338 aBaseURL, aDocPrincipal, aSheet,
14339 aLineNumber, aLineOffset);
14340 }