Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 et sw=2 tw=78: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
8 :
9 : #include "nsCSSParser.h"
10 :
11 : #include "mozilla/Attributes.h"
12 : #include "mozilla/ArrayUtils.h"
13 : #include "mozilla/DebugOnly.h"
14 : #include "mozilla/Maybe.h"
15 : #include "mozilla/Move.h"
16 : #include "mozilla/MathAlgorithms.h"
17 : #include "mozilla/TypedEnumBits.h"
18 : #include "mozilla/Unused.h"
19 :
20 : #include <algorithm> // for std::stable_sort
21 : #include <limits> // for std::numeric_limits
22 :
23 : #include "nsAlgorithm.h"
24 : #include "nsCSSProps.h"
25 : #include "nsCSSKeywords.h"
26 : #include "nsCSSScanner.h"
27 : #include "mozilla/css/ErrorReporter.h"
28 : #include "mozilla/css/Loader.h"
29 : #include "mozilla/css/StyleRule.h"
30 : #include "mozilla/css/ImportRule.h"
31 : #include "mozilla/css/URLMatchingFunction.h"
32 : #include "nsCSSRules.h"
33 : #include "nsCSSCounterStyleRule.h"
34 : #include "nsCSSFontFaceRule.h"
35 : #include "mozilla/css/NameSpaceRule.h"
36 : #include "nsTArray.h"
37 : #include "mozilla/StyleSheetInlines.h"
38 : #include "mozilla/css/Declaration.h"
39 : #include "nsStyleConsts.h"
40 : #include "nsNetUtil.h"
41 : #include "nsCOMPtr.h"
42 : #include "nsString.h"
43 : #include "nsIAtom.h"
44 : #include "nsColor.h"
45 : #include "nsCSSPseudoClasses.h"
46 : #include "nsCSSPseudoElements.h"
47 : #include "nsCSSAnonBoxes.h"
48 : #include "nsNameSpaceManager.h"
49 : #include "nsXMLNameSpaceMap.h"
50 : #include "nsError.h"
51 : #include "nsMediaList.h"
52 : #include "nsStyleUtil.h"
53 : #include "nsIPrincipal.h"
54 : #include "mozilla/Sprintf.h"
55 : #include "nsContentUtils.h"
56 : #include "nsAutoPtr.h"
57 : #include "CSSCalc.h"
58 : #include "nsMediaFeatures.h"
59 : #include "nsLayoutUtils.h"
60 : #include "mozilla/LookAndFeel.h"
61 : #include "mozilla/Preferences.h"
62 : #include "nsRuleData.h"
63 : #include "mozilla/CSSVariableValues.h"
64 : #include "mozilla/dom/AnimationEffectReadOnlyBinding.h"
65 : #include "mozilla/dom/URL.h"
66 : #include "gfxFontFamilyList.h"
67 :
68 : using namespace mozilla;
69 : using namespace mozilla::css;
70 :
71 : typedef nsCSSProps::KTableEntry KTableEntry;
72 :
73 : // pref-backed bool values (hooked up in nsCSSParser::Startup)
74 : static bool sOpentypeSVGEnabled;
75 : static bool sWebkitPrefixedAliasesEnabled;
76 : static bool sWebkitDevicePixelRatioEnabled;
77 : static bool sMozGradientsEnabled;
78 : static bool sControlCharVisibility;
79 : static bool sFramesTimingFunctionEnabled;
80 :
81 : const uint32_t
82 : nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
83 : #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \
84 : stylestruct_, stylestructoffset_, animtype_) \
85 : parsevariant_,
86 : #define CSS_PROP_LIST_INCLUDE_LOGICAL
87 : #include "nsCSSPropList.h"
88 : #undef CSS_PROP_LIST_INCLUDE_LOGICAL
89 : #undef CSS_PROP
90 : };
91 :
92 : // Maximum number of repetitions for the repeat() function
93 : // in the grid-template-rows and grid-template-columns properties,
94 : // to limit high memory usage from small stylesheets.
95 : // Must be a positive integer. Should be large-ish.
96 : #define GRID_TEMPLATE_MAX_REPETITIONS 10000
97 :
98 : // End-of-array marker for mask arguments to ParseBitmaskValues
99 : #define MASK_END_VALUE (-1)
100 :
101 : enum class CSSParseResult : int32_t {
102 : // Parsed something successfully:
103 : Ok,
104 : // Did not find what we were looking for, but did not consume any token:
105 : NotFound,
106 : // Unexpected token or token value, too late for UngetToken() to be enough:
107 : Error
108 : };
109 :
110 : enum class GridTrackSizeFlags {
111 : eDefaultTrackSize = 0x0,
112 : eFixedTrackSize = 0x1, // parse a <fixed-size> instead of <track-size>
113 : };
114 0 : MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackSizeFlags)
115 :
116 : enum class GridTrackListFlags {
117 : eDefaultTrackList = 0x0, // parse a <track-list>
118 : eExplicitTrackList = 0x1, // parse an <explicit-track-list> instead
119 : };
120 0 : MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackListFlags)
121 :
122 : namespace {
123 :
124 : // Rule processing function
125 : typedef void (* RuleAppendFunc) (css::Rule* aRule, void* aData);
126 : static void AssignRuleToPointer(css::Rule* aRule, void* aPointer);
127 : static void AppendRuleToSheet(css::Rule* aRule, void* aParser);
128 :
129 14916 : struct CSSParserInputState {
130 : nsCSSScannerPosition mPosition;
131 : nsCSSToken mToken;
132 : bool mHavePushBack;
133 : };
134 :
135 : static_assert(css::eAuthorSheetFeatures == 0 &&
136 : css::eUserSheetFeatures == 1 &&
137 : css::eAgentSheetFeatures == 2,
138 : "sheet parsing mode constants won't fit "
139 : "in CSSParserImpl::mParsingMode");
140 :
141 : // Your basic top-down recursive descent style parser
142 : // The exposed methods and members of this class are precisely those
143 : // needed by nsCSSParser, far below.
144 : class CSSParserImpl {
145 : public:
146 : CSSParserImpl();
147 : ~CSSParserImpl();
148 :
149 : nsresult SetStyleSheet(CSSStyleSheet* aSheet);
150 :
151 : nsIDocument* GetDocument();
152 :
153 : nsresult SetQuirkMode(bool aQuirkMode);
154 :
155 : nsresult SetChildLoader(mozilla::css::Loader* aChildLoader);
156 :
157 : // Clears everything set by the above Set*() functions.
158 : void Reset();
159 :
160 : nsresult ParseSheet(const nsAString& aInput,
161 : nsIURI* aSheetURI,
162 : nsIURI* aBaseURI,
163 : nsIPrincipal* aSheetPrincipal,
164 : uint32_t aLineNumber,
165 : css::LoaderReusableStyleSheets* aReusableSheets);
166 :
167 : already_AddRefed<css::Declaration>
168 : ParseStyleAttribute(const nsAString& aAttributeValue,
169 : nsIURI* aDocURL,
170 : nsIURI* aBaseURL,
171 : nsIPrincipal* aNodePrincipal);
172 :
173 : nsresult ParseDeclarations(const nsAString& aBuffer,
174 : nsIURI* aSheetURL,
175 : nsIURI* aBaseURL,
176 : nsIPrincipal* aSheetPrincipal,
177 : css::Declaration* aDeclaration,
178 : bool* aChanged);
179 :
180 : nsresult ParseRule(const nsAString& aRule,
181 : nsIURI* aSheetURL,
182 : nsIURI* aBaseURL,
183 : nsIPrincipal* aSheetPrincipal,
184 : css::Rule** aResult);
185 :
186 : void ParseProperty(const nsCSSPropertyID aPropID,
187 : const nsAString& aPropValue,
188 : nsIURI* aSheetURL,
189 : nsIURI* aBaseURL,
190 : nsIPrincipal* aSheetPrincipal,
191 : css::Declaration* aDeclaration,
192 : bool* aChanged,
193 : bool aIsImportant,
194 : bool aIsSVGMode);
195 : void ParseLonghandProperty(const nsCSSPropertyID aPropID,
196 : const nsAString& aPropValue,
197 : nsIURI* aSheetURL,
198 : nsIURI* aBaseURL,
199 : nsIPrincipal* aSheetPrincipal,
200 : nsCSSValue& aValue);
201 :
202 : bool ParseTransformProperty(const nsAString& aPropValue,
203 : bool aDisallowRelativeValues,
204 : nsCSSValue& aResult);
205 :
206 : void ParseMediaList(const nsAString& aBuffer,
207 : nsIURI* aURL, // for error reporting
208 : uint32_t aLineNumber, // for error reporting
209 : nsMediaList* aMediaList);
210 :
211 : bool ParseSourceSizeList(const nsAString& aBuffer,
212 : nsIURI* aURI, // for error reporting
213 : uint32_t aLineNumber, // for error reporting
214 : InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
215 : InfallibleTArray<nsCSSValue>& aValues);
216 :
217 : void ParseVariable(const nsAString& aVariableName,
218 : const nsAString& aPropValue,
219 : nsIURI* aSheetURL,
220 : nsIURI* aBaseURL,
221 : nsIPrincipal* aSheetPrincipal,
222 : css::Declaration* aDeclaration,
223 : bool* aChanged,
224 : bool aIsImportant);
225 :
226 : bool ParseFontFamilyListString(const nsAString& aBuffer,
227 : nsIURI* aURL, // for error reporting
228 : uint32_t aLineNumber, // for error reporting
229 : nsCSSValue& aValue);
230 :
231 : bool ParseColorString(const nsAString& aBuffer,
232 : nsIURI* aURL, // for error reporting
233 : uint32_t aLineNumber, // for error reporting
234 : nsCSSValue& aValue,
235 : bool aSuppressErrors /* false */);
236 :
237 : bool ParseMarginString(const nsAString& aBuffer,
238 : nsIURI* aURL, // for error reporting
239 : uint32_t aLineNumber, // for error reporting
240 : nsCSSValue& aValue,
241 : bool aSuppressErrors /* false */);
242 :
243 : nsresult ParseSelectorString(const nsAString& aSelectorString,
244 : nsIURI* aURL, // for error reporting
245 : uint32_t aLineNumber, // for error reporting
246 : nsCSSSelectorList **aSelectorList);
247 :
248 : already_AddRefed<nsCSSKeyframeRule>
249 : ParseKeyframeRule(const nsAString& aBuffer,
250 : nsIURI* aURL,
251 : uint32_t aLineNumber);
252 :
253 : bool ParseKeyframeSelectorString(const nsAString& aSelectorString,
254 : nsIURI* aURL, // for error reporting
255 : uint32_t aLineNumber, // for error reporting
256 : InfallibleTArray<float>& aSelectorList);
257 :
258 : bool EvaluateSupportsDeclaration(const nsAString& aProperty,
259 : const nsAString& aValue,
260 : nsIURI* aDocURL,
261 : nsIURI* aBaseURL,
262 : nsIPrincipal* aDocPrincipal);
263 :
264 : bool EvaluateSupportsCondition(const nsAString& aCondition,
265 : nsIURI* aDocURL,
266 : nsIURI* aBaseURL,
267 : nsIPrincipal* aDocPrincipal,
268 : SupportsParsingSettings aSettings
269 : = SupportsParsingSettings::Normal);
270 :
271 : already_AddRefed<nsIAtom> ParseCounterStyleName(const nsAString& aBuffer,
272 : nsIURI* aURL);
273 :
274 : bool ParseCounterDescriptor(nsCSSCounterDesc aDescID,
275 : const nsAString& aBuffer,
276 : nsIURI* aSheetURL,
277 : nsIURI* aBaseURL,
278 : nsIPrincipal* aSheetPrincipal,
279 : nsCSSValue& aValue);
280 :
281 : bool ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
282 : const nsAString& aBuffer,
283 : nsIURI* aSheetURL,
284 : nsIURI* aBaseURL,
285 : nsIPrincipal* aSheetPrincipal,
286 : nsCSSValue& aValue);
287 :
288 : bool IsValueValidForProperty(const nsCSSPropertyID aPropID,
289 : const nsAString& aPropValue);
290 :
291 : typedef nsCSSParser::VariableEnumFunc VariableEnumFunc;
292 :
293 : /**
294 : * Parses a CSS token stream value and invokes a callback function for each
295 : * variable reference that is encountered.
296 : *
297 : * @param aPropertyValue The CSS token stream value.
298 : * @param aFunc The callback function to invoke; its parameters are the
299 : * variable name found and the aData argument passed in to this function.
300 : * @param aData User data to pass in to the callback.
301 : * @return Whether aPropertyValue could be parsed as a valid CSS token stream
302 : * value (e.g., without syntactic errors in variable references).
303 : */
304 : bool EnumerateVariableReferences(const nsAString& aPropertyValue,
305 : VariableEnumFunc aFunc,
306 : void* aData);
307 :
308 : /**
309 : * Parses aPropertyValue as a CSS token stream value and resolves any
310 : * variable references using the variables in aVariables.
311 : *
312 : * @param aPropertyValue The CSS token stream value.
313 : * @param aVariables The set of variable values to use when resolving variable
314 : * references.
315 : * @param aResult Out parameter that gets the resolved value.
316 : * @param aFirstToken Out parameter that gets the type of the first token in
317 : * aResult.
318 : * @param aLastToken Out parameter that gets the type of the last token in
319 : * aResult.
320 : * @return Whether aResult could be parsed successfully and variable reference
321 : * substitution succeeded.
322 : */
323 : bool ResolveVariableValue(const nsAString& aPropertyValue,
324 : const CSSVariableValues* aVariables,
325 : nsString& aResult,
326 : nsCSSTokenSerializationType& aFirstToken,
327 : nsCSSTokenSerializationType& aLastToken);
328 :
329 : /**
330 : * Parses a string as a CSS token stream value for particular property,
331 : * resolving any variable references. The parsed property value is stored
332 : * in the specified nsRuleData object. If aShorthandPropertyID has a value
333 : * other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
334 : * otherwise, aPropertyID will be parsed. Either way, only aPropertyID,
335 : * a longhand property, will be copied over to the rule data.
336 : *
337 : * If the property cannot be parsed, it will be treated as if 'initial' or
338 : * 'inherit' were specified, for non-inherited and inherited properties
339 : * respectively.
340 : *
341 : * @param aPropertyID The ID of the longhand property whose value is to be
342 : * copied to the rule data.
343 : * @param aShorthandPropertyID The ID of the shorthand property to be parsed.
344 : * If a longhand property is to be parsed, aPropertyID is that property,
345 : * and aShorthandPropertyID must be eCSSProperty_UNKNOWN.
346 : * @param aValue The CSS token stream value.
347 : * @param aVariables The set of variable values to use when resolving variable
348 : * references.
349 : * @param aRuleData The rule data object into which parsed property value for
350 : * aPropertyID will be stored.
351 : */
352 : void ParsePropertyWithVariableReferences(nsCSSPropertyID aPropertyID,
353 : nsCSSPropertyID aShorthandPropertyID,
354 : const nsAString& aValue,
355 : const CSSVariableValues* aVariables,
356 : nsRuleData* aRuleData,
357 : nsIURI* aDocURL,
358 : nsIURI* aBaseURL,
359 : nsIPrincipal* aDocPrincipal,
360 : CSSStyleSheet* aSheet,
361 : uint32_t aLineNumber,
362 : uint32_t aLineOffset);
363 :
364 16350 : bool AgentRulesEnabled() const {
365 16350 : return mParsingMode == css::eAgentSheetFeatures;
366 : }
367 16258 : bool ChromeRulesEnabled() const {
368 16258 : return mIsChrome;
369 : }
370 :
371 16258 : CSSEnabledState EnabledState() const {
372 : static_assert(int(CSSEnabledState::eForAllContent) == 0,
373 : "CSSEnabledState::eForAllContent should be zero for "
374 : "this bitfield to work");
375 16258 : CSSEnabledState enabledState = CSSEnabledState::eForAllContent;
376 16258 : if (AgentRulesEnabled()) {
377 6578 : enabledState |= CSSEnabledState::eInUASheets;
378 : }
379 16258 : if (ChromeRulesEnabled()) {
380 10274 : enabledState |= CSSEnabledState::eInChrome;
381 : }
382 16258 : return enabledState;
383 : }
384 :
385 6472 : nsCSSPropertyID LookupEnabledProperty(const nsAString& aProperty) {
386 6472 : return nsCSSProps::LookupProperty(aProperty, EnabledState());
387 : }
388 :
389 : protected:
390 : class nsAutoParseCompoundProperty;
391 : friend class nsAutoParseCompoundProperty;
392 :
393 : class nsAutoFailingSupportsRule;
394 : friend class nsAutoFailingSupportsRule;
395 :
396 : class nsAutoSuppressErrors;
397 : friend class nsAutoSuppressErrors;
398 :
399 : void AppendRule(css::Rule* aRule);
400 : friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule
401 :
402 : /**
403 : * This helper class automatically calls SetParsingCompoundProperty in its
404 : * constructor and takes care of resetting it to false in its destructor.
405 : */
406 : class nsAutoParseCompoundProperty {
407 : public:
408 560 : explicit nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
409 : {
410 560 : NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
411 : "already parsing compound property");
412 560 : NS_ASSERTION(aParser, "Null parser?");
413 560 : aParser->SetParsingCompoundProperty(true);
414 560 : }
415 :
416 560 : ~nsAutoParseCompoundProperty()
417 560 : {
418 560 : mParser->SetParsingCompoundProperty(false);
419 560 : }
420 : private:
421 : CSSParserImpl* mParser;
422 : };
423 :
424 : /**
425 : * This helper class conditionally sets mInFailingSupportsRule to
426 : * true if aCondition = false, and resets it to its original value in its
427 : * destructor. If we are already somewhere within a failing @supports
428 : * rule, passing in aCondition = true does not change mInFailingSupportsRule.
429 : */
430 : class nsAutoFailingSupportsRule {
431 : public:
432 0 : nsAutoFailingSupportsRule(CSSParserImpl* aParser,
433 : bool aCondition)
434 0 : : mParser(aParser),
435 0 : mOriginalValue(aParser->mInFailingSupportsRule)
436 : {
437 0 : if (!aCondition) {
438 0 : mParser->mInFailingSupportsRule = true;
439 : }
440 0 : }
441 :
442 0 : ~nsAutoFailingSupportsRule()
443 0 : {
444 0 : mParser->mInFailingSupportsRule = mOriginalValue;
445 0 : }
446 :
447 : private:
448 : CSSParserImpl* mParser;
449 : bool mOriginalValue;
450 : };
451 :
452 : /**
453 : * Auto class to set aParser->mSuppressErrors to the specified value
454 : * and restore it to its original value later.
455 : */
456 : class nsAutoSuppressErrors {
457 : public:
458 6839 : explicit nsAutoSuppressErrors(CSSParserImpl* aParser,
459 : bool aSuppressErrors = true)
460 6839 : : mParser(aParser),
461 6839 : mOriginalValue(aParser->mSuppressErrors)
462 : {
463 6839 : mParser->mSuppressErrors = aSuppressErrors;
464 6839 : }
465 :
466 6839 : ~nsAutoSuppressErrors()
467 6839 : {
468 6839 : mParser->mSuppressErrors = mOriginalValue;
469 6839 : }
470 :
471 : private:
472 : CSSParserImpl* mParser;
473 : bool mOriginalValue;
474 : };
475 :
476 : /**
477 : * RAII class to set aParser->mInSupportsCondition to true and restore it
478 : * to false later.
479 : */
480 : class MOZ_RAII nsAutoInSupportsCondition
481 : {
482 : public:
483 0 : explicit nsAutoInSupportsCondition(CSSParserImpl* aParser)
484 0 : : mParser(aParser)
485 : {
486 0 : MOZ_ASSERT(!aParser->mInSupportsCondition,
487 : "nsAutoInSupportsCondition is not designed to be used "
488 : "re-entrantly");
489 0 : mParser->mInSupportsCondition = true;
490 0 : }
491 :
492 0 : ~nsAutoInSupportsCondition()
493 0 : {
494 0 : mParser->mInSupportsCondition = false;
495 0 : }
496 :
497 : private:
498 : CSSParserImpl* const mParser;
499 : };
500 :
501 : // the caller must hold on to aString until parsing is done
502 : void InitScanner(nsCSSScanner& aScanner,
503 : css::ErrorReporter& aReporter,
504 : nsIURI* aSheetURI, nsIURI* aBaseURI,
505 : nsIPrincipal* aSheetPrincipal);
506 : void ReleaseScanner(void);
507 :
508 : /**
509 : * Saves the current input state, which includes any currently pushed
510 : * back token, and the current position of the scanner.
511 : */
512 : void SaveInputState(CSSParserInputState& aState);
513 :
514 : /**
515 : * Restores the saved input state by pushing back any saved pushback
516 : * token and calling RestoreSavedPosition on the scanner.
517 : */
518 : void RestoreSavedInputState(const CSSParserInputState& aState);
519 :
520 : bool GetToken(bool aSkipWS);
521 : void UngetToken();
522 : bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum);
523 3056 : void AssertNextTokenAt(uint32_t aLine, uint32_t aCol)
524 : {
525 : // Beware that this method will call GetToken/UngetToken (in
526 : // GetNextTokenLocation) in DEBUG builds, but not in non-DEBUG builds.
527 6112 : DebugOnly<uint32_t> lineAfter, colAfter;
528 3056 : MOZ_ASSERT(GetNextTokenLocation(true, &lineAfter, &colAfter) &&
529 : lineAfter == aLine && colAfter == aCol,
530 : "shouldn't have consumed any tokens");
531 3056 : }
532 :
533 : bool ExpectSymbol(char16_t aSymbol, bool aSkipWS);
534 : bool ExpectEndProperty();
535 : bool CheckEndProperty();
536 : nsAString* NextIdent();
537 :
538 : // returns true when the stop symbol is found, and false for EOF
539 : bool SkipUntil(char16_t aStopSymbol);
540 : void SkipUntilOneOf(const char16_t* aStopSymbolChars);
541 : // For each character in aStopSymbolChars from the end of the array
542 : // to the start, calls SkipUntil with that character.
543 : typedef AutoTArray<char16_t, 16> StopSymbolCharStack;
544 : void SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars);
545 : // returns true if the stop symbol or EOF is found, and false for an
546 : // unexpected ')', ']' or '}'; this not safe to call outside variable
547 : // resolution, as it doesn't handle mismatched content
548 : bool SkipBalancedContentUntil(char16_t aStopSymbol);
549 :
550 : void SkipRuleSet(bool aInsideBraces);
551 : bool SkipAtRule(bool aInsideBlock);
552 : MOZ_MUST_USE bool SkipDeclaration(bool aCheckForBraces);
553 :
554 : void PushGroup(css::GroupRule* aRule);
555 : void PopGroup();
556 :
557 : bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData,
558 : bool aInsideBraces = false);
559 : bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData,
560 : bool aInAtRule);
561 : bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
562 : bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
563 : bool ParseURLOrString(nsString& aURL);
564 : bool GatherMedia(nsMediaList* aMedia, bool aInAtRule);
565 :
566 : enum eMediaQueryType { eMediaQueryNormal,
567 : // Parsing an at rule
568 : eMediaQueryAtRule,
569 : // Attempt to consume a single media-condition and
570 : // stop. Note that the spec defines "expression and/or
571 : // expression" as one condition but "expression,
572 : // expression" as two.
573 : eMediaQuerySingleCondition };
574 : bool ParseMediaQuery(eMediaQueryType aMode, nsMediaQuery **aQuery,
575 : bool *aHitStop);
576 : bool ParseMediaQueryExpression(nsMediaQuery* aQuery);
577 : void ProcessImport(const nsString& aURLSpec,
578 : nsMediaList* aMedia,
579 : RuleAppendFunc aAppendFunc,
580 : void* aProcessData,
581 : uint32_t aLineNumber,
582 : uint32_t aColumnNumber);
583 : bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc,
584 : void* aProcessData);
585 : bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
586 : bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
587 : bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
588 : void ProcessNameSpace(const nsString& aPrefix,
589 : const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
590 : void* aProcessData,
591 : uint32_t aLineNumber, uint32_t aColumnNumber);
592 :
593 : bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
594 : bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
595 : void* aProcessData);
596 : bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule);
597 : bool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
598 : bool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
599 : nsCSSValue& aValue);
600 :
601 : bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
602 : bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData);
603 : already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule();
604 : bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList);
605 :
606 : bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData);
607 : bool ParseSupportsCondition(bool& aConditionMet);
608 : bool ParseSupportsConditionNegation(bool& aConditionMet);
609 : bool ParseSupportsConditionInParens(bool& aConditionMet);
610 : bool ParseSupportsMozBoolPrefName(bool& aConditionMet);
611 : bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet);
612 : bool ParseSupportsConditionTerms(bool& aConditionMet);
613 : enum SupportsConditionTermOperator { eAnd, eOr };
614 : bool ParseSupportsConditionTermsAfterOperator(
615 : bool& aConditionMet,
616 : SupportsConditionTermOperator aOperator);
617 :
618 : bool ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aProcessData);
619 : already_AddRefed<nsIAtom> ParseCounterStyleName(bool aForDefinition);
620 : bool ParseCounterStyleNameValue(nsCSSValue& aValue);
621 : bool ParseCounterDescriptor(nsCSSCounterStyleRule *aRule);
622 : bool ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
623 : nsCSSValue& aValue);
624 : bool ParseCounterRange(nsCSSValuePair& aPair);
625 :
626 : /**
627 : * Parses the current input stream for a CSS token stream value and resolves
628 : * any variable references using the variables in aVariables.
629 : *
630 : * @param aVariables The set of variable values to use when resolving variable
631 : * references.
632 : * @param aResult Out parameter that, if the function returns true, will be
633 : * replaced with the resolved value.
634 : * @return Whether aResult could be parsed successfully and variable reference
635 : * substitution succeeded.
636 : */
637 : bool ResolveValueWithVariableReferences(
638 : const CSSVariableValues* aVariables,
639 : nsString& aResult,
640 : nsCSSTokenSerializationType& aResultFirstToken,
641 : nsCSSTokenSerializationType& aResultLastToken);
642 : // Helper function for ResolveValueWithVariableReferences.
643 : bool ResolveValueWithVariableReferencesRec(
644 : nsString& aResult,
645 : nsCSSTokenSerializationType& aResultFirstToken,
646 : nsCSSTokenSerializationType& aResultLastToken,
647 : const CSSVariableValues* aVariables);
648 :
649 : enum nsSelectorParsingStatus {
650 : // we have parsed a selector and we saw a token that cannot be
651 : // part of a selector:
652 : eSelectorParsingStatus_Done,
653 : // we should continue parsing the selector:
654 : eSelectorParsingStatus_Continue,
655 : // we saw an unexpected token or token value,
656 : // or we saw end-of-file with an unfinished selector:
657 : eSelectorParsingStatus_Error
658 : };
659 : nsSelectorParsingStatus ParseIDSelector(int32_t& aDataMask,
660 : nsCSSSelector& aSelector);
661 :
662 : nsSelectorParsingStatus ParseClassSelector(int32_t& aDataMask,
663 : nsCSSSelector& aSelector);
664 :
665 : // aPseudoElement and aPseudoElementArgs are the location where
666 : // pseudo-elements (as opposed to pseudo-classes) are stored;
667 : // pseudo-classes are stored on aSelector. aPseudoElement and
668 : // aPseudoElementArgs must be non-null iff !aIsNegated.
669 : nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask,
670 : nsCSSSelector& aSelector,
671 : bool aIsNegated,
672 : nsIAtom** aPseudoElement,
673 : nsAtomList** aPseudoElementArgs,
674 : CSSPseudoElementType* aPseudoElementType);
675 :
676 : nsSelectorParsingStatus ParseAttributeSelector(int32_t& aDataMask,
677 : nsCSSSelector& aSelector);
678 :
679 : nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask,
680 : nsCSSSelector& aSelector,
681 : bool aIsNegated);
682 :
683 : nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
684 : CSSPseudoClassType aType);
685 :
686 : nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
687 : CSSPseudoClassType aType);
688 :
689 : nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
690 : CSSPseudoClassType aType);
691 :
692 : nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask,
693 : nsCSSSelector& aSelector);
694 :
695 : // If aStopChar is non-zero, the selector list is done when we hit
696 : // aStopChar. Otherwise, it's done when we hit EOF.
697 : bool ParseSelectorList(nsCSSSelectorList*& aListHead,
698 : char16_t aStopChar);
699 : bool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
700 : bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator);
701 :
702 : enum {
703 : eParseDeclaration_InBraces = 1 << 0,
704 : eParseDeclaration_AllowImportant = 1 << 1
705 : };
706 : enum nsCSSContextType {
707 : eCSSContext_General,
708 : eCSSContext_Page
709 : };
710 :
711 : already_AddRefed<css::Declaration>
712 : ParseDeclarationBlock(uint32_t aFlags,
713 : nsCSSContextType aContext = eCSSContext_General);
714 : bool ParseDeclaration(css::Declaration* aDeclaration,
715 : uint32_t aFlags,
716 : bool aMustCallValueAppended,
717 : bool* aChanged,
718 : nsCSSContextType aContext = eCSSContext_General);
719 :
720 : // A "prefix-aware" wrapper for nsCSSKeywords::LookupKeyword().
721 : // Use this instead of LookupKeyword() if you might be parsing an unprefixed
722 : // property (like "display") for which we emulate a vendor-prefixed value
723 : // (like "-webkit-box").
724 : nsCSSKeyword LookupKeywordPrefixAware(nsAString& aKeywordStr,
725 : const KTableEntry aKeywordTable[]);
726 :
727 : bool ParseProperty(nsCSSPropertyID aPropID);
728 : bool ParsePropertyByFunction(nsCSSPropertyID aPropID);
729 : CSSParseResult ParseSingleValueProperty(nsCSSValue& aValue,
730 : nsCSSPropertyID aPropID);
731 : bool ParseSingleValuePropertyByFunction(nsCSSValue& aValue,
732 : nsCSSPropertyID aPropID);
733 :
734 : // This is similar to ParseSingleValueProperty but only works for
735 : // properties that are parsed with ParseBoxProperties or
736 : // ParseGroupedBoxProperty.
737 : //
738 : // Only works with variants with the following flags:
739 : // A, C, H, K, L, N, P, CALC.
740 : CSSParseResult ParseBoxProperty(nsCSSValue& aValue,
741 : nsCSSPropertyID aPropID);
742 :
743 : enum PriorityParsingStatus {
744 : ePriority_None,
745 : ePriority_Important,
746 : ePriority_Error
747 : };
748 : PriorityParsingStatus ParsePriority();
749 :
750 : #ifdef MOZ_XUL
751 : bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs);
752 : #endif
753 :
754 : // Property specific parsing routines
755 : bool ParseImageLayers(const nsCSSPropertyID aTable[]);
756 :
757 : struct ImageLayersShorthandParseState {
758 : nsCSSValue& mColor;
759 : nsCSSValueList* mImage;
760 : nsCSSValuePairList* mRepeat;
761 : nsCSSValueList* mAttachment; // A property for background layer only
762 : nsCSSValueList* mClip;
763 : nsCSSValueList* mOrigin;
764 : nsCSSValueList* mPositionX;
765 : nsCSSValueList* mPositionY;
766 : nsCSSValuePairList* mSize;
767 : nsCSSValueList* mComposite; // A property for mask layer only
768 : nsCSSValueList* mMode; // A property for mask layer only
769 66 : ImageLayersShorthandParseState(
770 : nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
771 : nsCSSValueList* aAttachment, nsCSSValueList* aClip,
772 : nsCSSValueList* aOrigin,
773 : nsCSSValueList* aPositionX, nsCSSValueList* aPositionY,
774 : nsCSSValuePairList* aSize, nsCSSValueList* aComposite,
775 66 : nsCSSValueList* aMode) :
776 : mColor(aColor), mImage(aImage), mRepeat(aRepeat),
777 : mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
778 : mPositionX(aPositionX), mPositionY(aPositionY),
779 : mSize(aSize), mComposite(aComposite),
780 66 : mMode(aMode) {};
781 : };
782 :
783 : bool IsFunctionTokenValidForImageLayerImage(const nsCSSToken& aToken) const;
784 : bool ParseImageLayersItem(ImageLayersShorthandParseState& aState,
785 : const nsCSSPropertyID aTable[]);
786 :
787 : bool ParseValueList(nsCSSPropertyID aPropID); // a single value prop-id
788 : bool ParseImageLayerRepeat(nsCSSPropertyID aPropID);
789 : bool ParseImageLayerRepeatValues(nsCSSValuePair& aValue);
790 : bool ParseImageLayerPosition(const nsCSSPropertyID aTable[]);
791 : bool ParseImageLayerPositionCoord(nsCSSPropertyID aPropID, bool aIsHorizontal);
792 :
793 : // ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
794 : // which is still used by some properties. See ParsePositionValue
795 : // for the css3-background syntax.
796 : bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit,
797 : bool aAllowExplicitCenter = true); // deprecated
798 :
799 : // ParsePositionValue parses a CSS <position> value, which is used by
800 : // the 'background-position' property.
801 : bool ParsePositionValue(nsCSSValue& aOut);
802 : bool ParsePositionValueSeparateCoords(nsCSSValue& aOutX, nsCSSValue& aOutY);
803 :
804 : bool ParseImageLayerPositionCoordItem(nsCSSValue& aOut, bool aIsHorizontal);
805 : bool ParseImageLayerSize(nsCSSPropertyID aPropID);
806 : bool ParseImageLayerSizeValues(nsCSSValuePair& aOut);
807 : bool ParseBorderColor();
808 : bool ParseBorderColors(nsCSSPropertyID aProperty);
809 : void SetBorderImageInitialValues();
810 : bool ParseBorderImageRepeat(bool aAcceptsInherit);
811 : // If ParseBorderImageSlice returns false, aConsumedTokens indicates
812 : // whether or not any tokens were consumed (in other words, was the property
813 : // in error or just not present). If ParseBorderImageSlice returns true
814 : // aConsumedTokens is always true.
815 : bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens);
816 : bool ParseBorderImageWidth(bool aAcceptsInherit);
817 : bool ParseBorderImageOutset(bool aAcceptsInherit);
818 : bool ParseBorderImage();
819 : bool ParseBorderSpacing();
820 : bool ParseBorderSide(const nsCSSPropertyID aPropIDs[],
821 : bool aSetAllSides);
822 : bool ParseBorderStyle();
823 : bool ParseBorderWidth();
824 :
825 : bool ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask);
826 : bool ParseCalcAdditiveExpression(nsCSSValue& aValue,
827 : uint32_t& aVariantMask);
828 : bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
829 : uint32_t& aVariantMask,
830 : bool *aHadFinalWS);
831 : bool ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask);
832 : bool ParseContextProperties();
833 : bool RequireWhitespace();
834 :
835 : // For "flex" shorthand property, defined in CSS Flexbox spec
836 : bool ParseFlex();
837 : // For "flex-flow" shorthand property, defined in CSS Flexbox spec
838 : bool ParseFlexFlow();
839 :
840 : // CSS Grid
841 : bool ParseGridAutoFlow();
842 :
843 : // Parse a <line-names> expression.
844 : // If successful, either leave aValue untouched,
845 : // to indicate that we parsed the empty list,
846 : // or set it to a eCSSUnit_List of eCSSUnit_Ident.
847 : //
848 : // To parse an optional <line-names> (ie. if not finding an open bracket
849 : // is considered the same as an empty list),
850 : // treat CSSParseResult::NotFound the same as CSSParseResult::Ok.
851 : //
852 : // If aValue is already a eCSSUnit_List, append to that list.
853 : CSSParseResult ParseGridLineNames(nsCSSValue& aValue);
854 : bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr);
855 : bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue);
856 :
857 : CSSParseResult ParseGridTrackBreadth(nsCSSValue& aValue);
858 : // eFixedTrackSize in aFlags makes it parse a <fixed-size>.
859 : CSSParseResult ParseGridTrackSize(nsCSSValue& aValue,
860 : GridTrackSizeFlags aFlags = GridTrackSizeFlags::eDefaultTrackSize);
861 :
862 : bool ParseGridAutoColumnsRows(nsCSSPropertyID aPropID);
863 : bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr);
864 : bool ParseGridTrackRepeatIntro(bool aForSubgrid,
865 : int32_t* aRepetitions,
866 : Maybe<int32_t>* aRepeatAutoEnum);
867 :
868 : // Assuming a [ <line-names>? ] has already been parsed,
869 : // parse the rest of a <track-list>.
870 : //
871 : // This exists because [ <line-names>? ] is ambiguous in the 'grid-template'
872 : // shorthand: it can be either the start of a <track-list> (in
873 : // a <'grid-template-rows'>) or of the intertwined syntax that sets both
874 : // grid-template-rows and grid-template-areas.
875 : //
876 : // On success, |aValue| will be a list of odd length >= 3,
877 : // starting with a <line-names> (which is itself a list)
878 : // and alternating between that and <track-size>.
879 : bool ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
880 : const nsCSSValue& aFirstLineNames,
881 : GridTrackListFlags aFlags = GridTrackListFlags::eDefaultTrackList);
882 :
883 : bool ParseGridTrackList(nsCSSPropertyID aPropID,
884 : GridTrackListFlags aFlags = GridTrackListFlags::eDefaultTrackList);
885 : bool ParseGridTemplateColumnsRows(nsCSSPropertyID aPropID);
886 :
887 : // |aAreaIndices| is a lookup table to help us parse faster,
888 : // mapping area names to indices in |aResult.mNamedAreas|.
889 : bool ParseGridTemplateAreasLine(const nsAutoString& aInput,
890 : css::GridTemplateAreasValue* aResult,
891 : nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices);
892 : bool ParseGridTemplateAreas();
893 : bool ParseGridTemplateColumnsOrAutoFlow(bool aForGridShorthand);
894 : bool ParseGridTemplate(bool aForGridShorthand = false);
895 : bool ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames);
896 : bool ParseGrid();
897 : CSSParseResult ParseGridShorthandAutoProps(int32_t aAutoFlowAxis);
898 : bool ParseGridLine(nsCSSValue& aValue);
899 : bool ParseGridColumnRowStartEnd(nsCSSPropertyID aPropID);
900 : bool ParseGridColumnRow(nsCSSPropertyID aStartPropID,
901 : nsCSSPropertyID aEndPropID);
902 : bool ParseGridArea();
903 : bool ParseGridGap();
904 :
905 : bool ParseInitialLetter();
906 :
907 : // parsing 'align/justify-items/self' from the css-align spec
908 : bool ParseAlignJustifyPosition(nsCSSValue& aResult,
909 : const KTableEntry aTable[]);
910 : bool ParseJustifyItems();
911 : bool ParseAlignItems();
912 : bool ParseAlignJustifySelf(nsCSSPropertyID aPropID);
913 : // parsing 'align/justify-content' from the css-align spec
914 : bool ParseAlignJustifyContent(nsCSSPropertyID aPropID);
915 : bool ParsePlaceContent();
916 : bool ParsePlaceItems();
917 : bool ParsePlaceSelf();
918 :
919 : // for 'clip' and '-moz-image-region'
920 : bool ParseRect(nsCSSPropertyID aPropID);
921 : bool ParseColumns();
922 : bool ParseContain(nsCSSValue& aValue);
923 : bool ParseContent();
924 : bool ParseCounterData(nsCSSPropertyID aPropID);
925 : bool ParseCursor();
926 : bool ParseFont();
927 : bool ParseFontSynthesis(nsCSSValue& aValue);
928 : bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue);
929 : bool ParseFontVariantAlternates(nsCSSValue& aValue);
930 : bool MergeBitmaskValue(int32_t aNewValue, const int32_t aMasks[],
931 : int32_t& aMergedValue);
932 : bool ParseBitmaskValues(nsCSSValue& aValue,
933 : const KTableEntry aKeywordTable[],
934 : const int32_t aMasks[]);
935 : bool ParseFontVariantEastAsian(nsCSSValue& aValue);
936 : bool ParseFontVariantLigatures(nsCSSValue& aValue);
937 : bool ParseFontVariantNumeric(nsCSSValue& aValue);
938 : bool ParseFontVariant();
939 : bool ParseFontWeight(nsCSSValue& aValue);
940 : bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword, bool& aQuoted);
941 : bool ParseFamily(nsCSSValue& aValue);
942 : bool ParseFontFeatureSettings(nsCSSValue& aValue);
943 : bool ParseFontVariationSettings(nsCSSValue& aValue);
944 : bool ParseFontSrc(nsCSSValue& aValue);
945 : bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
946 : bool ParseFontRanges(nsCSSValue& aValue);
947 : bool ParseListStyle();
948 : bool ParseListStyleType(nsCSSValue& aValue);
949 : bool ParseMargin();
950 : bool ParseClipPath(nsCSSValue& aValue);
951 : bool ParseTransform(bool aIsPrefixed, nsCSSPropertyID aProperty,
952 : bool aDisallowRelativeValues = false);
953 : bool ParseObjectPosition();
954 : bool ParseOutline();
955 : bool ParseOverflow();
956 : bool ParsePadding();
957 : bool ParseQuotes();
958 : bool ParseTextAlign(nsCSSValue& aValue,
959 : const KTableEntry aTable[]);
960 : bool ParseTextAlign(nsCSSValue& aValue);
961 : bool ParseTextAlignLast(nsCSSValue& aValue);
962 : bool ParseTextDecoration();
963 : bool ParseTextDecorationLine(nsCSSValue& aValue);
964 : bool ParseTextEmphasis();
965 : bool ParseTextEmphasisPosition(nsCSSValue& aValue);
966 : bool ParseTextEmphasisStyle(nsCSSValue& aValue);
967 : bool ParseTextCombineUpright(nsCSSValue& aValue);
968 : bool ParseTextOverflow(nsCSSValue& aValue);
969 : bool ParseTouchAction(nsCSSValue& aValue);
970 :
971 : bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow);
972 : bool ParseShadowList(nsCSSPropertyID aProperty);
973 : bool ParseShapeOutside(nsCSSValue& aValue);
974 : bool ParseTransitionProperty();
975 : bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
976 : bool ParseTransitionTimingFunctionValueComponent(float& aComponent,
977 : char aStop,
978 : bool aIsXPoint);
979 : bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
980 : bool ParseTransitionFramesTimingFunctionValues(nsCSSValue& aValue);
981 : enum ParseAnimationOrTransitionShorthandResult {
982 : eParseAnimationOrTransitionShorthand_Values,
983 : eParseAnimationOrTransitionShorthand_Inherit,
984 : eParseAnimationOrTransitionShorthand_Error
985 : };
986 : ParseAnimationOrTransitionShorthandResult
987 : ParseAnimationOrTransitionShorthand(const nsCSSPropertyID* aProperties,
988 : const nsCSSValue* aInitialValues,
989 : nsCSSValue* aValues,
990 : size_t aNumProperties);
991 : bool ParseTransition();
992 : bool ParseAnimation();
993 : bool ParseWillChange();
994 :
995 : bool ParsePaint(nsCSSPropertyID aPropID);
996 : bool ParseDasharray();
997 : bool ParseMarker();
998 : bool ParsePaintOrder();
999 : bool ParseAll();
1000 : bool ParseScrollSnapType();
1001 : bool ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSPropertyID aPropID);
1002 : bool ParseScrollSnapDestination(nsCSSValue& aValue);
1003 : bool ParseScrollSnapCoordinate(nsCSSValue& aValue);
1004 : bool ParseWebkitTextStroke();
1005 :
1006 : /**
1007 : * Parses a variable value from a custom property declaration.
1008 : *
1009 : * @param aType Out parameter into which will be stored the type of variable
1010 : * value, indicating whether the parsed value was a token stream or one of
1011 : * the CSS-wide keywords.
1012 : * @param aValue Out parameter into which will be stored the token stream
1013 : * as a string, if the parsed custom property did take a token stream.
1014 : * @return Whether parsing succeeded.
1015 : */
1016 : bool ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
1017 : nsString& aValue);
1018 :
1019 : /**
1020 : * Parses a CSS variable value. This could be 'initial', 'inherit', 'unset'
1021 : * or a token stream, which may or may not include variable references.
1022 : *
1023 : * @param aType Out parameter into which the type of the variable value
1024 : * will be stored.
1025 : * @param aDropBackslash Out parameter indicating whether during variable
1026 : * value parsing there was a trailing backslash before EOF that must
1027 : * be dropped when serializing the variable value.
1028 : * @param aImpliedCharacters Out parameter appended to which will be any
1029 : * characters that were implied when encountering EOF and which
1030 : * must be included at the end of the serialized variable value.
1031 : * @param aFunc A callback function to invoke when a variable reference
1032 : * is encountered. May be null. Arguments are the variable name
1033 : * and the aData argument passed in to this function.
1034 : * @param User data to pass in to the callback.
1035 : * @return Whether parsing succeeded.
1036 : */
1037 : bool ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
1038 : bool* aDropBackslash,
1039 : nsString& aImpliedCharacters,
1040 : void (*aFunc)(const nsAString&, void*),
1041 : void* aData);
1042 :
1043 : /**
1044 : * Returns whether the scanner dropped a backslash just before EOF.
1045 : */
1046 : bool BackslashDropped();
1047 :
1048 : /**
1049 : * Calls AppendImpliedEOFCharacters on mScanner.
1050 : */
1051 : void AppendImpliedEOFCharacters(nsAString& aResult);
1052 :
1053 : // Reused utility parsing routines
1054 : void AppendValue(nsCSSPropertyID aPropID, const nsCSSValue& aValue);
1055 : bool ParseBoxProperties(const nsCSSPropertyID aPropIDs[]);
1056 : bool ParseGroupedBoxProperty(int32_t aVariantMask,
1057 : nsCSSValue& aValue,
1058 : uint32_t aRestrictions);
1059 : bool ParseBoxCornerRadius(const nsCSSPropertyID aPropID);
1060 : bool ParseBoxCornerRadiiInternals(nsCSSValue array[]);
1061 : bool ParseBoxCornerRadii(const nsCSSPropertyID aPropIDs[]);
1062 :
1063 : int32_t ParseChoice(nsCSSValue aValues[],
1064 : const nsCSSPropertyID aPropIDs[], int32_t aNumIDs);
1065 :
1066 : CSSParseResult ParseColor(nsCSSValue& aValue);
1067 :
1068 : template<typename ComponentType>
1069 : bool ParseRGBColor(ComponentType& aR,
1070 : ComponentType& aG,
1071 : ComponentType& aB,
1072 : ComponentType& aA);
1073 : bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
1074 : float& aOpacity);
1075 :
1076 : // The ParseColorOpacityAndCloseParen methods below attempt to parse an
1077 : // optional [ separator <alpha-value> ] expression, followed by a
1078 : // close-parenthesis, at the end of a css color function (e.g. "rgba()" or
1079 : // "hsla()"). If these functions simply encounter a close-parenthesis (without
1080 : // any [separator <alpha-value>]), they will still succeed (i.e. return true),
1081 : // with outparam 'aOpacity' set to a default opacity value (fully-opaque).
1082 : //
1083 : // The range of opacity component is [0, 255], and the default opacity value
1084 : // is 255 (fully-opaque) for this function.
1085 : bool ParseColorOpacityAndCloseParen(uint8_t& aOpacity,
1086 : char aSeparator);
1087 : // Similar to the previous one, but the range of opacity component is
1088 : // [0.0f, 1.0f] and the default opacity value is 1.0f (fully-opaque).
1089 : bool ParseColorOpacityAndCloseParen(float& aOpacity,
1090 : char aSeparator);
1091 :
1092 : // Parse a <number> color component. The range of color component is [0, 255].
1093 : // If |aSeparator| is provided, this function will also attempt to parse that
1094 : // character after parsing the color component.
1095 : bool ParseColorComponent(uint8_t& aComponent, const Maybe<char>& aSeparator);
1096 : // Similar to the previous one, but parse a <percentage> color component.
1097 : // The range of color component is [0.0f, 1.0f].
1098 : bool ParseColorComponent(float& aComponent, const Maybe<char>& aSeparator);
1099 :
1100 : // Parse a <hue> component.
1101 : // <hue> = <number> | <angle>
1102 : // The unit of outparam 'aAngle' is degree. Assume the unit to be degree if an
1103 : // unitless <number> is parsed.
1104 : bool ParseHue(float& aAngle);
1105 :
1106 : bool ParseEnum(nsCSSValue& aValue,
1107 : const KTableEntry aKeywordTable[]);
1108 :
1109 : // A special ParseEnum for the CSS Box Alignment properties that have
1110 : // 'baseline' values. In addition to the keywords in aKeywordTable, it also
1111 : // parses 'first baseline' and 'last baseline' as a single value.
1112 : // (aKeywordTable must contain 'baseline')
1113 : bool ParseAlignEnum(nsCSSValue& aValue, const KTableEntry aKeywordTable[]);
1114 :
1115 : // Variant parsing methods
1116 : CSSParseResult ParseVariant(nsCSSValue& aValue,
1117 : uint32_t aVariantMask,
1118 : const KTableEntry aKeywordTable[]);
1119 : CSSParseResult ParseVariantWithRestrictions(nsCSSValue& aValue,
1120 : int32_t aVariantMask,
1121 : const KTableEntry aKeywordTable[],
1122 : uint32_t aRestrictions);
1123 : CSSParseResult ParseNonNegativeVariant(nsCSSValue& aValue,
1124 : int32_t aVariantMask,
1125 : const KTableEntry aKeywordTable[]);
1126 : CSSParseResult ParseOneOrLargerVariant(nsCSSValue& aValue,
1127 : int32_t aVariantMask,
1128 : const KTableEntry aKeywordTable[]);
1129 :
1130 : // Variant parsing methods that are guaranteed to UngetToken any token
1131 : // consumed on failure
1132 :
1133 3864 : MOZ_MUST_USE bool ParseSingleTokenVariant(nsCSSValue& aValue,
1134 : int32_t aVariantMask,
1135 : const KTableEntry aKeywordTable[])
1136 : {
1137 3864 : MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS),
1138 : "use ParseVariant for variants in VARIANT_MULTIPLE_TOKENS");
1139 3864 : CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable);
1140 3864 : MOZ_ASSERT(result != CSSParseResult::Error);
1141 3864 : return result == CSSParseResult::Ok;
1142 : }
1143 439 : bool ParseSingleTokenNonNegativeVariant(nsCSSValue& aValue,
1144 : int32_t aVariantMask,
1145 : const KTableEntry aKeywordTable[])
1146 : {
1147 439 : MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS),
1148 : "use ParseNonNegativeVariant for variants in "
1149 : "VARIANT_MULTIPLE_TOKENS");
1150 : CSSParseResult result =
1151 439 : ParseNonNegativeVariant(aValue, aVariantMask, aKeywordTable);
1152 439 : MOZ_ASSERT(result != CSSParseResult::Error);
1153 439 : return result == CSSParseResult::Ok;
1154 : }
1155 3 : bool ParseSingleTokenOneOrLargerVariant(nsCSSValue& aValue,
1156 : int32_t aVariantMask,
1157 : const KTableEntry aKeywordTable[])
1158 : {
1159 3 : MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS),
1160 : "use ParseOneOrLargerVariant for variants in "
1161 : "VARIANT_MULTIPLE_TOKENS");
1162 : CSSParseResult result =
1163 3 : ParseOneOrLargerVariant(aValue, aVariantMask, aKeywordTable);
1164 3 : MOZ_ASSERT(result != CSSParseResult::Error);
1165 3 : return result == CSSParseResult::Ok;
1166 : }
1167 :
1168 : // Helpers for some common ParseSingleTokenNonNegativeVariant calls.
1169 344 : bool ParseNonNegativeInteger(nsCSSValue& aValue)
1170 : {
1171 344 : return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_INTEGER, nullptr);
1172 : }
1173 16 : bool ParseNonNegativeNumber(nsCSSValue& aValue)
1174 : {
1175 16 : return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_NUMBER, nullptr);
1176 : }
1177 :
1178 : // Helpers for some common ParseSingleTokenOneOrLargerVariant calls.
1179 0 : bool ParseOneOrLargerInteger(nsCSSValue& aValue)
1180 : {
1181 0 : return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_INTEGER, nullptr);
1182 : }
1183 0 : bool ParseOneOrLargerNumber(nsCSSValue& aValue)
1184 : {
1185 0 : return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_NUMBER, nullptr);
1186 : }
1187 :
1188 : // http://dev.w3.org/csswg/css-values/#custom-idents
1189 : // Parse an identifier that is none of:
1190 : // * a CSS-wide keyword
1191 : // * "default"
1192 : // * a keyword in |aExcludedKeywords|
1193 : // * a keyword in |aPropertyKTable|
1194 : //
1195 : // |aExcludedKeywords| is an array of nsCSSKeyword
1196 : // that ends with a eCSSKeyword_UNKNOWN marker.
1197 : //
1198 : // |aPropertyKTable| can be used if some of the keywords to exclude
1199 : // also appear in an existing nsCSSProps::KTableEntry,
1200 : // to avoid duplicating them.
1201 : bool ParseCustomIdent(nsCSSValue& aValue,
1202 : const nsAutoString& aIdentValue,
1203 : const nsCSSKeyword aExcludedKeywords[] = nullptr,
1204 : const nsCSSProps::KTableEntry aPropertyKTable[] = nullptr);
1205 : bool ParseCounter(nsCSSValue& aValue);
1206 : bool ParseAttr(nsCSSValue& aValue);
1207 : bool ParseSymbols(nsCSSValue& aValue);
1208 : bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL);
1209 : bool TranslateDimension(nsCSSValue& aValue, uint32_t aVariantMask,
1210 : float aNumber, const nsString& aUnit);
1211 : bool ParseImageOrientation(nsCSSValue& aAngle);
1212 : bool ParseImageRect(nsCSSValue& aImage);
1213 : bool ParseElement(nsCSSValue& aValue);
1214 : bool ParseColorStop(nsCSSValueGradient* aGradient);
1215 :
1216 : enum GradientParsingFlags {
1217 : eGradient_Repeating = 1 << 0, // repeating-{linear|radial}-gradient
1218 : eGradient_MozLegacy = 1 << 1, // -moz-{linear|radial}-gradient
1219 : eGradient_WebkitLegacy = 1 << 2, // -webkit-{linear|radial}-gradient
1220 :
1221 : // Mask to catch both "legacy" flags:
1222 : eGradient_AnyLegacy = eGradient_MozLegacy | eGradient_WebkitLegacy
1223 : };
1224 : bool ParseLinearGradient(nsCSSValue& aValue, uint8_t aFlags);
1225 : bool ParseRadialGradient(nsCSSValue& aValue, uint8_t aFlags);
1226 : bool IsLegacyGradientLine(const nsCSSTokenType& aType,
1227 : const nsString& aId);
1228 : bool ParseGradientColorStops(nsCSSValueGradient* aGradient,
1229 : nsCSSValue& aValue);
1230 :
1231 : // For the ancient "-webkit-gradient(linear|radial, ...)" syntax:
1232 : bool ParseWebkitGradientPointComponent(nsCSSValue& aComponent,
1233 : bool aIsHorizontal);
1234 : bool ParseWebkitGradientPoint(nsCSSValuePair& aPoint);
1235 : bool ParseWebkitGradientRadius(float& aRadius);
1236 : bool ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient);
1237 : bool ParseWebkitGradientColorStops(nsCSSValueGradient* aGradient);
1238 : void FinalizeLinearWebkitGradient(nsCSSValueGradient* aGradient,
1239 : const nsCSSValuePair& aStartPoint,
1240 : const nsCSSValuePair& aSecondPoint);
1241 : void FinalizeRadialWebkitGradient(nsCSSValueGradient* aGradient,
1242 : const nsCSSValuePair& aFirstCenter,
1243 : const nsCSSValuePair& aSecondCenter,
1244 : const float aFirstRadius,
1245 : const float aSecondRadius);
1246 : bool ParseWebkitGradient(nsCSSValue& aValue);
1247 :
1248 1120 : void SetParsingCompoundProperty(bool aBool) {
1249 1120 : mParsingCompoundProperty = aBool;
1250 1120 : }
1251 614 : bool IsParsingCompoundProperty(void) const {
1252 614 : return mParsingCompoundProperty;
1253 : }
1254 :
1255 : /* Functions for basic shapes */
1256 : bool ParseReferenceBoxAndBasicShape(nsCSSValue& aValue,
1257 : const KTableEntry aBoxKeywordTable[]);
1258 : bool ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens);
1259 : bool ParsePolygonFunction(nsCSSValue& aValue);
1260 : bool ParseCircleOrEllipseFunction(nsCSSKeyword, nsCSSValue& aValue);
1261 : bool ParseInsetFunction(nsCSSValue& aValue);
1262 : // We parse position values differently for basic-shape, by expanding defaults
1263 : // and replacing keywords with percentages
1264 : bool ParsePositionValueForBasicShape(nsCSSValue& aOut);
1265 :
1266 :
1267 : /* Functions for transform Parsing */
1268 : bool ParseSingleTransform(bool aIsPrefixed, bool aDisallowRelativeValues,
1269 : nsCSSValue& aValue);
1270 : bool ParseFunction(nsCSSKeyword aFunction, const uint32_t aAllowedTypes[],
1271 : uint32_t aVariantMaskAll, uint16_t aMinElems,
1272 : uint16_t aMaxElems, nsCSSValue &aValue);
1273 : bool ParseFunctionInternals(const uint32_t aVariantMask[],
1274 : uint32_t aVariantMaskAll,
1275 : uint16_t aMinElems,
1276 : uint16_t aMaxElems,
1277 : InfallibleTArray<nsCSSValue>& aOutput);
1278 :
1279 : /* Functions for transform-origin/perspective-origin Parsing */
1280 : bool ParseTransformOrigin(nsCSSPropertyID aProperty);
1281 :
1282 : /* Functions for filter parsing */
1283 : bool ParseFilter();
1284 : bool ParseSingleFilter(nsCSSValue* aValue);
1285 : bool ParseDropShadow(nsCSSValue* aValue);
1286 :
1287 : /* Find and return the namespace ID associated with aPrefix.
1288 : If aPrefix has not been declared in an @namespace rule, returns
1289 : kNameSpaceID_Unknown. */
1290 : int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
1291 :
1292 : /* Find the correct default namespace, and set it on aSelector. */
1293 : void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
1294 :
1295 : // Current token. The value is valid after calling GetToken and invalidated
1296 : // by UngetToken.
1297 : nsCSSToken mToken;
1298 :
1299 : // Our scanner.
1300 : nsCSSScanner* mScanner;
1301 :
1302 : // Our error reporter.
1303 : css::ErrorReporter* mReporter;
1304 :
1305 : // The URI to be used as a base for relative URIs.
1306 : nsCOMPtr<nsIURI> mBaseURI;
1307 :
1308 : // The URI to be used as an HTTP "Referer" and for error reporting.
1309 : nsCOMPtr<nsIURI> mSheetURI;
1310 :
1311 : // The principal of the sheet involved
1312 : nsCOMPtr<nsIPrincipal> mSheetPrincipal;
1313 :
1314 : // The sheet we're parsing into
1315 : RefPtr<CSSStyleSheet> mSheet;
1316 :
1317 : // Used for @import rules
1318 : css::Loader* mChildLoader; // not ref counted, it owns us
1319 :
1320 : // Any sheets we may reuse when parsing an @import.
1321 : css::LoaderReusableStyleSheets* mReusableSheets;
1322 :
1323 : // Sheet section we're in. This is used to enforce correct ordering of the
1324 : // various rule types (eg the fact that a @charset rule must come before
1325 : // anything else). Note that there are checks of similar things in various
1326 : // places in CSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
1327 : enum nsCSSSection {
1328 : eCSSSection_Charset,
1329 : eCSSSection_Import,
1330 : eCSSSection_NameSpace,
1331 : eCSSSection_General
1332 : };
1333 : nsCSSSection mSection;
1334 :
1335 : nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it
1336 :
1337 : // After an UngetToken is done this flag is true. The next call to
1338 : // GetToken clears the flag.
1339 : bool mHavePushBack : 1;
1340 :
1341 : // True if we are in quirks mode; false in standards or almost standards mode
1342 : bool mNavQuirkMode : 1;
1343 :
1344 : // True when the hashless color quirk applies.
1345 : bool mHashlessColorQuirk : 1;
1346 :
1347 : // True when the unitless length quirk applies.
1348 : bool mUnitlessLengthQuirk : 1;
1349 :
1350 : // True if we are in parsing rules for the chrome.
1351 : bool mIsChrome : 1;
1352 :
1353 : // True if we're parsing SVG presentation attributes
1354 : // These attributes allow non-calc lengths to be unitless (mapping to px)
1355 : bool mIsSVGMode : 1;
1356 :
1357 : // True if viewport units should be allowed.
1358 : bool mViewportUnitsEnabled : 1;
1359 :
1360 : // This flag is set when parsing a non-box shorthand; it's used to not apply
1361 : // some quirks during shorthand parsing
1362 : bool mParsingCompoundProperty : 1;
1363 :
1364 : // True if we are in the middle of parsing an @supports condition.
1365 : // This is used to avoid recording the input stream when variable references
1366 : // are encountered in a property declaration in the @supports condition.
1367 : bool mInSupportsCondition : 1;
1368 :
1369 : // True if we are somewhere within a @supports rule whose condition is
1370 : // false.
1371 : bool mInFailingSupportsRule : 1;
1372 :
1373 : // True if we will suppress all parse errors (except unexpected EOFs).
1374 : // This is used to prevent errors for declarations inside a failing
1375 : // @supports rule.
1376 : bool mSuppressErrors : 1;
1377 :
1378 : // True if any parsing of URL values requires a sheet principal to have
1379 : // been passed in the nsCSSScanner constructor. This is usually the case.
1380 : // It can be set to false, for example, when we create an nsCSSParser solely
1381 : // to parse a property value to test it for syntactic correctness. When
1382 : // false, an assertion that mSheetPrincipal is non-null is skipped. Should
1383 : // not be set to false if any nsCSSValues created during parsing can escape
1384 : // out of the parser.
1385 : bool mSheetPrincipalRequired;
1386 :
1387 : // Controls access to nonstandard style constructs that are not safe
1388 : // for use on the public Web but necessary in UA sheets and/or
1389 : // useful in user sheets.
1390 : css::SheetParsingMode mParsingMode;
1391 :
1392 : // This enum helps us track whether we've unprefixed "display: -webkit-box"
1393 : // (treating it as "display: flex") in an earlier declaration within a series
1394 : // of declarations. (This only impacts behavior if
1395 : // sWebkitPrefixedAliasesEnabled is true.)
1396 : enum WebkitBoxUnprefixState : uint8_t {
1397 : eNotParsingDecls, // We are *not* currently parsing a sequence of
1398 : // CSS declarations. (default state)
1399 :
1400 : // The next two enum values indicate that we *are* currently parsing a
1401 : // sequence of declarations (in ParseDeclarations or ParseDeclarationBlock)
1402 : // and...
1403 : eHaveNotUnprefixed, // ...we have not unprefixed 'display:-webkit-box' in
1404 : // this sequence of CSS declarations.
1405 : eHaveUnprefixed // ...we *have* unprefixed 'display:-webkit-box' earlier in
1406 : // this sequence of CSS declarations.
1407 : };
1408 : WebkitBoxUnprefixState mWebkitBoxUnprefixState;
1409 :
1410 : // Stack of rule groups; used for @media and such.
1411 : InfallibleTArray<RefPtr<css::GroupRule> > mGroupStack;
1412 :
1413 : // During the parsing of a property (which may be a shorthand), the data
1414 : // are stored in |mTempData|. (It is needed to ensure that parser
1415 : // errors cause the data to be ignored, and to ensure that a
1416 : // non-'!important' declaration does not override an '!important'
1417 : // one.)
1418 : nsCSSExpandedDataBlock mTempData;
1419 :
1420 : // All data from successfully parsed properties are placed into |mData|.
1421 : nsCSSExpandedDataBlock mData;
1422 :
1423 : public:
1424 : // Used from nsCSSParser constructors and destructors
1425 : CSSParserImpl* mNextFree;
1426 : };
1427 :
1428 0 : static void AssignRuleToPointer(css::Rule* aRule, void* aPointer)
1429 : {
1430 0 : css::Rule **pointer = static_cast<css::Rule**>(aPointer);
1431 0 : NS_ADDREF(*pointer = aRule);
1432 0 : }
1433 :
1434 3203 : static void AppendRuleToSheet(css::Rule* aRule, void* aParser)
1435 : {
1436 3203 : CSSParserImpl* parser = (CSSParserImpl*) aParser;
1437 3203 : parser->AppendRule(aRule);
1438 3203 : }
1439 :
1440 : #define REPORT_UNEXPECTED(msg_) \
1441 : { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_); }
1442 :
1443 : #define REPORT_UNEXPECTED_P(msg_, param_) \
1444 : { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_); }
1445 :
1446 : #define REPORT_UNEXPECTED_P_V(msg_, param_, value_) \
1447 : { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_, value_); }
1448 :
1449 : #define REPORT_UNEXPECTED_TOKEN(msg_) \
1450 : { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken); }
1451 :
1452 : #define REPORT_UNEXPECTED_TOKEN_CHAR(msg_, ch_) \
1453 : { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken, ch_); }
1454 :
1455 : #define REPORT_UNEXPECTED_EOF(lf_) \
1456 : mReporter->ReportUnexpectedEOF(#lf_)
1457 :
1458 : #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
1459 : mReporter->ReportUnexpectedEOF(ch_)
1460 :
1461 : #define OUTPUT_ERROR() \
1462 : mReporter->OutputError()
1463 :
1464 : #define OUTPUT_ERROR_WITH_POSITION(linenum_, lineoff_) \
1465 : mReporter->OutputError(linenum_, lineoff_)
1466 :
1467 : #define CLEAR_ERROR() \
1468 : mReporter->ClearError()
1469 :
1470 4 : CSSParserImpl::CSSParserImpl()
1471 : : mToken(),
1472 : mScanner(nullptr),
1473 : mReporter(nullptr),
1474 : mChildLoader(nullptr),
1475 : mReusableSheets(nullptr),
1476 : mSection(eCSSSection_Charset),
1477 : mNameSpaceMap(nullptr),
1478 : mHavePushBack(false),
1479 : mNavQuirkMode(false),
1480 : mHashlessColorQuirk(false),
1481 : mUnitlessLengthQuirk(false),
1482 : mIsChrome(false),
1483 : mIsSVGMode(false),
1484 : mViewportUnitsEnabled(true),
1485 : mParsingCompoundProperty(false),
1486 : mInSupportsCondition(false),
1487 : mInFailingSupportsRule(false),
1488 : mSuppressErrors(false),
1489 : mSheetPrincipalRequired(true),
1490 : mParsingMode(css::eAuthorSheetFeatures),
1491 : mWebkitBoxUnprefixState(eNotParsingDecls),
1492 4 : mNextFree(nullptr)
1493 : {
1494 4 : }
1495 :
1496 0 : CSSParserImpl::~CSSParserImpl()
1497 : {
1498 0 : mData.AssertInitialState();
1499 0 : mTempData.AssertInitialState();
1500 0 : }
1501 :
1502 : nsresult
1503 12085 : CSSParserImpl::SetStyleSheet(CSSStyleSheet* aSheet)
1504 : {
1505 12085 : if (aSheet != mSheet) {
1506 : // Switch to using the new sheet, if any
1507 120 : mGroupStack.Clear();
1508 120 : mSheet = aSheet;
1509 120 : if (mSheet) {
1510 60 : mNameSpaceMap = mSheet->GetNameSpaceMap();
1511 : } else {
1512 60 : mNameSpaceMap = nullptr;
1513 : }
1514 11965 : } else if (mSheet) {
1515 0 : mNameSpaceMap = mSheet->GetNameSpaceMap();
1516 : }
1517 :
1518 12085 : return NS_OK;
1519 : }
1520 :
1521 : nsIDocument*
1522 6571 : CSSParserImpl::GetDocument()
1523 : {
1524 6571 : if (!mSheet) {
1525 111 : return nullptr;
1526 : }
1527 6460 : return mSheet->GetAssociatedDocument();
1528 : }
1529 :
1530 : nsresult
1531 12325 : CSSParserImpl::SetQuirkMode(bool aQuirkMode)
1532 : {
1533 12325 : mNavQuirkMode = aQuirkMode;
1534 12325 : return NS_OK;
1535 : }
1536 :
1537 : nsresult
1538 12325 : CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader)
1539 : {
1540 12325 : mChildLoader = aChildLoader; // not ref counted, it owns us
1541 12325 : return NS_OK;
1542 : }
1543 :
1544 : void
1545 12025 : CSSParserImpl::Reset()
1546 : {
1547 12025 : NS_ASSERTION(!mScanner, "resetting with scanner active");
1548 12025 : SetStyleSheet(nullptr);
1549 12025 : SetQuirkMode(false);
1550 12025 : SetChildLoader(nullptr);
1551 12025 : }
1552 :
1553 : void
1554 2380 : CSSParserImpl::InitScanner(nsCSSScanner& aScanner,
1555 : css::ErrorReporter& aReporter,
1556 : nsIURI* aSheetURI, nsIURI* aBaseURI,
1557 : nsIPrincipal* aSheetPrincipal)
1558 : {
1559 2380 : NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
1560 2380 : NS_PRECONDITION(!mScanner, "already have scanner");
1561 :
1562 2380 : mScanner = &aScanner;
1563 2380 : mReporter = &aReporter;
1564 2380 : mScanner->SetErrorReporter(mReporter);
1565 :
1566 2380 : mBaseURI = aBaseURI;
1567 2380 : mSheetURI = aSheetURI;
1568 2380 : mSheetPrincipal = aSheetPrincipal;
1569 2380 : mHavePushBack = false;
1570 2380 : }
1571 :
1572 : void
1573 2380 : CSSParserImpl::ReleaseScanner()
1574 : {
1575 2380 : mScanner = nullptr;
1576 2380 : mReporter = nullptr;
1577 2380 : mBaseURI = nullptr;
1578 2380 : mSheetURI = nullptr;
1579 2380 : mSheetPrincipal = nullptr;
1580 2380 : }
1581 :
1582 : nsresult
1583 60 : CSSParserImpl::ParseSheet(const nsAString& aInput,
1584 : nsIURI* aSheetURI,
1585 : nsIURI* aBaseURI,
1586 : nsIPrincipal* aSheetPrincipal,
1587 : uint32_t aLineNumber,
1588 : css::LoaderReusableStyleSheets* aReusableSheets)
1589 : {
1590 60 : NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1591 60 : NS_PRECONDITION(aBaseURI, "need base URI");
1592 60 : NS_PRECONDITION(aSheetURI, "need sheet URI");
1593 60 : NS_PRECONDITION(mSheet, "Must have sheet to parse into");
1594 60 : NS_ENSURE_STATE(mSheet);
1595 :
1596 : #ifdef DEBUG
1597 60 : nsIURI* uri = mSheet->GetSheetURI();
1598 : bool equal;
1599 60 : NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
1600 : "Sheet URI does not match passed URI");
1601 60 : NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
1602 : &equal)) &&
1603 : equal,
1604 : "Sheet principal does not match passed principal");
1605 : #endif
1606 :
1607 120 : nsCSSScanner scanner(aInput, aLineNumber);
1608 120 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1609 60 : InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1610 :
1611 60 : int32_t ruleCount = mSheet->StyleRuleCount();
1612 60 : if (0 < ruleCount) {
1613 0 : const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1);
1614 0 : if (lastRule) {
1615 0 : switch (lastRule->GetType()) {
1616 : case css::Rule::CHARSET_RULE:
1617 : case css::Rule::IMPORT_RULE:
1618 0 : mSection = eCSSSection_Import;
1619 0 : break;
1620 : case css::Rule::NAMESPACE_RULE:
1621 0 : mSection = eCSSSection_NameSpace;
1622 0 : break;
1623 : default:
1624 0 : mSection = eCSSSection_General;
1625 0 : break;
1626 : }
1627 : }
1628 : }
1629 : else {
1630 60 : mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
1631 : }
1632 :
1633 60 : mParsingMode = mSheet->ParsingMode();
1634 60 : mIsChrome = dom::IsChromeURI(aSheetURI);
1635 60 : mReusableSheets = aReusableSheets;
1636 :
1637 60 : nsCSSToken* tk = &mToken;
1638 : for (;;) {
1639 : // Get next non-whitespace token
1640 6208 : if (!GetToken(true)) {
1641 60 : OUTPUT_ERROR();
1642 60 : break;
1643 : }
1644 3074 : if (eCSSToken_HTMLComment == tk->mType) {
1645 0 : continue; // legal here only
1646 : }
1647 3074 : if (eCSSToken_AtKeyword == tk->mType) {
1648 286 : ParseAtRule(AppendRuleToSheet, this, false);
1649 286 : continue;
1650 : }
1651 2788 : UngetToken();
1652 2788 : if (ParseRuleSet(AppendRuleToSheet, this)) {
1653 2788 : mSection = eCSSSection_General;
1654 : }
1655 : }
1656 60 : ReleaseScanner();
1657 :
1658 60 : mParsingMode = css::eAuthorSheetFeatures;
1659 60 : mIsChrome = false;
1660 60 : mReusableSheets = nullptr;
1661 :
1662 60 : return NS_OK;
1663 : }
1664 :
1665 : /**
1666 : * Determines whether the identifier contained in the given string is a
1667 : * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
1668 : */
1669 : static bool
1670 0 : NonMozillaVendorIdentifier(const nsAString& ident)
1671 : {
1672 0 : return (ident.First() == char16_t('-') &&
1673 0 : !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
1674 0 : ident.First() == char16_t('_');
1675 :
1676 : }
1677 :
1678 : already_AddRefed<css::Declaration>
1679 7 : CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
1680 : nsIURI* aDocURI,
1681 : nsIURI* aBaseURI,
1682 : nsIPrincipal* aNodePrincipal)
1683 : {
1684 7 : NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
1685 7 : NS_PRECONDITION(aBaseURI, "need base URI");
1686 :
1687 : // XXX line number?
1688 14 : nsCSSScanner scanner(aAttributeValue, 0);
1689 14 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI);
1690 7 : InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal);
1691 :
1692 7 : mSection = eCSSSection_General;
1693 :
1694 7 : uint32_t parseFlags = eParseDeclaration_AllowImportant;
1695 :
1696 14 : RefPtr<css::Declaration> declaration = ParseDeclarationBlock(parseFlags);
1697 :
1698 7 : ReleaseScanner();
1699 :
1700 14 : return declaration.forget();
1701 : }
1702 :
1703 : nsresult
1704 0 : CSSParserImpl::ParseDeclarations(const nsAString& aBuffer,
1705 : nsIURI* aSheetURI,
1706 : nsIURI* aBaseURI,
1707 : nsIPrincipal* aSheetPrincipal,
1708 : css::Declaration* aDeclaration,
1709 : bool* aChanged)
1710 : {
1711 0 : *aChanged = false;
1712 :
1713 0 : NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1714 :
1715 0 : nsCSSScanner scanner(aBuffer, 0);
1716 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1717 0 : InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1718 :
1719 0 : MOZ_ASSERT(mWebkitBoxUnprefixState == eNotParsingDecls,
1720 : "Someone forgot to clear mWebkitBoxUnprefixState!");
1721 0 : AutoRestore<WebkitBoxUnprefixState> autoRestore(mWebkitBoxUnprefixState);
1722 0 : mWebkitBoxUnprefixState = eHaveNotUnprefixed;
1723 :
1724 0 : mSection = eCSSSection_General;
1725 :
1726 0 : mData.AssertInitialState();
1727 0 : aDeclaration->ClearData();
1728 : // We could check if it was already empty, but...
1729 0 : *aChanged = true;
1730 :
1731 : for (;;) {
1732 : // If we cleared the old decl, then we want to be calling
1733 : // ValueAppended as we parse.
1734 0 : if (!ParseDeclaration(aDeclaration, eParseDeclaration_AllowImportant,
1735 : true, aChanged)) {
1736 0 : if (!SkipDeclaration(false)) {
1737 0 : break;
1738 : }
1739 : }
1740 : }
1741 :
1742 0 : aDeclaration->CompressFrom(&mData);
1743 0 : ReleaseScanner();
1744 0 : return NS_OK;
1745 : }
1746 :
1747 : nsresult
1748 0 : CSSParserImpl::ParseRule(const nsAString& aRule,
1749 : nsIURI* aSheetURI,
1750 : nsIURI* aBaseURI,
1751 : nsIPrincipal* aSheetPrincipal,
1752 : css::Rule** aResult)
1753 : {
1754 0 : NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1755 0 : NS_PRECONDITION(aBaseURI, "need base URI");
1756 :
1757 0 : *aResult = nullptr;
1758 :
1759 0 : nsCSSScanner scanner(aRule, 0);
1760 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1761 0 : InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1762 :
1763 0 : mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
1764 :
1765 0 : nsCSSToken* tk = &mToken;
1766 : // Get first non-whitespace token
1767 0 : nsresult rv = NS_OK;
1768 0 : if (!GetToken(true)) {
1769 0 : REPORT_UNEXPECTED(PEParseRuleWSOnly);
1770 0 : OUTPUT_ERROR();
1771 0 : rv = NS_ERROR_DOM_SYNTAX_ERR;
1772 : } else {
1773 0 : if (eCSSToken_AtKeyword == tk->mType) {
1774 : // FIXME: perhaps aInsideBlock should be true when we are?
1775 0 : ParseAtRule(AssignRuleToPointer, aResult, false);
1776 : } else {
1777 0 : UngetToken();
1778 0 : ParseRuleSet(AssignRuleToPointer, aResult);
1779 : }
1780 :
1781 0 : if (*aResult && GetToken(true)) {
1782 : // garbage after rule
1783 0 : REPORT_UNEXPECTED_TOKEN(PERuleTrailing);
1784 0 : NS_RELEASE(*aResult);
1785 : }
1786 :
1787 0 : if (!*aResult) {
1788 0 : rv = NS_ERROR_DOM_SYNTAX_ERR;
1789 0 : OUTPUT_ERROR();
1790 : }
1791 : }
1792 :
1793 0 : ReleaseScanner();
1794 0 : return rv;
1795 : }
1796 :
1797 : void
1798 0 : CSSParserImpl::ParseLonghandProperty(const nsCSSPropertyID aPropID,
1799 : const nsAString& aPropValue,
1800 : nsIURI* aSheetURL,
1801 : nsIURI* aBaseURL,
1802 : nsIPrincipal* aSheetPrincipal,
1803 : nsCSSValue& aValue)
1804 : {
1805 0 : MOZ_ASSERT(aPropID < eCSSProperty_COUNT_no_shorthands,
1806 : "ParseLonghandProperty must only take a longhand property");
1807 :
1808 0 : RefPtr<css::Declaration> declaration = new css::Declaration;
1809 0 : declaration->InitializeEmpty();
1810 :
1811 : bool changed;
1812 0 : ParseProperty(aPropID, aPropValue, aSheetURL, aBaseURL, aSheetPrincipal,
1813 : declaration, &changed,
1814 : /* aIsImportant */ false,
1815 0 : /* aIsSVGMode */ false);
1816 :
1817 0 : if (changed) {
1818 0 : aValue = *declaration->GetNormalBlock()->ValueFor(aPropID);
1819 : } else {
1820 0 : aValue.Reset();
1821 : }
1822 0 : }
1823 :
1824 : bool
1825 0 : CSSParserImpl::ParseTransformProperty(const nsAString& aPropValue,
1826 : bool aDisallowRelativeValues,
1827 : nsCSSValue& aValue)
1828 : {
1829 0 : RefPtr<css::Declaration> declaration = new css::Declaration();
1830 0 : declaration->InitializeEmpty();
1831 :
1832 0 : mData.AssertInitialState();
1833 0 : mTempData.AssertInitialState();
1834 :
1835 0 : nsCSSScanner scanner(aPropValue, 0);
1836 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr);
1837 0 : InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
1838 :
1839 0 : bool parsedOK = ParseTransform(false, eCSSProperty_transform,
1840 0 : aDisallowRelativeValues);
1841 : // We should now be at EOF
1842 0 : if (parsedOK && GetToken(true)) {
1843 0 : parsedOK = false;
1844 : }
1845 :
1846 0 : bool changed = false;
1847 0 : if (parsedOK) {
1848 0 : declaration->ExpandTo(&mData);
1849 0 : changed = mData.TransferFromBlock(mTempData, eCSSProperty_transform,
1850 : EnabledState(), false,
1851 : true, false, declaration,
1852 0 : GetDocument());
1853 0 : declaration->CompressFrom(&mData);
1854 : }
1855 :
1856 0 : if (changed) {
1857 0 : aValue = *declaration->GetNormalBlock()->ValueFor(eCSSProperty_transform);
1858 : } else {
1859 0 : aValue.Reset();
1860 : }
1861 :
1862 0 : ReleaseScanner();
1863 :
1864 0 : return parsedOK;
1865 : }
1866 :
1867 : void
1868 103 : CSSParserImpl::ParseProperty(const nsCSSPropertyID aPropID,
1869 : const nsAString& aPropValue,
1870 : nsIURI* aSheetURI,
1871 : nsIURI* aBaseURI,
1872 : nsIPrincipal* aSheetPrincipal,
1873 : css::Declaration* aDeclaration,
1874 : bool* aChanged,
1875 : bool aIsImportant,
1876 : bool aIsSVGMode)
1877 : {
1878 103 : NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1879 103 : NS_PRECONDITION(aBaseURI, "need base URI");
1880 103 : NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
1881 103 : MOZ_ASSERT(aPropID != eCSSPropertyExtra_variable);
1882 :
1883 103 : mData.AssertInitialState();
1884 103 : mTempData.AssertInitialState();
1885 103 : aDeclaration->AssertMutable();
1886 :
1887 206 : nsCSSScanner scanner(aPropValue, 0);
1888 206 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1889 103 : InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1890 103 : mSection = eCSSSection_General;
1891 :
1892 103 : *aChanged = false;
1893 :
1894 : // Check for unknown or preffed off properties
1895 206 : if (eCSSProperty_UNKNOWN == aPropID ||
1896 103 : !nsCSSProps::IsEnabled(aPropID, EnabledState())) {
1897 0 : NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1898 0 : REPORT_UNEXPECTED_P(PEUnknownProperty, propName);
1899 0 : REPORT_UNEXPECTED(PEDeclDropped);
1900 0 : OUTPUT_ERROR();
1901 0 : ReleaseScanner();
1902 0 : return;
1903 : }
1904 :
1905 103 : mIsSVGMode = aIsSVGMode;
1906 103 : bool parsedOK = ParseProperty(aPropID);
1907 : // We should now be at EOF
1908 103 : if (parsedOK && GetToken(true)) {
1909 0 : REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1910 0 : parsedOK = false;
1911 : }
1912 :
1913 103 : if (!parsedOK) {
1914 0 : NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1915 0 : REPORT_UNEXPECTED_P(PEValueParsingError, propName);
1916 0 : REPORT_UNEXPECTED(PEDeclDropped);
1917 0 : OUTPUT_ERROR();
1918 0 : mTempData.ClearProperty(aPropID);
1919 : } else {
1920 :
1921 : // We know we don't need to force a ValueAppended call for the new
1922 : // value. So if we are not processing a shorthand, and there's
1923 : // already a value for this property in the declaration at the
1924 : // same importance level, then we can just copy our parsed value
1925 : // directly into the declaration without going through the whole
1926 : // expand/compress thing.
1927 103 : if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData,
1928 : aChanged)) {
1929 : // Do it the slow way
1930 99 : aDeclaration->ExpandTo(&mData);
1931 99 : *aChanged = mData.TransferFromBlock(mTempData, aPropID,
1932 : EnabledState(), aIsImportant,
1933 : true, false, aDeclaration,
1934 : GetDocument());
1935 99 : aDeclaration->CompressFrom(&mData);
1936 : }
1937 103 : CLEAR_ERROR();
1938 : }
1939 :
1940 103 : mTempData.AssertInitialState();
1941 103 : mIsSVGMode = false;
1942 :
1943 103 : ReleaseScanner();
1944 : }
1945 :
1946 : void
1947 0 : CSSParserImpl::ParseVariable(const nsAString& aVariableName,
1948 : const nsAString& aPropValue,
1949 : nsIURI* aSheetURI,
1950 : nsIURI* aBaseURI,
1951 : nsIPrincipal* aSheetPrincipal,
1952 : css::Declaration* aDeclaration,
1953 : bool* aChanged,
1954 : bool aIsImportant)
1955 : {
1956 0 : NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1957 0 : NS_PRECONDITION(aBaseURI, "need base URI");
1958 0 : NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
1959 :
1960 0 : mData.AssertInitialState();
1961 0 : mTempData.AssertInitialState();
1962 0 : aDeclaration->AssertMutable();
1963 :
1964 0 : nsCSSScanner scanner(aPropValue, 0);
1965 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
1966 0 : InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
1967 0 : mSection = eCSSSection_General;
1968 :
1969 0 : *aChanged = false;
1970 :
1971 : CSSVariableDeclarations::Type variableType;
1972 0 : nsString variableValue;
1973 :
1974 0 : bool parsedOK = ParseVariableDeclaration(&variableType, variableValue);
1975 :
1976 : // We should now be at EOF
1977 0 : if (parsedOK && GetToken(true)) {
1978 0 : REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1979 0 : parsedOK = false;
1980 : }
1981 :
1982 0 : if (!parsedOK) {
1983 0 : REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("--") +
1984 : aVariableName);
1985 0 : REPORT_UNEXPECTED(PEDeclDropped);
1986 0 : OUTPUT_ERROR();
1987 : } else {
1988 0 : CLEAR_ERROR();
1989 0 : aDeclaration->AddVariable(aVariableName, variableType,
1990 0 : variableValue, aIsImportant, true);
1991 0 : *aChanged = true;
1992 : }
1993 :
1994 0 : mTempData.AssertInitialState();
1995 :
1996 0 : ReleaseScanner();
1997 0 : }
1998 :
1999 : void
2000 3 : CSSParserImpl::ParseMediaList(const nsAString& aBuffer,
2001 : nsIURI* aURI, // for error reporting
2002 : uint32_t aLineNumber, // for error reporting
2003 : nsMediaList* aMediaList)
2004 : {
2005 : // XXX Are there cases where the caller wants to keep what it already
2006 : // has in case of parser error? If GatherMedia ever changes to return
2007 : // a value other than true, we probably should avoid modifying aMediaList.
2008 3 : aMediaList->Clear();
2009 :
2010 : // fake base URI since media lists don't have URIs in them
2011 6 : nsCSSScanner scanner(aBuffer, aLineNumber);
2012 6 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2013 3 : InitScanner(scanner, reporter, aURI, aURI, nullptr);
2014 :
2015 6 : DebugOnly<bool> parsedOK = GatherMedia(aMediaList, false);
2016 3 : NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid "
2017 : "trashing aMediaList");
2018 :
2019 3 : CLEAR_ERROR();
2020 3 : ReleaseScanner();
2021 3 : }
2022 :
2023 : // <source-size-list> = <source-size>#?
2024 : // <source-size> = <media-condition>? <length>
2025 : bool
2026 0 : CSSParserImpl::ParseSourceSizeList(const nsAString& aBuffer,
2027 : nsIURI* aURI, // for error reporting
2028 : uint32_t aLineNumber, // for error reporting
2029 : InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
2030 : InfallibleTArray<nsCSSValue>& aValues)
2031 : {
2032 0 : aQueries.Clear();
2033 0 : aValues.Clear();
2034 :
2035 : // fake base URI since media value lists don't have URIs in them
2036 0 : nsCSSScanner scanner(aBuffer, aLineNumber);
2037 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2038 0 : InitScanner(scanner, reporter, aURI, aURI, nullptr);
2039 :
2040 : // https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-sizes-attribute
2041 0 : bool hitEnd = false;
2042 0 : do {
2043 0 : bool hitError = false;
2044 : // Parse single <media-condition> <source-size-value>
2045 : do {
2046 0 : nsAutoPtr<nsMediaQuery> query;
2047 0 : nsCSSValue value;
2048 :
2049 : bool hitStop;
2050 0 : if (!ParseMediaQuery(eMediaQuerySingleCondition, getter_Transfers(query),
2051 : &hitStop)) {
2052 0 : NS_ASSERTION(!hitStop, "should return true when hit stop");
2053 0 : hitError = true;
2054 0 : break;
2055 : }
2056 :
2057 0 : if (!query) {
2058 0 : REPORT_UNEXPECTED_EOF(PEParseSourceSizeListEOF);
2059 0 : NS_ASSERTION(hitStop,
2060 : "should return hitStop or an error if returning no query");
2061 0 : hitError = true;
2062 0 : break;
2063 : }
2064 :
2065 0 : if (hitStop) {
2066 : // Empty conditions (e.g. just a bare value) should be treated as always
2067 : // matching (a query with no expressions fails to match, so a negated one
2068 : // always matches.)
2069 0 : query->SetNegated();
2070 : }
2071 :
2072 : // https://html.spec.whatwg.org/multipage/embedded-content.html#source-size-value
2073 : // Percentages are not allowed in a <source-size-value>, to avoid
2074 : // confusion about what it would be relative to.
2075 0 : if (ParseNonNegativeVariant(value, VARIANT_LCALC, nullptr) !=
2076 : CSSParseResult::Ok) {
2077 0 : hitError = true;
2078 0 : break;
2079 : }
2080 :
2081 0 : if (GetToken(true)) {
2082 0 : if (!mToken.IsSymbol(',')) {
2083 0 : REPORT_UNEXPECTED_TOKEN(PEParseSourceSizeListNotComma);
2084 0 : hitError = true;
2085 0 : break;
2086 : }
2087 : } else {
2088 0 : hitEnd = true;
2089 : }
2090 :
2091 0 : aQueries.AppendElement(query.forget());
2092 0 : aValues.AppendElement(value);
2093 : } while(0);
2094 :
2095 0 : if (hitError) {
2096 0 : OUTPUT_ERROR();
2097 :
2098 : // Per spec, we just skip the current entry if there was a parse error.
2099 : // Jumps to next entry of <source-size-list> which is a comma-separated list.
2100 0 : if (!SkipUntil(',')) {
2101 0 : hitEnd = true;
2102 : }
2103 : }
2104 0 : } while (!hitEnd);
2105 :
2106 0 : CLEAR_ERROR();
2107 0 : ReleaseScanner();
2108 :
2109 0 : return !aQueries.IsEmpty();
2110 : }
2111 :
2112 : bool
2113 152 : CSSParserImpl::ParseColorString(const nsAString& aBuffer,
2114 : nsIURI* aURI, // for error reporting
2115 : uint32_t aLineNumber, // for error reporting
2116 : nsCSSValue& aValue,
2117 : bool aSuppressErrors /* false */)
2118 : {
2119 304 : nsCSSScanner scanner(aBuffer, aLineNumber);
2120 304 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2121 152 : InitScanner(scanner, reporter, aURI, aURI, nullptr);
2122 :
2123 304 : nsAutoSuppressErrors suppressErrors(this, aSuppressErrors);
2124 :
2125 : // Parse a color, and check that there's nothing else after it.
2126 304 : bool colorParsed = ParseColor(aValue) == CSSParseResult::Ok &&
2127 304 : !GetToken(true);
2128 :
2129 152 : if (aSuppressErrors) {
2130 0 : CLEAR_ERROR();
2131 : } else {
2132 152 : OUTPUT_ERROR();
2133 : }
2134 :
2135 152 : ReleaseScanner();
2136 304 : return colorParsed;
2137 : }
2138 :
2139 : bool
2140 0 : CSSParserImpl::ParseMarginString(const nsAString& aBuffer,
2141 : nsIURI* aURI, // for error reporting
2142 : uint32_t aLineNumber, // for error reporting
2143 : nsCSSValue& aValue,
2144 : bool aSuppressErrors /* false */)
2145 : {
2146 0 : nsCSSScanner scanner(aBuffer, aLineNumber);
2147 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2148 0 : InitScanner(scanner, reporter, aURI, aURI, nullptr);
2149 :
2150 0 : nsAutoSuppressErrors suppressErrors(this, aSuppressErrors);
2151 :
2152 : // Parse a margin, and check that there's nothing else after it.
2153 0 : bool marginParsed = ParseGroupedBoxProperty(VARIANT_LP, aValue, 0) && !GetToken(true);
2154 :
2155 0 : if (aSuppressErrors) {
2156 0 : CLEAR_ERROR();
2157 : } else {
2158 0 : OUTPUT_ERROR();
2159 : }
2160 :
2161 0 : ReleaseScanner();
2162 0 : return marginParsed;
2163 : }
2164 :
2165 : bool
2166 0 : CSSParserImpl::ParseFontFamilyListString(const nsAString& aBuffer,
2167 : nsIURI* aURI, // for error reporting
2168 : uint32_t aLineNumber, // for error reporting
2169 : nsCSSValue& aValue)
2170 : {
2171 0 : nsCSSScanner scanner(aBuffer, aLineNumber);
2172 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2173 0 : InitScanner(scanner, reporter, aURI, aURI, nullptr);
2174 :
2175 : // Parse a font family list, and check that there's nothing else after it.
2176 0 : bool familyParsed = ParseFamily(aValue) && !GetToken(true);
2177 0 : OUTPUT_ERROR();
2178 0 : ReleaseScanner();
2179 0 : return familyParsed;
2180 : }
2181 :
2182 : nsresult
2183 7 : CSSParserImpl::ParseSelectorString(const nsAString& aSelectorString,
2184 : nsIURI* aURI, // for error reporting
2185 : uint32_t aLineNumber, // for error reporting
2186 : nsCSSSelectorList **aSelectorList)
2187 : {
2188 14 : nsCSSScanner scanner(aSelectorString, aLineNumber);
2189 14 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2190 7 : InitScanner(scanner, reporter, aURI, aURI, nullptr);
2191 :
2192 7 : bool success = ParseSelectorList(*aSelectorList, char16_t(0));
2193 :
2194 : // We deliberately do not call OUTPUT_ERROR here, because all our
2195 : // callers map a failure return to a JS exception, and if that JS
2196 : // exception is caught, people don't want to see parser diagnostics;
2197 : // see e.g. http://bugs.jquery.com/ticket/7535
2198 : // It would be nice to be able to save the parser diagnostics into
2199 : // the exception, so that if it _isn't_ caught we can report them
2200 : // along with the usual uncaught-exception message, but we don't
2201 : // have any way to do that at present; see bug 631621.
2202 7 : CLEAR_ERROR();
2203 7 : ReleaseScanner();
2204 :
2205 7 : if (success) {
2206 7 : NS_ASSERTION(*aSelectorList, "Should have list!");
2207 7 : return NS_OK;
2208 : }
2209 :
2210 0 : NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
2211 :
2212 0 : return NS_ERROR_DOM_SYNTAX_ERR;
2213 : }
2214 :
2215 :
2216 : already_AddRefed<nsCSSKeyframeRule>
2217 0 : CSSParserImpl::ParseKeyframeRule(const nsAString& aBuffer,
2218 : nsIURI* aURI,
2219 : uint32_t aLineNumber)
2220 : {
2221 0 : nsCSSScanner scanner(aBuffer, aLineNumber);
2222 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2223 0 : InitScanner(scanner, reporter, aURI, aURI, nullptr);
2224 :
2225 0 : RefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule();
2226 0 : if (GetToken(true)) {
2227 : // extra garbage at the end
2228 0 : result = nullptr;
2229 : }
2230 :
2231 0 : OUTPUT_ERROR();
2232 0 : ReleaseScanner();
2233 :
2234 0 : return result.forget();
2235 : }
2236 :
2237 : bool
2238 0 : CSSParserImpl::ParseKeyframeSelectorString(const nsAString& aSelectorString,
2239 : nsIURI* aURI, // for error reporting
2240 : uint32_t aLineNumber, // for error reporting
2241 : InfallibleTArray<float>& aSelectorList)
2242 : {
2243 0 : MOZ_ASSERT(aSelectorList.IsEmpty(), "given list should start empty");
2244 :
2245 0 : nsCSSScanner scanner(aSelectorString, aLineNumber);
2246 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
2247 0 : InitScanner(scanner, reporter, aURI, aURI, nullptr);
2248 :
2249 0 : bool success = ParseKeyframeSelectorList(aSelectorList) &&
2250 : // must consume entire input string
2251 0 : !GetToken(true);
2252 :
2253 0 : OUTPUT_ERROR();
2254 0 : ReleaseScanner();
2255 :
2256 0 : if (success) {
2257 0 : NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty");
2258 : } else {
2259 0 : aSelectorList.Clear();
2260 : }
2261 :
2262 0 : return success;
2263 : }
2264 :
2265 : bool
2266 0 : CSSParserImpl::EvaluateSupportsDeclaration(const nsAString& aProperty,
2267 : const nsAString& aValue,
2268 : nsIURI* aDocURL,
2269 : nsIURI* aBaseURL,
2270 : nsIPrincipal* aDocPrincipal)
2271 : {
2272 0 : nsCSSPropertyID propID = LookupEnabledProperty(aProperty);
2273 0 : if (propID == eCSSProperty_UNKNOWN) {
2274 0 : return false;
2275 : }
2276 :
2277 0 : nsCSSScanner scanner(aValue, 0);
2278 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
2279 0 : InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2280 0 : nsAutoSuppressErrors suppressErrors(this);
2281 :
2282 : bool parsedOK;
2283 :
2284 0 : if (propID == eCSSPropertyExtra_variable) {
2285 0 : MOZ_ASSERT(Substring(aProperty, 0,
2286 : CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
2287 : const nsDependentSubstring varName =
2288 0 : Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH); // remove '--'
2289 : CSSVariableDeclarations::Type variableType;
2290 0 : nsString variableValue;
2291 0 : parsedOK = ParseVariableDeclaration(&variableType, variableValue) &&
2292 0 : !GetToken(true);
2293 : } else {
2294 0 : parsedOK = ParseProperty(propID) && !GetToken(true);
2295 :
2296 0 : mTempData.ClearProperty(propID);
2297 0 : mTempData.AssertInitialState();
2298 : }
2299 :
2300 0 : CLEAR_ERROR();
2301 0 : ReleaseScanner();
2302 :
2303 0 : return parsedOK;
2304 : }
2305 :
2306 : bool
2307 0 : CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration,
2308 : nsIURI* aDocURL,
2309 : nsIURI* aBaseURL,
2310 : nsIPrincipal* aDocPrincipal,
2311 : SupportsParsingSettings aSettings)
2312 : {
2313 0 : nsCSSScanner scanner(aDeclaration, 0);
2314 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
2315 0 : InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2316 0 : nsAutoSuppressErrors suppressErrors(this);
2317 :
2318 : bool conditionMet;
2319 : bool parsedOK;
2320 :
2321 0 : if (aSettings == SupportsParsingSettings::ImpliedParentheses) {
2322 0 : parsedOK = ParseSupportsConditionInParensInsideParens(conditionMet) && !GetToken(true);
2323 : } else {
2324 0 : parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true);
2325 : }
2326 :
2327 0 : CLEAR_ERROR();
2328 0 : ReleaseScanner();
2329 :
2330 0 : return parsedOK && conditionMet;
2331 : }
2332 :
2333 : bool
2334 141 : CSSParserImpl::EnumerateVariableReferences(const nsAString& aPropertyValue,
2335 : VariableEnumFunc aFunc,
2336 : void* aData)
2337 : {
2338 282 : nsCSSScanner scanner(aPropertyValue, 0);
2339 282 : css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
2340 141 : InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
2341 282 : nsAutoSuppressErrors suppressErrors(this);
2342 :
2343 : CSSVariableDeclarations::Type type;
2344 : bool dropBackslash;
2345 282 : nsString impliedCharacters;
2346 141 : bool result = ParseValueWithVariables(&type, &dropBackslash,
2347 282 : impliedCharacters, aFunc, aData) &&
2348 282 : !GetToken(true);
2349 :
2350 141 : ReleaseScanner();
2351 :
2352 282 : return result;
2353 : }
2354 :
2355 : static bool
2356 3132 : SeparatorRequiredBetweenTokens(nsCSSTokenSerializationType aToken1,
2357 : nsCSSTokenSerializationType aToken2)
2358 : {
2359 : // The two lines marked with (*) do not correspond to entries in
2360 : // the table in the css-syntax spec but which we need to handle,
2361 : // as we treat them as whole tokens.
2362 3132 : switch (aToken1) {
2363 : case eCSSTokenSerialization_Ident:
2364 0 : return aToken2 == eCSSTokenSerialization_Ident ||
2365 0 : aToken2 == eCSSTokenSerialization_Function ||
2366 0 : aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
2367 0 : aToken2 == eCSSTokenSerialization_Symbol_Minus ||
2368 0 : aToken2 == eCSSTokenSerialization_Number ||
2369 0 : aToken2 == eCSSTokenSerialization_Percentage ||
2370 0 : aToken2 == eCSSTokenSerialization_Dimension ||
2371 0 : aToken2 == eCSSTokenSerialization_URange ||
2372 0 : aToken2 == eCSSTokenSerialization_CDC ||
2373 0 : aToken2 == eCSSTokenSerialization_Symbol_OpenParen;
2374 : case eCSSTokenSerialization_AtKeyword_or_Hash:
2375 : case eCSSTokenSerialization_Dimension:
2376 953 : return aToken2 == eCSSTokenSerialization_Ident ||
2377 953 : aToken2 == eCSSTokenSerialization_Function ||
2378 953 : aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
2379 953 : aToken2 == eCSSTokenSerialization_Symbol_Minus ||
2380 953 : aToken2 == eCSSTokenSerialization_Number ||
2381 953 : aToken2 == eCSSTokenSerialization_Percentage ||
2382 953 : aToken2 == eCSSTokenSerialization_Dimension ||
2383 1906 : aToken2 == eCSSTokenSerialization_URange ||
2384 953 : aToken2 == eCSSTokenSerialization_CDC;
2385 : case eCSSTokenSerialization_Symbol_Hash:
2386 : case eCSSTokenSerialization_Symbol_Minus:
2387 0 : return aToken2 == eCSSTokenSerialization_Ident ||
2388 0 : aToken2 == eCSSTokenSerialization_Function ||
2389 0 : aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
2390 0 : aToken2 == eCSSTokenSerialization_Symbol_Minus ||
2391 0 : aToken2 == eCSSTokenSerialization_Number ||
2392 0 : aToken2 == eCSSTokenSerialization_Percentage ||
2393 0 : aToken2 == eCSSTokenSerialization_Dimension ||
2394 0 : aToken2 == eCSSTokenSerialization_URange;
2395 : case eCSSTokenSerialization_Number:
2396 0 : return aToken2 == eCSSTokenSerialization_Ident ||
2397 0 : aToken2 == eCSSTokenSerialization_Function ||
2398 0 : aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
2399 0 : aToken2 == eCSSTokenSerialization_Number ||
2400 0 : aToken2 == eCSSTokenSerialization_Percentage ||
2401 0 : aToken2 == eCSSTokenSerialization_Dimension ||
2402 0 : aToken2 == eCSSTokenSerialization_URange;
2403 : case eCSSTokenSerialization_Symbol_At:
2404 0 : return aToken2 == eCSSTokenSerialization_Ident ||
2405 0 : aToken2 == eCSSTokenSerialization_Function ||
2406 0 : aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
2407 0 : aToken2 == eCSSTokenSerialization_Symbol_Minus ||
2408 0 : aToken2 == eCSSTokenSerialization_URange;
2409 : case eCSSTokenSerialization_URange:
2410 0 : return aToken2 == eCSSTokenSerialization_Ident ||
2411 0 : aToken2 == eCSSTokenSerialization_Function ||
2412 0 : aToken2 == eCSSTokenSerialization_Number ||
2413 0 : aToken2 == eCSSTokenSerialization_Percentage ||
2414 0 : aToken2 == eCSSTokenSerialization_Dimension ||
2415 0 : aToken2 == eCSSTokenSerialization_Symbol_Question;
2416 : case eCSSTokenSerialization_Symbol_Dot_or_Plus:
2417 0 : return aToken2 == eCSSTokenSerialization_Number ||
2418 0 : aToken2 == eCSSTokenSerialization_Percentage ||
2419 0 : aToken2 == eCSSTokenSerialization_Dimension;
2420 : case eCSSTokenSerialization_Symbol_Assorted:
2421 : case eCSSTokenSerialization_Symbol_Asterisk:
2422 0 : return aToken2 == eCSSTokenSerialization_Symbol_Equals;
2423 : case eCSSTokenSerialization_Symbol_Bar:
2424 0 : return aToken2 == eCSSTokenSerialization_Symbol_Equals ||
2425 0 : aToken2 == eCSSTokenSerialization_Symbol_Bar ||
2426 0 : aToken2 == eCSSTokenSerialization_DashMatch; // (*)
2427 : case eCSSTokenSerialization_Symbol_Slash:
2428 0 : return aToken2 == eCSSTokenSerialization_Symbol_Asterisk ||
2429 0 : aToken2 == eCSSTokenSerialization_ContainsMatch; // (*)
2430 : default:
2431 2179 : MOZ_ASSERT(aToken1 == eCSSTokenSerialization_Nothing ||
2432 : aToken1 == eCSSTokenSerialization_Whitespace ||
2433 : aToken1 == eCSSTokenSerialization_Percentage ||
2434 : aToken1 == eCSSTokenSerialization_URL_or_BadURL ||
2435 : aToken1 == eCSSTokenSerialization_Function ||
2436 : aToken1 == eCSSTokenSerialization_CDC ||
2437 : aToken1 == eCSSTokenSerialization_Symbol_OpenParen ||
2438 : aToken1 == eCSSTokenSerialization_Symbol_Question ||
2439 : aToken1 == eCSSTokenSerialization_Symbol_Assorted ||
2440 : aToken1 == eCSSTokenSerialization_Symbol_Asterisk ||
2441 : aToken1 == eCSSTokenSerialization_Symbol_Equals ||
2442 : aToken1 == eCSSTokenSerialization_Symbol_Bar ||
2443 : aToken1 == eCSSTokenSerialization_Symbol_Slash ||
2444 : aToken1 == eCSSTokenSerialization_Other,
2445 : "unexpected nsCSSTokenSerializationType value");
2446 2179 : return false;
2447 : }
2448 : }
2449 :
2450 : /**
2451 : * Appends aValue to aResult, possibly inserting an empty CSS
2452 : * comment between the two to ensure that tokens from both strings
2453 : * remain separated.
2454 : */
2455 : static void
2456 2108 : AppendTokens(nsAString& aResult,
2457 : nsCSSTokenSerializationType& aResultFirstToken,
2458 : nsCSSTokenSerializationType& aResultLastToken,
2459 : nsCSSTokenSerializationType aValueFirstToken,
2460 : nsCSSTokenSerializationType aValueLastToken,
2461 : const nsAString& aValue)
2462 : {
2463 2108 : if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
2464 0 : aResult.AppendLiteral("/**/");
2465 : }
2466 2108 : aResult.Append(aValue);
2467 2108 : if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
2468 894 : aResultFirstToken = aValueFirstToken;
2469 : }
2470 2108 : if (aValueLastToken != eCSSTokenSerialization_Nothing) {
2471 2108 : aResultLastToken = aValueLastToken;
2472 : }
2473 2108 : }
2474 :
2475 : /**
2476 : * Stops the given scanner recording, and appends the recorded result
2477 : * to aResult, possibly inserting an empty CSS comment between the two to
2478 : * ensure that tokens from both strings remain separated.
2479 : */
2480 : static void
2481 1024 : StopRecordingAndAppendTokens(nsString& aResult,
2482 : nsCSSTokenSerializationType& aResultFirstToken,
2483 : nsCSSTokenSerializationType& aResultLastToken,
2484 : nsCSSTokenSerializationType aValueFirstToken,
2485 : nsCSSTokenSerializationType aValueLastToken,
2486 : nsCSSScanner* aScanner)
2487 : {
2488 1024 : if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
2489 0 : aResult.AppendLiteral("/**/");
2490 : }
2491 1024 : aScanner->StopRecording(aResult);
2492 1024 : if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
2493 130 : aResultFirstToken = aValueFirstToken;
2494 : }
2495 1024 : if (aValueLastToken != eCSSTokenSerialization_Nothing) {
2496 442 : aResultLastToken = aValueLastToken;
2497 : }
2498 1024 : }
2499 :
2500 : bool
2501 1024 : CSSParserImpl::ResolveValueWithVariableReferencesRec(
2502 : nsString& aResult,
2503 : nsCSSTokenSerializationType& aResultFirstToken,
2504 : nsCSSTokenSerializationType& aResultLastToken,
2505 : const CSSVariableValues* aVariables)
2506 : {
2507 : // This function assumes we are already recording, and will leave the scanner
2508 : // recording when it returns.
2509 1024 : MOZ_ASSERT(mScanner->IsRecording());
2510 1024 : MOZ_ASSERT(aResult.IsEmpty());
2511 :
2512 : // Stack of closing characters for currently open constructs.
2513 2048 : AutoTArray<char16_t, 16> stack;
2514 :
2515 : // The resolved value for this ResolveValueWithVariableReferencesRec call.
2516 2048 : nsString value;
2517 :
2518 : // The length of the scanner's recording before the currently parsed token.
2519 : // This is used so that when we encounter a "var(" token, we can strip
2520 : // it off the end of the recording, regardless of how long the token was.
2521 : // (With escapes, it could be longer than four characters.)
2522 1024 : uint32_t lengthBeforeVar = 0;
2523 :
2524 : // Tracking the type of token that appears at the start and end of |value|
2525 : // and that appears at the start and end of the scanner recording. These are
2526 : // used to determine whether we need to insert "/**/" when pasting token
2527 : // streams together.
2528 1024 : nsCSSTokenSerializationType valueFirstToken = eCSSTokenSerialization_Nothing,
2529 1024 : valueLastToken = eCSSTokenSerialization_Nothing,
2530 1024 : recFirstToken = eCSSTokenSerialization_Nothing,
2531 1024 : recLastToken = eCSSTokenSerialization_Nothing;
2532 :
2533 : #define UPDATE_RECORDING_TOKENS(type) \
2534 : if (recFirstToken == eCSSTokenSerialization_Nothing) { \
2535 : recFirstToken = type; \
2536 : } \
2537 : recLastToken = type;
2538 :
2539 11932 : while (GetToken(false)) {
2540 5454 : switch (mToken.mType) {
2541 : case eCSSToken_Symbol: {
2542 577 : nsCSSTokenSerializationType type = eCSSTokenSerialization_Other;
2543 577 : if (mToken.mSymbol == '(') {
2544 13 : stack.AppendElement(')');
2545 13 : type = eCSSTokenSerialization_Symbol_OpenParen;
2546 564 : } else if (mToken.mSymbol == '[') {
2547 0 : stack.AppendElement(']');
2548 564 : } else if (mToken.mSymbol == '{') {
2549 0 : stack.AppendElement('}');
2550 564 : } else if (mToken.mSymbol == ';') {
2551 0 : if (stack.IsEmpty()) {
2552 : // A ';' that is at the top level of the value or at the top level
2553 : // of a variable reference's fallback is invalid.
2554 0 : return false;
2555 : }
2556 564 : } else if (mToken.mSymbol == '!') {
2557 0 : if (stack.IsEmpty()) {
2558 : // An '!' that is at the top level of the value or at the top level
2559 : // of a variable reference's fallback is invalid.
2560 0 : return false;
2561 : }
2562 788 : } else if (mToken.mSymbol == ')' &&
2563 224 : stack.IsEmpty()) {
2564 : // We're closing a "var(".
2565 0 : nsString finalTokens;
2566 0 : mScanner->StopRecording(finalTokens);
2567 0 : MOZ_ASSERT(finalTokens[finalTokens.Length() - 1] == ')');
2568 0 : finalTokens.Truncate(finalTokens.Length() - 1);
2569 0 : aResult.Append(value);
2570 :
2571 0 : AppendTokens(aResult, valueFirstToken, valueLastToken,
2572 0 : recFirstToken, recLastToken, finalTokens);
2573 :
2574 0 : mScanner->StartRecording();
2575 0 : UngetToken();
2576 0 : aResultFirstToken = valueFirstToken;
2577 0 : aResultLastToken = valueLastToken;
2578 0 : return true;
2579 904 : } else if (mToken.mSymbol == ')' ||
2580 680 : mToken.mSymbol == ']' ||
2581 340 : mToken.mSymbol == '}') {
2582 448 : if (stack.IsEmpty() ||
2583 224 : stack.LastElement() != mToken.mSymbol) {
2584 : // A mismatched closing bracket is invalid.
2585 0 : return false;
2586 : }
2587 224 : stack.TruncateLength(stack.Length() - 1);
2588 340 : } else if (mToken.mSymbol == '#') {
2589 0 : type = eCSSTokenSerialization_Symbol_Hash;
2590 340 : } else if (mToken.mSymbol == '@') {
2591 0 : type = eCSSTokenSerialization_Symbol_At;
2592 680 : } else if (mToken.mSymbol == '.' ||
2593 340 : mToken.mSymbol == '+') {
2594 109 : type = eCSSTokenSerialization_Symbol_Dot_or_Plus;
2595 231 : } else if (mToken.mSymbol == '-') {
2596 28 : type = eCSSTokenSerialization_Symbol_Minus;
2597 203 : } else if (mToken.mSymbol == '?') {
2598 0 : type = eCSSTokenSerialization_Symbol_Question;
2599 406 : } else if (mToken.mSymbol == '$' ||
2600 406 : mToken.mSymbol == '^' ||
2601 203 : mToken.mSymbol == '~') {
2602 0 : type = eCSSTokenSerialization_Symbol_Assorted;
2603 203 : } else if (mToken.mSymbol == '=') {
2604 0 : type = eCSSTokenSerialization_Symbol_Equals;
2605 203 : } else if (mToken.mSymbol == '|') {
2606 0 : type = eCSSTokenSerialization_Symbol_Bar;
2607 203 : } else if (mToken.mSymbol == '/') {
2608 13 : type = eCSSTokenSerialization_Symbol_Slash;
2609 190 : } else if (mToken.mSymbol == '*') {
2610 98 : type = eCSSTokenSerialization_Symbol_Asterisk;
2611 : }
2612 577 : UPDATE_RECORDING_TOKENS(type);
2613 577 : break;
2614 : }
2615 :
2616 : case eCSSToken_Function:
2617 1265 : if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
2618 : // Save the tokens before the "var(" to our resolved value.
2619 2108 : nsString recording;
2620 1054 : mScanner->StopRecording(recording);
2621 1054 : recording.Truncate(lengthBeforeVar);
2622 : AppendTokens(value, valueFirstToken, valueLastToken,
2623 1054 : recFirstToken, recLastToken, recording);
2624 1054 : recFirstToken = eCSSTokenSerialization_Nothing;
2625 1054 : recLastToken = eCSSTokenSerialization_Nothing;
2626 :
2627 3162 : if (!GetToken(true) ||
2628 2108 : mToken.mType != eCSSToken_Ident ||
2629 1054 : !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
2630 : // "var(" must be followed by an identifier, and it must be a
2631 : // custom property name.
2632 0 : return false;
2633 : }
2634 :
2635 : // Turn the custom property name into a variable name by removing the
2636 : // '--' prefix.
2637 1054 : MOZ_ASSERT(Substring(mToken.mIdent, 0,
2638 : CSS_CUSTOM_NAME_PREFIX_LENGTH).
2639 : EqualsLiteral("--"));
2640 : nsDependentString variableName(mToken.mIdent,
2641 2108 : CSS_CUSTOM_NAME_PREFIX_LENGTH);
2642 :
2643 : // Get the value of the identified variable. Note that we
2644 : // check if the variable value is the empty string, as that means
2645 : // that the variable was invalid at computed value time due to
2646 : // unresolveable variable references or cycles.
2647 2108 : nsString variableValue;
2648 : nsCSSTokenSerializationType varFirstToken, varLastToken;
2649 1054 : bool valid = aVariables->Get(variableName, variableValue,
2650 2108 : varFirstToken, varLastToken) &&
2651 2108 : !variableValue.IsEmpty();
2652 :
2653 2108 : if (!GetToken(true) ||
2654 1054 : mToken.IsSymbol(')')) {
2655 1054 : mScanner->StartRecording();
2656 1054 : if (!valid) {
2657 : // Invalid variable with no fallback.
2658 0 : return false;
2659 : }
2660 : // Valid variable with no fallback.
2661 : AppendTokens(value, valueFirstToken, valueLastToken,
2662 1054 : varFirstToken, varLastToken, variableValue);
2663 0 : } else if (mToken.IsSymbol(',')) {
2664 0 : mScanner->StartRecording();
2665 0 : if (!GetToken(false) ||
2666 0 : mToken.IsSymbol(')')) {
2667 : // Comma must be followed by at least one fallback token.
2668 0 : return false;
2669 : }
2670 0 : UngetToken();
2671 0 : if (valid) {
2672 : // Valid variable with ignored fallback.
2673 0 : mScanner->StopRecording();
2674 : AppendTokens(value, valueFirstToken, valueLastToken,
2675 0 : varFirstToken, varLastToken, variableValue);
2676 0 : bool ok = SkipBalancedContentUntil(')');
2677 0 : mScanner->StartRecording();
2678 0 : if (!ok) {
2679 0 : return false;
2680 : }
2681 : } else {
2682 0 : nsString fallback;
2683 0 : if (!ResolveValueWithVariableReferencesRec(fallback,
2684 : varFirstToken,
2685 : varLastToken,
2686 : aVariables)) {
2687 : // Fallback value had invalid tokens or an invalid variable reference
2688 : // that itself had no fallback.
2689 0 : return false;
2690 : }
2691 : AppendTokens(value, valueFirstToken, valueLastToken,
2692 0 : varFirstToken, varLastToken, fallback);
2693 : // Now we're either at the pushed back ')' that finished the
2694 : // fallback or at EOF.
2695 0 : DebugOnly<bool> gotToken = GetToken(false);
2696 0 : MOZ_ASSERT(!gotToken || mToken.IsSymbol(')'));
2697 : }
2698 : } else {
2699 : // Expected ',' or ')' after the variable name.
2700 0 : mScanner->StartRecording();
2701 0 : return false;
2702 : }
2703 : } else {
2704 211 : stack.AppendElement(')');
2705 211 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function);
2706 : }
2707 1265 : break;
2708 :
2709 : case eCSSToken_Bad_String:
2710 : case eCSSToken_Bad_URL:
2711 0 : return false;
2712 :
2713 : case eCSSToken_Whitespace:
2714 2421 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace);
2715 2421 : break;
2716 :
2717 : case eCSSToken_AtKeyword:
2718 : case eCSSToken_Hash:
2719 6 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash);
2720 6 : break;
2721 :
2722 : case eCSSToken_Number:
2723 514 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number);
2724 514 : break;
2725 :
2726 : case eCSSToken_Dimension:
2727 268 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension);
2728 268 : break;
2729 :
2730 : case eCSSToken_Ident:
2731 323 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident);
2732 323 : break;
2733 :
2734 : case eCSSToken_Percentage:
2735 64 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage);
2736 64 : break;
2737 :
2738 : case eCSSToken_URange:
2739 0 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange);
2740 0 : break;
2741 :
2742 : case eCSSToken_URL:
2743 4 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL);
2744 4 : break;
2745 :
2746 : case eCSSToken_HTMLComment:
2747 0 : if (mToken.mIdent[0] == '-') {
2748 0 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC);
2749 : } else {
2750 0 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
2751 : }
2752 0 : break;
2753 :
2754 : case eCSSToken_Dashmatch:
2755 0 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch);
2756 0 : break;
2757 :
2758 : case eCSSToken_Containsmatch:
2759 0 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch);
2760 0 : break;
2761 :
2762 : default:
2763 0 : MOZ_FALLTHROUGH_ASSERT("unexpected token type");
2764 : case eCSSToken_ID:
2765 : case eCSSToken_String:
2766 : case eCSSToken_Includes:
2767 : case eCSSToken_Beginsmatch:
2768 : case eCSSToken_Endsmatch:
2769 12 : UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
2770 12 : break;
2771 : }
2772 :
2773 5454 : lengthBeforeVar = mScanner->RecordingLength();
2774 : }
2775 :
2776 : #undef UPDATE_RECORDING_TOKENS
2777 :
2778 1024 : aResult.Append(value);
2779 1024 : StopRecordingAndAppendTokens(aResult, valueFirstToken, valueLastToken,
2780 1024 : recFirstToken, recLastToken, mScanner);
2781 :
2782 : // Append any implicitly closed brackets.
2783 1024 : if (!stack.IsEmpty()) {
2784 0 : do {
2785 0 : aResult.Append(stack.LastElement());
2786 0 : stack.TruncateLength(stack.Length() - 1);
2787 0 : } while (!stack.IsEmpty());
2788 0 : valueLastToken = eCSSTokenSerialization_Other;
2789 : }
2790 :
2791 1024 : mScanner->StartRecording();
2792 1024 : aResultFirstToken = valueFirstToken;
2793 1024 : aResultLastToken = valueLastToken;
2794 1024 : return true;
2795 : }
2796 :
2797 : bool
2798 1024 : CSSParserImpl::ResolveValueWithVariableReferences(
2799 : const CSSVariableValues* aVariables,
2800 : nsString& aResult,
2801 : nsCSSTokenSerializationType& aFirstToken,
2802 : nsCSSTokenSerializationType& aLastToken)
2803 : {
2804 1024 : aResult.Truncate(0);
2805 :
2806 : // Start recording before we read the first token.
2807 1024 : mScanner->StartRecording();
2808 :
2809 1024 : if (!GetToken(false)) {
2810 : // Value was empty since we reached EOF.
2811 0 : mScanner->StopRecording();
2812 0 : return false;
2813 : }
2814 :
2815 1024 : UngetToken();
2816 :
2817 2048 : nsString value;
2818 : nsCSSTokenSerializationType firstToken, lastToken;
2819 2048 : bool ok = ResolveValueWithVariableReferencesRec(value, firstToken, lastToken, aVariables) &&
2820 2048 : !GetToken(true);
2821 :
2822 1024 : mScanner->StopRecording();
2823 :
2824 1024 : if (ok) {
2825 1024 : aResult = value;
2826 1024 : aFirstToken = firstToken;
2827 1024 : aLastToken = lastToken;
2828 : }
2829 1024 : return ok;
2830 : }
2831 :
2832 : bool
2833 141 : CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue,
2834 : const CSSVariableValues* aVariables,
2835 : nsString& aResult,
2836 : nsCSSTokenSerializationType& aFirstToken,
2837 : nsCSSTokenSerializationType& aLastToken)
2838 : {
2839 282 : nsCSSScanner scanner(aPropertyValue, 0);
2840 :
2841 : // At this point, we know that aPropertyValue is syntactically correct
2842 : // for a token stream that has variable references. We also won't be
2843 : // interpreting any of the stream as we parse it, apart from expanding
2844 : // var() references, so we don't need a base URL etc. or any useful
2845 : // error reporting.
2846 282 : css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
2847 141 : InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
2848 :
2849 : bool valid = ResolveValueWithVariableReferences(aVariables, aResult,
2850 141 : aFirstToken, aLastToken);
2851 :
2852 141 : ReleaseScanner();
2853 282 : return valid;
2854 : }
2855 :
2856 : void
2857 883 : CSSParserImpl::ParsePropertyWithVariableReferences(
2858 : nsCSSPropertyID aPropertyID,
2859 : nsCSSPropertyID aShorthandPropertyID,
2860 : const nsAString& aValue,
2861 : const CSSVariableValues* aVariables,
2862 : nsRuleData* aRuleData,
2863 : nsIURI* aDocURL,
2864 : nsIURI* aBaseURL,
2865 : nsIPrincipal* aDocPrincipal,
2866 : CSSStyleSheet* aSheet,
2867 : uint32_t aLineNumber,
2868 : uint32_t aLineOffset)
2869 : {
2870 883 : mTempData.AssertInitialState();
2871 :
2872 : bool valid;
2873 1766 : nsString expandedValue;
2874 :
2875 : // Resolve any variable references in the property value.
2876 : {
2877 1766 : nsCSSScanner scanner(aValue, 0);
2878 1766 : css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
2879 883 : InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2880 :
2881 : nsCSSTokenSerializationType firstToken, lastToken;
2882 : valid = ResolveValueWithVariableReferences(aVariables, expandedValue,
2883 883 : firstToken, lastToken);
2884 883 : if (!valid) {
2885 0 : NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropertyID));
2886 0 : REPORT_UNEXPECTED(PEInvalidVariableReference);
2887 0 : REPORT_UNEXPECTED_P(PEValueParsingError, propName);
2888 0 : if (nsCSSProps::IsInherited(aPropertyID)) {
2889 0 : REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
2890 : } else {
2891 0 : REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
2892 : }
2893 0 : OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
2894 : }
2895 883 : ReleaseScanner();
2896 : }
2897 :
2898 : nsCSSPropertyID propertyToParse =
2899 883 : aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID :
2900 883 : aPropertyID;
2901 :
2902 : // Parse the property with that resolved value.
2903 883 : if (valid) {
2904 1766 : nsCSSScanner scanner(expandedValue, 0);
2905 1766 : css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
2906 883 : InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
2907 883 : valid = ParseProperty(propertyToParse);
2908 883 : if (valid && GetToken(true)) {
2909 0 : REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
2910 0 : valid = false;
2911 : }
2912 883 : if (!valid) {
2913 : NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(
2914 0 : propertyToParse));
2915 0 : REPORT_UNEXPECTED_P_V(PEValueWithVariablesParsingErrorInValue,
2916 : propName, expandedValue);
2917 0 : if (nsCSSProps::IsInherited(aPropertyID)) {
2918 0 : REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
2919 : } else {
2920 0 : REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
2921 : }
2922 0 : OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
2923 : }
2924 883 : ReleaseScanner();
2925 : }
2926 :
2927 : // If the property could not be parsed with the resolved value, then we
2928 : // treat it as if the value were 'initial' or 'inherit', depending on whether
2929 : // the property is an inherited property.
2930 883 : if (!valid) {
2931 0 : nsCSSValue defaultValue;
2932 0 : if (nsCSSProps::IsInherited(aPropertyID)) {
2933 0 : defaultValue.SetInheritValue();
2934 : } else {
2935 0 : defaultValue.SetInitialValue();
2936 : }
2937 0 : mTempData.AddLonghandProperty(aPropertyID, defaultValue);
2938 : }
2939 :
2940 : // Copy the property value into the rule data.
2941 883 : mTempData.MapRuleInfoInto(aPropertyID, aRuleData);
2942 :
2943 883 : mTempData.ClearProperty(propertyToParse);
2944 883 : mTempData.AssertInitialState();
2945 883 : }
2946 :
2947 : already_AddRefed<nsIAtom>
2948 0 : CSSParserImpl::ParseCounterStyleName(const nsAString& aBuffer, nsIURI* aURL)
2949 : {
2950 0 : nsCSSScanner scanner(aBuffer, 0);
2951 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURL);
2952 0 : InitScanner(scanner, reporter, aURL, aURL, nullptr);
2953 :
2954 0 : nsCOMPtr<nsIAtom> name = ParseCounterStyleName(true);
2955 0 : bool success = name && !GetToken(true);
2956 :
2957 0 : OUTPUT_ERROR();
2958 0 : ReleaseScanner();
2959 :
2960 0 : return success ? name.forget() : nullptr;
2961 : }
2962 :
2963 : bool
2964 0 : CSSParserImpl::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
2965 : const nsAString& aBuffer,
2966 : nsIURI* aSheetURL,
2967 : nsIURI* aBaseURL,
2968 : nsIPrincipal* aSheetPrincipal,
2969 : nsCSSValue& aValue)
2970 : {
2971 0 : nsCSSScanner scanner(aBuffer, 0);
2972 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
2973 0 : InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
2974 :
2975 0 : bool success = ParseCounterDescriptorValue(aDescID, aValue) &&
2976 0 : !GetToken(true);
2977 :
2978 0 : OUTPUT_ERROR();
2979 0 : ReleaseScanner();
2980 :
2981 0 : return success;
2982 : }
2983 :
2984 : bool
2985 0 : CSSParserImpl::ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
2986 : const nsAString& aBuffer,
2987 : nsIURI* aSheetURL,
2988 : nsIURI* aBaseURL,
2989 : nsIPrincipal* aSheetPrincipal,
2990 : nsCSSValue& aValue)
2991 : {
2992 0 : nsCSSScanner scanner(aBuffer, 0);
2993 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
2994 0 : InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
2995 :
2996 0 : bool success = ParseFontDescriptorValue(aDescID, aValue) &&
2997 0 : !GetToken(true);
2998 :
2999 0 : OUTPUT_ERROR();
3000 0 : ReleaseScanner();
3001 :
3002 0 : return success;
3003 : }
3004 :
3005 : //----------------------------------------------------------------------
3006 :
3007 : bool
3008 177452 : CSSParserImpl::GetToken(bool aSkipWS)
3009 : {
3010 177452 : if (mHavePushBack) {
3011 76929 : mHavePushBack = false;
3012 76929 : if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) {
3013 76911 : return true;
3014 : }
3015 : }
3016 100541 : return mScanner->Next(mToken, aSkipWS ?
3017 : eCSSScannerExclude_WhitespaceAndComments :
3018 100541 : eCSSScannerExclude_Comments);
3019 : }
3020 :
3021 : void
3022 77160 : CSSParserImpl::UngetToken()
3023 : {
3024 77160 : NS_PRECONDITION(!mHavePushBack, "double pushback");
3025 77160 : mHavePushBack = true;
3026 77160 : }
3027 :
3028 : bool
3029 22370 : CSSParserImpl::GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum)
3030 : {
3031 : // Peek at next token so that mScanner updates line and column vals
3032 22370 : if (!GetToken(aSkipWS)) {
3033 454 : return false;
3034 : }
3035 21916 : UngetToken();
3036 : // The scanner uses one-indexing for line numbers but zero-indexing
3037 : // for column numbers.
3038 21916 : *linenum = mScanner->GetLineNumber();
3039 21916 : *colnum = 1 + mScanner->GetColumnNumber();
3040 21916 : return true;
3041 : }
3042 :
3043 : bool
3044 24841 : CSSParserImpl::ExpectSymbol(char16_t aSymbol,
3045 : bool aSkipWS)
3046 : {
3047 24841 : if (!GetToken(aSkipWS)) {
3048 : // CSS2.1 specifies that all "open constructs" are to be closed at
3049 : // EOF. It simplifies higher layers if we claim to have found an
3050 : // ), ], }, or ; if we encounter EOF while looking for one of them.
3051 : // Do still issue a diagnostic, to aid debugging.
3052 372 : if (aSymbol == ')' || aSymbol == ']' ||
3053 372 : aSymbol == '}' || aSymbol == ';') {
3054 0 : REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
3055 0 : return true;
3056 : }
3057 : else
3058 372 : return false;
3059 : }
3060 24469 : if (mToken.IsSymbol(aSymbol)) {
3061 16687 : return true;
3062 : }
3063 7782 : UngetToken();
3064 7782 : return false;
3065 : }
3066 :
3067 : // Checks to see if we're at the end of a property. If an error occurs during
3068 : // the check, does not signal a parse error.
3069 : bool
3070 8295 : CSSParserImpl::CheckEndProperty()
3071 : {
3072 8295 : if (!GetToken(true)) {
3073 1057 : return true; // properties may end with eof
3074 : }
3075 13889 : if ((eCSSToken_Symbol == mToken.mType) &&
3076 7140 : ((';' == mToken.mSymbol) ||
3077 496 : ('!' == mToken.mSymbol) ||
3078 7 : ('}' == mToken.mSymbol) ||
3079 0 : (')' == mToken.mSymbol))) {
3080 : // XXX need to verify that ! is only followed by "important [;|}]
3081 : // XXX this requires a multi-token pushback buffer
3082 6651 : UngetToken();
3083 6651 : return true;
3084 : }
3085 587 : UngetToken();
3086 587 : return false;
3087 : }
3088 :
3089 : // Checks if we're at the end of a property, raising an error if we're not.
3090 : bool
3091 7498 : CSSParserImpl::ExpectEndProperty()
3092 : {
3093 7498 : if (CheckEndProperty())
3094 7454 : return true;
3095 :
3096 : // If we're here, we read something incorrect, so we should report it.
3097 44 : REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
3098 44 : return false;
3099 : }
3100 :
3101 : // Parses the priority suffix on a property, which at present may be
3102 : // either '!important' or nothing.
3103 : CSSParserImpl::PriorityParsingStatus
3104 6448 : CSSParserImpl::ParsePriority()
3105 : {
3106 6448 : if (!GetToken(true)) {
3107 3 : return ePriority_None; // properties may end with EOF
3108 : }
3109 6445 : if (!mToken.IsSymbol('!')) {
3110 5928 : UngetToken();
3111 5928 : return ePriority_None; // dunno what it is, but it's not a priority
3112 : }
3113 :
3114 517 : if (!GetToken(true)) {
3115 : // EOF is not ok after !
3116 0 : REPORT_UNEXPECTED_EOF(PEImportantEOF);
3117 0 : return ePriority_Error;
3118 : }
3119 :
3120 1034 : if (mToken.mType != eCSSToken_Ident ||
3121 517 : !mToken.mIdent.LowerCaseEqualsLiteral("important")) {
3122 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
3123 0 : UngetToken();
3124 0 : return ePriority_Error;
3125 : }
3126 :
3127 517 : return ePriority_Important;
3128 : }
3129 :
3130 : nsAString*
3131 325 : CSSParserImpl::NextIdent()
3132 : {
3133 : // XXX Error reporting?
3134 325 : if (!GetToken(true)) {
3135 0 : return nullptr;
3136 : }
3137 325 : if (eCSSToken_Ident != mToken.mType) {
3138 88 : UngetToken();
3139 88 : return nullptr;
3140 : }
3141 237 : return &mToken.mIdent;
3142 : }
3143 :
3144 : bool
3145 0 : CSSParserImpl::SkipAtRule(bool aInsideBlock)
3146 : {
3147 : for (;;) {
3148 0 : if (!GetToken(true)) {
3149 0 : REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2);
3150 0 : return false;
3151 : }
3152 0 : if (eCSSToken_Symbol == mToken.mType) {
3153 0 : char16_t symbol = mToken.mSymbol;
3154 0 : if (symbol == ';') {
3155 0 : break;
3156 : }
3157 0 : if (aInsideBlock && symbol == '}') {
3158 : // The closing } doesn't belong to us.
3159 0 : UngetToken();
3160 0 : break;
3161 : }
3162 0 : if (symbol == '{') {
3163 0 : SkipUntil('}');
3164 0 : break;
3165 0 : } else if (symbol == '(') {
3166 0 : SkipUntil(')');
3167 0 : } else if (symbol == '[') {
3168 0 : SkipUntil(']');
3169 : }
3170 0 : } else if (eCSSToken_Function == mToken.mType ||
3171 0 : eCSSToken_Bad_URL == mToken.mType) {
3172 0 : SkipUntil(')');
3173 : }
3174 0 : }
3175 0 : return true;
3176 : }
3177 :
3178 : bool
3179 286 : CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
3180 : void* aData,
3181 : bool aInAtRule)
3182 : {
3183 :
3184 : nsCSSSection newSection;
3185 : bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*);
3186 :
3187 328 : if ((mSection <= eCSSSection_Charset) &&
3188 42 : (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
3189 0 : parseFunc = &CSSParserImpl::ParseCharsetRule;
3190 0 : newSection = eCSSSection_Import; // only one charset allowed
3191 :
3192 330 : } else if ((mSection <= eCSSSection_Import) &&
3193 44 : mToken.mIdent.LowerCaseEqualsLiteral("import")) {
3194 3 : parseFunc = &CSSParserImpl::ParseImportRule;
3195 3 : newSection = eCSSSection_Import;
3196 :
3197 350 : } else if ((mSection <= eCSSSection_NameSpace) &&
3198 67 : mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
3199 64 : parseFunc = &CSSParserImpl::ParseNameSpaceRule;
3200 64 : newSection = eCSSSection_NameSpace;
3201 :
3202 219 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
3203 48 : parseFunc = &CSSParserImpl::ParseMediaRule;
3204 48 : newSection = eCSSSection_General;
3205 :
3206 171 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
3207 2 : parseFunc = &CSSParserImpl::ParseMozDocumentRule;
3208 2 : newSection = eCSSSection_General;
3209 :
3210 169 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
3211 0 : parseFunc = &CSSParserImpl::ParseFontFaceRule;
3212 0 : newSection = eCSSSection_General;
3213 :
3214 169 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values")) {
3215 0 : parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule;
3216 0 : newSection = eCSSSection_General;
3217 :
3218 169 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
3219 0 : parseFunc = &CSSParserImpl::ParsePageRule;
3220 0 : newSection = eCSSSection_General;
3221 :
3222 338 : } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation,
3223 169 : EnabledState()) &&
3224 338 : mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) ||
3225 338 : (nsCSSProps::IsEnabled(eCSSPropertyAlias_WebkitAnimation) &&
3226 507 : mToken.mIdent.LowerCaseEqualsLiteral("-webkit-keyframes")) ||
3227 169 : mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) {
3228 31 : parseFunc = &CSSParserImpl::ParseKeyframesRule;
3229 31 : newSection = eCSSSection_General;
3230 :
3231 138 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports")) {
3232 0 : parseFunc = &CSSParserImpl::ParseSupportsRule;
3233 0 : newSection = eCSSSection_General;
3234 :
3235 138 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("counter-style")) {
3236 138 : parseFunc = &CSSParserImpl::ParseCounterStyleRule;
3237 138 : newSection = eCSSSection_General;
3238 :
3239 : } else {
3240 0 : if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
3241 0 : REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
3242 0 : OUTPUT_ERROR();
3243 : }
3244 : // Skip over unsupported at rule, don't advance section
3245 0 : return SkipAtRule(aInAtRule);
3246 : }
3247 :
3248 : // Inside of @-rules, only the rules that can occur anywhere
3249 : // are allowed.
3250 286 : bool unnestable = aInAtRule && newSection != eCSSSection_General;
3251 286 : if (unnestable) {
3252 0 : REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule);
3253 : }
3254 :
3255 286 : if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) {
3256 : // Skip over invalid at rule, don't advance section
3257 0 : OUTPUT_ERROR();
3258 0 : return SkipAtRule(aInAtRule);
3259 : }
3260 :
3261 : // Nested @-rules don't affect the top-level rule chain requirement
3262 286 : if (!aInAtRule) {
3263 286 : mSection = newSection;
3264 : }
3265 :
3266 286 : return true;
3267 : }
3268 :
3269 : bool
3270 0 : CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
3271 : void* aData)
3272 : {
3273 : uint32_t linenum, colnum;
3274 0 : if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3275 0 : !GetToken(true)) {
3276 0 : REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
3277 0 : return false;
3278 : }
3279 :
3280 0 : if (eCSSToken_String != mToken.mType) {
3281 0 : UngetToken();
3282 0 : REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
3283 0 : return false;
3284 : }
3285 :
3286 0 : nsAutoString charset = mToken.mIdent;
3287 :
3288 0 : if (!ExpectSymbol(';', true)) {
3289 0 : return false;
3290 : }
3291 :
3292 : // It's intentional that we don't create a rule object for @charset rules.
3293 :
3294 0 : return true;
3295 : }
3296 :
3297 : bool
3298 83 : CSSParserImpl::ParseURLOrString(nsString& aURL)
3299 : {
3300 83 : if (!GetToken(true)) {
3301 0 : return false;
3302 : }
3303 83 : if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) {
3304 83 : aURL = mToken.mIdent;
3305 83 : return true;
3306 : }
3307 0 : UngetToken();
3308 0 : return false;
3309 : }
3310 :
3311 : bool
3312 51 : CSSParserImpl::ParseMediaQuery(eMediaQueryType aQueryType,
3313 : nsMediaQuery **aQuery,
3314 : bool *aHitStop)
3315 : {
3316 51 : *aQuery = nullptr;
3317 51 : *aHitStop = false;
3318 51 : bool inAtRule = aQueryType == eMediaQueryAtRule;
3319 : // Attempt to parse a single condition and stop
3320 51 : bool singleCondition = aQueryType == eMediaQuerySingleCondition;
3321 :
3322 : // "If the comma-separated list is the empty list it is assumed to
3323 : // specify the media query 'all'." (css3-mediaqueries, section
3324 : // "Media Queries")
3325 51 : if (!GetToken(true)) {
3326 0 : *aHitStop = true;
3327 : // expected termination by EOF
3328 0 : if (!inAtRule)
3329 0 : return true;
3330 :
3331 : // unexpected termination by EOF
3332 0 : REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
3333 0 : return true;
3334 : }
3335 :
3336 83 : if (eCSSToken_Symbol == mToken.mType && inAtRule &&
3337 64 : (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) {
3338 0 : *aHitStop = true;
3339 0 : UngetToken();
3340 0 : return true;
3341 : }
3342 51 : UngetToken();
3343 :
3344 51 : nsMediaQuery* query = new nsMediaQuery;
3345 51 : *aQuery = query;
3346 :
3347 51 : if (ExpectSymbol('(', true)) {
3348 : // we got an expression without a media type
3349 35 : UngetToken(); // so ParseMediaQueryExpression can handle it
3350 35 : query->SetType(nsGkAtoms::all);
3351 35 : query->SetTypeOmitted();
3352 : // Just parse the first expression here.
3353 35 : if (!ParseMediaQueryExpression(query)) {
3354 0 : OUTPUT_ERROR();
3355 0 : query->SetHadUnknownExpression();
3356 : }
3357 16 : } else if (singleCondition) {
3358 : // Since we are only trying to consume a single condition, which precludes
3359 : // media types and not/only, this should be the same as reaching immediate
3360 : // EOF (no condition to parse)
3361 0 : *aHitStop = true;
3362 0 : return true;
3363 : } else {
3364 32 : nsCOMPtr<nsIAtom> mediaType;
3365 16 : bool gotNotOrOnly = false;
3366 : for (;;) {
3367 20 : if (!GetToken(true)) {
3368 0 : REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
3369 0 : return false;
3370 : }
3371 18 : if (eCSSToken_Ident != mToken.mType) {
3372 0 : REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
3373 0 : UngetToken();
3374 0 : return false;
3375 : }
3376 : // case insensitive from CSS - must be lower cased
3377 18 : nsContentUtils::ASCIIToLower(mToken.mIdent);
3378 18 : mediaType = NS_Atomize(mToken.mIdent);
3379 18 : if (!gotNotOrOnly && mediaType == nsGkAtoms::_not) {
3380 2 : gotNotOrOnly = true;
3381 2 : query->SetNegated();
3382 16 : } else if (!gotNotOrOnly && mediaType == nsGkAtoms::only) {
3383 0 : gotNotOrOnly = true;
3384 0 : query->SetHasOnly();
3385 48 : } else if (mediaType == nsGkAtoms::_not ||
3386 32 : mediaType == nsGkAtoms::only ||
3387 48 : mediaType == nsGkAtoms::_and ||
3388 16 : mediaType == nsGkAtoms::_or) {
3389 0 : REPORT_UNEXPECTED_TOKEN(PEGatherMediaReservedMediaType);
3390 0 : UngetToken();
3391 0 : return false;
3392 : } else {
3393 : // valid media type
3394 16 : break;
3395 : }
3396 : }
3397 16 : query->SetType(mediaType);
3398 : }
3399 :
3400 : for (;;) {
3401 71 : if (!GetToken(true)) {
3402 3 : *aHitStop = true;
3403 : // expected termination by EOF
3404 3 : if (!inAtRule)
3405 3 : break;
3406 :
3407 : // unexpected termination by EOF
3408 0 : REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
3409 0 : break;
3410 : }
3411 :
3412 106 : if (eCSSToken_Symbol == mToken.mType && inAtRule &&
3413 96 : (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) {
3414 48 : *aHitStop = true;
3415 48 : UngetToken();
3416 48 : break;
3417 : }
3418 20 : if (!singleCondition &&
3419 10 : eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
3420 : // Done with the expressions for this query
3421 0 : break;
3422 : }
3423 20 : if (eCSSToken_Ident != mToken.mType ||
3424 10 : !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
3425 0 : if (singleCondition) {
3426 : // We have a condition at this point -- if we're not chained to other
3427 : // conditions with and/or, we're done.
3428 0 : UngetToken();
3429 0 : break;
3430 : } else {
3431 0 : REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
3432 0 : UngetToken();
3433 0 : return false;
3434 : }
3435 : }
3436 10 : if (!ParseMediaQueryExpression(query)) {
3437 0 : OUTPUT_ERROR();
3438 0 : query->SetHadUnknownExpression();
3439 : }
3440 : }
3441 51 : return true;
3442 : }
3443 :
3444 : // Returns false only when there is a low-level error in the scanner
3445 : // (out-of-memory).
3446 : bool
3447 51 : CSSParserImpl::GatherMedia(nsMediaList* aMedia,
3448 : bool aInAtRule)
3449 : {
3450 51 : eMediaQueryType type = aInAtRule ? eMediaQueryAtRule : eMediaQueryNormal;
3451 : for (;;) {
3452 51 : nsAutoPtr<nsMediaQuery> query;
3453 : bool hitStop;
3454 51 : if (!ParseMediaQuery(type, getter_Transfers(query), &hitStop)) {
3455 0 : NS_ASSERTION(!hitStop, "should return true when hit stop");
3456 0 : OUTPUT_ERROR();
3457 0 : if (query) {
3458 0 : query->SetHadUnknownExpression();
3459 : }
3460 0 : if (aInAtRule) {
3461 : const char16_t stopChars[] =
3462 0 : { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) };
3463 0 : SkipUntilOneOf(stopChars);
3464 : } else {
3465 0 : SkipUntil(',');
3466 : }
3467 : // Rely on SkipUntilOneOf leaving mToken around as the last token read.
3468 0 : if (mToken.mType == eCSSToken_Symbol && aInAtRule &&
3469 0 : (mToken.mSymbol == '{' || mToken.mSymbol == ';' || mToken.mSymbol == '}')) {
3470 0 : UngetToken();
3471 0 : hitStop = true;
3472 : }
3473 : }
3474 51 : if (query) {
3475 51 : aMedia->AppendQuery(query);
3476 : }
3477 51 : if (hitStop) {
3478 51 : break;
3479 : }
3480 0 : }
3481 51 : return true;
3482 : }
3483 :
3484 : bool
3485 45 : CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
3486 : {
3487 45 : if (!ExpectSymbol('(', true)) {
3488 0 : REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
3489 0 : return false;
3490 : }
3491 45 : if (! GetToken(true)) {
3492 0 : REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
3493 0 : return false;
3494 : }
3495 45 : if (eCSSToken_Ident != mToken.mType) {
3496 0 : REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
3497 0 : UngetToken();
3498 0 : SkipUntil(')');
3499 0 : return false;
3500 : }
3501 :
3502 45 : nsMediaExpression *expr = aQuery->NewExpression();
3503 :
3504 : // case insensitive from CSS - must be lower cased
3505 45 : nsContentUtils::ASCIIToLower(mToken.mIdent);
3506 90 : nsDependentString featureString(mToken.mIdent, 0);
3507 45 : uint8_t satisfiedReqFlags = 0;
3508 :
3509 : // Strip off "-webkit-" prefix from featureString:
3510 180 : if (sWebkitPrefixedAliasesEnabled &&
3511 180 : StringBeginsWith(featureString, NS_LITERAL_STRING("-webkit-"))) {
3512 0 : satisfiedReqFlags |= nsMediaFeature::eHasWebkitPrefix;
3513 0 : featureString.Rebind(featureString, 8);
3514 : }
3515 45 : if (sWebkitDevicePixelRatioEnabled) {
3516 0 : satisfiedReqFlags |= nsMediaFeature::eWebkitDevicePixelRatioPrefEnabled;
3517 : }
3518 :
3519 : // Strip off "min-"/"max-" prefix from featureString:
3520 45 : if (StringBeginsWith(featureString, NS_LITERAL_STRING("min-"))) {
3521 23 : expr->mRange = nsMediaExpression::eMin;
3522 23 : featureString.Rebind(featureString, 4);
3523 22 : } else if (StringBeginsWith(featureString, NS_LITERAL_STRING("max-"))) {
3524 6 : expr->mRange = nsMediaExpression::eMax;
3525 6 : featureString.Rebind(featureString, 4);
3526 : } else {
3527 16 : expr->mRange = nsMediaExpression::eEqual;
3528 : }
3529 :
3530 90 : nsCOMPtr<nsIAtom> mediaFeatureAtom = NS_Atomize(featureString);
3531 45 : const nsMediaFeature *feature = nsMediaFeatures::features;
3532 1169 : for (; feature->mName; ++feature) {
3533 : // See if name matches & all requirement flags are satisfied:
3534 : // (We check requirements by turning off all of the flags that have been
3535 : // satisfied, and then see if the result is 0.)
3536 652 : if (*(feature->mName) == mediaFeatureAtom &&
3537 45 : !(feature->mReqFlags & ~satisfiedReqFlags)) {
3538 45 : break;
3539 : }
3540 : }
3541 90 : if (!feature->mName ||
3542 74 : (expr->mRange != nsMediaExpression::eEqual &&
3543 29 : feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
3544 0 : REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
3545 0 : SkipUntil(')');
3546 0 : return false;
3547 : }
3548 45 : expr->mFeature = feature;
3549 :
3550 45 : if (!GetToken(true) || mToken.IsSymbol(')')) {
3551 : // Query expressions for any feature can be given without a value.
3552 : // However, min/max prefixes are not allowed.
3553 8 : if (expr->mRange != nsMediaExpression::eEqual) {
3554 0 : REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
3555 0 : return false;
3556 : }
3557 8 : expr->mValue.Reset();
3558 8 : return true;
3559 : }
3560 :
3561 37 : if (!mToken.IsSymbol(':')) {
3562 0 : REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
3563 0 : UngetToken();
3564 0 : SkipUntil(')');
3565 0 : return false;
3566 : }
3567 :
3568 37 : bool rv = false;
3569 37 : switch (feature->mValueType) {
3570 : case nsMediaFeature::eLength:
3571 6 : rv = ParseSingleTokenNonNegativeVariant(expr->mValue, VARIANT_LENGTH,
3572 6 : nullptr);
3573 6 : break;
3574 : case nsMediaFeature::eInteger:
3575 : case nsMediaFeature::eBoolInteger:
3576 0 : rv = ParseNonNegativeInteger(expr->mValue);
3577 : // Enforce extra restrictions for eBoolInteger
3578 0 : if (rv &&
3579 0 : feature->mValueType == nsMediaFeature::eBoolInteger &&
3580 0 : expr->mValue.GetIntValue() > 1)
3581 0 : rv = false;
3582 0 : break;
3583 : case nsMediaFeature::eFloat:
3584 0 : rv = ParseNonNegativeNumber(expr->mValue);
3585 0 : break;
3586 : case nsMediaFeature::eIntRatio:
3587 : {
3588 : // Two integers separated by '/', with optional whitespace on
3589 : // either side of the '/'.
3590 0 : RefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
3591 0 : expr->mValue.SetArrayValue(a, eCSSUnit_Array);
3592 : // We don't bother with ParseNonNegativeVariant since we have to
3593 : // check for != 0 as well; no need to worry about the UngetToken
3594 : // since we're throwing out up to the next ')' anyway.
3595 0 : rv = ParseSingleTokenVariant(a->Item(0), VARIANT_INTEGER, nullptr) &&
3596 0 : a->Item(0).GetIntValue() > 0 &&
3597 0 : ExpectSymbol('/', true) &&
3598 0 : ParseSingleTokenVariant(a->Item(1), VARIANT_INTEGER, nullptr) &&
3599 0 : a->Item(1).GetIntValue() > 0;
3600 : }
3601 0 : break;
3602 : case nsMediaFeature::eResolution:
3603 30 : rv = GetToken(true);
3604 30 : if (!rv)
3605 0 : break;
3606 30 : rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f;
3607 30 : if (!rv) {
3608 0 : UngetToken();
3609 0 : break;
3610 : }
3611 : // No worries about whether unitless zero is allowed, since the
3612 : // value must be positive (and we checked that above).
3613 30 : NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
3614 30 : if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
3615 0 : expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
3616 30 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) {
3617 30 : expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
3618 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
3619 0 : expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
3620 : } else {
3621 0 : rv = false;
3622 : }
3623 30 : break;
3624 : case nsMediaFeature::eEnumerated:
3625 0 : rv = ParseSingleTokenVariant(expr->mValue, VARIANT_KEYWORD,
3626 0 : feature->mData.mKeywordTable);
3627 0 : break;
3628 : case nsMediaFeature::eIdent:
3629 1 : rv = ParseSingleTokenVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr);
3630 1 : break;
3631 : }
3632 37 : if (!rv || !ExpectSymbol(')', true)) {
3633 0 : REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
3634 0 : SkipUntil(')');
3635 0 : return false;
3636 : }
3637 :
3638 37 : return true;
3639 : }
3640 :
3641 : // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
3642 : bool
3643 3 : CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
3644 : {
3645 6 : RefPtr<nsMediaList> media = new nsMediaList();
3646 :
3647 : uint32_t linenum, colnum;
3648 6 : nsAutoString url;
3649 6 : if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3650 3 : !ParseURLOrString(url)) {
3651 0 : REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
3652 0 : return false;
3653 : }
3654 :
3655 3 : if (!ExpectSymbol(';', true)) {
3656 0 : if (!GatherMedia(media, true) ||
3657 0 : !ExpectSymbol(';', true)) {
3658 0 : REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
3659 : // don't advance section, simply ignore invalid @import
3660 0 : return false;
3661 : }
3662 :
3663 : // Safe to assert this, since we ensured that there is something
3664 : // other than the ';' coming after the @import's url() token.
3665 0 : NS_ASSERTION(media->Length() != 0, "media list must be nonempty");
3666 : }
3667 :
3668 3 : ProcessImport(url, media, aAppendFunc, aData, linenum, colnum);
3669 3 : return true;
3670 : }
3671 :
3672 : void
3673 3 : CSSParserImpl::ProcessImport(const nsString& aURLSpec,
3674 : nsMediaList* aMedia,
3675 : RuleAppendFunc aAppendFunc,
3676 : void* aData,
3677 : uint32_t aLineNumber,
3678 : uint32_t aColumnNumber)
3679 : {
3680 : RefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec,
3681 : aLineNumber,
3682 6 : aColumnNumber);
3683 3 : (*aAppendFunc)(rule, aData);
3684 :
3685 : // Diagnose bad URIs even if we don't have a child loader.
3686 6 : nsCOMPtr<nsIURI> url;
3687 : // Charset will be deduced from mBaseURI, which is more or less correct.
3688 3 : nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI);
3689 :
3690 3 : if (NS_FAILED(rv)) {
3691 0 : if (rv == NS_ERROR_MALFORMED_URI) {
3692 : // import url is bad
3693 0 : REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
3694 0 : OUTPUT_ERROR();
3695 : }
3696 0 : return;
3697 : }
3698 :
3699 3 : if (mChildLoader) {
3700 3 : mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule, mReusableSheets);
3701 : }
3702 : }
3703 :
3704 : // Parse the {} part of an @media or @-moz-document rule.
3705 : bool
3706 50 : CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
3707 : RuleAppendFunc aAppendFunc,
3708 : void* aData)
3709 : {
3710 : // XXXbz this could use better error reporting throughout the method
3711 50 : if (!ExpectSymbol('{', true)) {
3712 0 : return false;
3713 : }
3714 :
3715 : // push rule on stack, loop over children
3716 50 : PushGroup(aRule);
3717 50 : nsCSSSection holdSection = mSection;
3718 50 : mSection = eCSSSection_General;
3719 :
3720 : for (;;) {
3721 : // Get next non-whitespace token
3722 308 : if (! GetToken(true)) {
3723 0 : REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2);
3724 0 : break;
3725 : }
3726 179 : if (mToken.IsSymbol('}')) { // done!
3727 50 : UngetToken();
3728 50 : break;
3729 : }
3730 129 : if (eCSSToken_AtKeyword == mToken.mType) {
3731 : // Parse for nested rules
3732 0 : ParseAtRule(aAppendFunc, aData, true);
3733 0 : continue;
3734 : }
3735 129 : UngetToken();
3736 129 : ParseRuleSet(AppendRuleToSheet, this, true);
3737 : }
3738 50 : PopGroup();
3739 :
3740 50 : if (!ExpectSymbol('}', true)) {
3741 0 : mSection = holdSection;
3742 0 : return false;
3743 : }
3744 50 : (*aAppendFunc)(aRule, aData);
3745 50 : return true;
3746 : }
3747 :
3748 : // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
3749 : bool
3750 48 : CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
3751 : {
3752 96 : RefPtr<nsMediaList> media = new nsMediaList();
3753 : uint32_t linenum, colnum;
3754 96 : if (GetNextTokenLocation(true, &linenum, &colnum) &&
3755 48 : GatherMedia(media, true)) {
3756 : // XXXbz this could use better error reporting throughout the method
3757 48 : RefPtr<css::MediaRule> rule = new css::MediaRule(linenum, colnum);
3758 : // Append first, so when we do SetMedia() the rule
3759 : // knows what its stylesheet is.
3760 48 : if (ParseGroupRule(rule, aAppendFunc, aData)) {
3761 48 : rule->SetMedia(media);
3762 48 : return true;
3763 : }
3764 : }
3765 :
3766 0 : return false;
3767 : }
3768 :
3769 : // Parse a @-moz-document rule. This is like an @media rule, but instead
3770 : // of a medium it has a nonempty list of items where each item is either
3771 : // url(), url-prefix(), or domain().
3772 : bool
3773 2 : CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
3774 : {
3775 2 : css::DocumentRule::URL *urls = nullptr;
3776 2 : css::DocumentRule::URL **next = &urls;
3777 :
3778 : uint32_t linenum, colnum;
3779 2 : if (!GetNextTokenLocation(true, &linenum, &colnum)) {
3780 0 : return false;
3781 : }
3782 :
3783 2 : do {
3784 2 : if (!GetToken(true)) {
3785 0 : REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF);
3786 0 : delete urls;
3787 0 : return false;
3788 : }
3789 :
3790 4 : if (!(eCSSToken_URL == mToken.mType ||
3791 4 : (eCSSToken_Function == mToken.mType &&
3792 2 : (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
3793 0 : mToken.mIdent.LowerCaseEqualsLiteral("domain") ||
3794 0 : mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) {
3795 0 : REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc2);
3796 0 : UngetToken();
3797 0 : delete urls;
3798 0 : return false;
3799 : }
3800 2 : css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL;
3801 2 : next = &cur->next;
3802 2 : if (mToken.mType == eCSSToken_URL) {
3803 0 : cur->func = URLMatchingFunction::eURL;
3804 0 : CopyUTF16toUTF8(mToken.mIdent, cur->url);
3805 2 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) {
3806 : // regexp() is different from url-prefix() and domain() (but
3807 : // probably the way they *should* have been* in that it requires a
3808 : // string argument, and doesn't try to behave like url().
3809 0 : cur->func = URLMatchingFunction::eRegExp;
3810 0 : GetToken(true);
3811 : // copy before we know it's valid (but before ExpectSymbol changes
3812 : // mToken.mIdent)
3813 0 : CopyUTF16toUTF8(mToken.mIdent, cur->url);
3814 0 : if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) {
3815 0 : REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString);
3816 0 : SkipUntil(')');
3817 0 : delete urls;
3818 0 : return false;
3819 : }
3820 : } else {
3821 2 : if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
3822 2 : cur->func = URLMatchingFunction::eURLPrefix;
3823 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
3824 0 : cur->func = URLMatchingFunction::eDomain;
3825 : }
3826 :
3827 2 : NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point");
3828 2 : mScanner->NextURL(mToken);
3829 2 : if (mToken.mType != eCSSToken_URL) {
3830 0 : REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
3831 0 : SkipUntil(')');
3832 0 : delete urls;
3833 0 : return false;
3834 : }
3835 :
3836 : // We could try to make the URL (as long as it's not domain())
3837 : // canonical and absolute with NS_NewURI and GetSpec, but I'm
3838 : // inclined to think we shouldn't.
3839 2 : CopyUTF16toUTF8(mToken.mIdent, cur->url);
3840 : }
3841 : } while (ExpectSymbol(',', true));
3842 :
3843 4 : RefPtr<css::DocumentRule> rule = new css::DocumentRule(linenum, colnum);
3844 2 : rule->SetURLs(urls);
3845 :
3846 2 : return ParseGroupRule(rule, aAppendFunc, aData);
3847 : }
3848 :
3849 : // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
3850 : bool
3851 64 : CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
3852 : {
3853 : uint32_t linenum, colnum;
3854 128 : if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3855 64 : !GetToken(true)) {
3856 0 : REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
3857 0 : return false;
3858 : }
3859 :
3860 128 : nsAutoString prefix;
3861 128 : nsAutoString url;
3862 :
3863 64 : if (eCSSToken_Ident == mToken.mType) {
3864 28 : prefix = mToken.mIdent;
3865 : // user-specified identifiers are case-sensitive (bug 416106)
3866 : } else {
3867 36 : UngetToken();
3868 : }
3869 :
3870 64 : if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) {
3871 0 : if (mHavePushBack) {
3872 0 : REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
3873 : } else {
3874 0 : REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
3875 : }
3876 0 : return false;
3877 : }
3878 :
3879 64 : ProcessNameSpace(prefix, url, aAppendFunc, aData, linenum, colnum);
3880 64 : return true;
3881 : }
3882 :
3883 : void
3884 64 : CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
3885 : const nsString& aURLSpec,
3886 : RuleAppendFunc aAppendFunc,
3887 : void* aData,
3888 : uint32_t aLineNumber,
3889 : uint32_t aColumnNumber)
3890 : {
3891 128 : nsCOMPtr<nsIAtom> prefix;
3892 :
3893 64 : if (!aPrefix.IsEmpty()) {
3894 28 : prefix = NS_Atomize(aPrefix);
3895 : }
3896 :
3897 : RefPtr<css::NameSpaceRule> rule = new css::NameSpaceRule(prefix, aURLSpec,
3898 : aLineNumber,
3899 192 : aColumnNumber);
3900 64 : (*aAppendFunc)(rule, aData);
3901 :
3902 : // If this was the first namespace rule encountered, it will trigger
3903 : // creation of a namespace map.
3904 64 : if (!mNameSpaceMap) {
3905 39 : mNameSpaceMap = mSheet->GetNameSpaceMap();
3906 : }
3907 64 : }
3908 :
3909 : // font-face-rule: '@font-face' '{' font-description '}'
3910 : // font-description: font-descriptor+
3911 : bool
3912 0 : CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
3913 : {
3914 : uint32_t linenum, colnum;
3915 0 : if (!GetNextTokenLocation(true, &linenum, &colnum) ||
3916 0 : !ExpectSymbol('{', true)) {
3917 0 : REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart);
3918 0 : return false;
3919 : }
3920 :
3921 0 : RefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule(linenum, colnum));
3922 :
3923 : for (;;) {
3924 0 : if (!GetToken(true)) {
3925 0 : REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
3926 0 : break;
3927 : }
3928 0 : if (mToken.IsSymbol('}')) { // done!
3929 0 : UngetToken();
3930 0 : break;
3931 : }
3932 :
3933 : // ignore extra semicolons
3934 0 : if (mToken.IsSymbol(';'))
3935 0 : continue;
3936 :
3937 0 : if (!ParseFontDescriptor(rule)) {
3938 0 : REPORT_UNEXPECTED(PEDeclSkipped);
3939 0 : OUTPUT_ERROR();
3940 0 : if (!SkipDeclaration(true))
3941 0 : break;
3942 : }
3943 : }
3944 0 : if (!ExpectSymbol('}', true)) {
3945 0 : REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd);
3946 0 : return false;
3947 : }
3948 0 : (*aAppendFunc)(rule, aData);
3949 0 : return true;
3950 : }
3951 :
3952 : // font-descriptor: font-family-desc
3953 : // | font-style-desc
3954 : // | font-weight-desc
3955 : // | font-stretch-desc
3956 : // | font-src-desc
3957 : // | unicode-range-desc
3958 : //
3959 : // All font-*-desc productions follow the pattern
3960 : // IDENT ':' value ';'
3961 : //
3962 : // On entry to this function, mToken is the IDENT.
3963 :
3964 : bool
3965 0 : CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
3966 : {
3967 0 : if (eCSSToken_Ident != mToken.mType) {
3968 0 : REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
3969 0 : return false;
3970 : }
3971 :
3972 0 : nsString descName = mToken.mIdent;
3973 0 : if (!ExpectSymbol(':', true)) {
3974 0 : REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3975 0 : OUTPUT_ERROR();
3976 0 : return false;
3977 : }
3978 :
3979 0 : nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
3980 0 : nsCSSValue value;
3981 :
3982 0 : if (descID == eCSSFontDesc_UNKNOWN ||
3983 0 : (descID == eCSSFontDesc_Display &&
3984 0 : !Preferences::GetBool("layout.css.font-display.enabled"))) {
3985 0 : if (NonMozillaVendorIdentifier(descName)) {
3986 : // silently skip other vendors' extensions
3987 0 : Unused << SkipDeclaration(true);
3988 0 : return true;
3989 : } else {
3990 0 : REPORT_UNEXPECTED_P(PEUnknownFontDesc, descName);
3991 0 : return false;
3992 : }
3993 : }
3994 :
3995 0 : if (!ParseFontDescriptorValue(descID, value)) {
3996 0 : REPORT_UNEXPECTED_P(PEValueParsingError, descName);
3997 0 : return false;
3998 : }
3999 :
4000 : // Expect termination by ;, }, or EOF.
4001 0 : if (GetToken(true)) {
4002 0 : if (!mToken.IsSymbol(';') &&
4003 0 : !mToken.IsSymbol('}')) {
4004 0 : UngetToken();
4005 0 : REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
4006 0 : return false;
4007 : }
4008 0 : UngetToken();
4009 : }
4010 :
4011 0 : aRule->SetDesc(descID, value);
4012 0 : return true;
4013 : }
4014 :
4015 : // @font-feature-values <font-family># {
4016 : // @<feature-type> {
4017 : // <feature-ident> : <feature-index>+;
4018 : // <feature-ident> : <feature-index>+;
4019 : // ...
4020 : // }
4021 : // ...
4022 : // }
4023 :
4024 : bool
4025 0 : CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
4026 : void* aData)
4027 : {
4028 : uint32_t linenum, colnum;
4029 0 : if (!GetNextTokenLocation(true, &linenum, &colnum)) {
4030 0 : return false;
4031 : }
4032 :
4033 : RefPtr<nsCSSFontFeatureValuesRule>
4034 0 : valuesRule(new nsCSSFontFeatureValuesRule(linenum, colnum));
4035 :
4036 : // parse family list
4037 0 : nsCSSValue fontlistValue;
4038 :
4039 0 : if (!ParseFamily(fontlistValue) ||
4040 0 : fontlistValue.GetUnit() != eCSSUnit_FontFamilyList)
4041 : {
4042 0 : REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily);
4043 0 : return false;
4044 : }
4045 :
4046 : // add family to rule
4047 0 : const FontFamilyList* fontlist = fontlistValue.GetFontFamilyListValue();
4048 :
4049 : // family list has generic ==> parse error
4050 0 : if (fontlist->HasGeneric()) {
4051 0 : REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList);
4052 0 : return false;
4053 : }
4054 :
4055 0 : valuesRule->SetFamilyList(*fontlist);
4056 :
4057 : // open brace
4058 0 : if (!ExpectSymbol('{', true)) {
4059 0 : REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart);
4060 0 : return false;
4061 : }
4062 :
4063 : // list of sets of feature values, each set bound to a specific
4064 : // feature-type (e.g. swash, annotation)
4065 : for (;;) {
4066 0 : if (!GetToken(true)) {
4067 0 : REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
4068 0 : break;
4069 : }
4070 0 : if (mToken.IsSymbol('}')) { // done!
4071 0 : UngetToken();
4072 0 : break;
4073 : }
4074 :
4075 0 : if (!ParseFontFeatureValueSet(valuesRule)) {
4076 0 : if (!SkipAtRule(false)) {
4077 0 : break;
4078 : }
4079 : }
4080 : }
4081 0 : if (!ExpectSymbol('}', true)) {
4082 0 : REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd);
4083 0 : SkipUntil('}');
4084 0 : return false;
4085 : }
4086 :
4087 0 : (*aAppendFunc)(valuesRule, aData);
4088 0 : return true;
4089 : }
4090 :
4091 : #define NUMVALUES_NO_LIMIT 0xFFFF
4092 :
4093 : // parse a single value set containing name-value pairs for a single feature type
4094 : // @<feature-type> { [ <feature-ident> : <feature-index>+ ; ]* }
4095 : // Ex: @swash { flowing: 1; delicate: 2; }
4096 : bool
4097 0 : CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule
4098 : *aFeatureValuesRule)
4099 : {
4100 : // -- @keyword (e.g. swash, styleset)
4101 0 : if (eCSSToken_AtKeyword != mToken.mType) {
4102 0 : REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt);
4103 0 : OUTPUT_ERROR();
4104 0 : UngetToken();
4105 0 : return false;
4106 : }
4107 :
4108 : // which font-specific variant of font-variant-alternates
4109 : int32_t whichVariant;
4110 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
4111 0 : if (!nsCSSProps::FindKeyword(keyword,
4112 : nsCSSProps::kFontVariantAlternatesFuncsKTable,
4113 : whichVariant))
4114 : {
4115 0 : if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
4116 0 : REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue);
4117 0 : OUTPUT_ERROR();
4118 : }
4119 0 : UngetToken();
4120 0 : return false;
4121 : }
4122 :
4123 0 : nsAutoString featureType(mToken.mIdent);
4124 :
4125 : // open brace
4126 0 : if (!ExpectSymbol('{', true)) {
4127 0 : REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart);
4128 0 : return false;
4129 : }
4130 :
4131 : // styleset and character-variant can be multi-valued, otherwise single value
4132 : int32_t limitNumValues;
4133 :
4134 0 : switch (keyword) {
4135 : case eCSSKeyword_styleset:
4136 0 : limitNumValues = NUMVALUES_NO_LIMIT;
4137 0 : break;
4138 : case eCSSKeyword_character_variant:
4139 0 : limitNumValues = 2;
4140 0 : break;
4141 : default:
4142 0 : limitNumValues = 1;
4143 0 : break;
4144 : }
4145 :
4146 : // -- ident integer+ [, ident integer+]
4147 0 : AutoTArray<gfxFontFeatureValueSet::ValueList, 5> values;
4148 :
4149 : // list of font-feature-values-declaration's
4150 : for (;;) {
4151 0 : nsAutoString valueId;
4152 :
4153 0 : if (!GetToken(true)) {
4154 0 : REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
4155 0 : break;
4156 : }
4157 :
4158 : // ignore extra semicolons
4159 0 : if (mToken.IsSymbol(';')) {
4160 0 : continue;
4161 : }
4162 :
4163 : // close brace ==> done
4164 0 : if (mToken.IsSymbol('}')) {
4165 0 : break;
4166 : }
4167 :
4168 : // ident
4169 0 : if (eCSSToken_Ident != mToken.mType) {
4170 0 : REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent);
4171 0 : if (!SkipDeclaration(true)) {
4172 0 : break;
4173 : }
4174 0 : continue;
4175 : }
4176 :
4177 0 : valueId.Assign(mToken.mIdent);
4178 :
4179 : // colon
4180 0 : if (!ExpectSymbol(':', true)) {
4181 0 : REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
4182 0 : OUTPUT_ERROR();
4183 0 : if (!SkipDeclaration(true)) {
4184 0 : break;
4185 : }
4186 0 : continue;
4187 : }
4188 :
4189 : // value list
4190 0 : AutoTArray<uint32_t,4> featureSelectors;
4191 :
4192 0 : nsCSSValue intValue;
4193 0 : while (ParseNonNegativeInteger(intValue)) {
4194 0 : featureSelectors.AppendElement(uint32_t(intValue.GetIntValue()));
4195 : }
4196 :
4197 0 : int32_t numValues = featureSelectors.Length();
4198 :
4199 0 : if (numValues == 0) {
4200 0 : REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue);
4201 0 : OUTPUT_ERROR();
4202 0 : if (!SkipDeclaration(true)) {
4203 0 : break;
4204 : }
4205 0 : continue;
4206 : }
4207 :
4208 0 : if (numValues > limitNumValues) {
4209 0 : REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType);
4210 0 : OUTPUT_ERROR();
4211 0 : if (!SkipDeclaration(true)) {
4212 0 : break;
4213 : }
4214 0 : continue;
4215 : }
4216 :
4217 0 : if (!GetToken(true)) {
4218 0 : REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
4219 0 : gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
4220 0 : values.AppendElement(v);
4221 0 : break;
4222 : }
4223 :
4224 : // ';' or '}' to end definition
4225 0 : if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) {
4226 0 : REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing);
4227 0 : OUTPUT_ERROR();
4228 0 : if (!SkipDeclaration(true)) {
4229 0 : break;
4230 : }
4231 0 : continue;
4232 : }
4233 :
4234 0 : gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
4235 0 : values.AppendElement(v);
4236 :
4237 0 : if (mToken.IsSymbol('}')) {
4238 0 : break;
4239 : }
4240 0 : }
4241 :
4242 0 : aFeatureValuesRule->AddValueList(whichVariant, values);
4243 0 : return true;
4244 : }
4245 :
4246 : bool
4247 31 : CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData)
4248 : {
4249 : uint32_t linenum, colnum;
4250 62 : if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4251 31 : !GetToken(true)) {
4252 0 : REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF);
4253 0 : return false;
4254 : }
4255 :
4256 31 : if (mToken.mType != eCSSToken_Ident && mToken.mType != eCSSToken_String) {
4257 0 : REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
4258 0 : UngetToken();
4259 0 : return false;
4260 : }
4261 :
4262 31 : if (mToken.mType == eCSSToken_Ident) {
4263 : // Check for keywords that are not allowed as custom-ident for the
4264 : // keyframes-name: standard CSS-wide keywords, plus 'none'.
4265 : static const nsCSSKeyword excludedKeywords[] = {
4266 : eCSSKeyword_none,
4267 : eCSSKeyword_UNKNOWN
4268 : };
4269 62 : nsCSSValue value;
4270 31 : if (!ParseCustomIdent(value, mToken.mIdent, excludedKeywords)) {
4271 0 : REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
4272 0 : UngetToken();
4273 0 : return false;
4274 : }
4275 : }
4276 :
4277 62 : nsString name(mToken.mIdent);
4278 :
4279 31 : if (!ExpectSymbol('{', true)) {
4280 0 : REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace);
4281 0 : return false;
4282 : }
4283 :
4284 : RefPtr<nsCSSKeyframesRule> rule = new nsCSSKeyframesRule(name,
4285 62 : linenum, colnum);
4286 :
4287 187 : while (!ExpectSymbol('}', true)) {
4288 156 : RefPtr<nsCSSKeyframeRule> kid = ParseKeyframeRule();
4289 78 : if (kid) {
4290 78 : rule->AppendStyleRule(kid);
4291 : } else {
4292 0 : OUTPUT_ERROR();
4293 0 : SkipRuleSet(true);
4294 : }
4295 : }
4296 :
4297 31 : (*aAppendFunc)(rule, aData);
4298 31 : return true;
4299 : }
4300 :
4301 : bool
4302 0 : CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
4303 : {
4304 : uint32_t linenum, colnum;
4305 0 : if (!GetNextTokenLocation(true, &linenum, &colnum)) {
4306 0 : return false;
4307 : }
4308 :
4309 : // TODO: There can be page selectors after @page such as ":first", ":left".
4310 : uint32_t parseFlags = eParseDeclaration_InBraces |
4311 0 : eParseDeclaration_AllowImportant;
4312 :
4313 : // Forbid viewport units in @page rules. See bug 811391.
4314 0 : MOZ_ASSERT(mViewportUnitsEnabled,
4315 : "Viewport units should be enabled outside of @page rules.");
4316 0 : mViewportUnitsEnabled = false;
4317 : RefPtr<css::Declaration> declaration =
4318 0 : ParseDeclarationBlock(parseFlags, eCSSContext_Page);
4319 0 : mViewportUnitsEnabled = true;
4320 :
4321 0 : if (!declaration) {
4322 0 : return false;
4323 : }
4324 :
4325 : RefPtr<nsCSSPageRule> rule =
4326 0 : new nsCSSPageRule(declaration, linenum, colnum);
4327 :
4328 0 : (*aAppendFunc)(rule, aData);
4329 0 : return true;
4330 : }
4331 :
4332 : already_AddRefed<nsCSSKeyframeRule>
4333 78 : CSSParserImpl::ParseKeyframeRule()
4334 : {
4335 156 : InfallibleTArray<float> selectorList;
4336 : uint32_t linenum, colnum;
4337 156 : if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4338 78 : !ParseKeyframeSelectorList(selectorList)) {
4339 0 : REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
4340 0 : return nullptr;
4341 : }
4342 :
4343 : // Ignore !important in keyframe rules
4344 78 : uint32_t parseFlags = eParseDeclaration_InBraces;
4345 156 : RefPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags));
4346 78 : if (!declaration) {
4347 0 : return nullptr;
4348 : }
4349 :
4350 : // Takes ownership of declaration
4351 : RefPtr<nsCSSKeyframeRule> rule =
4352 156 : new nsCSSKeyframeRule(Move(selectorList), declaration.forget(),
4353 312 : linenum, colnum);
4354 78 : return rule.forget();
4355 : }
4356 :
4357 : bool
4358 86 : CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList)
4359 : {
4360 : for (;;) {
4361 86 : if (!GetToken(true)) {
4362 : // The first time through the loop, this means we got an empty
4363 : // list. Otherwise, it means we have a trailing comma.
4364 78 : return false;
4365 : }
4366 : float value;
4367 86 : switch (mToken.mType) {
4368 : case eCSSToken_Percentage:
4369 41 : value = mToken.mNumber;
4370 41 : break;
4371 : case eCSSToken_Ident:
4372 45 : if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
4373 22 : value = 0.0f;
4374 22 : break;
4375 : }
4376 23 : if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
4377 23 : value = 1.0f;
4378 23 : break;
4379 : }
4380 : MOZ_FALLTHROUGH;
4381 : default:
4382 0 : UngetToken();
4383 : // The first time through the loop, this means we got an empty
4384 : // list. Otherwise, it means we have a trailing comma.
4385 0 : return false;
4386 : }
4387 86 : aSelectorList.AppendElement(value);
4388 86 : if (!ExpectSymbol(',', true)) {
4389 78 : return true;
4390 : }
4391 8 : }
4392 : }
4393 :
4394 : // supports_rule
4395 : // : "@supports" supports_condition group_rule_body
4396 : // ;
4397 : bool
4398 0 : CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData)
4399 : {
4400 0 : bool conditionMet = false;
4401 0 : nsString condition;
4402 :
4403 0 : mScanner->StartRecording();
4404 :
4405 : uint32_t linenum, colnum;
4406 0 : if (!GetNextTokenLocation(true, &linenum, &colnum)) {
4407 0 : return false;
4408 : }
4409 :
4410 0 : bool parsed = ParseSupportsCondition(conditionMet);
4411 :
4412 0 : if (!parsed) {
4413 0 : mScanner->StopRecording();
4414 0 : return false;
4415 : }
4416 :
4417 0 : if (!ExpectSymbol('{', true)) {
4418 0 : REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart);
4419 0 : mScanner->StopRecording();
4420 0 : return false;
4421 : }
4422 :
4423 0 : UngetToken();
4424 0 : mScanner->StopRecording(condition);
4425 :
4426 : // Remove the "{" that would follow the condition.
4427 0 : if (condition.Length() != 0) {
4428 0 : condition.Truncate(condition.Length() - 1);
4429 : }
4430 :
4431 : // Remove spaces from the start and end of the recorded supports condition.
4432 0 : condition.Trim(" ", true, true, false);
4433 :
4434 : // Record whether we are in a failing @supports, so that property parse
4435 : // errors don't get reported.
4436 0 : nsAutoFailingSupportsRule failing(this, conditionMet);
4437 :
4438 : RefPtr<css::GroupRule> rule =
4439 0 : new mozilla::CSSSupportsRule(conditionMet, condition, linenum, colnum);
4440 0 : return ParseGroupRule(rule, aAppendFunc, aProcessData);
4441 : }
4442 :
4443 : // supports_condition
4444 : // : supports_condition_in_parens supports_condition_terms
4445 : // | supports_condition_negation
4446 : // ;
4447 : bool
4448 0 : CSSParserImpl::ParseSupportsCondition(bool& aConditionMet)
4449 : {
4450 0 : nsAutoInSupportsCondition aisc(this);
4451 :
4452 0 : if (!GetToken(true)) {
4453 0 : REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2);
4454 0 : return false;
4455 : }
4456 :
4457 0 : UngetToken();
4458 :
4459 0 : mScanner->ClearSeenBadToken();
4460 :
4461 0 : if (mToken.IsSymbol('(') ||
4462 0 : mToken.mType == eCSSToken_Function ||
4463 0 : mToken.mType == eCSSToken_URL ||
4464 0 : mToken.mType == eCSSToken_Bad_URL) {
4465 0 : bool result = ParseSupportsConditionInParens(aConditionMet) &&
4466 0 : ParseSupportsConditionTerms(aConditionMet) &&
4467 0 : !mScanner->SeenBadToken();
4468 0 : return result;
4469 : }
4470 :
4471 0 : if (mToken.mType == eCSSToken_Ident &&
4472 0 : mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4473 0 : bool result = ParseSupportsConditionNegation(aConditionMet) &&
4474 0 : !mScanner->SeenBadToken();
4475 0 : return result;
4476 : }
4477 :
4478 0 : REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart);
4479 0 : return false;
4480 : }
4481 :
4482 : // supports_condition_negation
4483 : // : 'not' S+ supports_condition_in_parens
4484 : // ;
4485 : bool
4486 0 : CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet)
4487 : {
4488 0 : if (!GetToken(true)) {
4489 0 : REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF);
4490 0 : return false;
4491 : }
4492 :
4493 0 : if (mToken.mType != eCSSToken_Ident ||
4494 0 : !mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4495 0 : REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot);
4496 0 : return false;
4497 : }
4498 :
4499 0 : if (!RequireWhitespace()) {
4500 0 : REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
4501 0 : return false;
4502 : }
4503 :
4504 0 : if (ParseSupportsConditionInParens(aConditionMet)) {
4505 0 : aConditionMet = !aConditionMet;
4506 0 : return true;
4507 : }
4508 :
4509 0 : return false;
4510 : }
4511 :
4512 : // supports_condition_in_parens
4513 : // : '(' S* supports_condition_in_parens_inside_parens ')' S*
4514 : // | supports_condition_pref
4515 : // | general_enclosed
4516 : // ;
4517 : bool
4518 0 : CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet)
4519 : {
4520 0 : if (!GetToken(true)) {
4521 0 : REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF);
4522 0 : return false;
4523 : }
4524 :
4525 0 : if (mToken.mType == eCSSToken_URL) {
4526 0 : aConditionMet = false;
4527 0 : return true;
4528 : }
4529 :
4530 0 : if (AgentRulesEnabled() &&
4531 0 : mToken.mType == eCSSToken_Function &&
4532 0 : mToken.mIdent.LowerCaseEqualsLiteral("-moz-bool-pref")) {
4533 0 : return ParseSupportsMozBoolPrefName(aConditionMet);
4534 : }
4535 :
4536 0 : if (mToken.mType == eCSSToken_Function ||
4537 0 : mToken.mType == eCSSToken_Bad_URL) {
4538 0 : if (!SkipUntil(')')) {
4539 0 : REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
4540 0 : return false;
4541 : }
4542 0 : aConditionMet = false;
4543 0 : return true;
4544 : }
4545 :
4546 0 : if (!mToken.IsSymbol('(')) {
4547 0 : REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction);
4548 0 : UngetToken();
4549 0 : return false;
4550 : }
4551 :
4552 0 : if (!ParseSupportsConditionInParensInsideParens(aConditionMet)) {
4553 0 : if (!SkipUntil(')')) {
4554 0 : REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
4555 0 : return false;
4556 : }
4557 0 : aConditionMet = false;
4558 0 : return true;
4559 : }
4560 :
4561 0 : if (!(ExpectSymbol(')', true))) {
4562 0 : SkipUntil(')');
4563 0 : aConditionMet = false;
4564 0 : return true;
4565 : }
4566 :
4567 0 : return true;
4568 : }
4569 :
4570 : // supports_condition_pref
4571 : // : '-moz-bool-pref(' bool_pref_name ')'
4572 : // ;
4573 : bool
4574 0 : CSSParserImpl::ParseSupportsMozBoolPrefName(bool& aConditionMet)
4575 : {
4576 0 : if (!GetToken(true)) {
4577 0 : return false;
4578 : }
4579 :
4580 0 : if (mToken.mType != eCSSToken_String) {
4581 0 : SkipUntil(')');
4582 0 : return false;
4583 : }
4584 :
4585 0 : aConditionMet = Preferences::GetBool(
4586 0 : NS_ConvertUTF16toUTF8(mToken.mIdent).get());
4587 :
4588 0 : if (!ExpectSymbol(')', true)) {
4589 0 : SkipUntil(')');
4590 0 : return false;
4591 : }
4592 :
4593 0 : return true;
4594 : }
4595 :
4596 : // supports_condition_in_parens_inside_parens
4597 : // : core_declaration
4598 : // | supports_condition_negation
4599 : // | supports_condition_in_parens supports_condition_terms
4600 : // ;
4601 : bool
4602 0 : CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet)
4603 : {
4604 0 : if (!GetToken(true)) {
4605 0 : return false;
4606 : }
4607 :
4608 0 : if (mToken.mType == eCSSToken_Ident) {
4609 0 : if (!mToken.mIdent.LowerCaseEqualsLiteral("not")) {
4610 0 : nsAutoString propertyName = mToken.mIdent;
4611 0 : if (!ExpectSymbol(':', true)) {
4612 0 : return false;
4613 : }
4614 :
4615 0 : nsCSSPropertyID propID = LookupEnabledProperty(propertyName);
4616 0 : if (propID == eCSSProperty_UNKNOWN) {
4617 0 : if (ExpectSymbol(')', true)) {
4618 0 : UngetToken();
4619 0 : return false;
4620 : }
4621 0 : aConditionMet = false;
4622 0 : SkipUntil(')');
4623 0 : UngetToken();
4624 0 : } else if (propID == eCSSPropertyExtra_variable) {
4625 0 : if (ExpectSymbol(')', false)) {
4626 0 : UngetToken();
4627 0 : return false;
4628 : }
4629 : CSSVariableDeclarations::Type variableType;
4630 0 : nsString variableValue;
4631 0 : aConditionMet =
4632 0 : ParseVariableDeclaration(&variableType, variableValue) &&
4633 0 : ParsePriority() != ePriority_Error;
4634 0 : if (!aConditionMet) {
4635 0 : SkipUntil(')');
4636 0 : UngetToken();
4637 : }
4638 : } else {
4639 0 : if (ExpectSymbol(')', true)) {
4640 0 : UngetToken();
4641 0 : return false;
4642 : }
4643 0 : aConditionMet = ParseProperty(propID) &&
4644 0 : ParsePriority() != ePriority_Error;
4645 0 : if (!aConditionMet) {
4646 0 : SkipUntil(')');
4647 0 : UngetToken();
4648 : }
4649 0 : mTempData.ClearProperty(propID);
4650 0 : mTempData.AssertInitialState();
4651 : }
4652 0 : return true;
4653 : }
4654 :
4655 0 : UngetToken();
4656 0 : return ParseSupportsConditionNegation(aConditionMet);
4657 : }
4658 :
4659 0 : UngetToken();
4660 0 : return ParseSupportsConditionInParens(aConditionMet) &&
4661 0 : ParseSupportsConditionTerms(aConditionMet);
4662 : }
4663 :
4664 : // supports_condition_terms
4665 : // : S+ 'and' supports_condition_terms_after_operator('and')
4666 : // | S+ 'or' supports_condition_terms_after_operator('or')
4667 : // |
4668 : // ;
4669 : bool
4670 0 : CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet)
4671 : {
4672 0 : if (!RequireWhitespace() || !GetToken(false)) {
4673 0 : return true;
4674 : }
4675 :
4676 0 : if (mToken.mType != eCSSToken_Ident) {
4677 0 : UngetToken();
4678 0 : return true;
4679 : }
4680 :
4681 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("and")) {
4682 0 : return ParseSupportsConditionTermsAfterOperator(aConditionMet, eAnd);
4683 : }
4684 :
4685 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("or")) {
4686 0 : return ParseSupportsConditionTermsAfterOperator(aConditionMet, eOr);
4687 : }
4688 :
4689 0 : UngetToken();
4690 0 : return true;
4691 : }
4692 :
4693 : // supports_condition_terms_after_operator(operator)
4694 : // : S+ supports_condition_in_parens ( <operator> supports_condition_in_parens )*
4695 : // ;
4696 : bool
4697 0 : CSSParserImpl::ParseSupportsConditionTermsAfterOperator(
4698 : bool& aConditionMet,
4699 : CSSParserImpl::SupportsConditionTermOperator aOperator)
4700 : {
4701 0 : if (!RequireWhitespace()) {
4702 0 : REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
4703 0 : return false;
4704 : }
4705 :
4706 0 : const char* token = aOperator == eAnd ? "and" : "or";
4707 : for (;;) {
4708 0 : bool termConditionMet = false;
4709 0 : if (!ParseSupportsConditionInParens(termConditionMet)) {
4710 0 : return false;
4711 : }
4712 0 : aConditionMet = aOperator == eAnd ? aConditionMet && termConditionMet :
4713 0 : aConditionMet || termConditionMet;
4714 :
4715 0 : if (!GetToken(true)) {
4716 0 : return true;
4717 : }
4718 :
4719 0 : if (mToken.mType != eCSSToken_Ident ||
4720 0 : !mToken.mIdent.LowerCaseEqualsASCII(token)) {
4721 0 : UngetToken();
4722 0 : return true;
4723 : }
4724 0 : }
4725 : }
4726 :
4727 : bool
4728 138 : CSSParserImpl::ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aData)
4729 : {
4730 276 : nsCOMPtr<nsIAtom> name;
4731 : uint32_t linenum, colnum;
4732 552 : if (!GetNextTokenLocation(true, &linenum, &colnum) ||
4733 552 : !(name = ParseCounterStyleName(true))) {
4734 0 : REPORT_UNEXPECTED_TOKEN(PECounterStyleNotIdent);
4735 0 : return false;
4736 : }
4737 :
4738 138 : if (!ExpectSymbol('{', true)) {
4739 0 : REPORT_UNEXPECTED_TOKEN(PECounterStyleBadBlockStart);
4740 0 : return false;
4741 : }
4742 :
4743 : RefPtr<nsCSSCounterStyleRule> rule = new nsCSSCounterStyleRule(name,
4744 : linenum,
4745 414 : colnum);
4746 : for (;;) {
4747 1154 : if (!GetToken(true)) {
4748 0 : REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
4749 0 : break;
4750 : }
4751 646 : if (mToken.IsSymbol('}')) {
4752 138 : break;
4753 : }
4754 508 : if (mToken.IsSymbol(';')) {
4755 254 : continue;
4756 : }
4757 :
4758 254 : if (!ParseCounterDescriptor(rule)) {
4759 0 : REPORT_UNEXPECTED(PEDeclSkipped);
4760 0 : OUTPUT_ERROR();
4761 0 : if (!SkipDeclaration(true)) {
4762 0 : REPORT_UNEXPECTED_EOF(PECounterStyleEOF);
4763 0 : break;
4764 : }
4765 : }
4766 : }
4767 :
4768 138 : int32_t system = rule->GetSystem();
4769 138 : bool isCorrect = false;
4770 138 : switch (system) {
4771 : case NS_STYLE_COUNTER_SYSTEM_CYCLIC:
4772 : case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
4773 : case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
4774 : case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC:
4775 : case NS_STYLE_COUNTER_SYSTEM_FIXED: {
4776 : // check whether symbols is set and the length is sufficient
4777 66 : const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
4778 132 : if (symbols.GetUnit() == eCSSUnit_List &&
4779 66 : nsCSSCounterStyleRule::CheckDescValue(
4780 : system, eCSSCounterDesc_Symbols, symbols)) {
4781 66 : isCorrect = true;
4782 : }
4783 66 : break;
4784 : }
4785 : case NS_STYLE_COUNTER_SYSTEM_ADDITIVE: {
4786 : // for additive system, additive-symbols must be set
4787 : const nsCSSValue& symbols =
4788 12 : rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
4789 12 : if (symbols.GetUnit() == eCSSUnit_PairList) {
4790 12 : isCorrect = true;
4791 : }
4792 12 : break;
4793 : }
4794 : case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
4795 : // for extends system, symbols & additive-symbols must not be set
4796 60 : const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols);
4797 : const nsCSSValue& additiveSymbols =
4798 60 : rule->GetDesc(eCSSCounterDesc_AdditiveSymbols);
4799 120 : if (symbols.GetUnit() == eCSSUnit_Null &&
4800 60 : additiveSymbols.GetUnit() == eCSSUnit_Null) {
4801 60 : isCorrect = true;
4802 : }
4803 60 : break;
4804 : }
4805 : default:
4806 0 : NS_NOTREACHED("unknown system");
4807 : }
4808 :
4809 138 : if (isCorrect) {
4810 138 : (*aAppendFunc)(rule, aData);
4811 : }
4812 138 : return true;
4813 : }
4814 :
4815 : already_AddRefed<nsIAtom>
4816 214 : CSSParserImpl::ParseCounterStyleName(bool aForDefinition)
4817 : {
4818 214 : if (!GetToken(true)) {
4819 0 : return nullptr;
4820 : }
4821 :
4822 214 : if (mToken.mType != eCSSToken_Ident) {
4823 0 : UngetToken();
4824 0 : return nullptr;
4825 : }
4826 :
4827 : static const nsCSSKeyword kReservedNames[] = {
4828 : eCSSKeyword_none,
4829 : eCSSKeyword_decimal,
4830 : eCSSKeyword_disc,
4831 : eCSSKeyword_UNKNOWN
4832 : };
4833 :
4834 428 : nsCSSValue value; // we don't actually care about the value
4835 214 : if (!ParseCustomIdent(value, mToken.mIdent,
4836 : aForDefinition ? kReservedNames : nullptr)) {
4837 0 : REPORT_UNEXPECTED_TOKEN(PECounterStyleBadName);
4838 0 : UngetToken();
4839 0 : return nullptr;
4840 : }
4841 :
4842 428 : nsString name = mToken.mIdent;
4843 214 : if (nsCSSProps::IsPredefinedCounterStyle(name)) {
4844 152 : ToLowerCase(name);
4845 : }
4846 214 : return NS_Atomize(name);
4847 : }
4848 :
4849 : bool
4850 76 : CSSParserImpl::ParseCounterStyleNameValue(nsCSSValue& aValue)
4851 : {
4852 76 : if (nsCOMPtr<nsIAtom> name = ParseCounterStyleName(false)) {
4853 76 : aValue.SetAtomIdentValue(name.forget());
4854 76 : return true;
4855 : }
4856 0 : return false;
4857 : }
4858 :
4859 : bool
4860 254 : CSSParserImpl::ParseCounterDescriptor(nsCSSCounterStyleRule* aRule)
4861 : {
4862 254 : if (eCSSToken_Ident != mToken.mType) {
4863 0 : REPORT_UNEXPECTED_TOKEN(PECounterDescExpected);
4864 0 : return false;
4865 : }
4866 :
4867 508 : nsString descName = mToken.mIdent;
4868 254 : if (!ExpectSymbol(':', true)) {
4869 0 : REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
4870 0 : OUTPUT_ERROR();
4871 0 : return false;
4872 : }
4873 :
4874 254 : nsCSSCounterDesc descID = nsCSSProps::LookupCounterDesc(descName);
4875 508 : nsCSSValue value;
4876 :
4877 254 : if (descID == eCSSCounterDesc_UNKNOWN) {
4878 0 : REPORT_UNEXPECTED_P(PEUnknownCounterDesc, descName);
4879 0 : return false;
4880 : }
4881 :
4882 254 : if (!ParseCounterDescriptorValue(descID, value)) {
4883 0 : REPORT_UNEXPECTED_P(PEValueParsingError, descName);
4884 0 : return false;
4885 : }
4886 :
4887 254 : if (!ExpectEndProperty()) {
4888 0 : return false;
4889 : }
4890 :
4891 254 : aRule->SetDesc(descID, value);
4892 254 : return true;
4893 : }
4894 :
4895 : bool
4896 254 : CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID,
4897 : nsCSSValue& aValue)
4898 : {
4899 : // Should also include VARIANT_IMAGE, but it is not supported currently.
4900 : // See bug 1024179.
4901 : static const int32_t VARIANT_COUNTER_SYMBOL =
4902 : VARIANT_STRING | VARIANT_IDENTIFIER;
4903 :
4904 254 : switch (aDescID) {
4905 : case eCSSCounterDesc_System: {
4906 276 : nsCSSValue system;
4907 138 : if (!ParseEnum(system, nsCSSProps::kCounterSystemKTable)) {
4908 0 : return false;
4909 : }
4910 138 : switch (system.GetIntValue()) {
4911 : case NS_STYLE_COUNTER_SYSTEM_FIXED: {
4912 0 : nsCSSValue start;
4913 0 : if (!ParseSingleTokenVariant(start, VARIANT_INTEGER, nullptr)) {
4914 0 : start.SetIntValue(1, eCSSUnit_Integer);
4915 : }
4916 0 : aValue.SetPairValue(system, start);
4917 0 : return true;
4918 : }
4919 : case NS_STYLE_COUNTER_SYSTEM_EXTENDS: {
4920 120 : nsCSSValue name;
4921 60 : if (!ParseCounterStyleNameValue(name)) {
4922 0 : REPORT_UNEXPECTED_TOKEN(PECounterExtendsNotIdent);
4923 0 : return false;
4924 : }
4925 60 : aValue.SetPairValue(system, name);
4926 60 : return true;
4927 : }
4928 : default:
4929 78 : aValue = system;
4930 78 : return true;
4931 : }
4932 : }
4933 :
4934 : case eCSSCounterDesc_Negative: {
4935 0 : nsCSSValue first, second;
4936 0 : if (!ParseSingleTokenVariant(first, VARIANT_COUNTER_SYMBOL, nullptr)) {
4937 0 : return false;
4938 : }
4939 0 : if (!ParseSingleTokenVariant(second, VARIANT_COUNTER_SYMBOL, nullptr)) {
4940 0 : aValue = first;
4941 : } else {
4942 0 : aValue.SetPairValue(first, second);
4943 : }
4944 0 : return true;
4945 : }
4946 :
4947 : case eCSSCounterDesc_Prefix:
4948 : case eCSSCounterDesc_Suffix:
4949 18 : return ParseSingleTokenVariant(aValue, VARIANT_COUNTER_SYMBOL, nullptr);
4950 :
4951 : case eCSSCounterDesc_Range: {
4952 14 : if (ParseSingleTokenVariant(aValue, VARIANT_AUTO, nullptr)) {
4953 0 : return true;
4954 : }
4955 14 : nsCSSValuePairList* item = aValue.SetPairListValue();
4956 : for (;;) {
4957 14 : nsCSSValuePair pair;
4958 14 : if (!ParseCounterRange(pair)) {
4959 0 : return false;
4960 : }
4961 14 : item->mXValue = pair.mXValue;
4962 14 : item->mYValue = pair.mYValue;
4963 14 : if (!ExpectSymbol(',', true)) {
4964 14 : return true;
4965 : }
4966 0 : item->mNext = new nsCSSValuePairList;
4967 0 : item = item->mNext;
4968 0 : }
4969 : // should always return in the loop
4970 : }
4971 :
4972 : case eCSSCounterDesc_Pad: {
4973 4 : nsCSSValue width, symbol;
4974 2 : bool hasWidth = ParseNonNegativeInteger(width);
4975 4 : if (!ParseSingleTokenVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
4976 2 : (!hasWidth && !ParseNonNegativeInteger(width))) {
4977 0 : return false;
4978 : }
4979 2 : aValue.SetPairValue(width, symbol);
4980 2 : return true;
4981 : }
4982 :
4983 : case eCSSCounterDesc_Fallback:
4984 4 : return ParseCounterStyleNameValue(aValue);
4985 :
4986 : case eCSSCounterDesc_Symbols: {
4987 66 : nsCSSValueList* item = nullptr;
4988 : for (;;) {
4989 2546 : nsCSSValue value;
4990 1306 : if (!ParseSingleTokenVariant(value, VARIANT_COUNTER_SYMBOL, nullptr)) {
4991 66 : return !!item;
4992 : }
4993 1240 : if (!item) {
4994 66 : item = aValue.SetListValue();
4995 : } else {
4996 1174 : item->mNext = new nsCSSValueList;
4997 1174 : item = item->mNext;
4998 : }
4999 1240 : item->mValue = value;
5000 1240 : }
5001 : // should always return in the loop
5002 : }
5003 :
5004 : case eCSSCounterDesc_AdditiveSymbols: {
5005 12 : nsCSSValuePairList* item = nullptr;
5006 12 : int32_t lastWeight = -1;
5007 : for (;;) {
5008 672 : nsCSSValue weight, symbol;
5009 342 : bool hasWeight = ParseNonNegativeInteger(weight);
5010 684 : if (!ParseSingleTokenVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) ||
5011 342 : (!hasWeight && !ParseNonNegativeInteger(weight))) {
5012 0 : return false;
5013 : }
5014 342 : if (lastWeight != -1 && weight.GetIntValue() >= lastWeight) {
5015 0 : REPORT_UNEXPECTED(PECounterASWeight);
5016 0 : return false;
5017 : }
5018 342 : lastWeight = weight.GetIntValue();
5019 342 : if (!item) {
5020 12 : item = aValue.SetPairListValue();
5021 : } else {
5022 330 : item->mNext = new nsCSSValuePairList;
5023 330 : item = item->mNext;
5024 : }
5025 342 : item->mXValue = weight;
5026 342 : item->mYValue = symbol;
5027 342 : if (!ExpectSymbol(',', true)) {
5028 12 : return true;
5029 : }
5030 330 : }
5031 : // should always return in the loop
5032 : }
5033 :
5034 : case eCSSCounterDesc_SpeakAs:
5035 0 : if (ParseSingleTokenVariant(aValue, VARIANT_AUTO | VARIANT_KEYWORD,
5036 : nsCSSProps::kCounterSpeakAsKTable)) {
5037 0 : if (aValue.GetUnit() == eCSSUnit_Enumerated &&
5038 0 : aValue.GetIntValue() == NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT) {
5039 : // Currently spell-out is not supported, so it is explicitly
5040 : // rejected here rather than parsed as a custom identifier.
5041 : // See bug 1024178.
5042 0 : return false;
5043 : }
5044 0 : return true;
5045 : }
5046 0 : return ParseCounterStyleNameValue(aValue);
5047 :
5048 : default:
5049 0 : NS_NOTREACHED("unknown descriptor");
5050 0 : return false;
5051 : }
5052 : }
5053 :
5054 : bool
5055 14 : CSSParserImpl::ParseCounterRange(nsCSSValuePair& aPair)
5056 : {
5057 : static const int32_t VARIANT_BOUND = VARIANT_INTEGER | VARIANT_KEYWORD;
5058 28 : nsCSSValue lower, upper;
5059 28 : if (!ParseSingleTokenVariant(lower, VARIANT_BOUND,
5060 28 : nsCSSProps::kCounterRangeKTable) ||
5061 14 : !ParseSingleTokenVariant(upper, VARIANT_BOUND,
5062 : nsCSSProps::kCounterRangeKTable)) {
5063 0 : return false;
5064 : }
5065 42 : if (lower.GetUnit() != eCSSUnit_Enumerated &&
5066 26 : upper.GetUnit() != eCSSUnit_Enumerated &&
5067 12 : lower.GetIntValue() > upper.GetIntValue()) {
5068 0 : return false;
5069 : }
5070 14 : aPair = nsCSSValuePair(lower, upper);
5071 14 : return true;
5072 : }
5073 :
5074 : bool
5075 29 : CSSParserImpl::SkipUntil(char16_t aStopSymbol)
5076 : {
5077 29 : nsCSSToken* tk = &mToken;
5078 58 : AutoTArray<char16_t, 16> stack;
5079 29 : stack.AppendElement(aStopSymbol);
5080 : for (;;) {
5081 192 : if (!GetToken(true)) {
5082 0 : return false;
5083 : }
5084 192 : if (eCSSToken_Symbol == tk->mType) {
5085 90 : char16_t symbol = tk->mSymbol;
5086 90 : uint32_t stackTopIndex = stack.Length() - 1;
5087 90 : if (symbol == stack.ElementAt(stackTopIndex)) {
5088 62 : stack.RemoveElementAt(stackTopIndex);
5089 62 : if (stackTopIndex == 0) {
5090 29 : return true;
5091 : }
5092 :
5093 : // Just handle out-of-memory by parsing incorrectly. It's
5094 : // highly unlikely we're dealing with a legitimate style sheet
5095 : // anyway.
5096 28 : } else if ('{' == symbol) {
5097 0 : stack.AppendElement('}');
5098 28 : } else if ('[' == symbol) {
5099 0 : stack.AppendElement(']');
5100 28 : } else if ('(' == symbol) {
5101 0 : stack.AppendElement(')');
5102 : }
5103 171 : } else if (eCSSToken_Function == tk->mType ||
5104 69 : eCSSToken_Bad_URL == tk->mType) {
5105 33 : stack.AppendElement(')');
5106 : }
5107 163 : }
5108 : }
5109 :
5110 : bool
5111 0 : CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol)
5112 : {
5113 0 : nsCSSToken* tk = &mToken;
5114 0 : AutoTArray<char16_t, 16> stack;
5115 0 : stack.AppendElement(aStopSymbol);
5116 : for (;;) {
5117 0 : if (!GetToken(true)) {
5118 0 : return true;
5119 : }
5120 0 : if (eCSSToken_Symbol == tk->mType) {
5121 0 : char16_t symbol = tk->mSymbol;
5122 0 : uint32_t stackTopIndex = stack.Length() - 1;
5123 0 : if (symbol == stack.ElementAt(stackTopIndex)) {
5124 0 : stack.RemoveElementAt(stackTopIndex);
5125 0 : if (stackTopIndex == 0) {
5126 0 : return true;
5127 : }
5128 :
5129 : // Just handle out-of-memory by parsing incorrectly. It's
5130 : // highly unlikely we're dealing with a legitimate style sheet
5131 : // anyway.
5132 0 : } else if ('{' == symbol) {
5133 0 : stack.AppendElement('}');
5134 0 : } else if ('[' == symbol) {
5135 0 : stack.AppendElement(']');
5136 0 : } else if ('(' == symbol) {
5137 0 : stack.AppendElement(')');
5138 0 : } else if (')' == symbol ||
5139 0 : ']' == symbol ||
5140 : '}' == symbol) {
5141 0 : UngetToken();
5142 0 : return false;
5143 : }
5144 0 : } else if (eCSSToken_Function == tk->mType ||
5145 0 : eCSSToken_Bad_URL == tk->mType) {
5146 0 : stack.AppendElement(')');
5147 : }
5148 0 : }
5149 : }
5150 :
5151 : void
5152 0 : CSSParserImpl::SkipUntilOneOf(const char16_t* aStopSymbolChars)
5153 : {
5154 0 : nsCSSToken* tk = &mToken;
5155 0 : nsDependentString stopSymbolChars(aStopSymbolChars);
5156 : for (;;) {
5157 0 : if (!GetToken(true)) {
5158 0 : break;
5159 : }
5160 0 : if (eCSSToken_Symbol == tk->mType) {
5161 0 : char16_t symbol = tk->mSymbol;
5162 0 : if (stopSymbolChars.FindChar(symbol) != -1) {
5163 0 : break;
5164 0 : } else if ('{' == symbol) {
5165 0 : SkipUntil('}');
5166 0 : } else if ('[' == symbol) {
5167 0 : SkipUntil(']');
5168 0 : } else if ('(' == symbol) {
5169 0 : SkipUntil(')');
5170 : }
5171 0 : } else if (eCSSToken_Function == tk->mType ||
5172 0 : eCSSToken_Bad_URL == tk->mType) {
5173 0 : SkipUntil(')');
5174 : }
5175 0 : }
5176 0 : }
5177 :
5178 : void
5179 0 : CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars)
5180 : {
5181 0 : uint32_t i = aStopSymbolChars.Length();
5182 0 : while (i--) {
5183 0 : SkipUntil(aStopSymbolChars[i]);
5184 : }
5185 0 : }
5186 :
5187 : bool
5188 3002 : CSSParserImpl::SkipDeclaration(bool aCheckForBraces)
5189 : {
5190 3002 : nsCSSToken* tk = &mToken;
5191 : for (;;) {
5192 3002 : if (!GetToken(true)) {
5193 7 : if (aCheckForBraces) {
5194 0 : REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
5195 : }
5196 7 : return false;
5197 : }
5198 2995 : if (eCSSToken_Symbol == tk->mType) {
5199 2995 : char16_t symbol = tk->mSymbol;
5200 2995 : if (';' == symbol) {
5201 0 : break;
5202 : }
5203 2995 : if (aCheckForBraces) {
5204 2995 : if ('}' == symbol) {
5205 2995 : UngetToken();
5206 2995 : break;
5207 : }
5208 : }
5209 0 : if ('{' == symbol) {
5210 0 : SkipUntil('}');
5211 0 : } else if ('(' == symbol) {
5212 0 : SkipUntil(')');
5213 0 : } else if ('[' == symbol) {
5214 0 : SkipUntil(']');
5215 : }
5216 0 : } else if (eCSSToken_Function == tk->mType ||
5217 0 : eCSSToken_Bad_URL == tk->mType) {
5218 0 : SkipUntil(')');
5219 : }
5220 0 : }
5221 2995 : return true;
5222 : }
5223 :
5224 : void
5225 0 : CSSParserImpl::SkipRuleSet(bool aInsideBraces)
5226 : {
5227 0 : nsCSSToken* tk = &mToken;
5228 : for (;;) {
5229 0 : if (!GetToken(true)) {
5230 0 : REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
5231 0 : break;
5232 : }
5233 0 : if (eCSSToken_Symbol == tk->mType) {
5234 0 : char16_t symbol = tk->mSymbol;
5235 0 : if ('}' == symbol && aInsideBraces) {
5236 : // leave block closer for higher-level grammar to consume
5237 0 : UngetToken();
5238 0 : break;
5239 0 : } else if ('{' == symbol) {
5240 0 : SkipUntil('}');
5241 0 : break;
5242 0 : } else if ('(' == symbol) {
5243 0 : SkipUntil(')');
5244 0 : } else if ('[' == symbol) {
5245 0 : SkipUntil(']');
5246 : }
5247 0 : } else if (eCSSToken_Function == tk->mType ||
5248 0 : eCSSToken_Bad_URL == tk->mType) {
5249 0 : SkipUntil(')');
5250 : }
5251 0 : }
5252 0 : }
5253 :
5254 : void
5255 50 : CSSParserImpl::PushGroup(css::GroupRule* aRule)
5256 : {
5257 50 : mGroupStack.AppendElement(aRule);
5258 50 : }
5259 :
5260 : void
5261 50 : CSSParserImpl::PopGroup()
5262 : {
5263 50 : uint32_t count = mGroupStack.Length();
5264 50 : if (0 < count) {
5265 50 : mGroupStack.RemoveElementAt(count - 1);
5266 : }
5267 50 : }
5268 :
5269 : void
5270 3203 : CSSParserImpl::AppendRule(css::Rule* aRule)
5271 : {
5272 3203 : uint32_t count = mGroupStack.Length();
5273 3203 : if (0 < count) {
5274 129 : mGroupStack[count - 1]->AppendStyleRule(aRule);
5275 : }
5276 : else {
5277 3074 : mSheet->AppendStyleRule(aRule);
5278 : }
5279 3203 : }
5280 :
5281 : bool
5282 2917 : CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
5283 : bool aInsideBraces)
5284 : {
5285 : // First get the list of selectors for the rule
5286 2917 : nsCSSSelectorList* slist = nullptr;
5287 : uint32_t linenum, colnum;
5288 5834 : if (!GetNextTokenLocation(true, &linenum, &colnum) ||
5289 2917 : !ParseSelectorList(slist, char16_t('{'))) {
5290 0 : REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
5291 0 : OUTPUT_ERROR();
5292 0 : SkipRuleSet(aInsideBraces);
5293 0 : return false;
5294 : }
5295 2917 : NS_ASSERTION(nullptr != slist, "null selector list");
5296 2917 : CLEAR_ERROR();
5297 :
5298 : // Next parse the declaration block
5299 : uint32_t parseFlags = eParseDeclaration_InBraces |
5300 2917 : eParseDeclaration_AllowImportant;
5301 5834 : RefPtr<css::Declaration> declaration = ParseDeclarationBlock(parseFlags);
5302 2917 : if (nullptr == declaration) {
5303 0 : delete slist;
5304 0 : return false;
5305 : }
5306 :
5307 : #if 0
5308 : slist->Dump();
5309 : fputs("{\n", stdout);
5310 : declaration->List();
5311 : fputs("}\n", stdout);
5312 : #endif
5313 :
5314 : // Translate the selector list and declaration block into style data
5315 :
5316 : RefPtr<css::StyleRule> rule = new css::StyleRule(slist, declaration,
5317 8751 : linenum, colnum);
5318 2917 : (*aAppendFunc)(rule, aData);
5319 :
5320 2917 : return true;
5321 : }
5322 :
5323 : bool
5324 3086 : CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
5325 : char16_t aStopChar)
5326 : {
5327 3086 : nsCSSSelectorList* list = nullptr;
5328 3086 : if (! ParseSelectorGroup(list)) {
5329 : // must have at least one selector group
5330 0 : aListHead = nullptr;
5331 0 : return false;
5332 : }
5333 3086 : NS_ASSERTION(nullptr != list, "no selector list");
5334 3086 : aListHead = list;
5335 :
5336 : // After that there must either be a "," or a "{" (the latter if
5337 : // StopChar is nonzero)
5338 3086 : nsCSSToken* tk = &mToken;
5339 : for (;;) {
5340 5556 : if (! GetToken(true)) {
5341 7 : if (aStopChar == char16_t(0)) {
5342 7 : return true;
5343 : }
5344 :
5345 0 : REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
5346 0 : break;
5347 : }
5348 :
5349 5549 : if (eCSSToken_Symbol == tk->mType) {
5350 5549 : if (',' == tk->mSymbol) {
5351 2470 : nsCSSSelectorList* newList = nullptr;
5352 : // Another selector group must follow
5353 2470 : if (! ParseSelectorGroup(newList)) {
5354 0 : break;
5355 : }
5356 : // add new list to the end of the selector list
5357 2470 : list->mNext = newList;
5358 2470 : list = newList;
5359 2470 : continue;
5360 3079 : } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) {
5361 3079 : UngetToken();
5362 3079 : return true;
5363 : }
5364 : }
5365 0 : REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
5366 0 : UngetToken();
5367 0 : break;
5368 2470 : }
5369 :
5370 0 : delete aListHead;
5371 0 : aListHead = nullptr;
5372 0 : return false;
5373 : }
5374 :
5375 92 : static bool IsUniversalSelector(const nsCSSSelector& aSelector)
5376 : {
5377 184 : return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
5378 184 : (aSelector.mLowercaseTag == nullptr) &&
5379 184 : (aSelector.mIDList == nullptr) &&
5380 184 : (aSelector.mClassList == nullptr) &&
5381 184 : (aSelector.mAttrList == nullptr) &&
5382 276 : (aSelector.mNegations == nullptr) &&
5383 184 : (aSelector.mPseudoClassList == nullptr));
5384 : }
5385 :
5386 : bool
5387 5556 : CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
5388 : {
5389 5556 : char16_t combinator = 0;
5390 11112 : nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
5391 :
5392 : for (;;) {
5393 8359 : if (!ParseSelector(list, combinator)) {
5394 0 : return false;
5395 : }
5396 :
5397 : // Look for a combinator.
5398 8359 : if (!GetToken(false)) {
5399 7 : break; // EOF ok here
5400 : }
5401 :
5402 8352 : combinator = char16_t(0);
5403 8352 : if (mToken.mType == eCSSToken_Whitespace) {
5404 5471 : if (!GetToken(true)) {
5405 0 : break; // EOF ok here
5406 : }
5407 5471 : combinator = char16_t(' ');
5408 : }
5409 :
5410 8352 : if (mToken.mType != eCSSToken_Symbol) {
5411 352 : UngetToken(); // not a combinator
5412 : } else {
5413 8000 : char16_t symbol = mToken.mSymbol;
5414 8000 : if (symbol == '+' || symbol == '>' || symbol == '~') {
5415 2178 : combinator = mToken.mSymbol;
5416 : } else {
5417 5822 : UngetToken(); // not a combinator
5418 5822 : if (symbol == ',' || symbol == '{' || symbol == ')') {
5419 : break; // end of selector group
5420 : }
5421 : }
5422 : }
5423 :
5424 2803 : if (!combinator) {
5425 0 : REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
5426 0 : return false;
5427 : }
5428 2803 : }
5429 :
5430 5556 : aList = list.forget();
5431 5556 : return true;
5432 : }
5433 :
5434 : #define SEL_MASK_NSPACE 0x01
5435 : #define SEL_MASK_ELEM 0x02
5436 : #define SEL_MASK_ID 0x04
5437 : #define SEL_MASK_CLASS 0x08
5438 : #define SEL_MASK_ATTRIB 0x10
5439 : #define SEL_MASK_PCLASS 0x20
5440 : #define SEL_MASK_PELEM 0x40
5441 :
5442 : //
5443 : // Parses an ID selector #name
5444 : //
5445 : CSSParserImpl::nsSelectorParsingStatus
5446 1963 : CSSParserImpl::ParseIDSelector(int32_t& aDataMask,
5447 : nsCSSSelector& aSelector)
5448 : {
5449 1963 : NS_ASSERTION(!mToken.mIdent.IsEmpty(),
5450 : "Empty mIdent in eCSSToken_ID token?");
5451 1963 : aDataMask |= SEL_MASK_ID;
5452 1963 : aSelector.AddID(mToken.mIdent);
5453 1963 : return eSelectorParsingStatus_Continue;
5454 : }
5455 :
5456 : //
5457 : // Parses a class selector .name
5458 : //
5459 : CSSParserImpl::nsSelectorParsingStatus
5460 2497 : CSSParserImpl::ParseClassSelector(int32_t& aDataMask,
5461 : nsCSSSelector& aSelector)
5462 : {
5463 2497 : if (! GetToken(false)) { // get ident
5464 0 : REPORT_UNEXPECTED_EOF(PEClassSelEOF);
5465 0 : return eSelectorParsingStatus_Error;
5466 : }
5467 2497 : if (eCSSToken_Ident != mToken.mType) { // malformed selector
5468 0 : REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
5469 0 : UngetToken();
5470 0 : return eSelectorParsingStatus_Error;
5471 : }
5472 2497 : aDataMask |= SEL_MASK_CLASS;
5473 :
5474 2497 : aSelector.AddClass(mToken.mIdent);
5475 :
5476 2497 : return eSelectorParsingStatus_Continue;
5477 : }
5478 :
5479 : //
5480 : // Parse a type element selector or a universal selector
5481 : // namespace|type or namespace|* or *|* or *
5482 : //
5483 : CSSParserImpl::nsSelectorParsingStatus
5484 8389 : CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
5485 : nsCSSSelector& aSelector,
5486 : bool aIsNegated)
5487 : {
5488 16778 : nsAutoString buffer;
5489 8389 : if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
5490 178 : if (ExpectSymbol('|', false)) { // was namespace
5491 126 : aDataMask |= SEL_MASK_NSPACE;
5492 126 : aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
5493 :
5494 126 : if (! GetToken(false)) {
5495 0 : REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5496 0 : return eSelectorParsingStatus_Error;
5497 : }
5498 126 : if (eCSSToken_Ident == mToken.mType) { // element name
5499 0 : aDataMask |= SEL_MASK_ELEM;
5500 :
5501 0 : aSelector.SetTag(mToken.mIdent);
5502 : }
5503 126 : else if (mToken.IsSymbol('*')) { // universal selector
5504 126 : aDataMask |= SEL_MASK_ELEM;
5505 : // don't set tag
5506 : }
5507 : else {
5508 0 : REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5509 0 : UngetToken();
5510 0 : return eSelectorParsingStatus_Error;
5511 : }
5512 : }
5513 : else { // was universal element selector
5514 52 : SetDefaultNamespaceOnSelector(aSelector);
5515 52 : aDataMask |= SEL_MASK_ELEM;
5516 : // don't set any tag in the selector
5517 : }
5518 178 : if (! GetToken(false)) { // premature eof is ok (here!)
5519 0 : return eSelectorParsingStatus_Done;
5520 : }
5521 : }
5522 8211 : else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
5523 3657 : buffer = mToken.mIdent; // hang on to ident
5524 :
5525 3657 : if (ExpectSymbol('|', false)) { // was namespace
5526 76 : aDataMask |= SEL_MASK_NSPACE;
5527 76 : int32_t nameSpaceID = GetNamespaceIdForPrefix(buffer);
5528 76 : if (nameSpaceID == kNameSpaceID_Unknown) {
5529 0 : return eSelectorParsingStatus_Error;
5530 : }
5531 76 : aSelector.SetNameSpace(nameSpaceID);
5532 :
5533 76 : if (! GetToken(false)) {
5534 0 : REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5535 0 : return eSelectorParsingStatus_Error;
5536 : }
5537 76 : if (eCSSToken_Ident == mToken.mType) { // element name
5538 38 : aDataMask |= SEL_MASK_ELEM;
5539 38 : aSelector.SetTag(mToken.mIdent);
5540 : }
5541 38 : else if (mToken.IsSymbol('*')) { // universal selector
5542 38 : aDataMask |= SEL_MASK_ELEM;
5543 : // don't set tag
5544 : }
5545 : else {
5546 0 : REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5547 0 : UngetToken();
5548 0 : return eSelectorParsingStatus_Error;
5549 : }
5550 : }
5551 : else { // was element name
5552 3581 : SetDefaultNamespaceOnSelector(aSelector);
5553 3581 : aSelector.SetTag(buffer);
5554 :
5555 3581 : aDataMask |= SEL_MASK_ELEM;
5556 : }
5557 3657 : if (! GetToken(false)) { // premature eof is ok (here!)
5558 2 : return eSelectorParsingStatus_Done;
5559 : }
5560 : }
5561 4554 : else if (mToken.IsSymbol('|')) { // No namespace
5562 0 : aDataMask |= SEL_MASK_NSPACE;
5563 0 : aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
5564 :
5565 : // get mandatory tag
5566 0 : if (! GetToken(false)) {
5567 0 : REPORT_UNEXPECTED_EOF(PETypeSelEOF);
5568 0 : return eSelectorParsingStatus_Error;
5569 : }
5570 0 : if (eCSSToken_Ident == mToken.mType) { // element name
5571 0 : aDataMask |= SEL_MASK_ELEM;
5572 0 : aSelector.SetTag(mToken.mIdent);
5573 : }
5574 0 : else if (mToken.IsSymbol('*')) { // universal selector
5575 0 : aDataMask |= SEL_MASK_ELEM;
5576 : // don't set tag
5577 : }
5578 : else {
5579 0 : REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
5580 0 : UngetToken();
5581 0 : return eSelectorParsingStatus_Error;
5582 : }
5583 0 : if (! GetToken(false)) { // premature eof is ok (here!)
5584 0 : return eSelectorParsingStatus_Done;
5585 : }
5586 : }
5587 : else {
5588 4554 : SetDefaultNamespaceOnSelector(aSelector);
5589 : }
5590 :
5591 8387 : if (aIsNegated) {
5592 : // restore last token read in case of a negated type selector
5593 30 : UngetToken();
5594 : }
5595 8387 : return eSelectorParsingStatus_Continue;
5596 : }
5597 :
5598 : //
5599 : // Parse attribute selectors [attr], [attr=value], [attr|=value],
5600 : // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
5601 : //
5602 : CSSParserImpl::nsSelectorParsingStatus
5603 2382 : CSSParserImpl::ParseAttributeSelector(int32_t& aDataMask,
5604 : nsCSSSelector& aSelector)
5605 : {
5606 2382 : if (! GetToken(true)) { // premature EOF
5607 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5608 0 : return eSelectorParsingStatus_Error;
5609 : }
5610 :
5611 2382 : int32_t nameSpaceID = kNameSpaceID_None;
5612 4764 : nsAutoString attr;
5613 2382 : if (mToken.IsSymbol('*')) { // wildcard namespace
5614 0 : nameSpaceID = kNameSpaceID_Unknown;
5615 0 : if (ExpectSymbol('|', false)) {
5616 0 : if (! GetToken(false)) { // premature EOF
5617 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5618 0 : return eSelectorParsingStatus_Error;
5619 : }
5620 0 : if (eCSSToken_Ident == mToken.mType) { // attr name
5621 0 : attr = mToken.mIdent;
5622 : }
5623 : else {
5624 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5625 0 : UngetToken();
5626 0 : return eSelectorParsingStatus_Error;
5627 : }
5628 : }
5629 : else {
5630 0 : REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
5631 0 : return eSelectorParsingStatus_Error;
5632 : }
5633 : }
5634 2382 : else if (mToken.IsSymbol('|')) { // NO namespace
5635 0 : if (! GetToken(false)) { // premature EOF
5636 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5637 0 : return eSelectorParsingStatus_Error;
5638 : }
5639 0 : if (eCSSToken_Ident == mToken.mType) { // attr name
5640 0 : attr = mToken.mIdent;
5641 : }
5642 : else {
5643 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5644 0 : UngetToken();
5645 0 : return eSelectorParsingStatus_Error;
5646 : }
5647 : }
5648 2382 : else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
5649 2382 : attr = mToken.mIdent; // hang on to it
5650 2382 : if (ExpectSymbol('|', false)) { // was a namespace
5651 2 : nameSpaceID = GetNamespaceIdForPrefix(attr);
5652 2 : if (nameSpaceID == kNameSpaceID_Unknown) {
5653 0 : return eSelectorParsingStatus_Error;
5654 : }
5655 2 : if (! GetToken(false)) { // premature EOF
5656 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
5657 0 : return eSelectorParsingStatus_Error;
5658 : }
5659 2 : if (eCSSToken_Ident == mToken.mType) { // attr name
5660 2 : attr = mToken.mIdent;
5661 : }
5662 : else {
5663 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
5664 0 : UngetToken();
5665 0 : return eSelectorParsingStatus_Error;
5666 : }
5667 : }
5668 : }
5669 : else { // malformed
5670 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
5671 0 : UngetToken();
5672 0 : return eSelectorParsingStatus_Error;
5673 : }
5674 :
5675 2382 : bool gotEOF = false;
5676 2382 : if (! GetToken(true)) { // premature EOF
5677 : // Treat this just like we saw a ']', but do still output the
5678 : // warning, similar to what ExpectSymbol does.
5679 0 : REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
5680 0 : gotEOF = true;
5681 : }
5682 4764 : if (gotEOF ||
5683 2499 : (eCSSToken_Symbol == mToken.mType) ||
5684 129 : (eCSSToken_Includes == mToken.mType) ||
5685 24 : (eCSSToken_Dashmatch == mToken.mType) ||
5686 14 : (eCSSToken_Beginsmatch == mToken.mType) ||
5687 4 : (eCSSToken_Endsmatch == mToken.mType) ||
5688 2 : (eCSSToken_Containsmatch == mToken.mType)) {
5689 : uint8_t func;
5690 : // Important: Check the EOF/']' case first, since if gotEOF we
5691 : // don't want to be examining mToken.
5692 2382 : if (gotEOF || ']' == mToken.mSymbol) {
5693 737 : aDataMask |= SEL_MASK_ATTRIB;
5694 737 : aSelector.AddAttribute(nameSpaceID, attr);
5695 737 : func = NS_ATTR_FUNC_SET;
5696 : }
5697 1645 : else if (eCSSToken_Includes == mToken.mType) {
5698 105 : func = NS_ATTR_FUNC_INCLUDES;
5699 : }
5700 1540 : else if (eCSSToken_Dashmatch == mToken.mType) {
5701 0 : func = NS_ATTR_FUNC_DASHMATCH;
5702 : }
5703 1540 : else if (eCSSToken_Beginsmatch == mToken.mType) {
5704 10 : func = NS_ATTR_FUNC_BEGINSMATCH;
5705 : }
5706 1530 : else if (eCSSToken_Endsmatch == mToken.mType) {
5707 0 : func = NS_ATTR_FUNC_ENDSMATCH;
5708 : }
5709 1530 : else if (eCSSToken_Containsmatch == mToken.mType) {
5710 2 : func = NS_ATTR_FUNC_CONTAINSMATCH;
5711 : }
5712 1528 : else if ('=' == mToken.mSymbol) {
5713 1528 : func = NS_ATTR_FUNC_EQUALS;
5714 : }
5715 : else {
5716 0 : REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
5717 0 : UngetToken(); // bad function
5718 0 : return eSelectorParsingStatus_Error;
5719 : }
5720 2382 : if (NS_ATTR_FUNC_SET != func) { // get value
5721 1645 : if (! GetToken(true)) { // premature EOF
5722 0 : REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
5723 0 : return eSelectorParsingStatus_Error;
5724 : }
5725 1645 : if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
5726 3290 : nsAutoString value(mToken.mIdent);
5727 : // Avoid duplicating the eof handling by just not checking for
5728 : // the 'i' annotation if we got eof.
5729 : typedef nsAttrSelector::ValueCaseSensitivity ValueCaseSensitivity;
5730 : ValueCaseSensitivity valueCaseSensitivity =
5731 1645 : ValueCaseSensitivity::CaseSensitive;
5732 1645 : bool eof = !GetToken(true);
5733 1645 : if (!eof) {
5734 1645 : if (eCSSToken_Ident == mToken.mType &&
5735 0 : mToken.mIdent.LowerCaseEqualsLiteral("i")) {
5736 0 : valueCaseSensitivity = ValueCaseSensitivity::CaseInsensitive;
5737 0 : eof = !GetToken(true);
5738 : }
5739 : }
5740 : bool gotClosingBracket;
5741 1645 : if (eof) { // premature EOF
5742 : // Report a warning, but then treat it as a closing bracket.
5743 0 : REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
5744 0 : gotClosingBracket = true;
5745 : } else {
5746 1645 : gotClosingBracket = mToken.IsSymbol(']');
5747 : }
5748 1645 : if (gotClosingBracket) {
5749 : // For cases when this style sheet is applied to an HTML
5750 : // element in an HTML document, and the attribute selector is
5751 : // for a non-namespaced attribute, then check to see if it's
5752 : // one of the known attributes whose VALUE is
5753 : // case-insensitive.
5754 1645 : if (valueCaseSensitivity != ValueCaseSensitivity::CaseInsensitive &&
5755 : nameSpaceID == kNameSpaceID_None) {
5756 : static const char* caseInsensitiveHTMLAttribute[] = {
5757 : // list based on http://www.w3.org/TR/html4/
5758 : "lang",
5759 : "dir",
5760 : "http-equiv",
5761 : "text",
5762 : "link",
5763 : "vlink",
5764 : "alink",
5765 : "compact",
5766 : "align",
5767 : "frame",
5768 : "rules",
5769 : "valign",
5770 : "scope",
5771 : "axis",
5772 : "nowrap",
5773 : "hreflang",
5774 : "rel",
5775 : "rev",
5776 : "charset",
5777 : "codetype",
5778 : "declare",
5779 : "valuetype",
5780 : "shape",
5781 : "nohref",
5782 : "media",
5783 : "bgcolor",
5784 : "clear",
5785 : "color",
5786 : "face",
5787 : "noshade",
5788 : "noresize",
5789 : "scrolling",
5790 : "target",
5791 : "method",
5792 : "enctype",
5793 : "accept-charset",
5794 : "accept",
5795 : "checked",
5796 : "multiple",
5797 : "selected",
5798 : "disabled",
5799 : "readonly",
5800 : "language",
5801 : "defer",
5802 : "type",
5803 : // additional attributes not in HTML4
5804 : "direction", // marquee
5805 : nullptr
5806 : };
5807 1643 : short i = 0;
5808 : const char* htmlAttr;
5809 141417 : while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
5810 70426 : if (attr.LowerCaseEqualsASCII(htmlAttr)) {
5811 539 : valueCaseSensitivity = ValueCaseSensitivity::CaseInsensitiveInHTML;
5812 539 : break;
5813 : }
5814 : }
5815 : }
5816 1645 : aDataMask |= SEL_MASK_ATTRIB;
5817 1645 : aSelector.AddAttribute(nameSpaceID, attr, func, value,
5818 1645 : valueCaseSensitivity);
5819 : }
5820 : else {
5821 0 : REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
5822 0 : UngetToken();
5823 0 : return eSelectorParsingStatus_Error;
5824 1645 : }
5825 : }
5826 : else {
5827 0 : REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
5828 0 : UngetToken();
5829 0 : return eSelectorParsingStatus_Error;
5830 : }
5831 2382 : }
5832 : }
5833 : else {
5834 0 : REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
5835 0 : UngetToken(); // bad dog, no biscut!
5836 0 : return eSelectorParsingStatus_Error;
5837 : }
5838 2382 : return eSelectorParsingStatus_Continue;
5839 : }
5840 :
5841 : //
5842 : // Parse pseudo-classes and pseudo-elements
5843 : //
5844 : CSSParserImpl::nsSelectorParsingStatus
5845 2857 : CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
5846 : nsCSSSelector& aSelector,
5847 : bool aIsNegated,
5848 : nsIAtom** aPseudoElement,
5849 : nsAtomList** aPseudoElementArgs,
5850 : CSSPseudoElementType* aPseudoElementType)
5851 : {
5852 2857 : NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs),
5853 : "expected location to store pseudo element");
5854 2857 : NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs),
5855 : "negated selectors shouldn't have a place to store "
5856 : "pseudo elements");
5857 2857 : if (! GetToken(false)) { // premature eof
5858 0 : REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
5859 0 : return eSelectorParsingStatus_Error;
5860 : }
5861 :
5862 : // First, find out whether we are parsing a CSS3 pseudo-element
5863 2857 : bool parsingPseudoElement = false;
5864 2857 : if (mToken.IsSymbol(':')) {
5865 344 : parsingPseudoElement = true;
5866 344 : if (! GetToken(false)) { // premature eof
5867 0 : REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
5868 0 : return eSelectorParsingStatus_Error;
5869 : }
5870 : }
5871 :
5872 : // Do some sanity-checking on the token
5873 2857 : if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
5874 : // malformed selector
5875 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
5876 0 : UngetToken();
5877 0 : return eSelectorParsingStatus_Error;
5878 : }
5879 :
5880 : // OK, now we know we have an mIdent. Atomize it. All the atoms, for
5881 : // pseudo-classes as well as pseudo-elements, start with a single ':'.
5882 5714 : nsAutoString buffer;
5883 2857 : buffer.Append(char16_t(':'));
5884 2857 : buffer.Append(mToken.mIdent);
5885 2857 : nsContentUtils::ASCIIToLower(buffer);
5886 5714 : nsCOMPtr<nsIAtom> pseudo = NS_Atomize(buffer);
5887 :
5888 : // stash away some info about this pseudo so we only have to get it once.
5889 2857 : bool isTreePseudo = false;
5890 2857 : CSSEnabledState enabledState = EnabledState();
5891 : CSSPseudoElementType pseudoElementType =
5892 2857 : nsCSSPseudoElements::GetPseudoType(pseudo, enabledState);
5893 : CSSPseudoClassType pseudoClassType =
5894 2857 : nsCSSPseudoClasses::GetPseudoType(pseudo, enabledState);
5895 : bool pseudoClassIsUserAction =
5896 2857 : nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
5897 :
5898 2857 : if (nsCSSAnonBoxes::IsNonElement(pseudo)) {
5899 : // Non-element anonymous boxes should not match any rule.
5900 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
5901 0 : UngetToken();
5902 0 : return eSelectorParsingStatus_Error;
5903 : }
5904 :
5905 : // We currently allow :-moz-placeholder and ::-moz-placeholder and
5906 : // ::placeholder. We have to be a bit stricter regarding the
5907 : // pseudo-element parsing rules.
5908 2857 : if (pseudoElementType == CSSPseudoElementType::placeholder &&
5909 : pseudoClassType == CSSPseudoClassType::mozPlaceholder) {
5910 0 : if (parsingPseudoElement) {
5911 0 : pseudoClassType = CSSPseudoClassType::NotPseudo;
5912 : } else {
5913 0 : pseudoElementType = CSSPseudoElementType::NotPseudo;
5914 : }
5915 : }
5916 :
5917 : #ifdef MOZ_XUL
5918 2857 : isTreePseudo = (pseudoElementType == CSSPseudoElementType::XULTree);
5919 : // If a tree pseudo-element is using the function syntax, it will
5920 : // get isTree set here and will pass the check below that only
5921 : // allows functions if they are in our list of things allowed to be
5922 : // functions. If it is _not_ using the function syntax, isTree will
5923 : // be false, and it will still pass that check. So the tree
5924 : // pseudo-elements are allowed to be either functions or not, as
5925 : // desired.
5926 2857 : bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
5927 : #endif
5928 2857 : bool isPseudoElement = (pseudoElementType < CSSPseudoElementType::Count);
5929 : // anonymous boxes are only allowed if they're the tree boxes or we have
5930 : // enabled agent rules
5931 3019 : bool isAnonBox = isTreePseudo ||
5932 2697 : ((pseudoElementType == CSSPseudoElementType::InheritingAnonBox ||
5933 92 : pseudoElementType == CSSPseudoElementType::NonInheritingAnonBox) &&
5934 2949 : AgentRulesEnabled());
5935 : bool isPseudoClass =
5936 2857 : (pseudoClassType != CSSPseudoClassType::NotPseudo);
5937 :
5938 2857 : NS_ASSERTION(!isPseudoClass ||
5939 : pseudoElementType == CSSPseudoElementType::NotPseudo,
5940 : "Why is this atom both a pseudo-class and a pseudo-element?");
5941 2857 : NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1,
5942 : "Shouldn't be more than one of these");
5943 :
5944 2857 : if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
5945 : // Not a pseudo-class, not a pseudo-element.... forget it
5946 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
5947 0 : UngetToken();
5948 0 : return eSelectorParsingStatus_Error;
5949 : }
5950 :
5951 : // If it's a function token, it better be on our "ok" list, and if the name
5952 : // is that of a function pseudo it better be a function token
5953 5714 : if ((eCSSToken_Function == mToken.mType) !=
5954 : (
5955 : #ifdef MOZ_XUL
5956 2812 : isTree ||
5957 : #endif
5958 2238 : CSSPseudoClassType::negation == pseudoClassType ||
5959 4300 : nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
5960 6980 : nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
5961 2061 : nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
5962 : // There are no other function pseudos
5963 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
5964 0 : UngetToken();
5965 0 : return eSelectorParsingStatus_Error;
5966 : }
5967 :
5968 : // If it starts with "::", it better be a pseudo-element
5969 3201 : if (parsingPseudoElement &&
5970 506 : !isPseudoElement &&
5971 162 : !isAnonBox) {
5972 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
5973 0 : UngetToken();
5974 0 : return eSelectorParsingStatus_Error;
5975 : }
5976 :
5977 2857 : if (aSelector.IsPseudoElement()) {
5978 0 : CSSPseudoElementType type = aSelector.PseudoType();
5979 0 : if (type >= CSSPseudoElementType::Count ||
5980 0 : !nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
5981 : // We only allow user action pseudo-classes on certain pseudo-elements.
5982 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
5983 0 : UngetToken();
5984 0 : return eSelectorParsingStatus_Error;
5985 : }
5986 0 : if (!isPseudoClass || !pseudoClassIsUserAction) {
5987 : // CSS 4 Selectors says that pseudo-elements can only be followed by
5988 : // a user action pseudo-class.
5989 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
5990 0 : UngetToken();
5991 0 : return eSelectorParsingStatus_Error;
5992 : }
5993 : }
5994 :
5995 2857 : if (!parsingPseudoElement &&
5996 : CSSPseudoClassType::negation == pseudoClassType) {
5997 574 : if (aIsNegated) { // :not() can't be itself negated
5998 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
5999 0 : UngetToken();
6000 0 : return eSelectorParsingStatus_Error;
6001 : }
6002 : // CSS 3 Negation pseudo-class takes one simple selector as argument
6003 : nsSelectorParsingStatus parsingStatus =
6004 574 : ParseNegatedSimpleSelector(aDataMask, aSelector);
6005 574 : if (eSelectorParsingStatus_Continue != parsingStatus) {
6006 0 : return parsingStatus;
6007 574 : }
6008 : }
6009 2283 : else if (!parsingPseudoElement && isPseudoClass) {
6010 1927 : aDataMask |= SEL_MASK_PCLASS;
6011 1927 : if (eCSSToken_Function == mToken.mType) {
6012 : nsSelectorParsingStatus parsingStatus;
6013 339 : if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
6014 : parsingStatus =
6015 176 : ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
6016 : }
6017 163 : else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) {
6018 : parsingStatus =
6019 1 : ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType);
6020 : }
6021 : else {
6022 162 : MOZ_ASSERT(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
6023 : "unexpected pseudo with function token");
6024 : parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
6025 162 : pseudoClassType);
6026 : }
6027 339 : if (eSelectorParsingStatus_Continue != parsingStatus) {
6028 0 : if (eSelectorParsingStatus_Error == parsingStatus) {
6029 0 : SkipUntil(')');
6030 : }
6031 0 : return parsingStatus;
6032 : }
6033 : }
6034 : else {
6035 1588 : aSelector.AddPseudoClass(pseudoClassType);
6036 1927 : }
6037 : }
6038 356 : else if (isPseudoElement || isAnonBox) {
6039 : // Pseudo-element. Make some more sanity checks.
6040 :
6041 356 : if (aIsNegated) { // pseudo-elements can't be negated
6042 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
6043 0 : UngetToken();
6044 0 : return eSelectorParsingStatus_Error;
6045 : }
6046 : // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
6047 : // to have a single ':' on them. Others (CSS3+ pseudo-elements and
6048 : // various -moz-* pseudo-elements) must have |parsingPseudoElement|
6049 : // set.
6050 724 : if (!parsingPseudoElement &&
6051 12 : !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
6052 : #ifdef MOZ_XUL
6053 356 : && !isTreePseudo
6054 : #endif
6055 : ) {
6056 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
6057 0 : UngetToken();
6058 0 : return eSelectorParsingStatus_Error;
6059 : }
6060 :
6061 356 : if (0 == (aDataMask & SEL_MASK_PELEM)) {
6062 356 : aDataMask |= SEL_MASK_PELEM;
6063 356 : NS_ADDREF(*aPseudoElement = pseudo);
6064 356 : *aPseudoElementType = pseudoElementType;
6065 :
6066 : #ifdef MOZ_XUL
6067 356 : if (isTree) {
6068 : // We have encountered a pseudoelement of the form
6069 : // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each
6070 : // item in the list to the pseudoclass list. They will be pulled
6071 : // from the list later along with the pseudo-element.
6072 45 : if (!ParseTreePseudoElement(aPseudoElementArgs)) {
6073 0 : return eSelectorParsingStatus_Error;
6074 : }
6075 : }
6076 : #endif
6077 :
6078 : // Pseudo-elements can only be followed by user action pseudo-classes
6079 : // or be the end of the selector. So the next non-whitespace token must
6080 : // be ':', '{' or ',' or EOF.
6081 356 : if (!GetToken(true)) { // premature eof is ok (here!)
6082 0 : return eSelectorParsingStatus_Done;
6083 : }
6084 356 : if (parsingPseudoElement && mToken.IsSymbol(':')) {
6085 0 : UngetToken();
6086 0 : return eSelectorParsingStatus_Continue;
6087 : }
6088 356 : if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
6089 356 : UngetToken();
6090 356 : return eSelectorParsingStatus_Done;
6091 : }
6092 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC);
6093 0 : UngetToken();
6094 0 : return eSelectorParsingStatus_Error;
6095 : }
6096 : else { // multiple pseudo elements, not legal
6097 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
6098 0 : UngetToken();
6099 0 : return eSelectorParsingStatus_Error;
6100 : }
6101 : }
6102 : #ifdef DEBUG
6103 : else {
6104 : // We should never end up here. Indeed, if we ended up here, we know (from
6105 : // the current if/else cascade) that !isPseudoElement and !isAnonBox. But
6106 : // then due to our earlier check we know that isPseudoClass. Since we
6107 : // didn't fall into the isPseudoClass case in this cascade, we must have
6108 : // parsingPseudoElement. But we've already checked the
6109 : // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
6110 : // it's happened.
6111 0 : NS_NOTREACHED("How did this happen?");
6112 : }
6113 : #endif
6114 2501 : return eSelectorParsingStatus_Continue;
6115 : }
6116 :
6117 : //
6118 : // Parse the argument of a negation pseudo-class :not()
6119 : //
6120 : CSSParserImpl::nsSelectorParsingStatus
6121 574 : CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask,
6122 : nsCSSSelector& aSelector)
6123 : {
6124 574 : if (! GetToken(true)) { // premature eof
6125 0 : REPORT_UNEXPECTED_EOF(PENegationEOF);
6126 0 : return eSelectorParsingStatus_Error;
6127 : }
6128 :
6129 574 : if (mToken.IsSymbol(')')) {
6130 0 : REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
6131 0 : return eSelectorParsingStatus_Error;
6132 : }
6133 :
6134 : // Create a new nsCSSSelector and add it to the end of
6135 : // aSelector.mNegations.
6136 : // Given the current parsing rules, every selector in mNegations
6137 : // contains only one simple selector (css3 definition) within it.
6138 : // This could easily change in future versions of CSS, and the only
6139 : // thing we need to change to support that is this parsing code and the
6140 : // serialization code for nsCSSSelector.
6141 574 : nsCSSSelector *newSel = new nsCSSSelector();
6142 574 : nsCSSSelector* negations = &aSelector;
6143 944 : while (negations->mNegations) {
6144 185 : negations = negations->mNegations;
6145 : }
6146 574 : negations->mNegations = newSel;
6147 :
6148 : nsSelectorParsingStatus parsingStatus;
6149 574 : if (eCSSToken_ID == mToken.mType) { // #id
6150 26 : parsingStatus = ParseIDSelector(aDataMask, *newSel);
6151 : }
6152 548 : else if (mToken.IsSymbol('.')) { // .class
6153 38 : parsingStatus = ParseClassSelector(aDataMask, *newSel);
6154 : }
6155 510 : else if (mToken.IsSymbol(':')) { // :pseudo
6156 : parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true,
6157 101 : nullptr, nullptr, nullptr);
6158 : }
6159 409 : else if (mToken.IsSymbol('[')) { // [attribute
6160 379 : parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
6161 379 : if (eSelectorParsingStatus_Error == parsingStatus) {
6162 : // Skip forward to the matching ']'
6163 0 : SkipUntil(']');
6164 : }
6165 : }
6166 : else {
6167 : // then it should be a type element or universal selector
6168 30 : parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true);
6169 : }
6170 574 : if (eSelectorParsingStatus_Error == parsingStatus) {
6171 0 : REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
6172 0 : SkipUntil(')');
6173 0 : return parsingStatus;
6174 : }
6175 : // close the parenthesis
6176 574 : if (!ExpectSymbol(')', true)) {
6177 0 : REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
6178 0 : SkipUntil(')');
6179 0 : return eSelectorParsingStatus_Error;
6180 : }
6181 :
6182 574 : NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown ||
6183 : (!newSel->mIDList && !newSel->mClassList &&
6184 : !newSel->mPseudoClassList && !newSel->mAttrList),
6185 : "Need to fix the serialization code to deal with this");
6186 :
6187 574 : return eSelectorParsingStatus_Continue;
6188 : }
6189 :
6190 : //
6191 : // Parse the argument of a pseudo-class that has an ident arg
6192 : //
6193 : CSSParserImpl::nsSelectorParsingStatus
6194 176 : CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
6195 : CSSPseudoClassType aType)
6196 : {
6197 176 : if (! GetToken(true)) { // premature eof
6198 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6199 0 : return eSelectorParsingStatus_Error;
6200 : }
6201 : // We expect an identifier with a language abbreviation
6202 176 : if (eCSSToken_Ident != mToken.mType) {
6203 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
6204 0 : UngetToken();
6205 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6206 : }
6207 :
6208 : // -moz-locale-dir and dir take an identifier argument. While
6209 : // only 'ltr' and 'rtl' (case-insensitively) will match anything, any
6210 : // other identifier is still valid.
6211 176 : if (aType == CSSPseudoClassType::mozLocaleDir ||
6212 : aType == CSSPseudoClassType::dir) {
6213 133 : nsContentUtils::ASCIIToLower(mToken.mIdent); // case insensitive
6214 : }
6215 :
6216 : // Add the pseudo with the language parameter
6217 176 : aSelector.AddPseudoClass(aType, mToken.mIdent.get());
6218 :
6219 : // close the parenthesis
6220 176 : if (!ExpectSymbol(')', true)) {
6221 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
6222 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6223 : }
6224 :
6225 176 : return eSelectorParsingStatus_Continue;
6226 : }
6227 :
6228 : CSSParserImpl::nsSelectorParsingStatus
6229 1 : CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
6230 : CSSPseudoClassType aType)
6231 : {
6232 1 : int32_t numbers[2] = { 0, 0 };
6233 1 : bool lookForB = true;
6234 1 : bool onlyN = false;
6235 1 : bool hasSign = false;
6236 1 : int sign = 1;
6237 :
6238 : // Follow the whitespace rules as proposed in
6239 : // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
6240 :
6241 1 : if (! GetToken(true)) {
6242 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6243 0 : return eSelectorParsingStatus_Error;
6244 : }
6245 :
6246 : // A helper function that checks if the token starts with literal string
6247 : // |aStr| using a case-insensitive match.
6248 4 : auto TokenBeginsWith = [this] (const nsLiteralString& aStr) {
6249 4 : return StringBeginsWith(mToken.mIdent, aStr,
6250 4 : nsASCIICaseInsensitiveStringComparator());
6251 5 : };
6252 :
6253 1 : if (mToken.IsSymbol('+')) {
6254 : // This can only be +n, since +an, -an, +a, -a will all
6255 : // parse a number as the first token, and -n is an ident token.
6256 0 : numbers[0] = 1;
6257 0 : onlyN = true;
6258 :
6259 : // consume the `n`
6260 : // We do not allow whitespace here
6261 : // https://drafts.csswg.org/css-syntax-3/#the-anb-type
6262 0 : if (! GetToken(false)) {
6263 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6264 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6265 : }
6266 : }
6267 :
6268 1 : if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
6269 : // The CSS tokenization doesn't handle :nth-child() containing - well:
6270 : // 2n-1 is a dimension
6271 : // n-1 is an identifier
6272 : // The easiest way to deal with that is to push everything from the
6273 : // minus on back onto the scanner's pushback buffer.
6274 1 : uint32_t truncAt = 0;
6275 1 : if (TokenBeginsWith(NS_LITERAL_STRING("n-"))) {
6276 0 : truncAt = 1;
6277 1 : } else if (TokenBeginsWith(NS_LITERAL_STRING("-n-"))) {
6278 0 : truncAt = 2;
6279 : }
6280 1 : if (truncAt != 0) {
6281 0 : mScanner->Backup(mToken.mIdent.Length() - truncAt);
6282 0 : mToken.mIdent.Truncate(truncAt);
6283 : }
6284 : }
6285 :
6286 1 : if (onlyN) {
6287 : // If we parsed a + or -, check that the truncated
6288 : // token is an "n"
6289 0 : if (eCSSToken_Ident != mToken.mType || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
6290 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6291 0 : return eSelectorParsingStatus_Error;
6292 : }
6293 : } else {
6294 1 : if (eCSSToken_Ident == mToken.mType) {
6295 1 : if (mToken.mIdent.LowerCaseEqualsLiteral("odd")) {
6296 1 : numbers[0] = 2;
6297 1 : numbers[1] = 1;
6298 1 : lookForB = false;
6299 : }
6300 0 : else if (mToken.mIdent.LowerCaseEqualsLiteral("even")) {
6301 0 : numbers[0] = 2;
6302 0 : numbers[1] = 0;
6303 0 : lookForB = false;
6304 : }
6305 0 : else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
6306 0 : numbers[0] = 1;
6307 : }
6308 0 : else if (mToken.mIdent.LowerCaseEqualsLiteral("-n")) {
6309 0 : numbers[0] = -1;
6310 : }
6311 : else {
6312 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6313 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6314 : }
6315 : }
6316 0 : else if (eCSSToken_Number == mToken.mType) {
6317 0 : if (!mToken.mIntegerValid) {
6318 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6319 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6320 : }
6321 :
6322 0 : numbers[1] = mToken.mInteger;
6323 0 : lookForB = false;
6324 : }
6325 0 : else if (eCSSToken_Dimension == mToken.mType) {
6326 0 : if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
6327 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6328 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6329 : }
6330 0 : numbers[0] = mToken.mInteger;
6331 : }
6332 : // XXX If it's a ')', is that valid? (as 0n+0)
6333 : else {
6334 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6335 0 : UngetToken();
6336 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6337 : }
6338 : }
6339 :
6340 1 : if (! GetToken(true)) {
6341 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6342 0 : return eSelectorParsingStatus_Error;
6343 : }
6344 1 : if (lookForB && !mToken.IsSymbol(')')) {
6345 : // The '+' or '-' sign can optionally be separated by whitespace.
6346 : // If it is separated by whitespace from what follows it, it appears
6347 : // as a separate token rather than part of the number token.
6348 0 : if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
6349 0 : hasSign = true;
6350 0 : if (mToken.IsSymbol('-')) {
6351 0 : sign = -1;
6352 : }
6353 0 : if (! GetToken(true)) {
6354 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6355 0 : return eSelectorParsingStatus_Error;
6356 : }
6357 : }
6358 0 : if (eCSSToken_Number != mToken.mType ||
6359 0 : !mToken.mIntegerValid || mToken.mHasSign == hasSign) {
6360 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
6361 0 : UngetToken();
6362 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6363 : }
6364 0 : numbers[1] = mToken.mInteger * sign;
6365 0 : if (! GetToken(true)) {
6366 0 : REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
6367 0 : return eSelectorParsingStatus_Error;
6368 : }
6369 : }
6370 1 : if (!mToken.IsSymbol(')')) {
6371 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
6372 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6373 : }
6374 1 : aSelector.AddPseudoClass(aType, numbers);
6375 1 : return eSelectorParsingStatus_Continue;
6376 : }
6377 :
6378 : //
6379 : // Parse the argument of a pseudo-class that has a selector list argument.
6380 : // Such selector lists cannot contain combinators, but can contain
6381 : // anything that goes between a pair of combinators.
6382 : //
6383 : CSSParserImpl::nsSelectorParsingStatus
6384 162 : CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
6385 : CSSPseudoClassType aType)
6386 : {
6387 324 : nsAutoPtr<nsCSSSelectorList> slist;
6388 162 : if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) {
6389 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6390 : }
6391 :
6392 : // Check that none of the selectors in the list have combinators or
6393 : // pseudo-elements.
6394 819 : for (nsCSSSelectorList *l = slist; l; l = l->mNext) {
6395 657 : nsCSSSelector *s = l->mSelectors;
6396 657 : if (s->mNext || s->IsPseudoElement()) {
6397 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6398 : }
6399 : }
6400 :
6401 : // Add the pseudo with the selector list parameter
6402 162 : aSelector.AddPseudoClass(aType, slist.forget());
6403 :
6404 : // close the parenthesis
6405 162 : if (!ExpectSymbol(')', true)) {
6406 0 : REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
6407 0 : return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
6408 : }
6409 :
6410 162 : return eSelectorParsingStatus_Continue;
6411 : }
6412 :
6413 :
6414 : /**
6415 : * This is the format for selectors:
6416 : * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
6417 : */
6418 : bool
6419 8359 : CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
6420 : char16_t aPrevCombinator)
6421 : {
6422 8359 : if (! GetToken(true)) {
6423 0 : REPORT_UNEXPECTED_EOF(PESelectorEOF);
6424 0 : return false;
6425 : }
6426 :
6427 8359 : nsCSSSelector* selector = aList->AddSelector(aPrevCombinator);
6428 16718 : nsCOMPtr<nsIAtom> pseudoElement;
6429 16718 : nsAutoPtr<nsAtomList> pseudoElementArgs;
6430 8359 : CSSPseudoElementType pseudoElementType = CSSPseudoElementType::NotPseudo;
6431 :
6432 8359 : int32_t dataMask = 0;
6433 : nsSelectorParsingStatus parsingStatus =
6434 8359 : ParseTypeOrUniversalSelector(dataMask, *selector, false);
6435 :
6436 25947 : while (parsingStatus == eSelectorParsingStatus_Continue) {
6437 17151 : if (mToken.IsSymbol(':')) { // :pseudo
6438 5512 : parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
6439 5512 : getter_AddRefs(pseudoElement),
6440 5512 : getter_Transfers(pseudoElementArgs),
6441 2756 : &pseudoElementType);
6442 3112 : if (pseudoElement &&
6443 3022 : pseudoElementType != CSSPseudoElementType::InheritingAnonBox &&
6444 266 : pseudoElementType != CSSPseudoElementType::NonInheritingAnonBox) {
6445 : // Pseudo-elements other than anonymous boxes are represented with
6446 : // a special ':' combinator.
6447 :
6448 264 : aList->mWeight += selector->CalcWeight();
6449 :
6450 264 : selector = aList->AddSelector(':');
6451 :
6452 264 : selector->mLowercaseTag.swap(pseudoElement);
6453 264 : selector->mClassList = pseudoElementArgs.forget();
6454 264 : selector->SetPseudoType(pseudoElementType);
6455 : }
6456 14395 : } else if (selector->IsPseudoElement()) {
6457 : // Once we parsed a pseudo-element, we can only parse
6458 : // pseudo-classes (and only a limited set, which
6459 : // ParsePseudoSelector knows how to handle).
6460 0 : parsingStatus = eSelectorParsingStatus_Done;
6461 0 : UngetToken();
6462 0 : break;
6463 14395 : } else if (eCSSToken_ID == mToken.mType) { // #id
6464 1937 : parsingStatus = ParseIDSelector(dataMask, *selector);
6465 12458 : } else if (mToken.IsSymbol('.')) { // .class
6466 2459 : parsingStatus = ParseClassSelector(dataMask, *selector);
6467 : }
6468 9999 : else if (mToken.IsSymbol('[')) { // [attribute
6469 2003 : parsingStatus = ParseAttributeSelector(dataMask, *selector);
6470 2003 : if (eSelectorParsingStatus_Error == parsingStatus) {
6471 0 : SkipUntil(']');
6472 : }
6473 : }
6474 : else { // not a selector token, we're done
6475 7996 : parsingStatus = eSelectorParsingStatus_Done;
6476 7996 : UngetToken();
6477 7996 : break;
6478 : }
6479 :
6480 9155 : if (parsingStatus != eSelectorParsingStatus_Continue) {
6481 356 : break;
6482 : }
6483 :
6484 8799 : if (! GetToken(false)) { // premature eof is ok (here!)
6485 5 : parsingStatus = eSelectorParsingStatus_Done;
6486 5 : break;
6487 : }
6488 : }
6489 :
6490 8359 : if (parsingStatus == eSelectorParsingStatus_Error) {
6491 0 : return false;
6492 : }
6493 :
6494 8359 : if (!dataMask) {
6495 0 : if (selector->mNext) {
6496 0 : REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
6497 : } else {
6498 0 : REPORT_UNEXPECTED(PESelectorGroupNoSelector);
6499 : }
6500 0 : return false;
6501 : }
6502 :
6503 16628 : if (pseudoElementType == CSSPseudoElementType::InheritingAnonBox ||
6504 8269 : pseudoElementType == CSSPseudoElementType::NonInheritingAnonBox) {
6505 : // We got an anonymous box pseudo-element; it must be the only
6506 : // thing in this selector group.
6507 92 : if (selector->mNext || !IsUniversalSelector(*selector)) {
6508 0 : REPORT_UNEXPECTED(PEAnonBoxNotAlone);
6509 0 : return false;
6510 : }
6511 :
6512 : // Rewrite the current selector as this pseudo-element.
6513 : // It does not contribute to selector weight.
6514 92 : selector->mLowercaseTag.swap(pseudoElement);
6515 92 : selector->mClassList = pseudoElementArgs.forget();
6516 92 : selector->SetPseudoType(pseudoElementType);
6517 92 : return true;
6518 : }
6519 :
6520 8267 : aList->mWeight += selector->CalcWeight();
6521 :
6522 8267 : return true;
6523 : }
6524 :
6525 : already_AddRefed<css::Declaration>
6526 3002 : CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext)
6527 : {
6528 3002 : bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
6529 :
6530 3002 : MOZ_ASSERT(mWebkitBoxUnprefixState == eNotParsingDecls,
6531 : "Someone forgot to clear mWebkitBoxUnprefixState!");
6532 6004 : AutoRestore<WebkitBoxUnprefixState> autoRestore(mWebkitBoxUnprefixState);
6533 3002 : mWebkitBoxUnprefixState = eHaveNotUnprefixed;
6534 :
6535 3002 : if (checkForBraces) {
6536 2995 : if (!ExpectSymbol('{', true)) {
6537 0 : REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
6538 0 : OUTPUT_ERROR();
6539 0 : return nullptr;
6540 : }
6541 : }
6542 6004 : RefPtr<css::Declaration> declaration = new css::Declaration();
6543 3002 : mData.AssertInitialState();
6544 : for (;;) {
6545 9548 : bool changed = false;
6546 9548 : if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) {
6547 3002 : if (!SkipDeclaration(checkForBraces)) {
6548 3009 : break;
6549 : }
6550 2995 : if (checkForBraces) {
6551 2995 : if (ExpectSymbol('}', true)) {
6552 2995 : break;
6553 : }
6554 : }
6555 : // Since the skipped declaration didn't end the block we parse
6556 : // the next declaration.
6557 : }
6558 6546 : }
6559 3002 : declaration->CompressFrom(&mData);
6560 3002 : return declaration.forget();
6561 : }
6562 :
6563 : static Maybe<int32_t>
6564 335 : GetEnumColorValue(nsCSSKeyword aKeyword, bool aIsChrome)
6565 : {
6566 : int32_t value;
6567 335 : if (!nsCSSProps::FindKeyword(aKeyword, nsCSSProps::kColorKTable, value)) {
6568 : // Unknown color keyword.
6569 22 : return Nothing();
6570 : }
6571 313 : if (value < 0) {
6572 : // Known special color keyword handled by style system,
6573 : // e.g. NS_COLOR_CURRENTCOLOR. See nsStyleConsts.h.
6574 18 : return Some(value);
6575 : }
6576 : nscolor color;
6577 295 : auto colorID = static_cast<LookAndFeel::ColorID>(value);
6578 295 : if (NS_FAILED(LookAndFeel::GetColor(colorID, !aIsChrome, &color))) {
6579 : // Known LookAndFeel::ColorID, but this platform's LookAndFeel impl
6580 : // doesn't map it to a color. (This might be a platform-specific
6581 : // ColorID, which only makes sense on another platform.)
6582 0 : return Nothing();
6583 : }
6584 : // Known color provided by LookAndFeel.
6585 295 : return Some(value);
6586 : }
6587 :
6588 : /// Returns the number of digits in a positive number
6589 : /// assuming it has <= 6 digits
6590 : static uint32_t
6591 0 : CountNumbersForHashlessColor(uint32_t number) {
6592 : /// Just use a simple match instead of calculating a log
6593 : /// or dividing in a loop to be more efficient.
6594 0 : if (number < 10) {
6595 0 : return 1;
6596 0 : } else if (number < 100) {
6597 0 : return 2;
6598 0 : } else if (number < 1000) {
6599 0 : return 3;
6600 0 : } else if (number < 10000) {
6601 0 : return 4;
6602 0 : } else if (number < 100000) {
6603 0 : return 5;
6604 0 : } else if (number < 1000000) {
6605 0 : return 6;
6606 : } else {
6607 : // we don't care about numbers with more than 6 digits other
6608 : // than the fact that they have more than 6 digits, so just return something
6609 : // larger than 6 here. This is incorrect in the general case.
6610 0 : return 100;
6611 : }
6612 : }
6613 :
6614 :
6615 : CSSParseResult
6616 1158 : CSSParserImpl::ParseColor(nsCSSValue& aValue)
6617 : {
6618 1158 : if (!GetToken(true)) {
6619 0 : REPORT_UNEXPECTED_EOF(PEColorEOF);
6620 0 : return CSSParseResult::NotFound;
6621 : }
6622 :
6623 1158 : nsCSSToken* tk = &mToken;
6624 : nscolor rgba;
6625 1158 : switch (tk->mType) {
6626 : case eCSSToken_ID:
6627 : case eCSSToken_Hash:
6628 : // #rgb, #rrggbb, #rgba, #rrggbbaa
6629 362 : if (NS_HexToRGBA(tk->mIdent, nsHexColorType::AllowAlpha, &rgba)) {
6630 : nsCSSUnit unit;
6631 362 : switch (tk->mIdent.Length()) {
6632 : case 3:
6633 59 : unit = eCSSUnit_ShortHexColor;
6634 59 : break;
6635 : case 4:
6636 0 : unit = eCSSUnit_ShortHexColorAlpha;
6637 0 : break;
6638 : case 6:
6639 303 : unit = eCSSUnit_HexColor;
6640 303 : break;
6641 : default:
6642 0 : MOZ_FALLTHROUGH_ASSERT("unexpected hex color length");
6643 : case 8:
6644 0 : unit = eCSSUnit_HexColorAlpha;
6645 0 : break;
6646 : }
6647 362 : aValue.SetIntegerColorValue(rgba, unit);
6648 362 : return CSSParseResult::Ok;
6649 : }
6650 0 : break;
6651 :
6652 : case eCSSToken_Ident: {
6653 557 : if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
6654 : // Lowercase color name, since keyword values should be
6655 : // serialized in lowercase.
6656 222 : nsContentUtils::ASCIIToLower(tk->mIdent);
6657 222 : aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
6658 222 : return CSSParseResult::Ok;
6659 : }
6660 335 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
6661 357 : if (Maybe<int32_t> value = GetEnumColorValue(keyword, mIsChrome)) {
6662 313 : aValue.SetIntValue(value.value(), eCSSUnit_EnumColor);
6663 313 : return CSSParseResult::Ok;
6664 : }
6665 22 : break;
6666 : }
6667 : case eCSSToken_Function: {
6668 : bool isRGB;
6669 : bool isHSL;
6670 464 : if ((isRGB = mToken.mIdent.LowerCaseEqualsLiteral("rgb")) ||
6671 225 : mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
6672 : // rgb() = rgb( <percentage>{3} [ / <alpha-value> ]? ) |
6673 : // rgb( <number>{3} [ / <alpha-value> ]? ) |
6674 : // rgb( <percentage>#{3} , <alpha-value>? ) |
6675 : // rgb( <number>#{3} , <alpha-value>? )
6676 : // <alpha-value> = <number> | <percentage>
6677 : // rgba is an alias of rgb.
6678 :
6679 70 : if (GetToken(true)) {
6680 70 : UngetToken();
6681 : }
6682 70 : if (mToken.mType == eCSSToken_Number) { // <number>
6683 : uint8_t r, g, b, a;
6684 :
6685 68 : if (ParseRGBColor(r, g, b, a)) {
6686 68 : aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a),
6687 68 : isRGB ? eCSSUnit_RGBColor : eCSSUnit_RGBAColor);
6688 68 : return CSSParseResult::Ok;
6689 : }
6690 : } else { // <percentage>
6691 : float r, g, b, a;
6692 :
6693 2 : if (ParseRGBColor(r, g, b, a)) {
6694 2 : aValue.SetFloatColorValue(r, g, b, a,
6695 2 : isRGB ? eCSSUnit_PercentageRGBColor : eCSSUnit_PercentageRGBAColor);
6696 2 : return CSSParseResult::Ok;
6697 : }
6698 : }
6699 0 : SkipUntil(')');
6700 0 : return CSSParseResult::Error;
6701 : }
6702 313 : else if ((isHSL = mToken.mIdent.LowerCaseEqualsLiteral("hsl")) ||
6703 144 : mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
6704 : // hsl() = hsl( <hue> <percentage> <percentage> [ / <alpha-value> ]? ) ||
6705 : // hsl( <hue>, <percentage>, <percentage>, <alpha-value>? )
6706 : // <hue> = <number> | <angle>
6707 : // hsla is an alias of hsl.
6708 :
6709 : float h, s, l, a;
6710 :
6711 169 : if (ParseHSLColor(h, s, l, a)) {
6712 169 : aValue.SetFloatColorValue(h, s, l, a,
6713 169 : isHSL ? eCSSUnit_HSLColor : eCSSUnit_HSLAColor);
6714 169 : return CSSParseResult::Ok;
6715 : }
6716 0 : SkipUntil(')');
6717 0 : return CSSParseResult::Error;
6718 : }
6719 0 : break;
6720 : }
6721 : default:
6722 0 : break;
6723 : }
6724 :
6725 : // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
6726 22 : if (mHashlessColorQuirk) {
6727 : // https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
6728 : //
6729 : // - If the string starts with 'a-f', the nsCSSScanner builds the
6730 : // token as a eCSSToken_Ident and we can parse the string as a
6731 : // 'xxyyzz' RGB color.
6732 : // - If it only contains up to six '0-9' digits, the token is a
6733 : // eCSSToken_Number and it must be converted back to a 6
6734 : // characters string to be parsed as a RGB color. The number cannot
6735 : // be specified as more than six digits.
6736 : // - If it starts with '0-9' and contains any 'a-f', the token is a
6737 : // eCSSToken_Dimension, the mNumber part must be converted back to
6738 : // a string and the mIdent part must be appended to that string so
6739 : // that the resulting string has 6 characters. The combined
6740 : // dimension cannot be longer than 6 characters.
6741 : // Note: This is a hack for Nav compatibility. Do not attempt to
6742 : // simplify it by hacking into the ncCSSScanner. This would be very
6743 : // bad.
6744 0 : nsAutoString str;
6745 : char buffer[20];
6746 0 : switch (tk->mType) {
6747 : case eCSSToken_Ident:
6748 0 : str.Assign(tk->mIdent);
6749 0 : break;
6750 :
6751 : case eCSSToken_Number:
6752 0 : if (tk->mIntegerValid && tk->mInteger < 1000000 && tk->mInteger >= 0) {
6753 0 : SprintfLiteral(buffer, "%06d", tk->mInteger);
6754 0 : str.AssignWithConversion(buffer);
6755 : }
6756 0 : break;
6757 :
6758 : case eCSSToken_Dimension:
6759 0 : if (tk->mIntegerValid &&
6760 0 : tk->mIdent.Length() + CountNumbersForHashlessColor(tk->mInteger) <= 6 &&
6761 0 : tk->mInteger >= 0) {
6762 0 : SprintfLiteral(buffer, "%06d", tk->mInteger);
6763 0 : nsAutoString temp;
6764 0 : temp.AssignWithConversion(buffer);
6765 0 : temp.Right(str, 6 - tk->mIdent.Length());
6766 0 : str.Append(tk->mIdent);
6767 : }
6768 0 : break;
6769 : default:
6770 : // There is a whole bunch of cases that are
6771 : // not handled by this switch. Ignore them.
6772 0 : break;
6773 : }
6774 : // The hashless color quirk does not support 4 & 8 digit colors with alpha.
6775 0 : if (NS_HexToRGBA(str, nsHexColorType::NoAlpha, &rgba)) {
6776 0 : aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor);
6777 0 : return CSSParseResult::Ok;
6778 : }
6779 : }
6780 :
6781 : // It's not a color
6782 22 : REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
6783 22 : UngetToken();
6784 22 : return CSSParseResult::NotFound;
6785 : }
6786 :
6787 : bool
6788 204 : CSSParserImpl::ParseColorComponent(uint8_t& aComponent, const Maybe<char>& aSeparator)
6789 : {
6790 204 : if (!GetToken(true)) {
6791 0 : REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
6792 0 : return false;
6793 : }
6794 :
6795 204 : if (mToken.mType != eCSSToken_Number) {
6796 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
6797 0 : UngetToken();
6798 0 : return false;
6799 : }
6800 :
6801 204 : float value = mToken.mNumber;
6802 :
6803 204 : if (aSeparator && !ExpectSymbol(*aSeparator, true)) {
6804 0 : REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator);
6805 0 : return false;
6806 : }
6807 :
6808 204 : if (value < 0.0f) value = 0.0f;
6809 204 : if (value > 255.0f) value = 255.0f;
6810 :
6811 204 : aComponent = NSToIntRound(value);
6812 204 : return true;
6813 : }
6814 :
6815 : bool
6816 344 : CSSParserImpl::ParseColorComponent(float& aComponent, const Maybe<char>& aSeparator)
6817 : {
6818 344 : if (!GetToken(true)) {
6819 0 : REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
6820 0 : return false;
6821 : }
6822 :
6823 344 : if (mToken.mType != eCSSToken_Percentage) {
6824 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
6825 0 : UngetToken();
6826 0 : return false;
6827 : }
6828 :
6829 344 : float value = mToken.mNumber;
6830 :
6831 344 : if (aSeparator && !ExpectSymbol(*aSeparator, true)) {
6832 0 : REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator);
6833 0 : return false;
6834 : }
6835 :
6836 344 : if (value < 0.0f) value = 0.0f;
6837 344 : if (value > 1.0f) value = 1.0f;
6838 :
6839 344 : aComponent = value;
6840 344 : return true;
6841 : }
6842 :
6843 : bool
6844 169 : CSSParserImpl::ParseHue(float& aAngle)
6845 : {
6846 338 : nsCSSValue value;
6847 : // <hue> = <number> | <angle>
6848 : // https://drafts.csswg.org/css-color/#typedef-hue
6849 169 : if (!ParseSingleTokenVariant(value,
6850 : VARIANT_NUMBER | VARIANT_ANGLE,
6851 : nullptr)) {
6852 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedNumberOrAngle);
6853 0 : return false;
6854 : }
6855 :
6856 : float unclampedResult;
6857 169 : if (value.GetUnit() == eCSSUnit_Number) {
6858 169 : unclampedResult = value.GetFloatValue();
6859 : } else {
6860 : // Convert double value of GetAngleValueInDegrees() to float.
6861 0 : unclampedResult = value.GetAngleValueInDegrees();
6862 : }
6863 :
6864 : // Clamp it as finite values in float.
6865 169 : aAngle = mozilla::clamped(unclampedResult,
6866 338 : -std::numeric_limits<float>::max(),
6867 507 : std::numeric_limits<float>::max());
6868 169 : return true;
6869 : }
6870 :
6871 : bool
6872 169 : CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
6873 : float& aOpacity)
6874 : {
6875 : // comma-less expression:
6876 : // hsl() = hsl( <hue> <saturation> <lightness> [ / <alpha-value> ]? )
6877 : // the expression with comma:
6878 : // hsl() = hsl( <hue>, <saturation>, <lightness>, <alpha-value>? )
6879 :
6880 169 : const char commaSeparator = ',';
6881 :
6882 : // Parse hue.
6883 : // <hue> = <number> | <angle>
6884 : float degreeAngle;
6885 169 : if (!ParseHue(degreeAngle)) {
6886 0 : return false;
6887 : }
6888 169 : aHue = degreeAngle / 360.0f;
6889 : // hue values are wraparound
6890 169 : aHue = aHue - floor(aHue);
6891 : // Look for a comma separator after "hue" component to determine if the
6892 : // expression is comma-less or not.
6893 169 : bool hasComma = ExpectSymbol(commaSeparator, true);
6894 :
6895 : // Parse saturation, lightness and opacity.
6896 : // The saturation and lightness are <percentage>, so reuse the float version
6897 : // of ParseColorComponent function for them. No need to check the separator
6898 : // after 'lightness'. It will be checked in opacity value parsing.
6899 169 : const char separatorBeforeAlpha = hasComma ? commaSeparator : '/';
6900 1014 : if (ParseColorComponent(aSaturation, hasComma ? Some(commaSeparator) : Nothing()) &&
6901 1014 : ParseColorComponent(aLightness, Nothing()) &&
6902 169 : ParseColorOpacityAndCloseParen(aOpacity, separatorBeforeAlpha)) {
6903 169 : return true;
6904 : }
6905 :
6906 0 : return false;
6907 : }
6908 :
6909 :
6910 : bool
6911 68 : CSSParserImpl::ParseColorOpacityAndCloseParen(uint8_t& aOpacity,
6912 : char aSeparator)
6913 : {
6914 : float floatOpacity;
6915 68 : if (!ParseColorOpacityAndCloseParen(floatOpacity, aSeparator)) {
6916 0 : return false;
6917 : }
6918 :
6919 68 : uint8_t value = nsStyleUtil::FloatToColorComponent(floatOpacity);
6920 : // Need to compare to something slightly larger
6921 : // than 0.5 due to floating point inaccuracies.
6922 68 : NS_ASSERTION(fabs(255.0f * floatOpacity - value) <= 0.51f,
6923 : "FloatToColorComponent did something weird");
6924 :
6925 68 : aOpacity = value;
6926 68 : return true;
6927 : }
6928 :
6929 : bool
6930 239 : CSSParserImpl::ParseColorOpacityAndCloseParen(float& aOpacity,
6931 : char aSeparator)
6932 : {
6933 239 : if (ExpectSymbol(')', true)) {
6934 : // The optional [separator <alpha-value>] was omitted, so set the opacity
6935 : // to a fully-opaque value '1.0f' and return success.
6936 33 : aOpacity = 1.0f;
6937 33 : return true;
6938 : }
6939 :
6940 206 : if (!ExpectSymbol(aSeparator, true)) {
6941 0 : REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aSeparator);
6942 0 : return false;
6943 : }
6944 :
6945 206 : if (!GetToken(true)) {
6946 0 : REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
6947 0 : return false;
6948 : }
6949 :
6950 : // eCSSToken_Number or eCSSToken_Percentage.
6951 206 : if (mToken.mType != eCSSToken_Number && mToken.mType != eCSSToken_Percentage) {
6952 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedNumberOrPercent);
6953 0 : UngetToken();
6954 0 : return false;
6955 : }
6956 :
6957 206 : if (!ExpectSymbol(')', true)) {
6958 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
6959 0 : return false;
6960 : }
6961 :
6962 206 : if (mToken.mNumber < 0.0f) {
6963 0 : mToken.mNumber = 0.0f;
6964 206 : } else if (mToken.mNumber > 1.0f) {
6965 0 : mToken.mNumber = 1.0f;
6966 : }
6967 :
6968 206 : aOpacity = mToken.mNumber;
6969 206 : return true;
6970 : }
6971 :
6972 : template<typename ComponentType>
6973 : bool
6974 70 : CSSParserImpl::ParseRGBColor(ComponentType& aR,
6975 : ComponentType& aG,
6976 : ComponentType& aB,
6977 : ComponentType& aA)
6978 : {
6979 : // comma-less expression:
6980 : // rgb() = rgb( component{3} [ / <alpha-value> ]? )
6981 : // the expression with comma:
6982 : // rgb() = rgb( component#{3} , <alpha-value>? )
6983 :
6984 70 : const char commaSeparator = ',';
6985 :
6986 : // Parse R.
6987 70 : if (!ParseColorComponent(aR, Nothing())) {
6988 0 : return false;
6989 : }
6990 : // Look for a comma separator after "r" component to determine if the
6991 : // expression is comma-less or not.
6992 70 : bool hasComma = ExpectSymbol(commaSeparator, true);
6993 :
6994 : // Parse G, B and A.
6995 : // No need to check the separator after 'B'. It will be checked in 'A' value
6996 : // parsing.
6997 70 : const char separatorBeforeAlpha = hasComma ? commaSeparator : '/';
6998 280 : if (ParseColorComponent(aG, hasComma ? Some(commaSeparator) : Nothing()) &&
6999 280 : ParseColorComponent(aB, Nothing()) &&
7000 : ParseColorOpacityAndCloseParen(aA, separatorBeforeAlpha)) {
7001 70 : return true;
7002 : }
7003 :
7004 0 : return false;
7005 : }
7006 :
7007 : #ifdef MOZ_XUL
7008 : bool
7009 45 : CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs)
7010 : {
7011 : // The argument to a tree pseudo-element is a sequence of identifiers
7012 : // that are either space- or comma-separated. (Was the intent to
7013 : // allow only comma-separated? That's not what was done.)
7014 90 : nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass
7015 :
7016 331 : while (!ExpectSymbol(')', true)) {
7017 143 : if (!GetToken(true)) {
7018 0 : return false;
7019 : }
7020 143 : if (eCSSToken_Ident == mToken.mType) {
7021 94 : fakeSelector.AddClass(mToken.mIdent);
7022 : }
7023 49 : else if (!mToken.IsSymbol(',')) {
7024 0 : UngetToken();
7025 0 : SkipUntil(')');
7026 0 : return false;
7027 : }
7028 : }
7029 45 : *aPseudoElementArgs = fakeSelector.mClassList;
7030 45 : fakeSelector.mClassList = nullptr;
7031 45 : return true;
7032 : }
7033 : #endif
7034 :
7035 : nsCSSKeyword
7036 4471 : CSSParserImpl::LookupKeywordPrefixAware(nsAString& aKeywordStr,
7037 : const KTableEntry aKeywordTable[])
7038 : {
7039 4471 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aKeywordStr);
7040 :
7041 4471 : if (!sWebkitPrefixedAliasesEnabled) {
7042 : // Not accepting webkit-prefixed keywords -> don't do anything special.
7043 0 : return keyword;
7044 : }
7045 :
7046 4471 : if (aKeywordTable == nsCSSProps::kDisplayKTable) {
7047 419 : if ((keyword == eCSSKeyword__webkit_box ||
7048 : keyword == eCSSKeyword__webkit_inline_box)) {
7049 : // Make a note that we're accepting some "-webkit-{inline-}box" styling,
7050 : // so we can give special treatment to subsequent "-moz-{inline}-box".
7051 : // (See special treatment below.)
7052 0 : if (mWebkitBoxUnprefixState == eHaveNotUnprefixed) {
7053 0 : mWebkitBoxUnprefixState = eHaveUnprefixed;
7054 : }
7055 419 : } else if (mWebkitBoxUnprefixState == eHaveUnprefixed &&
7056 0 : (keyword == eCSSKeyword__moz_box ||
7057 : keyword == eCSSKeyword__moz_inline_box)) {
7058 : // If we've seen "display: -webkit-box" (or "-webkit-inline-box") in an
7059 : // earlier declaration and we honored it, then we have to watch out for
7060 : // later "display: -moz-box" (and "-moz-inline-box") declarations; they're
7061 : // likely just a halfhearted attempt at compatibility, and they actually
7062 : // end up stomping on our emulation of the earlier -webkit-box
7063 : // display-value, via the CSS cascade. To prevent this problem, we treat
7064 : // "display: -moz-box" & "-moz-inline-box" as if they were simply a
7065 : // repetition of the webkit equivalent that we already parsed.
7066 0 : MOZ_ASSERT(sWebkitPrefixedAliasesEnabled,
7067 : "The only way mWebkitBoxUnprefixState can be eHaveUnprefixed "
7068 : "is if we're supporting webkit-prefixed aliases");
7069 0 : return (keyword == eCSSKeyword__moz_box) ?
7070 0 : eCSSKeyword__webkit_box : eCSSKeyword__webkit_inline_box;
7071 : }
7072 : }
7073 :
7074 4471 : return keyword;
7075 : }
7076 :
7077 : //----------------------------------------------------------------------
7078 :
7079 : bool
7080 9548 : CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
7081 : uint32_t aFlags,
7082 : bool aMustCallValueAppended,
7083 : bool* aChanged,
7084 : nsCSSContextType aContext)
7085 : {
7086 9548 : NS_PRECONDITION(aContext == eCSSContext_General ||
7087 : aContext == eCSSContext_Page,
7088 : "Must be page or general context");
7089 :
7090 9548 : bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
7091 :
7092 9548 : mTempData.AssertInitialState();
7093 :
7094 : // Get property name
7095 9548 : nsCSSToken* tk = &mToken;
7096 19096 : nsAutoString propertyName;
7097 : for (;;) {
7098 9548 : if (!GetToken(true)) {
7099 7 : if (checkForBraces) {
7100 0 : REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
7101 : }
7102 7 : return false;
7103 : }
7104 9541 : if (eCSSToken_Ident == tk->mType) {
7105 6546 : propertyName = tk->mIdent;
7106 : // grab the ident before the ExpectSymbol trashes the token
7107 6546 : if (!ExpectSymbol(':', true)) {
7108 0 : REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
7109 0 : REPORT_UNEXPECTED(PEDeclDropped);
7110 0 : OUTPUT_ERROR();
7111 0 : return false;
7112 : }
7113 6546 : break;
7114 : }
7115 2995 : if (tk->IsSymbol(';')) {
7116 : // dangling semicolons are skipped
7117 0 : continue;
7118 : }
7119 :
7120 2995 : if (!tk->IsSymbol('}')) {
7121 0 : REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
7122 0 : REPORT_UNEXPECTED(PEDeclSkipped);
7123 0 : OUTPUT_ERROR();
7124 :
7125 0 : if (eCSSToken_AtKeyword == tk->mType) {
7126 0 : SkipAtRule(checkForBraces);
7127 0 : return true; // Not a declaration, but don't skip until ';'
7128 : }
7129 : }
7130 : // Not a declaration...
7131 2995 : UngetToken();
7132 2995 : return false;
7133 : }
7134 :
7135 : // Don't report property parse errors if we're inside a failing @supports
7136 : // rule.
7137 13092 : nsAutoSuppressErrors suppressErrors(this, mInFailingSupportsRule);
7138 :
7139 : // Information about a parsed non-custom property.
7140 : nsCSSPropertyID propID;
7141 :
7142 : // Information about a parsed custom property.
7143 : CSSVariableDeclarations::Type variableType;
7144 13092 : nsString variableValue;
7145 :
7146 : // Check if the property name is a custom property.
7147 6546 : bool customProperty = nsCSSProps::IsCustomPropertyName(propertyName) &&
7148 6546 : aContext == eCSSContext_General;
7149 :
7150 6546 : if (customProperty) {
7151 74 : if (!ParseVariableDeclaration(&variableType, variableValue)) {
7152 0 : REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
7153 0 : REPORT_UNEXPECTED(PEDeclDropped);
7154 0 : OUTPUT_ERROR();
7155 0 : return false;
7156 : }
7157 : } else {
7158 : // Map property name to its ID.
7159 6472 : propID = LookupEnabledProperty(propertyName);
7160 12944 : if (eCSSProperty_UNKNOWN == propID ||
7161 12944 : eCSSPropertyExtra_variable == propID ||
7162 0 : (aContext == eCSSContext_Page &&
7163 0 : !nsCSSProps::PropHasFlags(propID,
7164 : CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property
7165 0 : if (!NonMozillaVendorIdentifier(propertyName)) {
7166 0 : REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName);
7167 0 : REPORT_UNEXPECTED(PEDeclDropped);
7168 0 : OUTPUT_ERROR();
7169 : }
7170 0 : return false;
7171 : }
7172 : // Then parse the property.
7173 6472 : if (!ParseProperty(propID)) {
7174 : // XXX Much better to put stuff in the value parsers instead...
7175 0 : REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
7176 0 : REPORT_UNEXPECTED(PEDeclDropped);
7177 0 : OUTPUT_ERROR();
7178 0 : mTempData.ClearProperty(propID);
7179 0 : mTempData.AssertInitialState();
7180 0 : return false;
7181 : }
7182 : }
7183 :
7184 6546 : CLEAR_ERROR();
7185 :
7186 : // Look for "!important".
7187 : PriorityParsingStatus status;
7188 6546 : if ((aFlags & eParseDeclaration_AllowImportant) != 0) {
7189 6448 : status = ParsePriority();
7190 : } else {
7191 98 : status = ePriority_None;
7192 : }
7193 :
7194 : // Look for a semicolon or close brace.
7195 6546 : if (status != ePriority_Error) {
7196 6546 : if (!GetToken(true)) {
7197 : // EOF is always ok
7198 6543 : } else if (mToken.IsSymbol(';')) {
7199 : // semicolon is always ok
7200 6 : } else if (mToken.IsSymbol('}')) {
7201 : // brace is ok if checkForBraces, but don't eat it
7202 6 : UngetToken();
7203 6 : if (!checkForBraces) {
7204 0 : status = ePriority_Error;
7205 : }
7206 : } else {
7207 0 : UngetToken();
7208 0 : status = ePriority_Error;
7209 : }
7210 : }
7211 :
7212 6546 : if (status == ePriority_Error) {
7213 0 : if (checkForBraces) {
7214 0 : REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
7215 : } else {
7216 0 : REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
7217 : }
7218 0 : REPORT_UNEXPECTED(PEDeclDropped);
7219 0 : OUTPUT_ERROR();
7220 0 : if (!customProperty) {
7221 0 : mTempData.ClearProperty(propID);
7222 : }
7223 0 : mTempData.AssertInitialState();
7224 0 : return false;
7225 : }
7226 :
7227 6546 : if (customProperty) {
7228 74 : MOZ_ASSERT(Substring(propertyName, 0,
7229 : CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
7230 : // remove '--'
7231 148 : nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
7232 74 : aDeclaration->AddVariable(varName, variableType, variableValue,
7233 74 : status == ePriority_Important, false);
7234 : } else {
7235 12944 : *aChanged |= mData.TransferFromBlock(mTempData, propID, EnabledState(),
7236 : status == ePriority_Important,
7237 : false, aMustCallValueAppended,
7238 6472 : aDeclaration, GetDocument());
7239 : }
7240 :
7241 6546 : return true;
7242 : }
7243 :
7244 : static const nsCSSPropertyID kBorderTopIDs[] = {
7245 : eCSSProperty_border_top_width,
7246 : eCSSProperty_border_top_style,
7247 : eCSSProperty_border_top_color
7248 : };
7249 : static const nsCSSPropertyID kBorderRightIDs[] = {
7250 : eCSSProperty_border_right_width,
7251 : eCSSProperty_border_right_style,
7252 : eCSSProperty_border_right_color
7253 : };
7254 : static const nsCSSPropertyID kBorderBottomIDs[] = {
7255 : eCSSProperty_border_bottom_width,
7256 : eCSSProperty_border_bottom_style,
7257 : eCSSProperty_border_bottom_color
7258 : };
7259 : static const nsCSSPropertyID kBorderLeftIDs[] = {
7260 : eCSSProperty_border_left_width,
7261 : eCSSProperty_border_left_style,
7262 : eCSSProperty_border_left_color
7263 : };
7264 : static const nsCSSPropertyID kBorderInlineStartIDs[] = {
7265 : eCSSProperty_border_inline_start_width,
7266 : eCSSProperty_border_inline_start_style,
7267 : eCSSProperty_border_inline_start_color
7268 : };
7269 : static const nsCSSPropertyID kBorderInlineEndIDs[] = {
7270 : eCSSProperty_border_inline_end_width,
7271 : eCSSProperty_border_inline_end_style,
7272 : eCSSProperty_border_inline_end_color
7273 : };
7274 : static const nsCSSPropertyID kBorderBlockStartIDs[] = {
7275 : eCSSProperty_border_block_start_width,
7276 : eCSSProperty_border_block_start_style,
7277 : eCSSProperty_border_block_start_color
7278 : };
7279 : static const nsCSSPropertyID kBorderBlockEndIDs[] = {
7280 : eCSSProperty_border_block_end_width,
7281 : eCSSProperty_border_block_end_style,
7282 : eCSSProperty_border_block_end_color
7283 : };
7284 : static const nsCSSPropertyID kColumnRuleIDs[] = {
7285 : eCSSProperty_column_rule_width,
7286 : eCSSProperty_column_rule_style,
7287 : eCSSProperty_column_rule_color
7288 : };
7289 :
7290 : bool
7291 316 : CSSParserImpl::ParseEnum(nsCSSValue& aValue,
7292 : const KTableEntry aKeywordTable[])
7293 : {
7294 316 : nsAString* ident = NextIdent();
7295 316 : if (nullptr == ident) {
7296 88 : return false;
7297 : }
7298 228 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
7299 : int32_t value;
7300 228 : if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
7301 217 : aValue.SetIntValue(value, eCSSUnit_Enumerated);
7302 217 : return true;
7303 : }
7304 :
7305 : // Put the unknown identifier back and return
7306 11 : UngetToken();
7307 11 : return false;
7308 : }
7309 :
7310 : bool
7311 9 : CSSParserImpl::ParseAlignEnum(nsCSSValue& aValue,
7312 : const KTableEntry aKeywordTable[])
7313 : {
7314 9 : MOZ_ASSERT(nsCSSProps::ValueToKeywordEnum(NS_STYLE_ALIGN_BASELINE,
7315 : aKeywordTable) !=
7316 : eCSSKeyword_UNKNOWN,
7317 : "Please use ParseEnum instead");
7318 9 : nsAString* ident = NextIdent();
7319 9 : if (!ident) {
7320 0 : return false;
7321 : }
7322 9 : nsCSSKeyword baselinePrefix = eCSSKeyword_first;
7323 9 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
7324 9 : if (keyword == eCSSKeyword_first || keyword == eCSSKeyword_last) {
7325 0 : baselinePrefix = keyword;
7326 0 : ident = NextIdent();
7327 0 : if (!ident) {
7328 0 : return false;
7329 : }
7330 0 : keyword = nsCSSKeywords::LookupKeyword(*ident);
7331 : }
7332 : int32_t value;
7333 9 : if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
7334 0 : if (baselinePrefix == eCSSKeyword_last &&
7335 : keyword == eCSSKeyword_baseline) {
7336 0 : value = NS_STYLE_ALIGN_LAST_BASELINE;
7337 : }
7338 0 : aValue.SetIntValue(value, eCSSUnit_Enumerated);
7339 0 : return true;
7340 : }
7341 :
7342 : // Put the unknown identifier back and return
7343 9 : UngetToken();
7344 9 : return false;
7345 : }
7346 :
7347 : struct UnitInfo {
7348 : char name[6]; // needs to be long enough for the longest unit, with
7349 : // terminating null.
7350 : uint32_t length;
7351 : nsCSSUnit unit;
7352 : int32_t type;
7353 : };
7354 :
7355 : #define STR_WITH_LEN(_str) \
7356 : _str, sizeof(_str) - 1
7357 :
7358 : const UnitInfo UnitData[] = {
7359 : { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
7360 : { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
7361 : { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
7362 : { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
7363 : { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
7364 : { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
7365 : { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
7366 : { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH },
7367 : { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
7368 : { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH },
7369 : { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH },
7370 : { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH },
7371 : { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH },
7372 : { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH },
7373 : { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
7374 : { STR_WITH_LEN("q"), eCSSUnit_Quarter, VARIANT_LENGTH },
7375 : { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
7376 : { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
7377 : { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
7378 : { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE },
7379 : { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
7380 : { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
7381 : { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
7382 : { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
7383 : };
7384 :
7385 : #undef STR_WITH_LEN
7386 :
7387 : bool
7388 4311 : CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
7389 : uint32_t aVariantMask,
7390 : float aNumber,
7391 : const nsString& aUnit)
7392 : {
7393 : nsCSSUnit units;
7394 4311 : int32_t type = 0;
7395 4311 : if (!aUnit.IsEmpty()) {
7396 : uint32_t i;
7397 9301 : for (i = 0; i < ArrayLength(UnitData); ++i) {
7398 9301 : if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
7399 9301 : UnitData[i].length)) {
7400 3332 : units = UnitData[i].unit;
7401 3332 : type = UnitData[i].type;
7402 3332 : break;
7403 : }
7404 : }
7405 :
7406 3332 : if (i == ArrayLength(UnitData)) {
7407 : // Unknown unit
7408 0 : return false;
7409 : }
7410 :
7411 3332 : if (!mViewportUnitsEnabled &&
7412 0 : (eCSSUnit_ViewportWidth == units ||
7413 0 : eCSSUnit_ViewportHeight == units ||
7414 0 : eCSSUnit_ViewportMin == units ||
7415 : eCSSUnit_ViewportMax == units)) {
7416 : // Viewport units aren't allowed right now, probably because we're
7417 : // inside an @page declaration. Fail.
7418 0 : return false;
7419 : }
7420 :
7421 3332 : if ((VARIANT_ABSOLUTE_DIMENSION & aVariantMask) != 0 &&
7422 0 : !nsCSSValue::IsPixelLengthUnit(units)) {
7423 0 : return false;
7424 : }
7425 : } else {
7426 : // Must be a zero number...
7427 979 : NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
7428 979 : if ((VARIANT_LENGTH & aVariantMask) != 0) {
7429 979 : units = eCSSUnit_Pixel;
7430 979 : type = VARIANT_LENGTH;
7431 : }
7432 0 : else if ((VARIANT_ANGLE & aVariantMask) != 0) {
7433 0 : NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE,
7434 : "must have allowed zero angle");
7435 0 : units = eCSSUnit_Degree;
7436 0 : type = VARIANT_ANGLE;
7437 : }
7438 : else {
7439 0 : NS_ERROR("Variant mask does not include dimension; why were we called?");
7440 0 : return false;
7441 : }
7442 : }
7443 4311 : if ((type & aVariantMask) != 0) {
7444 4311 : aValue.SetFloatValue(aNumber, units);
7445 4311 : return true;
7446 : }
7447 0 : return false;
7448 : }
7449 :
7450 : // Note that this does include VARIANT_CALC, which is numeric. This is
7451 : // because calc() parsing, as proposed, drops range restrictions inside
7452 : // the calc() expression and clamps the result of the calculation to the
7453 : // range.
7454 : #define VARIANT_ALL_NONNUMERIC \
7455 : VARIANT_KEYWORD | \
7456 : VARIANT_COLOR | \
7457 : VARIANT_URL | \
7458 : VARIANT_STRING | \
7459 : VARIANT_COUNTER | \
7460 : VARIANT_ATTR | \
7461 : VARIANT_IDENTIFIER | \
7462 : VARIANT_IDENTIFIER_NO_INHERIT | \
7463 : VARIANT_AUTO | \
7464 : VARIANT_INHERIT | \
7465 : VARIANT_NONE | \
7466 : VARIANT_NORMAL | \
7467 : VARIANT_SYSFONT | \
7468 : VARIANT_GRADIENT | \
7469 : VARIANT_TIMING_FUNCTION | \
7470 : VARIANT_ALL | \
7471 : VARIANT_CALC | \
7472 : VARIANT_OPENTYPE_SVG_KEYWORD
7473 :
7474 : CSSParseResult
7475 8844 : CSSParserImpl::ParseVariantWithRestrictions(nsCSSValue& aValue,
7476 : int32_t aVariantMask,
7477 : const KTableEntry aKeywordTable[],
7478 : uint32_t aRestrictions)
7479 : {
7480 8844 : switch (aRestrictions) {
7481 : default:
7482 0 : MOZ_FALLTHROUGH_ASSERT("should not be reached");
7483 : case 0:
7484 6362 : return ParseVariant(aValue, aVariantMask, aKeywordTable);
7485 : case CSS_PROPERTY_VALUE_NONNEGATIVE:
7486 2476 : return ParseNonNegativeVariant(aValue, aVariantMask, aKeywordTable);
7487 : case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
7488 6 : return ParseOneOrLargerVariant(aValue, aVariantMask, aKeywordTable);
7489 : }
7490 : }
7491 :
7492 : // Note that callers passing VARIANT_CALC in aVariantMask will get
7493 : // full-range parsing inside the calc() expression, and the code that
7494 : // computes the calc will be required to clamp the resulting value to an
7495 : // appropriate range.
7496 : CSSParseResult
7497 3981 : CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue,
7498 : int32_t aVariantMask,
7499 : const KTableEntry aKeywordTable[])
7500 : {
7501 : // The variant mask must only contain non-numeric variants or the ones
7502 : // that we specifically handle.
7503 3981 : MOZ_ASSERT((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
7504 : VARIANT_NUMBER |
7505 : VARIANT_LENGTH |
7506 : VARIANT_PERCENT |
7507 : VARIANT_INTEGER)) == 0,
7508 : "need to update code below to handle additional variants");
7509 :
7510 3981 : CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable);
7511 3981 : if (result == CSSParseResult::Ok) {
7512 6126 : if (eCSSUnit_Number == aValue.GetUnit() ||
7513 2991 : aValue.IsLengthUnit()){
7514 2407 : if (aValue.GetFloatValue() < 0) {
7515 0 : UngetToken();
7516 0 : return CSSParseResult::NotFound;
7517 : }
7518 : }
7519 728 : else if (aValue.GetUnit() == eCSSUnit_Percent) {
7520 109 : if (aValue.GetPercentValue() < 0) {
7521 0 : UngetToken();
7522 0 : return CSSParseResult::NotFound;
7523 : }
7524 619 : } else if (aValue.GetUnit() == eCSSUnit_Integer) {
7525 350 : if (aValue.GetIntValue() < 0) {
7526 0 : UngetToken();
7527 0 : return CSSParseResult::NotFound;
7528 : }
7529 : }
7530 : }
7531 3981 : return result;
7532 : }
7533 :
7534 : // Note that callers passing VARIANT_CALC in aVariantMask will get
7535 : // full-range parsing inside the calc() expression, and the code that
7536 : // computes the calc will be required to clamp the resulting value to an
7537 : // appropriate range.
7538 : CSSParseResult
7539 9 : CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue,
7540 : int32_t aVariantMask,
7541 : const KTableEntry aKeywordTable[])
7542 : {
7543 : // The variant mask must only contain non-numeric variants or the ones
7544 : // that we specifically handle.
7545 9 : MOZ_ASSERT((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
7546 : VARIANT_NUMBER |
7547 : VARIANT_INTEGER)) == 0,
7548 : "need to update code below to handle additional variants");
7549 :
7550 9 : CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable);
7551 9 : if (result == CSSParseResult::Ok) {
7552 9 : if (aValue.GetUnit() == eCSSUnit_Integer) {
7553 3 : if (aValue.GetIntValue() < 1) {
7554 0 : UngetToken();
7555 0 : return CSSParseResult::NotFound;
7556 : }
7557 6 : } else if (eCSSUnit_Number == aValue.GetUnit()) {
7558 0 : if (aValue.GetFloatValue() < 1.0f) {
7559 0 : UngetToken();
7560 0 : return CSSParseResult::NotFound;
7561 : }
7562 : }
7563 : }
7564 9 : return result;
7565 : }
7566 :
7567 : static bool
7568 1563 : IsCSSTokenCalcFunction(const nsCSSToken& aToken)
7569 : {
7570 1877 : return aToken.mType == eCSSToken_Function &&
7571 1877 : aToken.mIdent.LowerCaseEqualsLiteral("calc");
7572 : }
7573 :
7574 : // Assigns to aValue iff it returns CSSParseResult::Ok.
7575 : CSSParseResult
7576 15840 : CSSParserImpl::ParseVariant(nsCSSValue& aValue,
7577 : uint32_t aVariantMask,
7578 : const KTableEntry aKeywordTable[])
7579 : {
7580 15840 : NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
7581 : !(aVariantMask & VARIANT_NUMBER),
7582 : "can't distinguish colors from numbers");
7583 15840 : NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
7584 : !(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)),
7585 : "can't distinguish colors from lengths");
7586 15840 : NS_ASSERTION(!(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)) ||
7587 : !(aVariantMask & VARIANT_NUMBER),
7588 : "can't distinguish lengths from numbers");
7589 15840 : MOZ_ASSERT(!(aVariantMask & VARIANT_IDENTIFIER) ||
7590 : !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT),
7591 : "must not set both VARIANT_IDENTIFIER and "
7592 : "VARIANT_IDENTIFIER_NO_INHERIT");
7593 :
7594 : uint32_t lineBefore, colBefore;
7595 31226 : if (!GetNextTokenLocation(true, &lineBefore, &colBefore) ||
7596 15386 : !GetToken(true)) {
7597 : // Must be at EOF.
7598 454 : return CSSParseResult::NotFound;
7599 : }
7600 :
7601 15386 : nsCSSToken* tk = &mToken;
7602 26330 : if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
7603 10944 : (eCSSToken_Ident == tk->mType)) {
7604 4471 : nsCSSKeyword keyword = LookupKeywordPrefixAware(tk->mIdent,
7605 4471 : aKeywordTable);
7606 :
7607 4471 : if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
7608 3927 : if ((aVariantMask & VARIANT_AUTO) != 0) {
7609 121 : if (eCSSKeyword_auto == keyword) {
7610 67 : aValue.SetAutoValue();
7611 67 : return CSSParseResult::Ok;
7612 : }
7613 : }
7614 3860 : if ((aVariantMask & VARIANT_INHERIT) != 0) {
7615 : // XXX Should we check IsParsingCompoundProperty, or do all
7616 : // callers handle it? (Not all callers set it, though, since
7617 : // they want the quirks that are disabled by setting it.)
7618 :
7619 : // IMPORTANT: If new keywords are added here,
7620 : // they probably need to be added in ParseCustomIdent as well.
7621 3383 : if (eCSSKeyword_inherit == keyword) {
7622 410 : aValue.SetInheritValue();
7623 410 : return CSSParseResult::Ok;
7624 : }
7625 2973 : else if (eCSSKeyword_initial == keyword) {
7626 21 : aValue.SetInitialValue();
7627 21 : return CSSParseResult::Ok;
7628 : }
7629 2962 : else if (eCSSKeyword_unset == keyword &&
7630 10 : nsLayoutUtils::UnsetValueEnabled()) {
7631 10 : aValue.SetUnsetValue();
7632 10 : return CSSParseResult::Ok;
7633 : }
7634 : }
7635 3419 : if ((aVariantMask & VARIANT_NONE) != 0) {
7636 257 : if (eCSSKeyword_none == keyword) {
7637 105 : aValue.SetNoneValue();
7638 105 : return CSSParseResult::Ok;
7639 : }
7640 : }
7641 3314 : if ((aVariantMask & VARIANT_ALL) != 0) {
7642 7 : if (eCSSKeyword_all == keyword) {
7643 0 : aValue.SetAllValue();
7644 0 : return CSSParseResult::Ok;
7645 : }
7646 : }
7647 3314 : if ((aVariantMask & VARIANT_NORMAL) != 0) {
7648 41 : if (eCSSKeyword_normal == keyword) {
7649 24 : aValue.SetNormalValue();
7650 24 : return CSSParseResult::Ok;
7651 : }
7652 : }
7653 3290 : if ((aVariantMask & VARIANT_SYSFONT) != 0) {
7654 78 : if (eCSSKeyword__moz_use_system_font == keyword &&
7655 0 : !IsParsingCompoundProperty()) {
7656 0 : aValue.SetSystemFontValue();
7657 0 : return CSSParseResult::Ok;
7658 : }
7659 : }
7660 3290 : if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) {
7661 6 : if (sOpentypeSVGEnabled) {
7662 6 : aVariantMask |= VARIANT_KEYWORD;
7663 : }
7664 : }
7665 3290 : if ((aVariantMask & VARIANT_KEYWORD) != 0) {
7666 : int32_t value;
7667 2552 : if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
7668 2403 : aValue.SetIntValue(value, eCSSUnit_Enumerated);
7669 2403 : return CSSParseResult::Ok;
7670 : }
7671 : }
7672 : }
7673 : }
7674 : // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or
7675 : // VARIANT_ZERO_ANGLE.
7676 13368 : if (((aVariantMask & VARIANT_NUMBER) != 0) &&
7677 1022 : (eCSSToken_Number == tk->mType)) {
7678 574 : aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
7679 574 : return CSSParseResult::Ok;
7680 : }
7681 12182 : if (((aVariantMask & VARIANT_INTEGER) != 0) &&
7682 815 : (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
7683 405 : aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
7684 405 : return CSSParseResult::Ok;
7685 : }
7686 11367 : if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
7687 5874 : VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
7688 13909 : eCSSToken_Dimension == tk->mType) ||
7689 10312 : ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
7690 3290 : eCSSToken_Number == tk->mType &&
7691 1013 : tk->mNumber == 0.0f)) {
7692 4453 : if ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 &&
7693 142 : tk->mNumber < 0.0) {
7694 0 : UngetToken();
7695 0 : AssertNextTokenAt(lineBefore, colBefore);
7696 0 : return CSSParseResult::NotFound;
7697 : }
7698 4311 : if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
7699 4311 : return CSSParseResult::Ok;
7700 : }
7701 : // Put the token back; we didn't parse it, so we shouldn't consume it
7702 0 : UngetToken();
7703 0 : AssertNextTokenAt(lineBefore, colBefore);
7704 0 : return CSSParseResult::NotFound;
7705 : }
7706 8155 : if (((aVariantMask & VARIANT_PERCENT) != 0) &&
7707 1099 : (eCSSToken_Percentage == tk->mType)) {
7708 189 : aValue.SetPercentValue(tk->mNumber);
7709 189 : return CSSParseResult::Ok;
7710 : }
7711 6867 : if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px
7712 0 : if (((aVariantMask & VARIANT_LENGTH) != 0) &&
7713 0 : (eCSSToken_Number == tk->mType)) {
7714 0 : aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
7715 0 : return CSSParseResult::Ok;
7716 : }
7717 : }
7718 :
7719 6867 : if (mIsSVGMode && !IsParsingCompoundProperty()) {
7720 : // STANDARD: SVG Spec states that lengths and coordinates can be unitless
7721 : // in which case they default to user-units (1 px = 1 user unit)
7722 86 : if (((aVariantMask & VARIANT_LENGTH) != 0) &&
7723 34 : (eCSSToken_Number == tk->mType)) {
7724 34 : aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
7725 34 : return CSSParseResult::Ok;
7726 : }
7727 : }
7728 :
7729 7755 : if (((aVariantMask & VARIANT_URL) != 0) &&
7730 922 : eCSSToken_URL == tk->mType) {
7731 651 : SetValueToURL(aValue, tk->mIdent);
7732 651 : return CSSParseResult::Ok;
7733 : }
7734 6258 : if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
7735 76 : eCSSToken_Function == tk->mType) {
7736 : // a generated gradient
7737 101 : nsDependentString tmp(tk->mIdent, 0);
7738 76 : uint8_t gradientFlags = 0;
7739 304 : if (sMozGradientsEnabled &&
7740 304 : StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) {
7741 19 : tmp.Rebind(tmp, 5);
7742 19 : gradientFlags |= eGradient_MozLegacy;
7743 228 : } else if (sWebkitPrefixedAliasesEnabled &&
7744 228 : StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) {
7745 0 : tmp.Rebind(tmp, 8);
7746 0 : gradientFlags |= eGradient_WebkitLegacy;
7747 : }
7748 76 : if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) {
7749 1 : tmp.Rebind(tmp, 10);
7750 1 : gradientFlags |= eGradient_Repeating;
7751 : }
7752 :
7753 76 : if (tmp.LowerCaseEqualsLiteral("linear-gradient")) {
7754 49 : if (!ParseLinearGradient(aValue, gradientFlags)) {
7755 6 : return CSSParseResult::Error;
7756 : }
7757 43 : return CSSParseResult::Ok;
7758 : }
7759 27 : if (tmp.LowerCaseEqualsLiteral("radial-gradient")) {
7760 2 : if (!ParseRadialGradient(aValue, gradientFlags)) {
7761 0 : return CSSParseResult::Error;
7762 : }
7763 2 : return CSSParseResult::Ok;
7764 : }
7765 25 : if ((gradientFlags == eGradient_WebkitLegacy) &&
7766 0 : tmp.LowerCaseEqualsLiteral("gradient")) {
7767 : // Note: we check gradientFlags using '==' to select *exactly*
7768 : // eGradient_WebkitLegacy -- and exclude eGradient_Repeating -- because
7769 : // we don't want to accept -webkit-repeating-gradient() expressions.
7770 : // (This is not a recognized syntax.)
7771 0 : if (!ParseWebkitGradient(aValue)) {
7772 0 : return CSSParseResult::Error;
7773 : }
7774 0 : return CSSParseResult::Ok;
7775 : }
7776 : }
7777 12287 : if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
7778 6156 : eCSSToken_Function == tk->mType &&
7779 25 : tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
7780 16 : if (!ParseImageRect(aValue)) {
7781 0 : return CSSParseResult::Error;
7782 : }
7783 16 : return CSSParseResult::Ok;
7784 : }
7785 12239 : if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
7786 6124 : eCSSToken_Function == tk->mType &&
7787 9 : tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) {
7788 3 : if (!ParseElement(aValue)) {
7789 0 : return CSSParseResult::Error;
7790 : }
7791 3 : return CSSParseResult::Ok;
7792 : }
7793 6112 : if ((aVariantMask & VARIANT_COLOR) != 0) {
7794 4029 : if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
7795 2568 : (eCSSToken_ID == tk->mType) ||
7796 2358 : (eCSSToken_Hash == tk->mType) ||
7797 4058 : (eCSSToken_Ident == tk->mType) ||
7798 951 : ((eCSSToken_Function == tk->mType) &&
7799 736 : (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
7800 697 : tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
7801 616 : tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
7802 280 : tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
7803 : {
7804 : // Put token back so that parse color can get it
7805 1006 : UngetToken();
7806 1006 : return ParseColor(aValue);
7807 : }
7808 : }
7809 6843 : if (((aVariantMask & VARIANT_STRING) != 0) &&
7810 1737 : (eCSSToken_String == tk->mType)) {
7811 64 : nsAutoString buffer;
7812 32 : buffer.Append(tk->mIdent);
7813 32 : aValue.SetStringValue(buffer, eCSSUnit_String);
7814 32 : return CSSParseResult::Ok;
7815 : }
7816 10148 : if (((aVariantMask &
7817 1850 : (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
7818 8642 : (eCSSToken_Ident == tk->mType) &&
7819 1821 : ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
7820 111 : !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
7821 37 : tk->mIdent.LowerCaseEqualsLiteral("initial") ||
7822 37 : (tk->mIdent.LowerCaseEqualsLiteral("unset") &&
7823 0 : nsLayoutUtils::UnsetValueEnabled())))) {
7824 1784 : aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
7825 1784 : return CSSParseResult::Ok;
7826 : }
7827 6585 : if (((aVariantMask & VARIANT_COUNTER) != 0) &&
7828 3295 : (eCSSToken_Function == tk->mType) &&
7829 10 : (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
7830 5 : tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
7831 0 : if (!ParseCounter(aValue)) {
7832 0 : return CSSParseResult::Error;
7833 : }
7834 0 : return CSSParseResult::Ok;
7835 : }
7836 6585 : if (((aVariantMask & VARIANT_ATTR) != 0) &&
7837 3295 : (eCSSToken_Function == tk->mType) &&
7838 5 : tk->mIdent.LowerCaseEqualsLiteral("attr")) {
7839 5 : if (!ParseAttr(aValue)) {
7840 0 : SkipUntil(')');
7841 0 : return CSSParseResult::Error;
7842 : }
7843 5 : return CSSParseResult::Ok;
7844 : }
7845 3419 : if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
7846 134 : (eCSSToken_Function == tk->mType)) {
7847 8 : if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
7848 0 : if (!ParseTransitionTimingFunctionValues(aValue)) {
7849 0 : SkipUntil(')');
7850 0 : return CSSParseResult::Error;
7851 : }
7852 0 : return CSSParseResult::Ok;
7853 : }
7854 8 : if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
7855 3 : if (!ParseTransitionStepTimingFunctionValues(aValue)) {
7856 0 : SkipUntil(')');
7857 0 : return CSSParseResult::Error;
7858 : }
7859 3 : return CSSParseResult::Ok;
7860 : }
7861 10 : if (sFramesTimingFunctionEnabled &&
7862 5 : tk->mIdent.LowerCaseEqualsLiteral("frames")) {
7863 0 : if (!ParseTransitionFramesTimingFunctionValues(aValue)) {
7864 0 : SkipUntil(')');
7865 0 : return CSSParseResult::Error;
7866 : }
7867 0 : return CSSParseResult::Ok;
7868 : }
7869 : }
7870 4307 : if ((aVariantMask & VARIANT_CALC) &&
7871 1025 : IsCSSTokenCalcFunction(*tk)) {
7872 : // calc() currently allows only lengths, percents, numbers, and integers.
7873 : //
7874 : // Note that VARIANT_NUMBER can be mixed with VARIANT_LENGTH and
7875 : // VARIANT_PERCENTAGE in the list of allowed types (numbers can be used as
7876 : // coefficients).
7877 : // However, the the resulting type is not a mixed type with number.
7878 : // VARIANT_INTEGER can't be mixed with anything else.
7879 226 : if (!ParseCalc(aValue, aVariantMask & (VARIANT_LPN | VARIANT_INTEGER))) {
7880 21 : return CSSParseResult::Error;
7881 : }
7882 205 : return CSSParseResult::Ok;
7883 : }
7884 :
7885 3056 : UngetToken();
7886 3056 : AssertNextTokenAt(lineBefore, colBefore);
7887 3056 : return CSSParseResult::NotFound;
7888 : }
7889 :
7890 : bool
7891 245 : CSSParserImpl::ParseCustomIdent(nsCSSValue& aValue,
7892 : const nsAutoString& aIdentValue,
7893 : const nsCSSKeyword aExcludedKeywords[],
7894 : const nsCSSProps::KTableEntry aPropertyKTable[])
7895 : {
7896 245 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aIdentValue);
7897 245 : if (keyword == eCSSKeyword_UNKNOWN) {
7898 : // Fast path for identifiers that are not known CSS keywords:
7899 209 : aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
7900 209 : return true;
7901 : }
7902 72 : if (keyword == eCSSKeyword_inherit ||
7903 36 : keyword == eCSSKeyword_initial ||
7904 36 : keyword == eCSSKeyword_unset ||
7905 72 : keyword == eCSSKeyword_default ||
7906 0 : (aPropertyKTable &&
7907 0 : nsCSSProps::FindIndexOfKeyword(keyword, aPropertyKTable) >= 0)) {
7908 0 : return false;
7909 : }
7910 36 : if (aExcludedKeywords) {
7911 8 : for (uint32_t i = 0;; i++) {
7912 8 : nsCSSKeyword excludedKeyword = aExcludedKeywords[i];
7913 8 : if (excludedKeyword == eCSSKeyword_UNKNOWN) {
7914 2 : break;
7915 : }
7916 6 : if (excludedKeyword == keyword) {
7917 0 : return false;
7918 : }
7919 6 : }
7920 : }
7921 36 : aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
7922 36 : return true;
7923 : }
7924 :
7925 : bool
7926 0 : CSSParserImpl::ParseCounter(nsCSSValue& aValue)
7927 : {
7928 0 : nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
7929 0 : eCSSUnit_Counter : eCSSUnit_Counters);
7930 :
7931 : // A non-iterative for loop to break out when an error occurs.
7932 : for (;;) {
7933 0 : if (!GetToken(true)) {
7934 0 : break;
7935 : }
7936 0 : if (eCSSToken_Ident != mToken.mType) {
7937 0 : UngetToken();
7938 0 : break;
7939 : }
7940 :
7941 : RefPtr<nsCSSValue::Array> val =
7942 0 : nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
7943 :
7944 0 : val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
7945 :
7946 0 : if (eCSSUnit_Counters == unit) {
7947 : // must have a comma and then a separator string
7948 0 : if (!ExpectSymbol(',', true) || !GetToken(true)) {
7949 0 : break;
7950 : }
7951 0 : if (eCSSToken_String != mToken.mType) {
7952 0 : UngetToken();
7953 0 : break;
7954 : }
7955 0 : val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
7956 : }
7957 :
7958 : // get optional type
7959 0 : int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1;
7960 0 : nsCSSValue& type = val->Item(typeItem);
7961 0 : if (ExpectSymbol(',', true)) {
7962 0 : if (!ParseCounterStyleNameValue(type) && !ParseSymbols(type)) {
7963 0 : break;
7964 : }
7965 : } else {
7966 0 : type.SetAtomIdentValue(do_AddRef(nsGkAtoms::decimal));
7967 : }
7968 :
7969 0 : if (!ExpectSymbol(')', true)) {
7970 0 : break;
7971 : }
7972 :
7973 0 : aValue.SetArrayValue(val, unit);
7974 0 : return true;
7975 : }
7976 :
7977 0 : SkipUntil(')');
7978 0 : return false;
7979 : }
7980 :
7981 : bool
7982 39 : CSSParserImpl::ParseContextProperties()
7983 : {
7984 78 : nsCSSValue listValue;
7985 39 : nsCSSValueList* currentListValue = listValue.SetListValue();
7986 39 : bool first = true;
7987 : for (;;) {
7988 : const uint32_t variantMask = VARIANT_IDENTIFIER |
7989 : VARIANT_INHERIT |
7990 40 : VARIANT_NONE;
7991 41 : nsCSSValue value;
7992 40 : if (!ParseSingleTokenVariant(value, variantMask, nullptr)) {
7993 0 : return false;
7994 : }
7995 :
7996 40 : if (value.GetUnit() != eCSSUnit_Ident) {
7997 0 : if (first) {
7998 0 : AppendValue(eCSSProperty__moz_context_properties, value);
7999 0 : return true;
8000 : } else {
8001 0 : return false;
8002 : }
8003 : }
8004 :
8005 40 : value.AtomizeIdentValue();
8006 40 : nsIAtom* atom = value.GetAtomValue();
8007 40 : if (atom == nsGkAtoms::_default) {
8008 0 : return false;
8009 : }
8010 :
8011 40 : currentListValue->mValue = Move(value);
8012 :
8013 40 : if (!ExpectSymbol(',', true)) {
8014 39 : break;
8015 : }
8016 1 : currentListValue->mNext = new nsCSSValueList;
8017 1 : currentListValue = currentListValue->mNext;
8018 1 : first = false;
8019 1 : }
8020 :
8021 39 : AppendValue(eCSSProperty__moz_context_properties, listValue);
8022 39 : return true;
8023 : }
8024 :
8025 : bool
8026 5 : CSSParserImpl::ParseAttr(nsCSSValue& aValue)
8027 : {
8028 5 : if (!GetToken(true)) {
8029 0 : return false;
8030 : }
8031 :
8032 10 : nsAutoString attr;
8033 5 : if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
8034 10 : nsAutoString holdIdent(mToken.mIdent);
8035 5 : if (ExpectSymbol('|', false)) { // namespace
8036 0 : int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent);
8037 0 : if (nameSpaceID == kNameSpaceID_Unknown) {
8038 0 : return false;
8039 : }
8040 0 : attr.AppendInt(nameSpaceID, 10);
8041 0 : attr.Append(char16_t('|'));
8042 0 : if (! GetToken(false)) {
8043 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
8044 0 : return false;
8045 : }
8046 0 : if (eCSSToken_Ident == mToken.mType) {
8047 0 : attr.Append(mToken.mIdent);
8048 : }
8049 : else {
8050 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
8051 0 : UngetToken();
8052 0 : return false;
8053 : }
8054 : }
8055 : else { // no namespace
8056 5 : attr = holdIdent;
8057 : }
8058 : }
8059 0 : else if (mToken.IsSymbol('*')) { // namespace wildcard
8060 : // Wildcard namespace makes no sense here and is not allowed
8061 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
8062 0 : UngetToken();
8063 0 : return false;
8064 : }
8065 0 : else if (mToken.IsSymbol('|')) { // explicit NO namespace
8066 0 : if (! GetToken(false)) {
8067 0 : REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
8068 0 : return false;
8069 : }
8070 0 : if (eCSSToken_Ident == mToken.mType) {
8071 0 : attr.Append(mToken.mIdent);
8072 : }
8073 : else {
8074 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
8075 0 : UngetToken();
8076 0 : return false;
8077 : }
8078 : }
8079 : else {
8080 0 : REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
8081 0 : UngetToken();
8082 0 : return false;
8083 : }
8084 5 : if (!ExpectSymbol(')', true)) {
8085 0 : return false;
8086 : }
8087 5 : aValue.SetStringValue(attr, eCSSUnit_Attr);
8088 5 : return true;
8089 : }
8090 :
8091 : bool
8092 0 : CSSParserImpl::ParseSymbols(nsCSSValue& aValue)
8093 : {
8094 0 : if (!GetToken(true)) {
8095 0 : return false;
8096 : }
8097 0 : if (mToken.mType != eCSSToken_Function &&
8098 0 : !mToken.mIdent.LowerCaseEqualsLiteral("symbols")) {
8099 0 : UngetToken();
8100 0 : return false;
8101 : }
8102 :
8103 0 : RefPtr<nsCSSValue::Array> params = nsCSSValue::Array::Create(2);
8104 0 : nsCSSValue& type = params->Item(0);
8105 0 : nsCSSValue& symbols = params->Item(1);
8106 :
8107 0 : if (!ParseEnum(type, nsCSSProps::kCounterSymbolsSystemKTable)) {
8108 0 : type.SetIntValue(NS_STYLE_COUNTER_SYSTEM_SYMBOLIC, eCSSUnit_Enumerated);
8109 : }
8110 :
8111 0 : bool first = true;
8112 0 : nsCSSValueList* item = symbols.SetListValue();
8113 : for (;;) {
8114 : // FIXME Should also include VARIANT_IMAGE. See bug 1071436.
8115 0 : if (!ParseSingleTokenVariant(item->mValue, VARIANT_STRING, nullptr)) {
8116 0 : break;
8117 : }
8118 0 : if (ExpectSymbol(')', true)) {
8119 0 : if (first) {
8120 0 : switch (type.GetIntValue()) {
8121 : case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
8122 : case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
8123 : // require at least two symbols
8124 0 : return false;
8125 : }
8126 : }
8127 0 : aValue.SetArrayValue(params, eCSSUnit_Symbols);
8128 0 : return true;
8129 : }
8130 0 : item->mNext = new nsCSSValueList;
8131 0 : item = item->mNext;
8132 0 : first = false;
8133 : }
8134 :
8135 0 : SkipUntil(')');
8136 0 : return false;
8137 : }
8138 :
8139 : bool
8140 667 : CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL)
8141 : {
8142 667 : if (!mSheetPrincipal) {
8143 0 : if (!mSheetPrincipalRequired) {
8144 : /* Pretend to succeed. */
8145 0 : return true;
8146 : }
8147 :
8148 0 : NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
8149 : "origin principal");
8150 0 : return false;
8151 : }
8152 :
8153 : mozilla::css::URLValue *urlVal =
8154 1334 : new mozilla::css::URLValue(aURL, mBaseURI, mSheetURI, mSheetPrincipal);
8155 667 : aValue.SetURLValue(urlVal);
8156 667 : return true;
8157 : }
8158 :
8159 : /**
8160 : * Parse the image-orientation property, which has the grammar:
8161 : * <angle> flip? | flip | from-image
8162 : */
8163 : bool
8164 0 : CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue)
8165 : {
8166 0 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
8167 : // 'inherit', 'initial' and 'unset' must be alone
8168 0 : return true;
8169 : }
8170 :
8171 : // Check for an angle with optional 'flip'.
8172 0 : nsCSSValue angle;
8173 0 : if (ParseSingleTokenVariant(angle, VARIANT_ANGLE, nullptr)) {
8174 0 : nsCSSValue flip;
8175 :
8176 0 : if (ParseSingleTokenVariant(flip, VARIANT_KEYWORD,
8177 : nsCSSProps::kImageOrientationFlipKTable)) {
8178 0 : RefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(2);
8179 0 : array->Item(0) = angle;
8180 0 : array->Item(1) = flip;
8181 0 : aValue.SetArrayValue(array, eCSSUnit_Array);
8182 : } else {
8183 0 : aValue = angle;
8184 : }
8185 :
8186 0 : return true;
8187 : }
8188 :
8189 : // The remaining possibilities (bare 'flip' and 'from-image') are both
8190 : // keywords, so we can handle them at the same time.
8191 0 : nsCSSValue keyword;
8192 0 : if (ParseSingleTokenVariant(keyword, VARIANT_KEYWORD,
8193 : nsCSSProps::kImageOrientationKTable)) {
8194 0 : aValue = keyword;
8195 0 : return true;
8196 : }
8197 :
8198 : // All possibilities failed.
8199 0 : return false;
8200 : }
8201 :
8202 : /**
8203 : * Parse the arguments of -moz-image-rect() function.
8204 : * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
8205 : */
8206 : bool
8207 16 : CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
8208 : {
8209 : // A non-iterative for loop to break out when an error occurs.
8210 : for (;;) {
8211 16 : nsCSSValue newFunction;
8212 : static const uint32_t kNumArgs = 5;
8213 : nsCSSValue::Array* func =
8214 16 : newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs);
8215 :
8216 : // func->Item(0) is reserved for the function name.
8217 16 : nsCSSValue& url = func->Item(1);
8218 16 : nsCSSValue& top = func->Item(2);
8219 16 : nsCSSValue& right = func->Item(3);
8220 16 : nsCSSValue& bottom = func->Item(4);
8221 16 : nsCSSValue& left = func->Item(5);
8222 :
8223 16 : nsAutoString urlString;
8224 48 : if (!ParseURLOrString(urlString) ||
8225 32 : !SetValueToURL(url, urlString) ||
8226 16 : !ExpectSymbol(',', true)) {
8227 0 : break;
8228 : }
8229 :
8230 : static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT;
8231 48 : if (!ParseSingleTokenNonNegativeVariant(top, VARIANT_SIDE, nullptr) ||
8232 32 : !ExpectSymbol(',', true) ||
8233 32 : !ParseSingleTokenNonNegativeVariant(right, VARIANT_SIDE, nullptr) ||
8234 32 : !ExpectSymbol(',', true) ||
8235 32 : !ParseSingleTokenNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) ||
8236 32 : !ExpectSymbol(',', true) ||
8237 48 : !ParseSingleTokenNonNegativeVariant(left, VARIANT_SIDE, nullptr) ||
8238 16 : !ExpectSymbol(')', true))
8239 0 : break;
8240 :
8241 16 : aImage = newFunction;
8242 16 : return true;
8243 : }
8244 :
8245 0 : SkipUntil(')');
8246 0 : return false;
8247 : }
8248 :
8249 : // <element>: -moz-element(# <element_id> )
8250 : bool
8251 3 : CSSParserImpl::ParseElement(nsCSSValue& aValue)
8252 : {
8253 : // A non-iterative for loop to break out when an error occurs.
8254 : for (;;) {
8255 3 : if (!GetToken(true))
8256 0 : break;
8257 :
8258 3 : if (mToken.mType == eCSSToken_ID) {
8259 3 : aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element);
8260 : } else {
8261 0 : UngetToken();
8262 0 : break;
8263 : }
8264 :
8265 3 : if (!ExpectSymbol(')', true))
8266 0 : break;
8267 :
8268 3 : return true;
8269 : }
8270 :
8271 : // If we detect a syntax error, we must match the opening parenthesis of the
8272 : // function with the closing parenthesis and skip all the tokens in between.
8273 0 : SkipUntil(')');
8274 0 : return false;
8275 : }
8276 :
8277 : // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
8278 : bool
8279 22 : CSSParserImpl::ParseFlex()
8280 : {
8281 : // First check for inherit / initial / unset
8282 44 : nsCSSValue tmpVal;
8283 22 : if (ParseSingleTokenVariant(tmpVal, VARIANT_INHERIT, nullptr)) {
8284 0 : AppendValue(eCSSProperty_flex_grow, tmpVal);
8285 0 : AppendValue(eCSSProperty_flex_shrink, tmpVal);
8286 0 : AppendValue(eCSSProperty_flex_basis, tmpVal);
8287 0 : return true;
8288 : }
8289 :
8290 : // Next, check for 'none' == '0 0 auto'
8291 22 : if (ParseSingleTokenVariant(tmpVal, VARIANT_NONE, nullptr)) {
8292 6 : AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number));
8293 6 : AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number));
8294 6 : AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto));
8295 6 : return true;
8296 : }
8297 :
8298 : // OK, try parsing our value as individual per-subproperty components:
8299 : // [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
8300 :
8301 : // Each subproperty has a default value that it takes when it's omitted in a
8302 : // "flex" shorthand value. These default values are *only* for the shorthand
8303 : // syntax -- they're distinct from the subproperties' own initial values. We
8304 : // start with each subproperty at its default, as if we had "flex: 1 1 0%".
8305 32 : nsCSSValue flexGrow(1.0f, eCSSUnit_Number);
8306 32 : nsCSSValue flexShrink(1.0f, eCSSUnit_Number);
8307 32 : nsCSSValue flexBasis(0.0f, eCSSUnit_Percent);
8308 :
8309 : // OVERVIEW OF PARSING STRATEGY:
8310 : // =============================
8311 : // a) Parse the first component as either flex-basis or flex-grow.
8312 : // b) If it wasn't flex-grow, parse the _next_ component as flex-grow.
8313 : // c) Now we've just parsed flex-grow -- so try parsing the next thing as
8314 : // flex-shrink.
8315 : // d) Finally: If we didn't get flex-basis at the beginning, try to parse
8316 : // it now, at the end.
8317 : //
8318 : // More details in each section below.
8319 :
8320 : uint32_t flexBasisVariantMask =
8321 16 : (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT));
8322 :
8323 : // (a) Parse first component. It can be either be a 'flex-basis' value or a
8324 : // 'flex-grow' value, so we use the flex-basis-specific variant mask, along
8325 : // with VARIANT_NUMBER to accept 'flex-grow' values.
8326 : //
8327 : // NOTE: if we encounter unitless 0 here, we *must* interpret it as a
8328 : // 'flex-grow' value (a number), *not* as a 'flex-basis' value (a length).
8329 : // Conveniently, that's the behavior this combined variant-mask gives us --
8330 : // it'll treat unitless 0 as a number. The flexbox spec requires this:
8331 : // "a unitless zero that is not already preceded by two flex factors must be
8332 : // interpreted as a flex factor.
8333 16 : if (ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER,
8334 : nsCSSProps::kWidthKTable) != CSSParseResult::Ok) {
8335 : // First component was not a valid flex-basis or flex-grow value. Fail.
8336 0 : return false;
8337 : }
8338 :
8339 : // Record what we just parsed as either flex-basis or flex-grow:
8340 16 : bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number);
8341 16 : (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal;
8342 :
8343 : // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow.
8344 16 : bool doneParsing = false;
8345 16 : if (wasFirstComponentFlexBasis) {
8346 2 : if (ParseNonNegativeNumber(tmpVal)) {
8347 0 : flexGrow = tmpVal;
8348 : } else {
8349 : // Failed to parse anything after our flex-basis -- that's fine. We can
8350 : // skip the remaining parsing.
8351 2 : doneParsing = true;
8352 : }
8353 : }
8354 :
8355 16 : if (!doneParsing) {
8356 : // (c) OK -- the last thing we parsed was flex-grow, so look for a
8357 : // flex-shrink in the next position.
8358 14 : if (ParseNonNegativeNumber(tmpVal)) {
8359 7 : flexShrink = tmpVal;
8360 : }
8361 :
8362 : // d) Finally: If we didn't get flex-basis at the beginning, try to parse
8363 : // it now, at the end.
8364 : //
8365 : // NOTE: If we encounter unitless 0 in this final position, we'll parse it
8366 : // as a 'flex-basis' value. That's OK, because we know it must have
8367 : // been "preceded by 2 flex factors" (justification below), which gets us
8368 : // out of the spec's requirement of otherwise having to treat unitless 0
8369 : // as a flex factor.
8370 : //
8371 : // JUSTIFICATION: How do we know that a unitless 0 here must have been
8372 : // preceded by 2 flex factors? Well, suppose we had a unitless 0 that
8373 : // was preceded by only 1 flex factor. Then, we would have already
8374 : // accepted this unitless 0 as the 'flex-shrink' value, up above (since
8375 : // it's a valid flex-shrink value), and we'd have moved on to the next
8376 : // token (if any). And of course, if we instead had a unitless 0 preceded
8377 : // by *no* flex factors (if it were the first token), we would've already
8378 : // parsed it in our very first call to ParseNonNegativeVariant(). So, any
8379 : // unitless 0 encountered here *must* have been preceded by 2 flex factors.
8380 14 : if (!wasFirstComponentFlexBasis) {
8381 : CSSParseResult result =
8382 14 : ParseNonNegativeVariant(tmpVal, flexBasisVariantMask,
8383 14 : nsCSSProps::kWidthKTable);
8384 14 : if (result == CSSParseResult::Error) {
8385 0 : return false;
8386 14 : } else if (result == CSSParseResult::Ok) {
8387 9 : flexBasis = tmpVal;
8388 : }
8389 : }
8390 : }
8391 :
8392 16 : AppendValue(eCSSProperty_flex_grow, flexGrow);
8393 16 : AppendValue(eCSSProperty_flex_shrink, flexShrink);
8394 16 : AppendValue(eCSSProperty_flex_basis, flexBasis);
8395 :
8396 16 : return true;
8397 : }
8398 :
8399 : // flex-flow: <flex-direction> || <flex-wrap>
8400 : bool
8401 0 : CSSParserImpl::ParseFlexFlow()
8402 : {
8403 : static const nsCSSPropertyID kFlexFlowSubprops[] = {
8404 : eCSSProperty_flex_direction,
8405 : eCSSProperty_flex_wrap
8406 : };
8407 0 : const size_t numProps = MOZ_ARRAY_LENGTH(kFlexFlowSubprops);
8408 0 : nsCSSValue values[numProps];
8409 :
8410 0 : int32_t found = ParseChoice(values, kFlexFlowSubprops, numProps);
8411 :
8412 : // Bail if we didn't successfully parse anything
8413 0 : if (found < 1) {
8414 0 : return false;
8415 : }
8416 :
8417 : // If either property didn't get an explicit value, use its initial value.
8418 0 : if ((found & 1) == 0) {
8419 0 : values[0].SetIntValue(NS_STYLE_FLEX_DIRECTION_ROW, eCSSUnit_Enumerated);
8420 : }
8421 0 : if ((found & 2) == 0) {
8422 0 : values[1].SetIntValue(NS_STYLE_FLEX_WRAP_NOWRAP, eCSSUnit_Enumerated);
8423 : }
8424 :
8425 : // Store these values and declare success!
8426 0 : for (size_t i = 0; i < numProps; i++) {
8427 0 : AppendValue(kFlexFlowSubprops[i], values[i]);
8428 : }
8429 0 : return true;
8430 : }
8431 :
8432 : bool
8433 6 : CSSParserImpl::ParseGridAutoFlow()
8434 : {
8435 12 : nsCSSValue value;
8436 6 : if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
8437 6 : AppendValue(eCSSProperty_grid_auto_flow, value);
8438 6 : return true;
8439 : }
8440 :
8441 : static const int32_t mask[] = {
8442 : NS_STYLE_GRID_AUTO_FLOW_ROW | NS_STYLE_GRID_AUTO_FLOW_COLUMN,
8443 : MASK_END_VALUE
8444 : };
8445 0 : if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) {
8446 0 : return false;
8447 : }
8448 0 : int32_t bitField = value.GetIntValue();
8449 :
8450 : // If neither row nor column is provided, row is assumed.
8451 0 : if (!(bitField & (NS_STYLE_GRID_AUTO_FLOW_ROW |
8452 : NS_STYLE_GRID_AUTO_FLOW_COLUMN))) {
8453 0 : value.SetIntValue(bitField | NS_STYLE_GRID_AUTO_FLOW_ROW,
8454 0 : eCSSUnit_Enumerated);
8455 : }
8456 :
8457 0 : AppendValue(eCSSProperty_grid_auto_flow, value);
8458 0 : return true;
8459 : }
8460 :
8461 : static const nsCSSKeyword kGridLineKeywords[] = {
8462 : eCSSKeyword_span,
8463 : eCSSKeyword_UNKNOWN // End-of-array marker
8464 : };
8465 :
8466 : CSSParseResult
8467 0 : CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue)
8468 : {
8469 0 : if (!ExpectSymbol('[', true)) {
8470 0 : return CSSParseResult::NotFound;
8471 : }
8472 0 : if (!GetToken(true) || mToken.IsSymbol(']')) {
8473 0 : return CSSParseResult::Ok;
8474 : }
8475 : // 'return' so far leaves aValue untouched, to represent an empty list.
8476 :
8477 : nsCSSValueList* item;
8478 0 : if (aValue.GetUnit() == eCSSUnit_List) {
8479 : // Find the end of an existing list.
8480 : // The grid-template shorthand uses this, at most once for a given list.
8481 :
8482 : // NOTE: we could avoid this traversal by somehow keeping around
8483 : // a pointer to the last item from the previous call.
8484 : // It's not yet clear if this is worth the additional code complexity.
8485 0 : item = aValue.GetListValue();
8486 0 : while (item->mNext) {
8487 0 : item = item->mNext;
8488 : }
8489 0 : item->mNext = new nsCSSValueList;
8490 0 : item = item->mNext;
8491 : } else {
8492 0 : MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "Unexpected unit");
8493 0 : item = aValue.SetListValue();
8494 : }
8495 : for (;;) {
8496 0 : if (!(eCSSToken_Ident == mToken.mType &&
8497 0 : ParseCustomIdent(item->mValue, mToken.mIdent, kGridLineKeywords))) {
8498 0 : UngetToken();
8499 0 : SkipUntil(']');
8500 0 : return CSSParseResult::Error;
8501 : }
8502 0 : if (!GetToken(true) || mToken.IsSymbol(']')) {
8503 0 : return CSSParseResult::Ok;
8504 : }
8505 0 : item->mNext = new nsCSSValueList;
8506 0 : item = item->mNext;
8507 : }
8508 : }
8509 :
8510 : // Assuming the 'repeat(' function token has already been consumed,
8511 : // parse the rest of repeat(<positive-integer> | auto-fill, <line-names>+)
8512 : // Append to the linked list whose end is given by |aTailPtr|,
8513 : // and update |aTailPtr| to point to the new end of the list.
8514 : bool
8515 0 : CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr)
8516 : {
8517 : int32_t repetitions;
8518 0 : Maybe<int32_t> repeatAutoEnum;
8519 0 : if (!ParseGridTrackRepeatIntro(true, &repetitions, &repeatAutoEnum)) {
8520 0 : return false;
8521 : }
8522 0 : if (repeatAutoEnum.isSome()) {
8523 : // Parse exactly one <line-names>.
8524 0 : nsCSSValue listValue;
8525 0 : nsCSSValueList* list = listValue.SetListValue();
8526 0 : if (ParseGridLineNames(list->mValue) != CSSParseResult::Ok) {
8527 0 : return false;
8528 : }
8529 0 : if (!ExpectSymbol(')', true)) {
8530 0 : return false;
8531 : }
8532 : // Instead of hooking up this list into the flat name list as usual,
8533 : // we create a pair(Int, List) where the first value is the auto-fill
8534 : // keyword and the second is the name list to repeat.
8535 0 : nsCSSValue kwd;
8536 0 : kwd.SetIntValue(repeatAutoEnum.value(), eCSSUnit_Enumerated);
8537 0 : *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
8538 0 : (*aTailPtr)->mValue.SetPairValue(kwd, listValue);
8539 0 : return true;
8540 : }
8541 :
8542 : // Parse at least one <line-names>
8543 0 : nsCSSValueList* tail = *aTailPtr;
8544 0 : do {
8545 0 : tail->mNext = new nsCSSValueList;
8546 0 : tail = tail->mNext;
8547 0 : if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) {
8548 0 : return false;
8549 : }
8550 0 : } while (!ExpectSymbol(')', true));
8551 0 : nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext;
8552 0 : nsCSSValueList* lastRepeatedItem = tail;
8553 :
8554 : // Our repeated items are already in the target list once,
8555 : // so they need to be repeated |repetitions - 1| more times.
8556 0 : MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
8557 0 : while (--repetitions) {
8558 0 : nsCSSValueList* repeatedItem = firstRepeatedItem;
8559 : for (;;) {
8560 0 : tail->mNext = new nsCSSValueList;
8561 0 : tail = tail->mNext;
8562 0 : tail->mValue = repeatedItem->mValue;
8563 0 : if (repeatedItem == lastRepeatedItem) {
8564 0 : break;
8565 : }
8566 0 : repeatedItem = repeatedItem->mNext;
8567 : }
8568 : }
8569 0 : *aTailPtr = tail;
8570 0 : return true;
8571 : }
8572 :
8573 : // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>?
8574 : bool
8575 0 : CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
8576 : {
8577 0 : nsCSSValueList* item = aValue.SetListValue();
8578 : // This marker distinguishes the value from a <track-list>.
8579 0 : item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID,
8580 0 : eCSSUnit_Enumerated);
8581 0 : bool haveRepeatAuto = false;
8582 : for (;;) {
8583 : // First try to parse <name-repeat>, i.e.
8584 : // repeat(<positive-integer> | auto-fill, <line-names>+)
8585 0 : if (!GetToken(true)) {
8586 0 : return true;
8587 : }
8588 0 : if (mToken.mType == eCSSToken_Function &&
8589 0 : mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
8590 0 : nsCSSValueList* startOfRepeat = item;
8591 0 : if (!ParseGridLineNameListRepeat(&item)) {
8592 0 : SkipUntil(')');
8593 0 : return false;
8594 : }
8595 0 : if (startOfRepeat->mNext->mValue.GetUnit() == eCSSUnit_Pair) {
8596 0 : if (haveRepeatAuto) {
8597 0 : REPORT_UNEXPECTED(PEMoreThanOneGridRepeatAutoFillInNameList);
8598 0 : return false;
8599 : }
8600 0 : haveRepeatAuto = true;
8601 : }
8602 : } else {
8603 0 : UngetToken();
8604 :
8605 : // This was not a repeat() function. Try to parse <line-names>.
8606 0 : nsCSSValue lineNames;
8607 0 : CSSParseResult result = ParseGridLineNames(lineNames);
8608 0 : if (result == CSSParseResult::NotFound) {
8609 0 : return true;
8610 : }
8611 0 : if (result == CSSParseResult::Error) {
8612 0 : return false;
8613 : }
8614 0 : item->mNext = new nsCSSValueList;
8615 0 : item = item->mNext;
8616 0 : item->mValue = lineNames;
8617 : }
8618 0 : }
8619 : }
8620 :
8621 : CSSParseResult
8622 0 : CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
8623 : {
8624 : CSSParseResult result = ParseNonNegativeVariant(aValue,
8625 : VARIANT_AUTO | VARIANT_LPCALC | VARIANT_KEYWORD,
8626 0 : nsCSSProps::kGridTrackBreadthKTable);
8627 :
8628 0 : if (result == CSSParseResult::Ok ||
8629 : result == CSSParseResult::Error) {
8630 0 : return result;
8631 : }
8632 :
8633 : // Attempt to parse <flex> (a dimension with the "fr" unit).
8634 0 : if (!GetToken(true)) {
8635 0 : return CSSParseResult::NotFound;
8636 : }
8637 0 : if (!(eCSSToken_Dimension == mToken.mType &&
8638 0 : mToken.mIdent.LowerCaseEqualsLiteral("fr") &&
8639 0 : mToken.mNumber >= 0)) {
8640 0 : UngetToken();
8641 0 : return CSSParseResult::NotFound;
8642 : }
8643 0 : aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction);
8644 0 : return CSSParseResult::Ok;
8645 : }
8646 :
8647 : // Parse a <track-size>, or <fixed-size> when aFlags has eFixedTrackSize.
8648 : CSSParseResult
8649 0 : CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue,
8650 : GridTrackSizeFlags aFlags)
8651 : {
8652 : const bool requireFixedSize =
8653 0 : !!(aFlags & GridTrackSizeFlags::eFixedTrackSize);
8654 : // Attempt to parse a single <track-breadth>.
8655 0 : CSSParseResult result = ParseGridTrackBreadth(aValue);
8656 0 : if (requireFixedSize && result == CSSParseResult::Ok &&
8657 0 : !aValue.IsLengthPercentCalcUnit()) {
8658 0 : result = CSSParseResult::Error;
8659 : }
8660 0 : if (result == CSSParseResult::Error) {
8661 0 : return result;
8662 : }
8663 0 : if (result == CSSParseResult::Ok) {
8664 0 : if (aValue.GetUnit() == eCSSUnit_FlexFraction) {
8665 : // Single value <flex> is represented internally as minmax(auto, <flex>).
8666 0 : nsCSSValue minmax;
8667 0 : nsCSSValue::Array* func = minmax.InitFunction(eCSSKeyword_minmax, 2);
8668 0 : func->Item(1).SetAutoValue();
8669 0 : func->Item(2) = aValue;
8670 0 : aValue = minmax;
8671 : }
8672 0 : return result;
8673 : }
8674 :
8675 : // Attempt to parse a minmax() or fit-content() function.
8676 0 : if (!GetToken(true)) {
8677 0 : return CSSParseResult::NotFound;
8678 : }
8679 0 : if (eCSSToken_Function != mToken.mType) {
8680 0 : UngetToken();
8681 0 : return CSSParseResult::NotFound;
8682 : }
8683 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("fit-content")) {
8684 0 : if (requireFixedSize) {
8685 0 : UngetToken();
8686 0 : return CSSParseResult::Error;
8687 : }
8688 0 : nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_fit_content, 1);
8689 0 : if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok &&
8690 0 : func->Item(1).IsLengthPercentCalcUnit() &&
8691 0 : ExpectSymbol(')', true)) {
8692 0 : return CSSParseResult::Ok;
8693 : }
8694 0 : SkipUntil(')');
8695 0 : return CSSParseResult::Error;
8696 : }
8697 0 : if (!mToken.mIdent.LowerCaseEqualsLiteral("minmax")) {
8698 0 : UngetToken();
8699 0 : return CSSParseResult::NotFound;
8700 : }
8701 0 : nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2);
8702 0 : if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok &&
8703 0 : ExpectSymbol(',', true) &&
8704 0 : ParseGridTrackBreadth(func->Item(2)) == CSSParseResult::Ok &&
8705 0 : ExpectSymbol(')', true)) {
8706 0 : if (requireFixedSize &&
8707 0 : !func->Item(1).IsLengthPercentCalcUnit() &&
8708 0 : !func->Item(2).IsLengthPercentCalcUnit()) {
8709 0 : return CSSParseResult::Error;
8710 : }
8711 : // Reject <flex> min-sizing.
8712 0 : if (func->Item(1).GetUnit() == eCSSUnit_FlexFraction) {
8713 0 : return CSSParseResult::Error;
8714 : }
8715 0 : return CSSParseResult::Ok;
8716 : }
8717 0 : SkipUntil(')');
8718 0 : return CSSParseResult::Error;
8719 : }
8720 :
8721 : bool
8722 12 : CSSParserImpl::ParseGridAutoColumnsRows(nsCSSPropertyID aPropID)
8723 : {
8724 24 : nsCSSValue value;
8725 12 : if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) ||
8726 0 : ParseGridTrackSize(value) == CSSParseResult::Ok) {
8727 12 : AppendValue(aPropID, value);
8728 12 : return true;
8729 : }
8730 0 : return false;
8731 : }
8732 :
8733 : bool
8734 0 : CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
8735 : const nsCSSValue& aFirstLineNames,
8736 : GridTrackListFlags aFlags)
8737 : {
8738 0 : nsCSSValueList* firstLineNamesItem = aValue.SetListValue();
8739 0 : firstLineNamesItem->mValue = aFirstLineNames;
8740 :
8741 : // This function is trying to parse <track-list>, which is
8742 : // [ <line-names>? [ <track-size> | <repeat()> ] ]+ <line-names>?
8743 : // and we're already past the first "<line-names>?".
8744 : // If aFlags contains eExplicitTrackList then <repeat()> is disallowed.
8745 : //
8746 : // Each iteration of the following loop attempts to parse either a
8747 : // repeat() or a <track-size> expression, and then an (optional)
8748 : // <line-names> expression.
8749 : //
8750 : // The only successful exit point from this loop is the ::NotFound
8751 : // case after ParseGridTrackSize(); i.e. we'll greedily parse
8752 : // repeat()/<track-size> until we can't find one.
8753 0 : nsCSSValueList* item = firstLineNamesItem;
8754 0 : bool haveRepeatAuto = false;
8755 : for (;;) {
8756 : // First try to parse repeat()
8757 0 : if (!GetToken(true)) {
8758 0 : break;
8759 : }
8760 0 : if (!(aFlags & GridTrackListFlags::eExplicitTrackList) &&
8761 0 : mToken.mType == eCSSToken_Function &&
8762 0 : mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
8763 0 : nsCSSValueList* startOfRepeat = item;
8764 0 : if (!ParseGridTrackListRepeat(&item)) {
8765 0 : SkipUntil(')');
8766 0 : return false;
8767 : }
8768 0 : auto firstRepeat = startOfRepeat->mNext;
8769 0 : if (firstRepeat->mValue.GetUnit() == eCSSUnit_Pair) {
8770 0 : if (haveRepeatAuto) {
8771 0 : REPORT_UNEXPECTED(PEMoreThanOneGridRepeatAutoFillFitInTrackList);
8772 0 : return false;
8773 : }
8774 0 : haveRepeatAuto = true;
8775 : // We're parsing an <auto-track-list>, which requires that all tracks
8776 : // are <fixed-size>, so we need to check the ones we've parsed already.
8777 0 : for (nsCSSValueList* list = firstLineNamesItem->mNext;
8778 0 : list != firstRepeat; list = list->mNext) {
8779 0 : if (list->mValue.GetUnit() == eCSSUnit_Function) {
8780 0 : nsCSSValue::Array* func = list->mValue.GetArrayValue();
8781 0 : auto funcName = func->Item(0).GetKeywordValue();
8782 0 : if (funcName == eCSSKeyword_minmax) {
8783 0 : if (!func->Item(1).IsLengthPercentCalcUnit() &&
8784 0 : !func->Item(2).IsLengthPercentCalcUnit()) {
8785 0 : return false;
8786 : }
8787 : } else {
8788 0 : MOZ_ASSERT(funcName == eCSSKeyword_fit_content,
8789 : "Expected minmax() or fit-content() function");
8790 0 : return false; // fit-content() is not a <fixed-size>
8791 : }
8792 0 : } else if (!list->mValue.IsLengthPercentCalcUnit()) {
8793 0 : return false;
8794 : }
8795 0 : list = list->mNext; // skip line names
8796 : }
8797 : }
8798 : } else {
8799 0 : UngetToken();
8800 :
8801 : // Not a repeat() function; try to parse <track-size> | <fixed-size>.
8802 0 : nsCSSValue trackSize;
8803 : GridTrackSizeFlags flags = haveRepeatAuto
8804 0 : ? GridTrackSizeFlags::eFixedTrackSize
8805 0 : : GridTrackSizeFlags::eDefaultTrackSize;
8806 0 : CSSParseResult result = ParseGridTrackSize(trackSize, flags);
8807 0 : if (result == CSSParseResult::Error) {
8808 0 : return false;
8809 : }
8810 0 : if (result == CSSParseResult::NotFound) {
8811 : // What we've parsed so far is a valid <track-list>
8812 : // (modulo the "at least one <track-size>" check below.)
8813 : // Stop here.
8814 0 : break;
8815 : }
8816 0 : item->mNext = new nsCSSValueList;
8817 0 : item = item->mNext;
8818 0 : item->mValue = trackSize;
8819 :
8820 0 : item->mNext = new nsCSSValueList;
8821 0 : item = item->mNext;
8822 : }
8823 0 : if (ParseGridLineNames(item->mValue) == CSSParseResult::Error) {
8824 0 : return false;
8825 : }
8826 0 : }
8827 :
8828 : // Require at least one <track-size>.
8829 0 : if (item == firstLineNamesItem) {
8830 0 : return false;
8831 : }
8832 :
8833 0 : MOZ_ASSERT(aValue.GetListValue() &&
8834 : aValue.GetListValue()->mNext &&
8835 : aValue.GetListValue()->mNext->mNext,
8836 : "<track-list> should have a minimum length of 3");
8837 0 : return true;
8838 : }
8839 :
8840 : // Takes ownership of |aSecond|
8841 : static void
8842 0 : ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond)
8843 : {
8844 0 : if (aSecond.GetUnit() == eCSSUnit_Null) {
8845 : // Nothing to do.
8846 0 : return;
8847 : }
8848 0 : if (aFirst.GetUnit() == eCSSUnit_Null) {
8849 : // Empty or omitted <line-names>. Replace it.
8850 0 : aFirst = aSecond;
8851 0 : return;
8852 : }
8853 :
8854 : // Join the two <line-names> lists.
8855 0 : nsCSSValueList* source = aSecond.GetListValue();
8856 0 : nsCSSValueList* target = aFirst.GetListValue();
8857 : // Find the end:
8858 0 : while (target->mNext) {
8859 0 : target = target->mNext;
8860 : }
8861 : // Copy the first name. We can't take ownership of it
8862 : // as it'll be destroyed when |aSecond| goes out of scope.
8863 0 : target->mNext = new nsCSSValueList;
8864 0 : target = target->mNext;
8865 0 : target->mValue = source->mValue;
8866 : // Move the rest of the linked list.
8867 0 : target->mNext = source->mNext;
8868 0 : source->mNext = nullptr;
8869 : }
8870 :
8871 : // Assuming the 'repeat(' function token has already been consumed,
8872 : // parse "repeat( <positive-integer> | auto-fill | auto-fit ,"
8873 : // (or "repeat( <positive-integer> | auto-fill ," when aForSubgrid is true)
8874 : // and stop after the comma. Return true when parsing succeeds,
8875 : // with aRepetitions set to the number of repetitions and aRepeatAutoEnum set
8876 : // to an enum value for auto-fill | auto-fit (it's not set at all when
8877 : // <positive-integer> was parsed).
8878 : bool
8879 0 : CSSParserImpl::ParseGridTrackRepeatIntro(bool aForSubgrid,
8880 : int32_t* aRepetitions,
8881 : Maybe<int32_t>* aRepeatAutoEnum)
8882 : {
8883 0 : if (!GetToken(true)) {
8884 0 : return false;
8885 : }
8886 0 : if (mToken.mType == eCSSToken_Ident) {
8887 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("auto-fill")) {
8888 0 : aRepeatAutoEnum->emplace(NS_STYLE_GRID_REPEAT_AUTO_FILL);
8889 0 : } else if (!aForSubgrid &&
8890 0 : mToken.mIdent.LowerCaseEqualsLiteral("auto-fit")) {
8891 0 : aRepeatAutoEnum->emplace(NS_STYLE_GRID_REPEAT_AUTO_FIT);
8892 : } else {
8893 0 : return false;
8894 : }
8895 0 : *aRepetitions = 1;
8896 0 : } else if (mToken.mType == eCSSToken_Number) {
8897 0 : if (!(mToken.mIntegerValid &&
8898 0 : mToken.mInteger > 0)) {
8899 0 : return false;
8900 : }
8901 0 : *aRepetitions = std::min(mToken.mInteger, GRID_TEMPLATE_MAX_REPETITIONS);
8902 : } else {
8903 0 : return false;
8904 : }
8905 :
8906 0 : if (!ExpectSymbol(',', true)) {
8907 0 : return false;
8908 : }
8909 0 : return true;
8910 : }
8911 :
8912 : // Assuming the 'repeat(' function token has already been consumed,
8913 : // parse the rest of
8914 : // repeat( <positive-integer> | auto-fill | auto-fit ,
8915 : // [ <line-names>? <track-size> ]+ <line-names>? )
8916 : // Append to the linked list whose end is given by |aTailPtr|,
8917 : // and update |aTailPtr| to point to the new end of the list.
8918 : // Note: only one <track-size> is allowed for auto-fill/fit
8919 : bool
8920 0 : CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
8921 : {
8922 : int32_t repetitions;
8923 0 : Maybe<int32_t> repeatAutoEnum;
8924 0 : if (!ParseGridTrackRepeatIntro(false, &repetitions, &repeatAutoEnum)) {
8925 0 : return false;
8926 : }
8927 :
8928 : // Parse [ <line-names>? <track-size> ]+ <line-names>?
8929 : // but keep the first and last <line-names> separate
8930 : // because they'll need to be joined.
8931 : // http://dev.w3.org/csswg/css-grid/#repeat-notation
8932 0 : nsCSSValue firstLineNames;
8933 0 : nsCSSValue trackSize;
8934 0 : nsCSSValue lastLineNames;
8935 : // Optional
8936 0 : if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) {
8937 0 : return false;
8938 : }
8939 : // Required
8940 0 : GridTrackSizeFlags flags = repeatAutoEnum.isSome()
8941 0 : ? GridTrackSizeFlags::eFixedTrackSize
8942 0 : : GridTrackSizeFlags::eDefaultTrackSize;
8943 0 : if (ParseGridTrackSize(trackSize, flags) != CSSParseResult::Ok) {
8944 0 : return false;
8945 : }
8946 : // Use nsAutoPtr to free the list in case of early return.
8947 0 : nsAutoPtr<nsCSSValueList> firstTrackSizeItemAuto(new nsCSSValueList);
8948 0 : firstTrackSizeItemAuto->mValue = trackSize;
8949 :
8950 0 : nsCSSValueList* item = firstTrackSizeItemAuto;
8951 : for (;;) {
8952 : // Optional
8953 0 : if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) {
8954 0 : return false;
8955 : }
8956 :
8957 0 : if (ExpectSymbol(')', true)) {
8958 0 : break;
8959 : }
8960 :
8961 : // <auto-repeat> only accepts a single track size:
8962 : // <line-names>? <fixed-size> <line-names>?
8963 0 : if (repeatAutoEnum.isSome()) {
8964 0 : REPORT_UNEXPECTED(PEMoreThanOneGridRepeatTrackSize);
8965 0 : return false;
8966 : }
8967 :
8968 : // Required
8969 0 : if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
8970 0 : return false;
8971 : }
8972 :
8973 0 : item->mNext = new nsCSSValueList;
8974 0 : item = item->mNext;
8975 0 : item->mValue = lastLineNames;
8976 : // Do not append to this list at the next iteration.
8977 0 : lastLineNames.Reset();
8978 :
8979 0 : item->mNext = new nsCSSValueList;
8980 0 : item = item->mNext;
8981 0 : item->mValue = trackSize;
8982 : }
8983 0 : nsCSSValueList* lastTrackSizeItem = item;
8984 :
8985 : // [ <line-names>? <track-size> ]+ <line-names>? is now parsed into:
8986 : // * firstLineNames: the first <line-names>
8987 : // * a linked list of odd length >= 1, from firstTrackSizeItem
8988 : // (the first <track-size>) to lastTrackSizeItem (the last),
8989 : // with the <line-names> sublists in between
8990 : // * lastLineNames: the last <line-names>
8991 :
8992 0 : if (repeatAutoEnum.isSome()) {
8993 : // Instead of hooking up this list into the flat track/name list as usual,
8994 : // we create a pair(Int, List) where the first value is the auto-fill/fit
8995 : // keyword and the second is the list to repeat. There are three items
8996 : // in this list, the first is the list of line names before the track size,
8997 : // the second item is the track size, and the last item is the list of line
8998 : // names after the track size. Note that the line names are NOT merged
8999 : // with any line names before/after the repeat() itself.
9000 0 : nsCSSValue listValue;
9001 0 : nsCSSValueList* list = listValue.SetListValue();
9002 0 : list->mValue = firstLineNames;
9003 0 : list = list->mNext = new nsCSSValueList;
9004 0 : list->mValue = trackSize;
9005 0 : list = list->mNext = new nsCSSValueList;
9006 0 : list->mValue = lastLineNames;
9007 0 : nsCSSValue kwd;
9008 0 : kwd.SetIntValue(repeatAutoEnum.value(), eCSSUnit_Enumerated);
9009 0 : *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
9010 0 : (*aTailPtr)->mValue.SetPairValue(kwd, listValue);
9011 : // Append an empty list since the caller expects that to represent the names
9012 : // that follows the repeat() function.
9013 0 : *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
9014 0 : return true;
9015 : }
9016 :
9017 : // Join the last and first <line-names> (in that order.)
9018 : // For example, repeat(3, (a) 100px (b) 200px (c)) results in
9019 : // (a) 100px (b) 200px (c a) 100px (b) 200px (c a) 100px (b) 200px (c)
9020 : // This is (c a).
9021 : // Make deep copies: the originals will be moved.
9022 0 : nsCSSValue joinerLineNames;
9023 : {
9024 0 : nsCSSValueList* target = nullptr;
9025 0 : if (lastLineNames.GetUnit() != eCSSUnit_Null) {
9026 0 : target = joinerLineNames.SetListValue();
9027 0 : nsCSSValueList* source = lastLineNames.GetListValue();
9028 : for (;;) {
9029 0 : target->mValue = source->mValue;
9030 0 : source = source->mNext;
9031 0 : if (!source) {
9032 0 : break;
9033 : }
9034 0 : target->mNext = new nsCSSValueList;
9035 0 : target = target->mNext;
9036 : }
9037 : }
9038 :
9039 0 : if (firstLineNames.GetUnit() != eCSSUnit_Null) {
9040 0 : if (target) {
9041 0 : target->mNext = new nsCSSValueList;
9042 0 : target = target->mNext;
9043 : } else {
9044 0 : target = joinerLineNames.SetListValue();
9045 : }
9046 0 : nsCSSValueList* source = firstLineNames.GetListValue();
9047 : for (;;) {
9048 0 : target->mValue = source->mValue;
9049 0 : source = source->mNext;
9050 0 : if (!source) {
9051 0 : break;
9052 : }
9053 0 : target->mNext = new nsCSSValueList;
9054 0 : target = target->mNext;
9055 : }
9056 : }
9057 : }
9058 :
9059 : // Join our first <line-names> with the one before repeat().
9060 : // (a) repeat(1, (b) 20px) expands to (a b) 20px
9061 0 : nsCSSValueList* previousItemBeforeRepeat = *aTailPtr;
9062 0 : ConcatLineNames(previousItemBeforeRepeat->mValue, firstLineNames);
9063 :
9064 : // Move our linked list
9065 : // (first to last <track-size>, with the <line-names> sublists in between).
9066 : // This is the first repetition.
9067 0 : NS_ASSERTION(previousItemBeforeRepeat->mNext == nullptr,
9068 : "Expected the end of a linked list");
9069 0 : previousItemBeforeRepeat->mNext = firstTrackSizeItemAuto.forget();
9070 0 : nsCSSValueList* firstTrackSizeItem = previousItemBeforeRepeat->mNext;
9071 0 : nsCSSValueList* tail = lastTrackSizeItem;
9072 :
9073 : // Repeat |repetitions - 1| more times:
9074 : // * the joiner <line-names>
9075 : // * the linked list
9076 : // (first to last <track-size>, with the <line-names> sublists in between)
9077 0 : MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
9078 0 : while (--repetitions) {
9079 0 : tail->mNext = new nsCSSValueList;
9080 0 : tail = tail->mNext;
9081 0 : tail->mValue = joinerLineNames;
9082 :
9083 0 : nsCSSValueList* repeatedItem = firstTrackSizeItem;
9084 : for (;;) {
9085 0 : tail->mNext = new nsCSSValueList;
9086 0 : tail = tail->mNext;
9087 0 : tail->mValue = repeatedItem->mValue;
9088 0 : if (repeatedItem == lastTrackSizeItem) {
9089 0 : break;
9090 : }
9091 0 : repeatedItem = repeatedItem->mNext;
9092 : }
9093 : }
9094 :
9095 : // Finally, move our last <line-names>.
9096 : // Any <line-names> immediately after repeat() will append to it.
9097 0 : tail->mNext = new nsCSSValueList;
9098 0 : tail = tail->mNext;
9099 0 : tail->mValue = lastLineNames;
9100 :
9101 0 : *aTailPtr = tail;
9102 0 : return true;
9103 : }
9104 :
9105 : bool
9106 0 : CSSParserImpl::ParseGridTrackList(nsCSSPropertyID aPropID,
9107 : GridTrackListFlags aFlags)
9108 : {
9109 0 : nsCSSValue value;
9110 0 : nsCSSValue firstLineNames;
9111 0 : if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
9112 0 : !ParseGridTrackListWithFirstLineNames(value, firstLineNames, aFlags)) {
9113 0 : return false;
9114 : }
9115 0 : AppendValue(aPropID, value);
9116 0 : return true;
9117 : }
9118 :
9119 : bool
9120 12 : CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSPropertyID aPropID)
9121 : {
9122 24 : nsCSSValue value;
9123 12 : if (ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
9124 12 : AppendValue(aPropID, value);
9125 12 : return true;
9126 : }
9127 :
9128 0 : nsAString* ident = NextIdent();
9129 0 : if (ident) {
9130 0 : if (ident->LowerCaseEqualsLiteral("subgrid")) {
9131 0 : if (!nsLayoutUtils::IsGridTemplateSubgridValueEnabled()) {
9132 0 : REPORT_UNEXPECTED(PESubgridNotSupported);
9133 0 : return false;
9134 : }
9135 0 : if (!ParseOptionalLineNameListAfterSubgrid(value)) {
9136 0 : return false;
9137 : }
9138 0 : AppendValue(aPropID, value);
9139 0 : return true;
9140 : }
9141 0 : UngetToken();
9142 : }
9143 :
9144 0 : return ParseGridTrackList(aPropID);
9145 : }
9146 :
9147 : bool
9148 0 : CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput,
9149 : css::GridTemplateAreasValue* aAreas,
9150 : nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices)
9151 : {
9152 0 : aAreas->mTemplates.AppendElement(mToken.mIdent);
9153 :
9154 0 : nsCSSGridTemplateAreaScanner scanner(aInput);
9155 0 : nsCSSGridTemplateAreaToken token;
9156 0 : css::GridNamedArea* currentArea = nullptr;
9157 0 : uint32_t row = aAreas->NRows();
9158 : // Column numbers starts at 1, but we might not have any, eg
9159 : // grid-template-areas:""; which will result in mNColumns == 0.
9160 0 : uint32_t column = 0;
9161 0 : while (scanner.Next(token)) {
9162 0 : ++column;
9163 0 : if (token.isTrash) {
9164 0 : return false;
9165 : }
9166 0 : if (currentArea) {
9167 0 : if (token.mName == currentArea->mName) {
9168 0 : if (currentArea->mRowStart == row) {
9169 : // Next column in the first row of this named area.
9170 0 : currentArea->mColumnEnd++;
9171 : }
9172 0 : continue;
9173 : }
9174 : // We're exiting |currentArea|, so currentArea is ending at |column|.
9175 : // Make sure that this is consistent with currentArea on previous rows:
9176 0 : if (currentArea->mColumnEnd != column) {
9177 0 : NS_ASSERTION(currentArea->mRowStart != row,
9178 : "Inconsistent column end for the first row of a named area.");
9179 : // Not a rectangle
9180 0 : return false;
9181 : }
9182 0 : currentArea = nullptr;
9183 : }
9184 0 : if (!token.mName.IsEmpty()) {
9185 : // Named cell that doesn't have a cell with the same name on its left.
9186 :
9187 : // Check if this is the continuation of an existing named area:
9188 : uint32_t index;
9189 0 : if (aAreaIndices.Get(token.mName, &index)) {
9190 0 : MOZ_ASSERT(index < aAreas->mNamedAreas.Length(),
9191 : "Invalid aAreaIndices hash table");
9192 0 : currentArea = &aAreas->mNamedAreas[index];
9193 0 : if (currentArea->mColumnStart != column ||
9194 0 : currentArea->mRowEnd != row) {
9195 : // Existing named area, but not forming a rectangle
9196 0 : return false;
9197 : }
9198 : // Next row of an existing named area
9199 0 : currentArea->mRowEnd++;
9200 : } else {
9201 : // New named area
9202 0 : aAreaIndices.Put(token.mName, aAreas->mNamedAreas.Length());
9203 0 : currentArea = aAreas->mNamedAreas.AppendElement();
9204 0 : currentArea->mName = token.mName;
9205 : // For column or row N (starting at 1),
9206 : // the start line is N, the end line is N + 1
9207 0 : currentArea->mColumnStart = column;
9208 0 : currentArea->mColumnEnd = column + 1;
9209 0 : currentArea->mRowStart = row;
9210 0 : currentArea->mRowEnd = row + 1;
9211 : }
9212 : }
9213 : }
9214 0 : if (currentArea && currentArea->mColumnEnd != column + 1) {
9215 0 : NS_ASSERTION(currentArea->mRowStart != row,
9216 : "Inconsistent column end for the first row of a named area.");
9217 : // Not a rectangle
9218 0 : return false;
9219 : }
9220 :
9221 : // On the first row, set the number of columns
9222 : // that grid-template-areas contributes to the explicit grid.
9223 : // On other rows, check that the number of columns is consistent
9224 : // between rows.
9225 0 : if (row == 1) {
9226 0 : aAreas->mNColumns = column;
9227 0 : } else if (aAreas->mNColumns != column) {
9228 0 : return false;
9229 : }
9230 0 : return true;
9231 : }
9232 :
9233 : bool
9234 6 : CSSParserImpl::ParseGridTemplateAreas()
9235 : {
9236 12 : nsCSSValue value;
9237 6 : if (ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
9238 6 : AppendValue(eCSSProperty_grid_template_areas, value);
9239 6 : return true;
9240 : }
9241 :
9242 : RefPtr<css::GridTemplateAreasValue> areas =
9243 0 : new css::GridTemplateAreasValue();
9244 0 : nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
9245 : for (;;) {
9246 0 : if (!GetToken(true)) {
9247 0 : break;
9248 : }
9249 0 : if (eCSSToken_String != mToken.mType) {
9250 0 : UngetToken();
9251 0 : break;
9252 : }
9253 0 : if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
9254 0 : return false;
9255 : }
9256 : }
9257 :
9258 0 : if (areas->NRows() == 0) {
9259 0 : return false;
9260 : }
9261 :
9262 0 : AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
9263 0 : return true;
9264 : }
9265 :
9266 : // [ auto-flow && dense? ] <'grid-auto-columns'>? |
9267 : // <'grid-template-columns'>
9268 : bool
9269 0 : CSSParserImpl::ParseGridTemplateColumnsOrAutoFlow(bool aForGridShorthand)
9270 : {
9271 0 : if (aForGridShorthand) {
9272 0 : auto res = ParseGridShorthandAutoProps(NS_STYLE_GRID_AUTO_FLOW_COLUMN);
9273 0 : if (res == CSSParseResult::Error) {
9274 0 : return false;
9275 : }
9276 0 : if (res == CSSParseResult::Ok) {
9277 0 : nsCSSValue value(eCSSUnit_None);
9278 0 : AppendValue(eCSSProperty_grid_template_columns, value);
9279 0 : return true;
9280 : }
9281 : }
9282 0 : return ParseGridTemplateColumnsRows(eCSSProperty_grid_template_columns);
9283 : }
9284 :
9285 : bool
9286 0 : CSSParserImpl::ParseGridTemplate(bool aForGridShorthand)
9287 : {
9288 : // none |
9289 : // subgrid |
9290 : // <'grid-template-rows'> / <'grid-template-columns'> |
9291 : // [ <line-names>? <string> <track-size>? <line-names>? ]+ [ / <explicit-track-list>]?
9292 : // or additionally when aForGridShorthand is true:
9293 : // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>?
9294 0 : nsCSSValue value;
9295 0 : if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
9296 0 : AppendValue(eCSSProperty_grid_template_areas, value);
9297 0 : AppendValue(eCSSProperty_grid_template_rows, value);
9298 0 : AppendValue(eCSSProperty_grid_template_columns, value);
9299 0 : return true;
9300 : }
9301 :
9302 : // 'none' can appear either by itself,
9303 : // or as the beginning of <'grid-template-rows'> / <'grid-template-columns'>
9304 0 : if (ParseSingleTokenVariant(value, VARIANT_NONE, nullptr)) {
9305 0 : AppendValue(eCSSProperty_grid_template_rows, value);
9306 0 : AppendValue(eCSSProperty_grid_template_areas, value);
9307 0 : if (ExpectSymbol('/', true)) {
9308 0 : return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand);
9309 : }
9310 0 : AppendValue(eCSSProperty_grid_template_columns, value);
9311 0 : return true;
9312 : }
9313 :
9314 : // 'subgrid' can appear either by itself,
9315 : // or as the beginning of <'grid-template-rows'> / <'grid-template-columns'>
9316 0 : nsAString* ident = NextIdent();
9317 0 : if (ident) {
9318 0 : if (ident->LowerCaseEqualsLiteral("subgrid")) {
9319 0 : if (!nsLayoutUtils::IsGridTemplateSubgridValueEnabled()) {
9320 0 : REPORT_UNEXPECTED(PESubgridNotSupported);
9321 0 : return false;
9322 : }
9323 0 : if (!ParseOptionalLineNameListAfterSubgrid(value)) {
9324 0 : return false;
9325 : }
9326 0 : AppendValue(eCSSProperty_grid_template_rows, value);
9327 0 : AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(eCSSUnit_None));
9328 0 : if (ExpectSymbol('/', true)) {
9329 0 : return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand);
9330 : }
9331 0 : if (value.GetListValue()->mNext) {
9332 : // Non-empty <line-name-list> after 'subgrid'.
9333 : // This is only valid as part of <'grid-template-rows'>,
9334 : // which must be followed by a slash.
9335 0 : return false;
9336 : }
9337 : // 'subgrid' by itself sets both grid-template-rows/columns.
9338 0 : AppendValue(eCSSProperty_grid_template_columns, value);
9339 0 : return true;
9340 : }
9341 0 : UngetToken();
9342 : }
9343 :
9344 : // [ <line-names>? ] here is ambiguous:
9345 : // it can be either the start of a <track-list> (in a <'grid-template-rows'>),
9346 : // or the start of [ <line-names>? <string> <track-size>? <line-names>? ]+
9347 0 : nsCSSValue firstLineNames;
9348 0 : if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
9349 0 : !GetToken(true)) {
9350 0 : return false;
9351 : }
9352 0 : if (mToken.mType == eCSSToken_String) {
9353 : // It's the [ <line-names>? <string> <track-size>? <line-names>? ]+ case.
9354 0 : if (!ParseGridTemplateAfterString(firstLineNames)) {
9355 0 : return false;
9356 : }
9357 : // Parse an optional [ / <explicit-track-list> ] as the columns value.
9358 0 : if (ExpectSymbol('/', true)) {
9359 : return ParseGridTrackList(eCSSProperty_grid_template_columns,
9360 0 : GridTrackListFlags::eExplicitTrackList);
9361 : }
9362 0 : value.SetNoneValue(); // absent means 'none'
9363 0 : AppendValue(eCSSProperty_grid_template_columns, value);
9364 0 : return true;
9365 : }
9366 0 : UngetToken();
9367 :
9368 : // Finish parsing <'grid-template-rows'> with the |firstLineNames| we have,
9369 : // and then parse a mandatory [ / <'grid-template-columns'> ].
9370 0 : if (!ParseGridTrackListWithFirstLineNames(value, firstLineNames) ||
9371 0 : !ExpectSymbol('/', true)) {
9372 0 : return false;
9373 : }
9374 0 : AppendValue(eCSSProperty_grid_template_rows, value);
9375 0 : value.SetNoneValue();
9376 0 : AppendValue(eCSSProperty_grid_template_areas, value);
9377 0 : return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand);
9378 : }
9379 :
9380 : // Helper for parsing the 'grid-template' shorthand:
9381 : // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
9382 : // with a <line-names>? already consumed, stored in |aFirstLineNames|,
9383 : // and the current token a <string>
9384 : bool
9385 0 : CSSParserImpl::ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames)
9386 : {
9387 0 : MOZ_ASSERT(mToken.mType == eCSSToken_String,
9388 : "ParseGridTemplateAfterString called with a non-string token");
9389 :
9390 0 : nsCSSValue rowsValue;
9391 : RefPtr<css::GridTemplateAreasValue> areas =
9392 0 : new css::GridTemplateAreasValue();
9393 0 : nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
9394 0 : nsCSSValueList* rowsItem = rowsValue.SetListValue();
9395 0 : rowsItem->mValue = aFirstLineNames;
9396 :
9397 : for (;;) {
9398 0 : if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
9399 0 : return false;
9400 : }
9401 :
9402 0 : rowsItem->mNext = new nsCSSValueList;
9403 0 : rowsItem = rowsItem->mNext;
9404 0 : CSSParseResult result = ParseGridTrackSize(rowsItem->mValue);
9405 0 : if (result == CSSParseResult::Error) {
9406 0 : return false;
9407 : }
9408 0 : if (result == CSSParseResult::NotFound) {
9409 0 : rowsItem->mValue.SetAutoValue();
9410 : }
9411 :
9412 0 : rowsItem->mNext = new nsCSSValueList;
9413 0 : rowsItem = rowsItem->mNext;
9414 0 : result = ParseGridLineNames(rowsItem->mValue);
9415 0 : if (result == CSSParseResult::Error) {
9416 0 : return false;
9417 : }
9418 0 : if (result == CSSParseResult::Ok) {
9419 : // Append to the same list as the previous call to ParseGridLineNames.
9420 0 : result = ParseGridLineNames(rowsItem->mValue);
9421 0 : if (result == CSSParseResult::Error) {
9422 0 : return false;
9423 : }
9424 0 : if (result == CSSParseResult::Ok) {
9425 : // Parsed <line-name> twice.
9426 : // The property value can not end here, we expect a string next.
9427 0 : if (!GetToken(true)) {
9428 0 : return false;
9429 : }
9430 0 : if (eCSSToken_String != mToken.mType) {
9431 0 : UngetToken();
9432 0 : return false;
9433 : }
9434 0 : continue;
9435 : }
9436 : }
9437 :
9438 : // Did not find a <line-names>.
9439 : // Next, we expect either a string or the end of the property value.
9440 0 : if (!GetToken(true)) {
9441 0 : break;
9442 : }
9443 0 : if (eCSSToken_String != mToken.mType) {
9444 0 : UngetToken();
9445 0 : break;
9446 : }
9447 0 : }
9448 :
9449 0 : AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
9450 0 : AppendValue(eCSSProperty_grid_template_rows, rowsValue);
9451 0 : return true;
9452 : }
9453 :
9454 : // <'grid-template'> |
9455 : // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? |
9456 : // [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'>
9457 : bool
9458 0 : CSSParserImpl::ParseGrid()
9459 : {
9460 0 : nsCSSValue value;
9461 0 : if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
9462 0 : for (const nsCSSPropertyID* subprops =
9463 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid);
9464 0 : *subprops != eCSSProperty_UNKNOWN; ++subprops) {
9465 0 : AppendValue(*subprops, value);
9466 : }
9467 0 : return true;
9468 : }
9469 :
9470 : // https://drafts.csswg.org/css-grid/#grid-shorthand
9471 : // "Also, the gutter properties are reset by this shorthand,
9472 : // even though they can't be set by it."
9473 0 : value.SetFloatValue(0.0f, eCSSUnit_Pixel);
9474 0 : AppendValue(eCSSProperty_grid_row_gap, value);
9475 0 : AppendValue(eCSSProperty_grid_column_gap, value);
9476 :
9477 : // [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'>
9478 0 : auto res = ParseGridShorthandAutoProps(NS_STYLE_GRID_AUTO_FLOW_ROW);
9479 0 : if (res == CSSParseResult::Error) {
9480 0 : return false;
9481 : }
9482 0 : if (res == CSSParseResult::Ok) {
9483 0 : value.SetAutoValue();
9484 0 : AppendValue(eCSSProperty_grid_auto_columns, value);
9485 0 : nsCSSValue none(eCSSUnit_None);
9486 0 : AppendValue(eCSSProperty_grid_template_areas, none);
9487 0 : AppendValue(eCSSProperty_grid_template_rows, none);
9488 0 : if (!ExpectSymbol('/', true)) {
9489 0 : return false;
9490 : }
9491 0 : return ParseGridTemplateColumnsRows(eCSSProperty_grid_template_columns);
9492 : }
9493 :
9494 : // Set remaining subproperties that might not be set by ParseGridTemplate to
9495 : // their initial values and then parse <'grid-template'> |
9496 : // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? .
9497 0 : value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_ROW, eCSSUnit_Enumerated);
9498 0 : AppendValue(eCSSProperty_grid_auto_flow, value);
9499 0 : value.SetAutoValue();
9500 0 : AppendValue(eCSSProperty_grid_auto_rows, value);
9501 0 : AppendValue(eCSSProperty_grid_auto_columns, value);
9502 0 : return ParseGridTemplate(true);
9503 : }
9504 :
9505 : // Parse [ auto-flow && dense? ] <'grid-auto-[rows|columns]'>? for the 'grid'
9506 : // shorthand. If aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW then we're
9507 : // parsing row values, otherwise column values.
9508 : CSSParseResult
9509 0 : CSSParserImpl::ParseGridShorthandAutoProps(int32_t aAutoFlowAxis)
9510 : {
9511 0 : MOZ_ASSERT(aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW ||
9512 : aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_COLUMN);
9513 0 : if (!GetToken(true)) {
9514 0 : return CSSParseResult::NotFound;
9515 : }
9516 : // [ auto-flow && dense? ]
9517 0 : int32_t autoFlowValue = 0;
9518 0 : if (mToken.mType == eCSSToken_Ident) {
9519 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
9520 0 : if (keyword == eCSSKeyword_auto_flow) {
9521 0 : autoFlowValue = aAutoFlowAxis;
9522 0 : if (GetToken(true)) {
9523 0 : if (mToken.mType == eCSSToken_Ident &&
9524 0 : nsCSSKeywords::LookupKeyword(mToken.mIdent) == eCSSKeyword_dense) {
9525 0 : autoFlowValue |= NS_STYLE_GRID_AUTO_FLOW_DENSE;
9526 : } else {
9527 0 : UngetToken();
9528 : }
9529 : }
9530 0 : } else if (keyword == eCSSKeyword_dense) {
9531 0 : if (!GetToken(true)) {
9532 0 : return CSSParseResult::Error;
9533 : }
9534 0 : if (mToken.mType != eCSSToken_Ident ||
9535 0 : nsCSSKeywords::LookupKeyword(mToken.mIdent) != eCSSKeyword_auto_flow) {
9536 0 : UngetToken();
9537 0 : return CSSParseResult::Error;
9538 : }
9539 0 : autoFlowValue = aAutoFlowAxis | NS_STYLE_GRID_AUTO_FLOW_DENSE;
9540 : }
9541 : }
9542 0 : if (autoFlowValue) {
9543 0 : nsCSSValue value;
9544 0 : value.SetIntValue(autoFlowValue, eCSSUnit_Enumerated);
9545 0 : AppendValue(eCSSProperty_grid_auto_flow, value);
9546 : } else {
9547 0 : UngetToken();
9548 0 : return CSSParseResult::NotFound;
9549 : }
9550 :
9551 : // <'grid-auto-[rows|columns]'>?
9552 0 : nsCSSValue autoTrackValue;
9553 0 : CSSParseResult result = ParseGridTrackSize(autoTrackValue);
9554 0 : if (result == CSSParseResult::Error) {
9555 0 : return result;
9556 : }
9557 0 : if (result == CSSParseResult::NotFound) {
9558 0 : autoTrackValue.SetAutoValue();
9559 : }
9560 0 : AppendValue(aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW ?
9561 : eCSSProperty_grid_auto_rows : eCSSProperty_grid_auto_columns,
9562 0 : autoTrackValue);
9563 0 : return CSSParseResult::Ok;
9564 : }
9565 :
9566 : // Parse a <grid-line>.
9567 : // If successful, set aValue to eCSSUnit_Auto,
9568 : // or a eCSSUnit_List containing, in that order:
9569 : //
9570 : // * An optional eCSSUnit_Enumerated marking a "span" keyword.
9571 : // * An optional eCSSUnit_Integer
9572 : // * An optional eCSSUnit_Ident
9573 : //
9574 : // At least one of eCSSUnit_Integer or eCSSUnit_Ident is present.
9575 : bool
9576 0 : CSSParserImpl::ParseGridLine(nsCSSValue& aValue)
9577 : {
9578 : // <grid-line> =
9579 : // auto |
9580 : // <custom-ident> |
9581 : // [ <integer> && <custom-ident>? ] |
9582 : // [ span && [ <integer> || <custom-ident> ] ]
9583 : //
9584 : // Syntactically, this simplifies to:
9585 : //
9586 : // <grid-line> =
9587 : // auto |
9588 : // [ span? && [ <integer> || <custom-ident> ] ]
9589 :
9590 0 : if (ParseSingleTokenVariant(aValue, VARIANT_AUTO, nullptr)) {
9591 0 : return true;
9592 : }
9593 :
9594 0 : bool hasSpan = false;
9595 0 : bool hasIdent = false;
9596 0 : Maybe<int32_t> integer;
9597 0 : nsCSSValue ident;
9598 :
9599 : #ifdef MOZ_VALGRIND
9600 : // Make the contained value be defined even though we really want a
9601 : // Nothing here. This works around an otherwise difficult to avoid
9602 : // Memcheck false positive when this is compiled by gcc-5.3 -O2.
9603 : // See bug 1301856.
9604 : integer.emplace(0);
9605 : integer.reset();
9606 : #endif
9607 :
9608 0 : if (!GetToken(true)) {
9609 0 : return false;
9610 : }
9611 0 : if (mToken.mType == eCSSToken_Ident &&
9612 0 : mToken.mIdent.LowerCaseEqualsLiteral("span")) {
9613 0 : hasSpan = true;
9614 0 : if (!GetToken(true)) {
9615 0 : return false;
9616 : }
9617 : }
9618 :
9619 0 : do {
9620 0 : if (!hasIdent &&
9621 0 : mToken.mType == eCSSToken_Ident &&
9622 0 : ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) {
9623 0 : hasIdent = true;
9624 0 : } else if (integer.isNothing() &&
9625 0 : mToken.mType == eCSSToken_Number &&
9626 0 : mToken.mIntegerValid &&
9627 0 : mToken.mInteger != 0) {
9628 0 : integer.emplace(mToken.mInteger);
9629 : } else {
9630 0 : UngetToken();
9631 0 : break;
9632 : }
9633 0 : } while (!(integer.isSome() && hasIdent) && GetToken(true));
9634 :
9635 : // Require at least one of <integer> or <custom-ident>
9636 0 : if (!(integer.isSome() || hasIdent)) {
9637 0 : return false;
9638 : }
9639 :
9640 0 : if (!hasSpan && GetToken(true)) {
9641 0 : if (mToken.mType == eCSSToken_Ident &&
9642 0 : mToken.mIdent.LowerCaseEqualsLiteral("span")) {
9643 0 : hasSpan = true;
9644 : } else {
9645 0 : UngetToken();
9646 : }
9647 : }
9648 :
9649 0 : nsCSSValueList* item = aValue.SetListValue();
9650 0 : if (hasSpan) {
9651 : // Given "span", a negative <integer> is invalid.
9652 0 : if (integer.isSome() && integer.ref() < 0) {
9653 0 : return false;
9654 : }
9655 : // '1' here is a dummy value.
9656 : // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword.
9657 0 : item->mValue.SetIntValue(1, eCSSUnit_Enumerated);
9658 0 : item->mNext = new nsCSSValueList;
9659 0 : item = item->mNext;
9660 : }
9661 0 : if (integer.isSome()) {
9662 0 : item->mValue.SetIntValue(integer.ref(), eCSSUnit_Integer);
9663 0 : if (hasIdent) {
9664 0 : item->mNext = new nsCSSValueList;
9665 0 : item = item->mNext;
9666 : }
9667 : }
9668 0 : if (hasIdent) {
9669 0 : item->mValue = ident;
9670 : }
9671 0 : return true;
9672 : }
9673 :
9674 : bool
9675 8 : CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSPropertyID aPropID)
9676 : {
9677 16 : nsCSSValue value;
9678 8 : if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) ||
9679 0 : ParseGridLine(value)) {
9680 8 : AppendValue(aPropID, value);
9681 8 : return true;
9682 : }
9683 0 : return false;
9684 : }
9685 :
9686 : // If |aFallback| is a List containing a single Ident, set |aValue| to that.
9687 : // Otherwise, set |aValue| to Auto.
9688 : // Used with |aFallback| from ParseGridLine()
9689 : static void
9690 0 : HandleGridLineFallback(const nsCSSValue& aFallback, nsCSSValue& aValue)
9691 : {
9692 0 : if (aFallback.GetUnit() == eCSSUnit_List &&
9693 0 : aFallback.GetListValue()->mValue.GetUnit() == eCSSUnit_Ident &&
9694 0 : !aFallback.GetListValue()->mNext) {
9695 0 : aValue = aFallback;
9696 : } else {
9697 0 : aValue.SetAutoValue();
9698 : }
9699 0 : }
9700 :
9701 : bool
9702 0 : CSSParserImpl::ParseGridColumnRow(nsCSSPropertyID aStartPropID,
9703 : nsCSSPropertyID aEndPropID)
9704 : {
9705 0 : nsCSSValue value;
9706 0 : nsCSSValue secondValue;
9707 0 : if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
9708 0 : AppendValue(aStartPropID, value);
9709 0 : AppendValue(aEndPropID, value);
9710 0 : return true;
9711 : }
9712 :
9713 0 : if (!ParseGridLine(value)) {
9714 0 : return false;
9715 : }
9716 0 : if (GetToken(true)) {
9717 0 : if (mToken.IsSymbol('/')) {
9718 0 : if (ParseGridLine(secondValue)) {
9719 0 : AppendValue(aStartPropID, value);
9720 0 : AppendValue(aEndPropID, secondValue);
9721 0 : return true;
9722 : } else {
9723 0 : return false;
9724 : }
9725 : }
9726 0 : UngetToken();
9727 : }
9728 :
9729 : // A single <custom-ident> is repeated to both properties,
9730 : // anything else sets the grid-{column,row}-end property to 'auto'.
9731 0 : HandleGridLineFallback(value, secondValue);
9732 :
9733 0 : AppendValue(aStartPropID, value);
9734 0 : AppendValue(aEndPropID, secondValue);
9735 0 : return true;
9736 : }
9737 :
9738 : bool
9739 0 : CSSParserImpl::ParseGridArea()
9740 : {
9741 0 : nsCSSValue values[4];
9742 0 : if (ParseSingleTokenVariant(values[0], VARIANT_INHERIT, nullptr)) {
9743 0 : AppendValue(eCSSProperty_grid_row_start, values[0]);
9744 0 : AppendValue(eCSSProperty_grid_column_start, values[0]);
9745 0 : AppendValue(eCSSProperty_grid_row_end, values[0]);
9746 0 : AppendValue(eCSSProperty_grid_column_end, values[0]);
9747 0 : return true;
9748 : }
9749 :
9750 0 : int32_t i = 0;
9751 : for (;;) {
9752 0 : if (!ParseGridLine(values[i])) {
9753 0 : return false;
9754 : }
9755 0 : if (++i == 4 || !GetToken(true)) {
9756 0 : break;
9757 : }
9758 0 : if (!mToken.IsSymbol('/')) {
9759 0 : UngetToken();
9760 0 : break;
9761 : }
9762 : }
9763 :
9764 0 : MOZ_ASSERT(i >= 1, "should have parsed at least one grid-line (or returned)");
9765 0 : if (i < 2) {
9766 0 : HandleGridLineFallback(values[0], values[1]);
9767 : }
9768 0 : if (i < 3) {
9769 0 : HandleGridLineFallback(values[0], values[2]);
9770 : }
9771 0 : if (i < 4) {
9772 0 : HandleGridLineFallback(values[1], values[3]);
9773 : }
9774 :
9775 0 : AppendValue(eCSSProperty_grid_row_start, values[0]);
9776 0 : AppendValue(eCSSProperty_grid_column_start, values[1]);
9777 0 : AppendValue(eCSSProperty_grid_row_end, values[2]);
9778 0 : AppendValue(eCSSProperty_grid_column_end, values[3]);
9779 0 : return true;
9780 : }
9781 :
9782 : bool
9783 0 : CSSParserImpl::ParseGridGap()
9784 : {
9785 0 : nsCSSValue first;
9786 0 : if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
9787 0 : AppendValue(eCSSProperty_grid_row_gap, first);
9788 0 : AppendValue(eCSSProperty_grid_column_gap, first);
9789 0 : return true;
9790 : }
9791 0 : if (ParseNonNegativeVariant(first, VARIANT_LPCALC, nullptr) !=
9792 : CSSParseResult::Ok) {
9793 0 : return false;
9794 : }
9795 0 : nsCSSValue second;
9796 0 : auto result = ParseNonNegativeVariant(second, VARIANT_LPCALC, nullptr);
9797 0 : if (result == CSSParseResult::Error) {
9798 0 : return false;
9799 : }
9800 0 : AppendValue(eCSSProperty_grid_row_gap, first);
9801 0 : AppendValue(eCSSProperty_grid_column_gap,
9802 0 : result == CSSParseResult::NotFound ? first : second);
9803 0 : return true;
9804 : }
9805 :
9806 : // normal | [<number> <integer>?]
9807 : bool
9808 0 : CSSParserImpl::ParseInitialLetter()
9809 : {
9810 0 : nsCSSValue value;
9811 : // 'inherit', 'initial', 'unset', 'none', and 'normal' must be alone
9812 0 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NORMAL,
9813 : nullptr)) {
9814 0 : nsCSSValue first, second;
9815 0 : if (!ParseOneOrLargerNumber(first)) {
9816 0 : return false;
9817 : }
9818 :
9819 0 : if (!ParseOneOrLargerInteger(second)) {
9820 0 : AppendValue(eCSSProperty_initial_letter, first);
9821 0 : return true;
9822 : } else {
9823 0 : RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
9824 0 : val->Item(0) = first;
9825 0 : val->Item(1) = second;
9826 0 : value.SetArrayValue(val, eCSSUnit_Array);
9827 : }
9828 : }
9829 0 : AppendValue(eCSSProperty_initial_letter, value);
9830 0 : return true;
9831 : }
9832 :
9833 : // [ $aTable && <overflow-position>? ] ?
9834 : // $aTable is for <content-position> or <self-position>
9835 : bool
9836 9 : CSSParserImpl::ParseAlignJustifyPosition(nsCSSValue& aResult,
9837 : const KTableEntry aTable[])
9838 : {
9839 18 : nsCSSValue pos, overflowPos;
9840 9 : int32_t value = 0;
9841 9 : if (ParseEnum(pos, aTable)) {
9842 9 : value = pos.GetIntValue();
9843 9 : if (ParseEnum(overflowPos, nsCSSProps::kAlignOverflowPosition)) {
9844 0 : value |= overflowPos.GetIntValue();
9845 : }
9846 9 : aResult.SetIntValue(value, eCSSUnit_Enumerated);
9847 9 : return true;
9848 : }
9849 0 : if (ParseEnum(overflowPos, nsCSSProps::kAlignOverflowPosition)) {
9850 0 : if (ParseEnum(pos, aTable)) {
9851 0 : aResult.SetIntValue(pos.GetIntValue() | overflowPos.GetIntValue(),
9852 0 : eCSSUnit_Enumerated);
9853 0 : return true;
9854 : }
9855 0 : return false; // <overflow-position> must be followed by a value in $table
9856 : }
9857 0 : return true;
9858 : }
9859 :
9860 : // auto | normal | stretch | <baseline-position> |
9861 : // [ <self-position> && <overflow-position>? ] |
9862 : // [ legacy && [ left | right | center ] ]
9863 : bool
9864 6 : CSSParserImpl::ParseJustifyItems()
9865 : {
9866 12 : nsCSSValue value;
9867 6 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
9868 0 : if (MOZ_UNLIKELY(ParseEnum(value, nsCSSProps::kAlignLegacy))) {
9869 0 : nsCSSValue legacy;
9870 0 : if (!ParseEnum(legacy, nsCSSProps::kAlignLegacyPosition)) {
9871 0 : return false; // leading 'legacy' not followed by 'left' etc is an error
9872 : }
9873 0 : value.SetIntValue(value.GetIntValue() | legacy.GetIntValue(),
9874 0 : eCSSUnit_Enumerated);
9875 : } else {
9876 0 : if (!ParseAlignEnum(value, nsCSSProps::kAlignAutoNormalStretchBaseline)) {
9877 0 : if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) ||
9878 0 : value.GetUnit() == eCSSUnit_Null) {
9879 0 : return false;
9880 : }
9881 : // check for a trailing 'legacy' after 'left' etc
9882 0 : auto val = value.GetIntValue();
9883 0 : if (val == NS_STYLE_JUSTIFY_CENTER ||
9884 0 : val == NS_STYLE_JUSTIFY_LEFT ||
9885 : val == NS_STYLE_JUSTIFY_RIGHT) {
9886 0 : nsCSSValue legacy;
9887 0 : if (ParseEnum(legacy, nsCSSProps::kAlignLegacy)) {
9888 0 : value.SetIntValue(val | legacy.GetIntValue(), eCSSUnit_Enumerated);
9889 : }
9890 : }
9891 : }
9892 : }
9893 : }
9894 6 : AppendValue(eCSSProperty_justify_items, value);
9895 6 : return true;
9896 : }
9897 :
9898 : // normal | stretch | <baseline-position> |
9899 : // [ <overflow-position>? && <self-position> ]
9900 : bool
9901 10 : CSSParserImpl::ParseAlignItems()
9902 : {
9903 20 : nsCSSValue value;
9904 10 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
9905 4 : if (!ParseAlignEnum(value, nsCSSProps::kAlignNormalStretchBaseline)) {
9906 8 : if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) ||
9907 4 : value.GetUnit() == eCSSUnit_Null) {
9908 0 : return false;
9909 : }
9910 : }
9911 : }
9912 10 : AppendValue(eCSSProperty_align_items, value);
9913 10 : return true;
9914 : }
9915 :
9916 : // auto | normal | stretch | <baseline-position> |
9917 : // [ <overflow-position>? && <self-position> ]
9918 : bool
9919 7 : CSSParserImpl::ParseAlignJustifySelf(nsCSSPropertyID aPropID)
9920 : {
9921 14 : nsCSSValue value;
9922 7 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
9923 3 : if (!ParseAlignEnum(value, nsCSSProps::kAlignAutoNormalStretchBaseline)) {
9924 6 : if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) ||
9925 3 : value.GetUnit() == eCSSUnit_Null) {
9926 0 : return false;
9927 : }
9928 : }
9929 : }
9930 7 : AppendValue(aPropID, value);
9931 7 : return true;
9932 : }
9933 :
9934 : // normal | <baseline-position> | [ <content-distribution> ||
9935 : // [ <overflow-position>? && <content-position> ] ]
9936 : // (the part after the || is called <*-position> below)
9937 : bool
9938 14 : CSSParserImpl::ParseAlignJustifyContent(nsCSSPropertyID aPropID)
9939 : {
9940 28 : nsCSSValue value;
9941 14 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
9942 2 : if (!ParseAlignEnum(value, nsCSSProps::kAlignNormalBaseline)) {
9943 4 : nsCSSValue fallbackValue;
9944 2 : if (!ParseEnum(value, nsCSSProps::kAlignContentDistribution)) {
9945 4 : if (!ParseAlignJustifyPosition(fallbackValue,
9946 4 : nsCSSProps::kAlignContentPosition) ||
9947 2 : fallbackValue.GetUnit() == eCSSUnit_Null) {
9948 0 : return false;
9949 : }
9950 : // optional <content-distribution> after <*-position> ...
9951 2 : if (!ParseEnum(value, nsCSSProps::kAlignContentDistribution)) {
9952 : // ... is missing so the <*-position> is the value, not the fallback
9953 2 : value = fallbackValue;
9954 2 : fallbackValue.Reset();
9955 : }
9956 : } else {
9957 : // any optional <*-position> is a fallback value
9958 0 : if (!ParseAlignJustifyPosition(fallbackValue,
9959 : nsCSSProps::kAlignContentPosition)) {
9960 0 : return false;
9961 : }
9962 : }
9963 2 : if (fallbackValue.GetUnit() != eCSSUnit_Null) {
9964 0 : auto fallback = fallbackValue.GetIntValue();
9965 0 : value.SetIntValue(value.GetIntValue() |
9966 0 : (fallback << NS_STYLE_ALIGN_ALL_SHIFT),
9967 0 : eCSSUnit_Enumerated);
9968 : }
9969 : }
9970 : }
9971 14 : AppendValue(aPropID, value);
9972 14 : return true;
9973 : }
9974 :
9975 : // place-content: [ normal | <baseline-position> | <content-distribution> |
9976 : // <content-position> ]{1,2}
9977 : bool
9978 0 : CSSParserImpl::ParsePlaceContent()
9979 : {
9980 0 : nsCSSValue first;
9981 0 : if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
9982 0 : AppendValue(eCSSProperty_align_content, first);
9983 0 : AppendValue(eCSSProperty_justify_content, first);
9984 0 : return true;
9985 : }
9986 0 : if (!ParseAlignEnum(first, nsCSSProps::kAlignNormalBaseline) &&
9987 0 : !ParseEnum(first, nsCSSProps::kAlignContentDistribution) &&
9988 0 : !ParseEnum(first, nsCSSProps::kAlignContentPosition)) {
9989 0 : return false;
9990 : }
9991 0 : AppendValue(eCSSProperty_align_content, first);
9992 0 : nsCSSValue second;
9993 0 : if (!ParseAlignEnum(second, nsCSSProps::kAlignNormalBaseline) &&
9994 0 : !ParseEnum(second, nsCSSProps::kAlignContentDistribution) &&
9995 0 : !ParseEnum(second, nsCSSProps::kAlignContentPosition)) {
9996 0 : AppendValue(eCSSProperty_justify_content, first);
9997 : } else {
9998 0 : AppendValue(eCSSProperty_justify_content, second);
9999 : }
10000 0 : return true;
10001 : }
10002 :
10003 : // place-items: <x> [ auto | <x> ]?
10004 : // <x> = [ normal | stretch | <baseline-position> | <self-position> ]
10005 : bool
10006 0 : CSSParserImpl::ParsePlaceItems()
10007 : {
10008 0 : nsCSSValue first;
10009 0 : if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
10010 0 : AppendValue(eCSSProperty_align_items, first);
10011 0 : AppendValue(eCSSProperty_justify_items, first);
10012 0 : return true;
10013 : }
10014 0 : if (!ParseAlignEnum(first, nsCSSProps::kAlignNormalStretchBaseline) &&
10015 0 : !ParseEnum(first, nsCSSProps::kAlignSelfPosition)) {
10016 0 : return false;
10017 : }
10018 0 : AppendValue(eCSSProperty_align_items, first);
10019 0 : nsCSSValue second;
10020 : // Note: 'auto' is valid for justify-items, but not align-items.
10021 0 : if (!ParseAlignEnum(second, nsCSSProps::kAlignAutoNormalStretchBaseline) &&
10022 0 : !ParseEnum(second, nsCSSProps::kAlignSelfPosition)) {
10023 0 : AppendValue(eCSSProperty_justify_items, first);
10024 : } else {
10025 0 : AppendValue(eCSSProperty_justify_items, second);
10026 : }
10027 0 : return true;
10028 : }
10029 :
10030 : // place-self: [ auto | normal | stretch | <baseline-position> |
10031 : // <self-position> ]{1,2}
10032 : bool
10033 0 : CSSParserImpl::ParsePlaceSelf()
10034 : {
10035 0 : nsCSSValue first;
10036 0 : if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) {
10037 0 : AppendValue(eCSSProperty_align_self, first);
10038 0 : AppendValue(eCSSProperty_justify_self, first);
10039 0 : return true;
10040 : }
10041 0 : if (!ParseAlignEnum(first, nsCSSProps::kAlignAutoNormalStretchBaseline) &&
10042 0 : !ParseEnum(first, nsCSSProps::kAlignSelfPosition)) {
10043 0 : return false;
10044 : }
10045 0 : AppendValue(eCSSProperty_align_self, first);
10046 0 : nsCSSValue second;
10047 0 : if (!ParseAlignEnum(second, nsCSSProps::kAlignAutoNormalStretchBaseline) &&
10048 0 : !ParseEnum(second, nsCSSProps::kAlignSelfPosition)) {
10049 0 : AppendValue(eCSSProperty_justify_self, first);
10050 : } else {
10051 0 : AppendValue(eCSSProperty_justify_self, second);
10052 : }
10053 0 : return true;
10054 : }
10055 :
10056 : // <color-stop> : <color> [ <percentage> | <length> ]?
10057 : bool
10058 141 : CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
10059 : {
10060 141 : nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
10061 141 : CSSParseResult result = ParseVariant(stop->mColor, VARIANT_COLOR, nullptr);
10062 141 : if (result == CSSParseResult::Error) {
10063 0 : return false;
10064 141 : } else if (result == CSSParseResult::NotFound) {
10065 6 : stop->mIsInterpolationHint = true;
10066 : }
10067 :
10068 : // Stop positions do not have to fall between the starting-point and
10069 : // ending-point, so we don't use ParseNonNegativeVariant.
10070 141 : result = ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr);
10071 141 : if (result == CSSParseResult::Error) {
10072 0 : return false;
10073 141 : } else if (result == CSSParseResult::NotFound) {
10074 73 : if (stop->mIsInterpolationHint) {
10075 6 : return false;
10076 : }
10077 67 : stop->mLocation.SetNoneValue();
10078 : }
10079 135 : return true;
10080 : }
10081 :
10082 : // Helper for ParseLinearGradient -- returns true iff aPosition represents a
10083 : // box-position value which was parsed with only edge keywords.
10084 : // e.g. "left top", or "bottom", but not "left 10px"
10085 : //
10086 : // (NOTE: Even though callers may want to exclude explicit "center", we still
10087 : // need to allow for _CENTER here, because omitted position-values (e.g. the
10088 : // x-component of a value like "top") will have been parsed as being *implicit*
10089 : // center. The correct way to disallow *explicit* center is to pass "false" for
10090 : // ParseBoxPositionValues()'s "aAllowExplicitCenter" parameter, before you
10091 : // call this function.)
10092 : static bool
10093 4 : IsBoxPositionStrictlyEdgeKeywords(nsCSSValuePair& aPosition)
10094 : {
10095 4 : const nsCSSValue& xValue = aPosition.mXValue;
10096 4 : const nsCSSValue& yValue = aPosition.mYValue;
10097 8 : return (xValue.GetUnit() == eCSSUnit_Enumerated &&
10098 4 : (xValue.GetIntValue() & (NS_STYLE_IMAGELAYER_POSITION_LEFT |
10099 : NS_STYLE_IMAGELAYER_POSITION_CENTER |
10100 4 : NS_STYLE_IMAGELAYER_POSITION_RIGHT)) &&
10101 12 : yValue.GetUnit() == eCSSUnit_Enumerated &&
10102 4 : (yValue.GetIntValue() & (NS_STYLE_IMAGELAYER_POSITION_TOP |
10103 : NS_STYLE_IMAGELAYER_POSITION_CENTER |
10104 4 : NS_STYLE_IMAGELAYER_POSITION_BOTTOM)));
10105 : }
10106 :
10107 : // <gradient>
10108 : // : linear-gradient( <linear-gradient-line>? <color-stops> ')'
10109 : // | radial-gradient( <radial-gradient-line>? <color-stops> ')'
10110 : //
10111 : // <linear-gradient-line> : [ to [left | right] || [top | bottom] ] ,
10112 : // | <legacy-gradient-line>
10113 : // <radial-gradient-line> : [ <shape> || <size> ] [ at <position> ]? ,
10114 : // | [ at <position> ] ,
10115 : // | <legacy-gradient-line>? <legacy-shape-size>?
10116 : // <shape> : circle | ellipse
10117 : // <size> : closest-side | closest-corner | farthest-side | farthest-corner
10118 : // | <length> | [<length> | <percentage>]{2}
10119 : //
10120 : // <legacy-gradient-line> : [ <position> || <angle>] ,
10121 : //
10122 : // <legacy-shape-size> : [ <shape> || <legacy-size> ] ,
10123 : // <legacy-size> : closest-side | closest-corner | farthest-side
10124 : // | farthest-corner | contain | cover
10125 : //
10126 : // <color-stops> : <color-stop> , <color-stop> [, <color-stop>]*
10127 : bool
10128 49 : CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue,
10129 : uint8_t aFlags)
10130 : {
10131 : RefPtr<nsCSSValueGradient> cssGradient
10132 98 : = new nsCSSValueGradient(false, aFlags & eGradient_Repeating);
10133 :
10134 49 : if (!GetToken(true)) {
10135 0 : return false;
10136 : }
10137 :
10138 : // Check for "to" syntax (but not if parsing a -webkit-linear-gradient)
10139 147 : if (!(aFlags & eGradient_WebkitLegacy) &&
10140 69 : mToken.mType == eCSSToken_Ident &&
10141 20 : mToken.mIdent.LowerCaseEqualsLiteral("to")) {
10142 :
10143 : // "to" syntax doesn't allow explicit "center"
10144 4 : if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) {
10145 0 : SkipUntil(')');
10146 0 : return false;
10147 : }
10148 :
10149 : // [ to [left | right] || [top | bottom] ] ,
10150 4 : if (!IsBoxPositionStrictlyEdgeKeywords(cssGradient->mBgPos)) {
10151 0 : SkipUntil(')');
10152 0 : return false;
10153 : }
10154 :
10155 4 : if (!ExpectSymbol(',', true)) {
10156 0 : SkipUntil(')');
10157 0 : return false;
10158 : }
10159 :
10160 4 : return ParseGradientColorStops(cssGradient, aValue);
10161 : }
10162 :
10163 45 : if (!(aFlags & eGradient_AnyLegacy)) {
10164 : // We're parsing an unprefixed linear-gradient, and we tried & failed to
10165 : // parse a 'to' token above. Put the token back & try to re-parse our
10166 : // expression as <angle>? <color-stop-list>
10167 45 : UngetToken();
10168 :
10169 : // <angle> ,
10170 90 : if (ParseSingleTokenVariant(cssGradient->mAngle,
10171 46 : VARIANT_ANGLE_OR_ZERO, nullptr) &&
10172 1 : !ExpectSymbol(',', true)) {
10173 0 : SkipUntil(')');
10174 0 : return false;
10175 : }
10176 :
10177 45 : return ParseGradientColorStops(cssGradient, aValue);
10178 : }
10179 :
10180 : // If we get here, we're parsing a prefixed linear-gradient expression. Put
10181 : // back the first token (which we may have checked for "to" above) and try to
10182 : // parse expression as <legacy-gradient-line>? <color-stop-list>
10183 0 : bool haveGradientLine = IsLegacyGradientLine(mToken.mType, mToken.mIdent);
10184 0 : UngetToken();
10185 :
10186 0 : if (haveGradientLine) {
10187 : // Parse a <legacy-gradient-line>
10188 0 : cssGradient->mIsLegacySyntax = true;
10189 0 : cssGradient->mIsMozLegacySyntax = (aFlags & eGradient_MozLegacy);
10190 : // In -webkit-linear-gradient expressions (handled below), we need to accept
10191 : // unitless 0 for angles, to match WebKit/Blink.
10192 0 : int32_t angleFlags = (aFlags & eGradient_WebkitLegacy) ?
10193 : VARIANT_ANGLE | VARIANT_ZERO_ANGLE :
10194 0 : VARIANT_ANGLE;
10195 :
10196 : bool haveAngle =
10197 0 : ParseSingleTokenVariant(cssGradient->mAngle, angleFlags, nullptr);
10198 :
10199 : // If we got an angle, we might now have a comma, ending the gradient-line.
10200 0 : bool haveAngleComma = haveAngle && ExpectSymbol(',', true);
10201 :
10202 : // And in fact, if we're -webkit-linear-gradient and got an angle, there
10203 : // *must* be a comma after the angle. (Though -moz-linear-gradient is more
10204 : // permissive because it will allow box-position before the comma.)
10205 0 : if (aFlags & eGradient_WebkitLegacy && haveAngle && !haveAngleComma) {
10206 0 : SkipUntil(')');
10207 0 : return false;
10208 : }
10209 :
10210 : // If we're prefixed & didn't get an angle + comma, then proceed to parse a
10211 : // box-position. (Note that due to the above webkit-specific early-return,
10212 : // this will only parse an *initial* box-position inside of
10213 : // -webkit-linear-gradient, vs. an initial OR post-angle box-position
10214 : // inside of -moz-linear-gradient.)
10215 0 : if (((aFlags & eGradient_AnyLegacy) && !haveAngleComma)) {
10216 : // (Note: 3rd arg controls whether the "center" keyword is allowed.
10217 : // -moz-linear-gradient allows it; -webkit-linear-gradient does not.)
10218 0 : if (!ParseBoxPositionValues(cssGradient->mBgPos, false,
10219 0 : (aFlags & eGradient_MozLegacy))) {
10220 0 : SkipUntil(')');
10221 0 : return false;
10222 : }
10223 :
10224 : // -webkit-linear-gradient only supports edge keywords here.
10225 0 : if ((aFlags & eGradient_WebkitLegacy) &&
10226 0 : !IsBoxPositionStrictlyEdgeKeywords(cssGradient->mBgPos)) {
10227 0 : SkipUntil(')');
10228 0 : return false;
10229 : }
10230 :
10231 0 : if (!ExpectSymbol(',', true) &&
10232 : // If we didn't already get an angle, and we're not -webkit prefixed,
10233 : // we can parse an angle+comma now. Otherwise it's an error.
10234 0 : (haveAngle ||
10235 0 : (aFlags & eGradient_WebkitLegacy) ||
10236 0 : !ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE,
10237 0 : nullptr) ||
10238 : // now we better have a comma
10239 0 : !ExpectSymbol(',', true))) {
10240 0 : SkipUntil(')');
10241 0 : return false;
10242 : }
10243 : }
10244 : }
10245 :
10246 0 : return ParseGradientColorStops(cssGradient, aValue);
10247 : }
10248 :
10249 : bool
10250 2 : CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue,
10251 : uint8_t aFlags)
10252 : {
10253 : RefPtr<nsCSSValueGradient> cssGradient
10254 4 : = new nsCSSValueGradient(true, aFlags & eGradient_Repeating);
10255 :
10256 : // [ <shape> || <size> ]
10257 : bool haveShape =
10258 2 : ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
10259 2 : nsCSSProps::kRadialGradientShapeKTable);
10260 :
10261 : bool haveSize =
10262 2 : ParseSingleTokenVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
10263 2 : (aFlags & eGradient_AnyLegacy) ?
10264 : nsCSSProps::kRadialGradientLegacySizeKTable :
10265 2 : nsCSSProps::kRadialGradientSizeKTable);
10266 2 : if (haveSize) {
10267 0 : if (!haveShape) {
10268 : // <size> <shape>
10269 : haveShape =
10270 0 : ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
10271 0 : nsCSSProps::kRadialGradientShapeKTable);
10272 : }
10273 2 : } else if (!(aFlags & eGradient_AnyLegacy)) {
10274 : // Save RadialShape before parsing RadiusX because RadialShape and
10275 : // RadiusX share the storage.
10276 : int32_t shape =
10277 2 : cssGradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated ?
10278 2 : cssGradient->GetRadialShape().GetIntValue() : -1;
10279 : // <length> | [<length> | <percentage>]{2}
10280 2 : cssGradient->mIsExplicitSize = true;
10281 : haveSize =
10282 2 : ParseSingleTokenNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP,
10283 2 : nullptr);
10284 2 : if (!haveSize) {
10285 : // It was not an explicit size after all.
10286 : // Note that ParseNonNegativeVariant may have put something
10287 : // invalid into our storage, but only in the case where it was
10288 : // rejected only for being negative. Since this means the token
10289 : // was a length or a percentage, we know it's not valid syntax
10290 : // (which must be a comma, the 'at' keyword, or a color), so we
10291 : // know this value will be dropped. This means it doesn't matter
10292 : // that we have something invalid in our storage.
10293 0 : cssGradient->mIsExplicitSize = false;
10294 : } else {
10295 : // vertical extent is optional
10296 : bool haveYSize =
10297 2 : ParseSingleTokenNonNegativeVariant(cssGradient->GetRadiusY(),
10298 2 : VARIANT_LP, nullptr);
10299 2 : if (!haveShape) {
10300 4 : nsCSSValue shapeValue;
10301 : haveShape =
10302 : ParseSingleTokenVariant(shapeValue, VARIANT_KEYWORD,
10303 2 : nsCSSProps::kRadialGradientShapeKTable);
10304 2 : if (haveShape) {
10305 0 : shape = shapeValue.GetIntValue();
10306 : }
10307 : }
10308 2 : if (haveYSize
10309 2 : ? shape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR
10310 0 : : cssGradient->GetRadiusX().GetUnit() == eCSSUnit_Percent ||
10311 : shape == NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL) {
10312 0 : SkipUntil(')');
10313 0 : return false;
10314 : }
10315 : }
10316 : }
10317 :
10318 2 : if ((haveShape || haveSize) && ExpectSymbol(',', true)) {
10319 : // [ <shape> || <size> ] ,
10320 2 : return ParseGradientColorStops(cssGradient, aValue);
10321 : }
10322 :
10323 0 : if (!GetToken(true)) {
10324 0 : return false;
10325 : }
10326 :
10327 0 : if (!(aFlags & eGradient_AnyLegacy)) {
10328 0 : if (mToken.mType == eCSSToken_Ident &&
10329 0 : mToken.mIdent.LowerCaseEqualsLiteral("at")) {
10330 : // [ <shape> || <size> ]? at <position> ,
10331 0 : if (!ParseBoxPositionValues(cssGradient->mBgPos, false) ||
10332 0 : !ExpectSymbol(',', true)) {
10333 0 : SkipUntil(')');
10334 0 : return false;
10335 : }
10336 :
10337 0 : return ParseGradientColorStops(cssGradient, aValue);
10338 : }
10339 :
10340 : // <color-stops> only
10341 0 : UngetToken();
10342 0 : return ParseGradientColorStops(cssGradient, aValue);
10343 : }
10344 0 : MOZ_ASSERT(!cssGradient->mIsExplicitSize);
10345 :
10346 0 : nsCSSTokenType ty = mToken.mType;
10347 0 : nsString id = mToken.mIdent;
10348 0 : UngetToken();
10349 :
10350 : // <legacy-gradient-line>
10351 0 : bool haveGradientLine = false;
10352 : // if we already encountered a shape or size,
10353 : // we can not have a gradient-line in legacy syntax
10354 0 : if (!haveShape && !haveSize) {
10355 0 : haveGradientLine = IsLegacyGradientLine(ty, id);
10356 : }
10357 0 : if (haveGradientLine) {
10358 : // Note: -webkit-radial-gradient() doesn't accept angles.
10359 0 : bool haveAngle = (aFlags & eGradient_WebkitLegacy)
10360 0 : ? false
10361 0 : : ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
10362 :
10363 : // If we got an angle, we might now have a comma, ending the gradient-line
10364 0 : if (!haveAngle || !ExpectSymbol(',', true)) {
10365 0 : if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
10366 0 : SkipUntil(')');
10367 0 : return false;
10368 : }
10369 :
10370 0 : if (!ExpectSymbol(',', true) &&
10371 : // If we didn't already get an angle, and we're not -webkit prefixed,
10372 : // can parse an angle+comma now. Otherwise it's an error.
10373 0 : (haveAngle ||
10374 0 : (aFlags & eGradient_WebkitLegacy) ||
10375 0 : !ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE,
10376 0 : nullptr) ||
10377 : // now we better have a comma
10378 0 : !ExpectSymbol(',', true))) {
10379 0 : SkipUntil(')');
10380 0 : return false;
10381 : }
10382 : }
10383 :
10384 0 : if (cssGradient->mAngle.GetUnit() != eCSSUnit_None) {
10385 0 : cssGradient->mIsLegacySyntax = true;
10386 0 : cssGradient->mIsMozLegacySyntax = (aFlags & eGradient_MozLegacy);
10387 : }
10388 : }
10389 :
10390 : // radial gradients might have a shape and size here for legacy syntax
10391 0 : if (!haveShape && !haveSize) {
10392 : haveShape =
10393 0 : ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
10394 0 : nsCSSProps::kRadialGradientShapeKTable);
10395 : haveSize =
10396 0 : ParseSingleTokenVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
10397 0 : nsCSSProps::kRadialGradientLegacySizeKTable);
10398 :
10399 : // could be in either order
10400 0 : if (!haveShape) {
10401 : haveShape =
10402 0 : ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
10403 0 : nsCSSProps::kRadialGradientShapeKTable);
10404 : }
10405 : }
10406 :
10407 0 : if ((haveShape || haveSize) && !ExpectSymbol(',', true)) {
10408 0 : SkipUntil(')');
10409 0 : return false;
10410 : }
10411 :
10412 0 : return ParseGradientColorStops(cssGradient, aValue);
10413 : }
10414 :
10415 : bool
10416 0 : CSSParserImpl::IsLegacyGradientLine(const nsCSSTokenType& aType,
10417 : const nsString& aId)
10418 : {
10419 : // N.B. ParseBoxPositionValues is not guaranteed to put back
10420 : // everything it scanned if it fails, so we must only call it
10421 : // if there is no alternative to consuming a <box-position>.
10422 : // ParseVariant, as used here, will either succeed and consume
10423 : // a single token, or fail and consume none, so we can be more
10424 : // cavalier about calling it.
10425 :
10426 0 : bool haveGradientLine = false;
10427 0 : switch (aType) {
10428 : case eCSSToken_Percentage:
10429 : case eCSSToken_Number:
10430 : case eCSSToken_Dimension:
10431 0 : haveGradientLine = true;
10432 0 : break;
10433 :
10434 : case eCSSToken_Function:
10435 0 : if (aId.LowerCaseEqualsLiteral("calc")) {
10436 0 : haveGradientLine = true;
10437 0 : break;
10438 : }
10439 : MOZ_FALLTHROUGH;
10440 : case eCSSToken_ID:
10441 : case eCSSToken_Hash:
10442 : // this is a color
10443 0 : break;
10444 :
10445 : case eCSSToken_Ident: {
10446 : // This is only a gradient line if it's a box position keyword.
10447 0 : nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(aId);
10448 : int32_t junk;
10449 0 : if (nsCSSProps::FindKeyword(kw, nsCSSProps::kImageLayerPositionKTable,
10450 : junk)) {
10451 0 : haveGradientLine = true;
10452 : }
10453 0 : break;
10454 : }
10455 :
10456 : default:
10457 : // error
10458 0 : break;
10459 : }
10460 :
10461 0 : return haveGradientLine;
10462 : }
10463 :
10464 : bool
10465 51 : CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient,
10466 : nsCSSValue& aValue)
10467 : {
10468 : // At least two color stops are required
10469 151 : if (!ParseColorStop(aGradient) ||
10470 100 : !ExpectSymbol(',', true) ||
10471 49 : !ParseColorStop(aGradient)) {
10472 6 : SkipUntil(')');
10473 6 : return false;
10474 : }
10475 :
10476 : // Additional color stops
10477 127 : while (ExpectSymbol(',', true)) {
10478 41 : if (!ParseColorStop(aGradient)) {
10479 0 : SkipUntil(')');
10480 0 : return false;
10481 : }
10482 : }
10483 :
10484 45 : if (!ExpectSymbol(')', true)) {
10485 0 : SkipUntil(')');
10486 0 : return false;
10487 : }
10488 :
10489 : // Check if interpolation hints are in the correct location
10490 45 : bool previousPointWasInterpolationHint = true;
10491 176 : for (size_t x = 0; x < aGradient->mStops.Length(); x++) {
10492 131 : bool isInterpolationHint = aGradient->mStops[x].mIsInterpolationHint;
10493 131 : if (isInterpolationHint && previousPointWasInterpolationHint) {
10494 0 : return false;
10495 : }
10496 131 : previousPointWasInterpolationHint = isInterpolationHint;
10497 : }
10498 :
10499 45 : if (previousPointWasInterpolationHint) {
10500 0 : return false;
10501 : }
10502 :
10503 45 : aValue.SetGradientValue(aGradient);
10504 45 : return true;
10505 : }
10506 :
10507 : // Parses the x or y component of a -webkit-gradient() <point> expression.
10508 : // See ParseWebkitGradientPoint() documentation for more.
10509 : bool
10510 0 : CSSParserImpl::ParseWebkitGradientPointComponent(nsCSSValue& aComponent,
10511 : bool aIsHorizontal)
10512 : {
10513 0 : if (!GetToken(true)) {
10514 0 : return false;
10515 : }
10516 :
10517 : // Keyword tables to use for keyword-matching
10518 : // (Keyword order is important; we assume the index can be multiplied by 50%
10519 : // to convert to a percent-valued component.)
10520 : static const nsCSSKeyword kHorizKeywords[] = {
10521 : eCSSKeyword_left, // 0%
10522 : eCSSKeyword_center, // 50%
10523 : eCSSKeyword_right // 100%
10524 : };
10525 : static const nsCSSKeyword kVertKeywords[] = {
10526 : eCSSKeyword_top, // 0%
10527 : eCSSKeyword_center, // 50%
10528 : eCSSKeyword_bottom // 100%
10529 : };
10530 : static const size_t kNumKeywords = MOZ_ARRAY_LENGTH(kHorizKeywords);
10531 : static_assert(kNumKeywords == MOZ_ARRAY_LENGTH(kVertKeywords),
10532 : "Horizontal & vertical keyword tables must have same count");
10533 :
10534 : // Try to parse the component as a number, or a percent, or a
10535 : // keyword-converted-to-percent.
10536 0 : if (mToken.mType == eCSSToken_Number) {
10537 0 : aComponent.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
10538 0 : } else if (mToken.mType == eCSSToken_Percentage) {
10539 0 : aComponent.SetPercentValue(mToken.mNumber);
10540 0 : } else if (mToken.mType == eCSSToken_Ident) {
10541 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
10542 0 : if (keyword == eCSSKeyword_UNKNOWN) {
10543 0 : return false;
10544 : }
10545 : // Choose our keyword table:
10546 0 : const nsCSSKeyword* kwTable = aIsHorizontal ? kHorizKeywords : kVertKeywords;
10547 : // Convert keyword to percent value (0%, 50%, or 100%)
10548 0 : bool didAcceptKeyword = false;
10549 0 : for (size_t i = 0; i < kNumKeywords; i++) {
10550 0 : if (keyword == kwTable[i]) {
10551 : // 0%, 50%, or 100%:
10552 0 : aComponent.SetPercentValue(i * 0.5);
10553 0 : didAcceptKeyword = true;
10554 0 : break;
10555 : }
10556 : }
10557 0 : if (!didAcceptKeyword) {
10558 0 : return false;
10559 : }
10560 : } else {
10561 : // Unrecognized token type. Put it back. (It might be a closing-paren of an
10562 : // invalid -webkit-gradient(...) expression, and we need to be sure caller
10563 : // can see it & stops parsing at that point.)
10564 0 : UngetToken();
10565 0 : return false;
10566 : }
10567 :
10568 0 : MOZ_ASSERT(aComponent.GetUnit() == eCSSUnit_Pixel ||
10569 : aComponent.GetUnit() == eCSSUnit_Percent,
10570 : "If we get here, we should've successfully parsed a number (as a "
10571 : "pixel length), a percent, or a keyword (converted to percent)");
10572 0 : return true;
10573 : }
10574 :
10575 : // This function parses a "<point>" expression for -webkit-gradient(...)
10576 : // Quoting https://www.webkit.org/blog/175/introducing-css-gradients/ :
10577 : // "A point is a pair of space-separated values.
10578 : // The syntax supports numbers, percentages or
10579 : // the keywords top, bottom, left and right
10580 : // for point values."
10581 : //
10582 : // Two additional notes:
10583 : // - WebKit also accepts the "center" keyword (not listed in the text above).
10584 : // - WebKit only accepts horizontal-flavored keywords (left/center/right) in
10585 : // the first ("x") component, and vertical-flavored keywords
10586 : // (top/center/bottom) in the second ("y") component. (This is different
10587 : // from the standard gradient syntax, which accepts both orderings, e.g.
10588 : // "top left" as well as "left top".)
10589 : bool
10590 0 : CSSParserImpl::ParseWebkitGradientPoint(nsCSSValuePair& aPoint)
10591 : {
10592 0 : return ParseWebkitGradientPointComponent(aPoint.mXValue, true) &&
10593 0 : ParseWebkitGradientPointComponent(aPoint.mYValue, false);
10594 : }
10595 :
10596 : // Parse the next token as a <number> (for a <radius> in a -webkit-gradient
10597 : // expresison). Returns true on success; returns false & puts back
10598 : // whatever it parsed on failure.
10599 : bool
10600 0 : CSSParserImpl::ParseWebkitGradientRadius(float& aRadius)
10601 : {
10602 0 : if (!GetToken(true)) {
10603 0 : return false;
10604 : }
10605 :
10606 0 : if (mToken.mType != eCSSToken_Number) {
10607 0 : UngetToken();
10608 0 : return false;
10609 : }
10610 :
10611 0 : aRadius = mToken.mNumber;
10612 0 : return true;
10613 : }
10614 :
10615 : // Parse one of:
10616 : // color-stop(number|percent, color)
10617 : // from(color)
10618 : // to(color)
10619 : //
10620 : // Quoting https://www.webkit.org/blog/175/introducing-css-gradients/ :
10621 : // A stop is a function, color-stop, that takes two arguments, the stop value
10622 : // (either a percentage or a number between 0 and 1.0), and a color (any
10623 : // valid CSS color). In addition the shorthand functions from and to are
10624 : // supported. These functions only require a color argument and are
10625 : // equivalent to color-stop(0, ...) and color-stop(1.0, …) respectively.
10626 : bool
10627 0 : CSSParserImpl::ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient)
10628 : {
10629 0 : MOZ_ASSERT(aGradient, "null gradient");
10630 :
10631 0 : if (!GetToken(true)) {
10632 0 : return false;
10633 : }
10634 :
10635 : // We're expecting color-stop(...), from(...), or to(...) which are all
10636 : // functions. Bail if we got anything else.
10637 0 : if (mToken.mType != eCSSToken_Function) {
10638 0 : UngetToken();
10639 0 : return false;
10640 : }
10641 :
10642 0 : nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
10643 :
10644 : // Parse color-stop location (or infer it, for shorthands "from"/"to"):
10645 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("color-stop")) {
10646 : // Parse stop location, followed by comma.
10647 0 : if (!ParseSingleTokenVariant(stop->mLocation,
10648 : VARIANT_NUMBER | VARIANT_PERCENT,
10649 0 : nullptr) ||
10650 0 : !ExpectSymbol(',', true)) {
10651 0 : SkipUntil(')'); // Skip to end of color-stop(...) expression.
10652 0 : return false;
10653 : }
10654 :
10655 : // If we got a <number>, convert it to percentage for consistency:
10656 0 : if (stop->mLocation.GetUnit() == eCSSUnit_Number) {
10657 0 : stop->mLocation.SetPercentValue(stop->mLocation.GetFloatValue());
10658 : }
10659 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
10660 : // Shorthand for color-stop(0%, ...)
10661 0 : stop->mLocation.SetPercentValue(0.0f);
10662 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
10663 : // Shorthand for color-stop(100%, ...)
10664 0 : stop->mLocation.SetPercentValue(1.0f);
10665 : } else {
10666 : // Unrecognized function name (invalid for a -webkit-gradient color stop).
10667 0 : UngetToken();
10668 0 : return false;
10669 : }
10670 :
10671 0 : CSSParseResult result = ParseVariant(stop->mColor, VARIANT_COLOR, nullptr);
10672 0 : if (result != CSSParseResult::Ok ||
10673 0 : (stop->mColor.GetUnit() == eCSSUnit_EnumColor &&
10674 0 : stop->mColor.GetIntValue() == NS_COLOR_CURRENTCOLOR)) {
10675 : // Parse failure, or parsed "currentColor" which is forbidden in
10676 : // -webkit-gradient for some reason.
10677 0 : SkipUntil(')');
10678 0 : return false;
10679 : }
10680 :
10681 : // Parse color-stop function close-paren
10682 0 : if (!ExpectSymbol(')', true)) {
10683 0 : SkipUntil(')');
10684 0 : return false;
10685 : }
10686 :
10687 0 : MOZ_ASSERT(stop->mLocation.GetUnit() == eCSSUnit_Percent,
10688 : "Should produce only percent-valued stop-locations. "
10689 : "(Caller depends on this when sorting color stops.)");
10690 :
10691 0 : return true;
10692 : }
10693 :
10694 : // Comparatison function to use for sorting -webkit-gradient() stops by
10695 : // location. This function assumes stops have percent-valued locations (and
10696 : // CSSParserImpl::ParseWebkitGradientColorStop should enforce this).
10697 : static bool
10698 0 : IsColorStopPctLocationLessThan(const nsCSSValueGradientStop& aStop1,
10699 : const nsCSSValueGradientStop& aStop2) {
10700 0 : return (aStop1.mLocation.GetPercentValue() <
10701 0 : aStop2.mLocation.GetPercentValue());
10702 : }
10703 :
10704 : // This function parses a list of comma-separated color-stops for a
10705 : // -webkit-gradient(...) expression, and then pads & sorts the list as-needed.
10706 : bool
10707 0 : CSSParserImpl::ParseWebkitGradientColorStops(nsCSSValueGradient* aGradient)
10708 : {
10709 0 : MOZ_ASSERT(aGradient, "null gradient");
10710 :
10711 : // Parse any number of ", <color-stop>" expressions. (0 or more)
10712 : // Note: This is different from unprefixed gradient syntax, which
10713 : // requires at least 2 stops.
10714 0 : while (ExpectSymbol(',', true)) {
10715 0 : if (!ParseWebkitGradientColorStop(aGradient)) {
10716 0 : return false;
10717 : }
10718 : }
10719 :
10720 : // Pad up to 2 stops as-needed:
10721 : // (Modern gradient expressions are required to have at least 2 stops, so we
10722 : // depend on this internally -- e.g. we have an assertion about this in
10723 : // nsCSSRendering.cpp. -webkit-gradient syntax allows 0 stops or 1 stop,
10724 : // though, so we just pad up to 2 stops in this case).
10725 :
10726 : // If we have no stops, pad with transparent-black:
10727 0 : if (aGradient->mStops.IsEmpty()) {
10728 0 : nsCSSValueGradientStop* stop1 = aGradient->mStops.AppendElement();
10729 0 : stop1->mColor.SetIntegerColorValue(NS_RGBA(0, 0, 0, 0),
10730 0 : eCSSUnit_RGBAColor);
10731 0 : stop1->mLocation.SetPercentValue(0.0f);
10732 :
10733 0 : nsCSSValueGradientStop* stop2 = aGradient->mStops.AppendElement();
10734 0 : stop2->mColor.SetIntegerColorValue(NS_RGBA(0, 0, 0, 0),
10735 0 : eCSSUnit_RGBAColor);
10736 0 : stop2->mLocation.SetPercentValue(1.0f);
10737 0 : } else if (aGradient->mStops.Length() == 1) {
10738 : // Copy whatever the author provided in the first stop:
10739 0 : nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
10740 0 : *stop = aGradient->mStops[0];
10741 : } else {
10742 : // We have >2 stops. Sort them in order of increasing location.
10743 0 : std::stable_sort(aGradient->mStops.begin(),
10744 0 : aGradient->mStops.end(),
10745 0 : IsColorStopPctLocationLessThan);
10746 : }
10747 0 : return true;
10748 : }
10749 :
10750 : // Compares aStartCoord to aEndCoord, and returns true iff they share the same
10751 : // unit (both pixel, or both percent) and aStartCoord is larger.
10752 : static bool
10753 0 : IsWebkitGradientCoordLarger(const nsCSSValue& aStartCoord,
10754 : const nsCSSValue& aEndCoord)
10755 : {
10756 0 : if (aStartCoord.GetUnit() == eCSSUnit_Percent &&
10757 0 : aEndCoord.GetUnit() == eCSSUnit_Percent) {
10758 0 : return aStartCoord.GetPercentValue() > aEndCoord.GetPercentValue();
10759 : }
10760 :
10761 0 : if (aStartCoord.GetUnit() == eCSSUnit_Pixel &&
10762 0 : aEndCoord.GetUnit() == eCSSUnit_Pixel) {
10763 0 : return aStartCoord.GetFloatValue() > aEndCoord.GetFloatValue();
10764 : }
10765 :
10766 : // We can't compare them, since their units differ. Returning false suggests
10767 : // that aEndCoord is larger, which is probably a decent guess anyway.
10768 0 : return false;
10769 : }
10770 :
10771 : // Finalize our internal representation of a -webkit-gradient(linear, ...)
10772 : // expression, given the parsed points. (The parsed color stops
10773 : // should already be hanging off of the passed-in nsCSSValueGradient.)
10774 : //
10775 : // Note: linear gradients progress along a line between two points. The
10776 : // -webkit-gradient(linear, ...) syntax lets the author precisely specify the
10777 : // starting and ending point. However, our internal gradient structures only
10778 : // store ONE point (which for modern linear-gradient() expressions is a side or
10779 : // corner & represents the ending point of the gradient -- and the starting
10780 : // point is implicitly the opposite side/corner).
10781 : //
10782 : // In this function, we analyze the start & end points from a
10783 : // -webkit-gradient(linear, ...) expression, and we choose an appropriate
10784 : // target point to produce a modern linear-gradient() representation that has
10785 : // the same rough trajectory. If we can't determine a target point for some
10786 : // reason, we just fall back to the default top-to-bottom linear-gradient()
10787 : // directionality.
10788 : void
10789 0 : CSSParserImpl::FinalizeLinearWebkitGradient(nsCSSValueGradient* aGradient,
10790 : const nsCSSValuePair& aStartPoint,
10791 : const nsCSSValuePair& aEndPoint)
10792 : {
10793 0 : MOZ_ASSERT(!aGradient->mIsRadial, "passed-in gradient must be linear");
10794 :
10795 0 : if (aStartPoint == aEndPoint ||
10796 0 : aStartPoint.mXValue.GetUnit() != aEndPoint.mXValue.GetUnit() ||
10797 0 : aStartPoint.mYValue.GetUnit() != aEndPoint.mYValue.GetUnit()) {
10798 : // Start point & end point are the same, OR they use different units for at
10799 : // least one coordinate. Either way, this isn't something we can represent
10800 : // using an unprefixed linear-gradient expression, so instead we'll just
10801 : // arbitrarily pretend this is a top-to-bottom gradient (which is the
10802 : // default for modern linear-gradient if a direction is unspecified).
10803 0 : aGradient->mBgPos.mYValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_BOTTOM,
10804 0 : eCSSUnit_Enumerated);
10805 0 : aGradient->mBgPos.mXValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_CENTER,
10806 0 : eCSSUnit_Enumerated);
10807 0 : return;
10808 : }
10809 :
10810 : // Set the target point to one of the box's corners (or the center of an
10811 : // edge), by comparing aStartPoint and aEndPoint to extract a general
10812 : // direction.
10813 :
10814 : int32_t targetX;
10815 0 : if (aStartPoint.mXValue == aEndPoint.mXValue) {
10816 0 : targetX = NS_STYLE_IMAGELAYER_POSITION_CENTER;
10817 0 : } else if (IsWebkitGradientCoordLarger(aStartPoint.mXValue,
10818 : aEndPoint.mXValue)) {
10819 0 : targetX = NS_STYLE_IMAGELAYER_POSITION_LEFT;
10820 : } else {
10821 0 : MOZ_ASSERT(IsWebkitGradientCoordLarger(aEndPoint.mXValue,
10822 : aStartPoint.mXValue),
10823 : "IsWebkitGradientCoordLarger returning inconsistent results?");
10824 0 : targetX = NS_STYLE_IMAGELAYER_POSITION_RIGHT;
10825 : }
10826 :
10827 : int32_t targetY;
10828 0 : if (aStartPoint.mYValue == aEndPoint.mYValue) {
10829 0 : targetY = NS_STYLE_IMAGELAYER_POSITION_CENTER;
10830 0 : } else if (IsWebkitGradientCoordLarger(aStartPoint.mYValue,
10831 : aEndPoint.mYValue)) {
10832 0 : targetY = NS_STYLE_IMAGELAYER_POSITION_TOP;
10833 : } else {
10834 0 : MOZ_ASSERT(IsWebkitGradientCoordLarger(aEndPoint.mYValue,
10835 : aStartPoint.mYValue),
10836 : "IsWebkitGradientCoordLarger returning inconsistent results?");
10837 0 : targetY = NS_STYLE_IMAGELAYER_POSITION_BOTTOM;
10838 : }
10839 :
10840 0 : aGradient->mBgPos.mXValue.SetIntValue(targetX, eCSSUnit_Enumerated);
10841 0 : aGradient->mBgPos.mYValue.SetIntValue(targetY, eCSSUnit_Enumerated);
10842 : }
10843 :
10844 : // Finalize our internal representation of a -webkit-gradient(radial, ...)
10845 : // expression, given the parsed points & radii. (The parsed color-stops
10846 : // should already be hanging off of the passed-in nsCSSValueGradient).
10847 : void
10848 0 : CSSParserImpl::FinalizeRadialWebkitGradient(nsCSSValueGradient* aGradient,
10849 : const nsCSSValuePair& aFirstCenter,
10850 : const nsCSSValuePair& aSecondCenter,
10851 : const float aFirstRadius,
10852 : const float aSecondRadius)
10853 : {
10854 0 : MOZ_ASSERT(aGradient->mIsRadial, "passed-in gradient must be radial");
10855 :
10856 : // NOTE: -webkit-gradient(radial, ...) has *two arbitrary circles*, with the
10857 : // gradient stretching between the circles' edges. In contrast, the standard
10858 : // syntax (and hence our data structures) can only represent *one* circle,
10859 : // with the gradient going from its center to its edge. To bridge this gap
10860 : // in expressiveness, we'll just see which of our two circles is smaller, and
10861 : // we'll treat that circle as if it were zero-sized and located at the center
10862 : // of the larger circle. Then, we'll be able to use the same data structures
10863 : // that we use for the standard radial-gradient syntax.
10864 0 : if (aSecondRadius >= aFirstRadius) {
10865 : // Second circle is larger.
10866 0 : aGradient->mBgPos = aSecondCenter;
10867 0 : aGradient->mIsExplicitSize = true;
10868 0 : aGradient->GetRadiusX().SetFloatValue(aSecondRadius, eCSSUnit_Pixel);
10869 0 : return;
10870 : }
10871 :
10872 : // First circle is larger, so we'll have it be the outer circle.
10873 0 : aGradient->mBgPos = aFirstCenter;
10874 0 : aGradient->mIsExplicitSize = true;
10875 0 : aGradient->GetRadiusX().SetFloatValue(aFirstRadius, eCSSUnit_Pixel);
10876 :
10877 : // For this to work properly (with the earlier color stops attached to the
10878 : // first circle), we need to also reverse the color-stop list, so that
10879 : // e.g. the author's "from" color is attached to the outer edge (the first
10880 : // circle), rather than attached to the center (the collapsed second circle).
10881 0 : std::reverse(aGradient->mStops.begin(), aGradient->mStops.end());
10882 :
10883 : // And now invert the stop locations:
10884 0 : for (nsCSSValueGradientStop& colorStop : aGradient->mStops) {
10885 0 : float origLocation = colorStop.mLocation.GetPercentValue();
10886 0 : colorStop.mLocation.SetPercentValue(1.0f - origLocation);
10887 : }
10888 : }
10889 :
10890 : bool
10891 0 : CSSParserImpl::ParseWebkitGradient(nsCSSValue& aValue)
10892 : {
10893 : // Parse type of gradient
10894 0 : if (!GetToken(true)) {
10895 0 : return false;
10896 : }
10897 :
10898 0 : if (mToken.mType != eCSSToken_Ident) {
10899 0 : UngetToken(); // Important; the token might be ")", which we're about to
10900 : // seek to.
10901 0 : SkipUntil(')');
10902 0 : return false;
10903 : }
10904 :
10905 : bool isRadial;
10906 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("radial")) {
10907 0 : isRadial = true;
10908 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("linear")) {
10909 0 : isRadial = false;
10910 : } else {
10911 : // Unrecognized gradient type.
10912 0 : SkipUntil(')');
10913 0 : return false;
10914 : }
10915 :
10916 : // Parse a comma + first point:
10917 0 : nsCSSValuePair firstPoint;
10918 0 : if (!ExpectSymbol(',', true) ||
10919 0 : !ParseWebkitGradientPoint(firstPoint)) {
10920 0 : SkipUntil(')');
10921 0 : return false;
10922 : }
10923 :
10924 : // If radial, parse comma + first radius:
10925 : float firstRadius;
10926 0 : if (isRadial) {
10927 0 : if (!ExpectSymbol(',', true) ||
10928 0 : !ParseWebkitGradientRadius(firstRadius)) {
10929 0 : SkipUntil(')');
10930 0 : return false;
10931 : }
10932 : }
10933 :
10934 : // Parse a comma + second point:
10935 0 : nsCSSValuePair secondPoint;
10936 0 : if (!ExpectSymbol(',', true) ||
10937 0 : !ParseWebkitGradientPoint(secondPoint)) {
10938 0 : SkipUntil(')');
10939 0 : return false;
10940 : }
10941 :
10942 : // If radial, parse comma + second radius:
10943 : float secondRadius;
10944 0 : if (isRadial) {
10945 0 : if (!ExpectSymbol(',', true) ||
10946 0 : !ParseWebkitGradientRadius(secondRadius)) {
10947 0 : SkipUntil(')');
10948 0 : return false;
10949 : }
10950 : }
10951 :
10952 : // Construct a nsCSSValueGradient object, and parse color stops into it:
10953 : RefPtr<nsCSSValueGradient> cssGradient =
10954 0 : new nsCSSValueGradient(isRadial, false /* aIsRepeating */);
10955 :
10956 0 : if (!ParseWebkitGradientColorStops(cssGradient) ||
10957 0 : !ExpectSymbol(')', true)) {
10958 : // Failed to parse color-stops, or found trailing junk between them & ')'.
10959 0 : SkipUntil(')');
10960 0 : return false;
10961 : }
10962 :
10963 : // Finish building cssGradient, based on our parsed positioning/sizing info:
10964 0 : if (isRadial) {
10965 0 : FinalizeRadialWebkitGradient(cssGradient, firstPoint, secondPoint,
10966 0 : firstRadius, secondRadius);
10967 : } else {
10968 0 : FinalizeLinearWebkitGradient(cssGradient, firstPoint, secondPoint);
10969 : }
10970 :
10971 0 : aValue.SetGradientValue(cssGradient);
10972 0 : return true;
10973 : }
10974 :
10975 : bool
10976 0 : CSSParserImpl::ParseWebkitTextStroke()
10977 : {
10978 : static const nsCSSPropertyID kWebkitTextStrokeIDs[] = {
10979 : eCSSProperty__webkit_text_stroke_width,
10980 : eCSSProperty__webkit_text_stroke_color
10981 : };
10982 :
10983 0 : const size_t numProps = MOZ_ARRAY_LENGTH(kWebkitTextStrokeIDs);
10984 0 : nsCSSValue values[numProps];
10985 :
10986 0 : int32_t found = ParseChoice(values, kWebkitTextStrokeIDs, numProps);
10987 0 : if (found < 1) {
10988 0 : return false;
10989 : }
10990 :
10991 0 : if (!(found & 1)) { // Provide default -webkit-text-stroke-width
10992 0 : values[0].SetFloatValue(0, eCSSUnit_Pixel);
10993 : }
10994 :
10995 0 : if (!(found & 2)) { // Provide default -webkit-text-stroke-color
10996 0 : values[1].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
10997 : }
10998 :
10999 0 : for (size_t index = 0; index < numProps; ++index) {
11000 0 : AppendValue(kWebkitTextStrokeIDs[index], values[index]);
11001 : }
11002 :
11003 0 : return true;
11004 : }
11005 :
11006 : int32_t
11007 412 : CSSParserImpl::ParseChoice(nsCSSValue aValues[],
11008 : const nsCSSPropertyID aPropIDs[], int32_t aNumIDs)
11009 : {
11010 412 : int32_t found = 0;
11011 824 : nsAutoParseCompoundProperty compound(this);
11012 :
11013 : int32_t loop;
11014 1342 : for (loop = 0; loop < aNumIDs; loop++) {
11015 : // Try each property parser in order
11016 1121 : int32_t hadFound = found;
11017 : int32_t index;
11018 2544 : for (index = 0; index < aNumIDs; index++) {
11019 2353 : int32_t bit = 1 << index;
11020 2353 : if ((found & bit) == 0) {
11021 : CSSParseResult result =
11022 1375 : ParseSingleValueProperty(aValues[index], aPropIDs[index]);
11023 1375 : if (result == CSSParseResult::Error) {
11024 0 : return -1;
11025 : }
11026 1375 : if (result == CSSParseResult::Ok) {
11027 930 : found |= bit;
11028 : // It's more efficient to break since it will reset |hadFound|
11029 : // to |found|. Furthermore, ParseListStyle depends on our going
11030 : // through the properties in order for each value..
11031 930 : break;
11032 : }
11033 : }
11034 : }
11035 1121 : if (found == hadFound) { // found nothing new
11036 191 : break;
11037 : }
11038 : }
11039 412 : if (0 < found) {
11040 405 : if (1 == found) { // only first property
11041 43 : if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
11042 36 : for (loop = 1; loop < aNumIDs; loop++) {
11043 24 : aValues[loop].SetInheritValue();
11044 : }
11045 12 : found = ((1 << aNumIDs) - 1);
11046 : }
11047 31 : else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
11048 0 : for (loop = 1; loop < aNumIDs; loop++) {
11049 0 : aValues[loop].SetInitialValue();
11050 : }
11051 0 : found = ((1 << aNumIDs) - 1);
11052 : }
11053 31 : else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset
11054 6 : for (loop = 1; loop < aNumIDs; loop++) {
11055 4 : aValues[loop].SetUnsetValue();
11056 : }
11057 2 : found = ((1 << aNumIDs) - 1);
11058 : }
11059 : }
11060 : else { // more than one value, verify no inherits, initials or unsets
11061 1450 : for (loop = 0; loop < aNumIDs; loop++) {
11062 1088 : if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
11063 0 : found = -1;
11064 0 : break;
11065 : }
11066 1088 : else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
11067 0 : found = -1;
11068 0 : break;
11069 : }
11070 1088 : else if (eCSSUnit_Unset == aValues[loop].GetUnit()) {
11071 0 : found = -1;
11072 0 : break;
11073 : }
11074 : }
11075 : }
11076 : }
11077 412 : return found;
11078 : }
11079 :
11080 : void
11081 17610 : CSSParserImpl::AppendValue(nsCSSPropertyID aPropID, const nsCSSValue& aValue)
11082 : {
11083 17610 : mTempData.AddLonghandProperty(aPropID, aValue);
11084 17610 : }
11085 :
11086 : /**
11087 : * Parse a "box" property. Box properties have 1 to 4 values. When less
11088 : * than 4 values are provided a standard mapping is used to replicate
11089 : * existing values.
11090 : */
11091 : bool
11092 702 : CSSParserImpl::ParseBoxProperties(const nsCSSPropertyID aPropIDs[])
11093 : {
11094 : // Get up to four values for the property
11095 702 : int32_t count = 0;
11096 1404 : nsCSSRect result;
11097 1738 : NS_FOR_CSS_SIDES (index) {
11098 : CSSParseResult parseResult =
11099 1722 : ParseBoxProperty(result.*(nsCSSRect::sides[index]), aPropIDs[index]);
11100 1722 : if (parseResult == CSSParseResult::NotFound) {
11101 683 : break;
11102 : }
11103 1039 : if (parseResult == CSSParseResult::Error) {
11104 3 : return false;
11105 : }
11106 1036 : count++;
11107 : }
11108 699 : if (count == 0) {
11109 9 : return false;
11110 : }
11111 :
11112 690 : if (1 < count) { // verify no more than single inherit, initial or unset
11113 1070 : NS_FOR_CSS_SIDES (index) {
11114 856 : nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
11115 856 : if (eCSSUnit_Inherit == unit ||
11116 856 : eCSSUnit_Initial == unit ||
11117 : eCSSUnit_Unset == unit) {
11118 0 : return false;
11119 : }
11120 : }
11121 : }
11122 :
11123 : // Provide missing values by replicating some of the values found
11124 690 : switch (count) {
11125 : case 1: // Make right == top
11126 476 : result.mRight = result.mTop;
11127 : MOZ_FALLTHROUGH;
11128 : case 2: // Make bottom == top
11129 577 : result.mBottom = result.mTop;
11130 : MOZ_FALLTHROUGH;
11131 : case 3: // Make left == right
11132 674 : result.mLeft = result.mRight;
11133 : }
11134 :
11135 3450 : NS_FOR_CSS_SIDES (index) {
11136 2760 : AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index]));
11137 : }
11138 690 : return true;
11139 : }
11140 :
11141 : // Similar to ParseBoxProperties, except there is only one property
11142 : // with the result as its value, not four.
11143 : bool
11144 4 : CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask,
11145 : /** outparam */ nsCSSValue& aValue,
11146 : uint32_t aRestrictions)
11147 : {
11148 4 : nsCSSRect& result = aValue.SetRectValue();
11149 :
11150 4 : int32_t count = 0;
11151 8 : NS_FOR_CSS_SIDES (index) {
11152 : CSSParseResult parseResult =
11153 8 : ParseVariantWithRestrictions(result.*(nsCSSRect::sides[index]),
11154 : aVariantMask, nullptr,
11155 8 : aRestrictions);
11156 8 : if (parseResult == CSSParseResult::NotFound) {
11157 4 : break;
11158 : }
11159 4 : if (parseResult == CSSParseResult::Error) {
11160 0 : return false;
11161 : }
11162 4 : count++;
11163 : }
11164 :
11165 4 : if (count == 0) {
11166 0 : return false;
11167 : }
11168 :
11169 : // Provide missing values by replicating some of the values found
11170 4 : switch (count) {
11171 : case 1: // Make right == top
11172 4 : result.mRight = result.mTop;
11173 : MOZ_FALLTHROUGH;
11174 : case 2: // Make bottom == top
11175 4 : result.mBottom = result.mTop;
11176 : MOZ_FALLTHROUGH;
11177 : case 3: // Make left == right
11178 4 : result.mLeft = result.mRight;
11179 : }
11180 :
11181 4 : return true;
11182 : }
11183 :
11184 : bool
11185 22 : CSSParserImpl::ParseBoxCornerRadius(nsCSSPropertyID aPropID)
11186 : {
11187 44 : nsCSSValue dimenX, dimenY;
11188 : // required first value
11189 22 : if (ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr) !=
11190 : CSSParseResult::Ok) {
11191 0 : return false;
11192 : }
11193 :
11194 : // optional second value (forbidden if first value is inherit/initial/unset)
11195 66 : if (dimenX.GetUnit() != eCSSUnit_Inherit &&
11196 44 : dimenX.GetUnit() != eCSSUnit_Initial &&
11197 22 : dimenX.GetUnit() != eCSSUnit_Unset) {
11198 22 : if (ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr) ==
11199 : CSSParseResult::Error) {
11200 0 : return false;
11201 : }
11202 : }
11203 :
11204 22 : if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) {
11205 22 : AppendValue(aPropID, dimenX);
11206 : } else {
11207 0 : nsCSSValue value;
11208 0 : value.SetPairValue(dimenX, dimenY);
11209 0 : AppendValue(aPropID, value);
11210 : }
11211 22 : return true;
11212 : }
11213 :
11214 : bool
11215 355 : CSSParserImpl::ParseBoxCornerRadiiInternals(nsCSSValue array[])
11216 : {
11217 : // Rectangles are used as scratch storage.
11218 : // top => top-left, right => top-right,
11219 : // bottom => bottom-right, left => bottom-left.
11220 710 : nsCSSRect dimenX, dimenY;
11221 355 : int32_t countX = 0, countY = 0;
11222 :
11223 1007 : NS_FOR_CSS_SIDES (side) {
11224 : CSSParseResult result =
11225 907 : ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side],
11226 907 : (side > 0 ? 0 : VARIANT_INHERIT) |
11227 : VARIANT_LP | VARIANT_CALC,
11228 907 : nullptr);
11229 907 : if (result == CSSParseResult::Error) {
11230 0 : return false;
11231 907 : } else if (result == CSSParseResult::NotFound) {
11232 255 : break;
11233 : }
11234 652 : countX++;
11235 : }
11236 355 : if (countX == 0)
11237 3 : return false;
11238 :
11239 352 : if (ExpectSymbol('/', true)) {
11240 0 : NS_FOR_CSS_SIDES (side) {
11241 : CSSParseResult result =
11242 0 : ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side],
11243 0 : VARIANT_LP | VARIANT_CALC, nullptr);
11244 0 : if (result == CSSParseResult::Error) {
11245 0 : return false;
11246 0 : } else if (result == CSSParseResult::NotFound) {
11247 0 : break;
11248 : }
11249 0 : countY++;
11250 : }
11251 0 : if (countY == 0)
11252 0 : return false;
11253 : }
11254 :
11255 : // if 'initial', 'inherit' or 'unset' was used, it must be the only value
11256 352 : if (countX > 1 || countY > 0) {
11257 100 : nsCSSUnit unit = dimenX.mTop.GetUnit();
11258 100 : if (eCSSUnit_Inherit == unit ||
11259 100 : eCSSUnit_Initial == unit ||
11260 : eCSSUnit_Unset == unit)
11261 0 : return false;
11262 : }
11263 :
11264 : // if we have no Y-values, use the X-values
11265 352 : if (countY == 0) {
11266 352 : dimenY = dimenX;
11267 352 : countY = countX;
11268 : }
11269 :
11270 : // Provide missing values by replicating some of the values found
11271 352 : switch (countX) {
11272 : case 1: // Make top-right same as top-left
11273 252 : dimenX.mRight = dimenX.mTop;
11274 : MOZ_FALLTHROUGH;
11275 : case 2: // Make bottom-right same as top-left
11276 252 : dimenX.mBottom = dimenX.mTop;
11277 : MOZ_FALLTHROUGH;
11278 : case 3: // Make bottom-left same as top-right
11279 252 : dimenX.mLeft = dimenX.mRight;
11280 : }
11281 :
11282 352 : switch (countY) {
11283 : case 1: // Make top-right same as top-left
11284 252 : dimenY.mRight = dimenY.mTop;
11285 : MOZ_FALLTHROUGH;
11286 : case 2: // Make bottom-right same as top-left
11287 252 : dimenY.mBottom = dimenY.mTop;
11288 : MOZ_FALLTHROUGH;
11289 : case 3: // Make bottom-left same as top-right
11290 252 : dimenY.mLeft = dimenY.mRight;
11291 : }
11292 :
11293 1760 : NS_FOR_CSS_SIDES(side) {
11294 1408 : nsCSSValue& x = dimenX.*nsCSSRect::sides[side];
11295 1408 : nsCSSValue& y = dimenY.*nsCSSRect::sides[side];
11296 :
11297 1408 : if (x == y) {
11298 1408 : array[side] = x;
11299 : } else {
11300 0 : nsCSSValue pair;
11301 0 : pair.SetPairValue(x, y);
11302 0 : array[side] = pair;
11303 : }
11304 : }
11305 352 : return true;
11306 : }
11307 :
11308 : bool
11309 355 : CSSParserImpl::ParseBoxCornerRadii(const nsCSSPropertyID aPropIDs[])
11310 : {
11311 710 : nsCSSValue value[4];
11312 355 : if (!ParseBoxCornerRadiiInternals(value)) {
11313 3 : return false;
11314 : }
11315 :
11316 1760 : NS_FOR_CSS_SIDES(side) {
11317 1408 : AppendValue(aPropIDs[side], value[side]);
11318 : }
11319 352 : return true;
11320 : }
11321 :
11322 : // These must be in CSS order (top,right,bottom,left) for indexing to work
11323 : static const nsCSSPropertyID kBorderStyleIDs[] = {
11324 : eCSSProperty_border_top_style,
11325 : eCSSProperty_border_right_style,
11326 : eCSSProperty_border_bottom_style,
11327 : eCSSProperty_border_left_style
11328 : };
11329 : static const nsCSSPropertyID kBorderWidthIDs[] = {
11330 : eCSSProperty_border_top_width,
11331 : eCSSProperty_border_right_width,
11332 : eCSSProperty_border_bottom_width,
11333 : eCSSProperty_border_left_width
11334 : };
11335 : static const nsCSSPropertyID kBorderColorIDs[] = {
11336 : eCSSProperty_border_top_color,
11337 : eCSSProperty_border_right_color,
11338 : eCSSProperty_border_bottom_color,
11339 : eCSSProperty_border_left_color
11340 : };
11341 : static const nsCSSPropertyID kBorderRadiusIDs[] = {
11342 : eCSSProperty_border_top_left_radius,
11343 : eCSSProperty_border_top_right_radius,
11344 : eCSSProperty_border_bottom_right_radius,
11345 : eCSSProperty_border_bottom_left_radius
11346 : };
11347 : static const nsCSSPropertyID kOutlineRadiusIDs[] = {
11348 : eCSSProperty__moz_outline_radius_topleft,
11349 : eCSSProperty__moz_outline_radius_topright,
11350 : eCSSProperty__moz_outline_radius_bottomright,
11351 : eCSSProperty__moz_outline_radius_bottomleft
11352 : };
11353 :
11354 : void
11355 7458 : CSSParserImpl::SaveInputState(CSSParserInputState& aState)
11356 : {
11357 7458 : aState.mToken = mToken;
11358 7458 : aState.mHavePushBack = mHavePushBack;
11359 7458 : mScanner->SavePosition(aState.mPosition);
11360 7458 : }
11361 :
11362 : void
11363 258 : CSSParserImpl::RestoreSavedInputState(const CSSParserInputState& aState)
11364 : {
11365 258 : mToken = aState.mToken;
11366 258 : mHavePushBack = aState.mHavePushBack;
11367 258 : mScanner->RestoreSavedPosition(aState.mPosition);
11368 258 : }
11369 :
11370 : bool
11371 7458 : CSSParserImpl::ParseProperty(nsCSSPropertyID aPropID)
11372 : {
11373 : // Can't use AutoRestore<bool> because it's a bitfield.
11374 7458 : MOZ_ASSERT(!mHashlessColorQuirk,
11375 : "hashless color quirk should not be set");
11376 7458 : MOZ_ASSERT(!mUnitlessLengthQuirk,
11377 : "unitless length quirk should not be set");
11378 7458 : MOZ_ASSERT(aPropID != eCSSPropertyExtra_variable);
11379 :
11380 7458 : if (mNavQuirkMode) {
11381 0 : mHashlessColorQuirk =
11382 0 : nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_HASHLESS_COLOR_QUIRK);
11383 0 : mUnitlessLengthQuirk =
11384 0 : nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_UNITLESS_LENGTH_QUIRK);
11385 : }
11386 :
11387 : // Save the current input state so that we can restore it later if we
11388 : // have to re-parse the property value as a variable-reference-containing
11389 : // token stream.
11390 14916 : CSSParserInputState stateBeforeProperty;
11391 7458 : SaveInputState(stateBeforeProperty);
11392 7458 : mScanner->ClearSeenVariableReference();
11393 :
11394 7458 : NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
11395 7458 : bool allowVariables = true;
11396 : bool result;
11397 7458 : switch (nsCSSProps::PropertyParseType(aPropID)) {
11398 : case CSS_PROPERTY_PARSE_INACCESSIBLE: {
11399 : // The user can't use these
11400 0 : REPORT_UNEXPECTED(PEInaccessibleProperty2);
11401 0 : allowVariables = false;
11402 0 : result = false;
11403 0 : break;
11404 : }
11405 : case CSS_PROPERTY_PARSE_FUNCTION: {
11406 2594 : result = ParsePropertyByFunction(aPropID);
11407 2594 : break;
11408 : }
11409 : case CSS_PROPERTY_PARSE_VALUE: {
11410 4622 : result = false;
11411 9244 : nsCSSValue value;
11412 4622 : if (ParseSingleValueProperty(value, aPropID) == CSSParseResult::Ok) {
11413 4475 : AppendValue(aPropID, value);
11414 4475 : result = true;
11415 : }
11416 : // XXX Report errors?
11417 4622 : break;
11418 : }
11419 : case CSS_PROPERTY_PARSE_VALUE_LIST: {
11420 242 : result = ParseValueList(aPropID);
11421 242 : break;
11422 : }
11423 : default: {
11424 0 : result = false;
11425 0 : allowVariables = false;
11426 0 : MOZ_ASSERT(false,
11427 : "Property's flags field in nsCSSPropList.h is missing "
11428 : "one of the CSS_PROPERTY_PARSE_* constants");
11429 : break;
11430 : }
11431 : }
11432 :
11433 7458 : if (result) {
11434 : // We need to call ExpectEndProperty() to decide whether to reparse
11435 : // with variables. This is needed because the property parsing may
11436 : // have stopped upon finding a variable (e.g., 'margin: 1px var(a)')
11437 : // in a way that future variable substitutions will be valid, or
11438 : // because it parsed everything that's possible but we still want to
11439 : // act as though the property contains variables even though we know
11440 : // the substitution will never work (e.g., for 'margin: 1px 2px 3px
11441 : // 4px 5px var(a)').
11442 : //
11443 : // It would be nice to find a better solution here
11444 : // (and for the SkipUntilOneOf below), though, that doesn't depend
11445 : // on using what we don't accept for doing parsing correctly.
11446 7244 : if (!ExpectEndProperty()) {
11447 44 : result = false;
11448 : }
11449 : }
11450 :
11451 14916 : bool seenVariable = mScanner->SeenVariableReference() ||
11452 7200 : (stateBeforeProperty.mHavePushBack &&
11453 0 : stateBeforeProperty.mToken.mType == eCSSToken_Function &&
11454 7458 : stateBeforeProperty.mToken.mIdent.LowerCaseEqualsLiteral("var"));
11455 : bool parseAsTokenStream;
11456 :
11457 7458 : if (!result && allowVariables) {
11458 258 : parseAsTokenStream = true;
11459 258 : if (!seenVariable) {
11460 : // We might have stopped parsing the property before its end and before
11461 : // finding a variable reference. Keep checking until the end of the
11462 : // property.
11463 0 : CSSParserInputState stateAtError;
11464 0 : SaveInputState(stateAtError);
11465 :
11466 0 : const char16_t stopChars[] = { ';', '!', '}', ')', 0 };
11467 0 : SkipUntilOneOf(stopChars);
11468 0 : UngetToken();
11469 0 : parseAsTokenStream = mScanner->SeenVariableReference();
11470 :
11471 0 : if (!parseAsTokenStream) {
11472 : // If we parsed to the end of the propery and didn't find any variable
11473 : // references, then the real position we want to report the error at
11474 : // is |stateAtError|.
11475 0 : RestoreSavedInputState(stateAtError);
11476 : }
11477 258 : }
11478 : } else {
11479 7200 : parseAsTokenStream = false;
11480 : }
11481 :
11482 7458 : if (parseAsTokenStream) {
11483 : // Go back to the start of the property value and parse it to make sure
11484 : // its variable references are syntactically valid and is otherwise
11485 : // balanced.
11486 258 : RestoreSavedInputState(stateBeforeProperty);
11487 :
11488 258 : if (!mInSupportsCondition) {
11489 258 : mScanner->StartRecording();
11490 : }
11491 :
11492 : CSSVariableDeclarations::Type type;
11493 : bool dropBackslash;
11494 516 : nsString impliedCharacters;
11495 516 : nsCSSValue value;
11496 258 : if (ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
11497 : nullptr, nullptr)) {
11498 258 : MOZ_ASSERT(type == CSSVariableDeclarations::eTokenStream,
11499 : "a non-custom property reparsed since it contained variable "
11500 : "references should not have been 'initial' or 'inherit'");
11501 :
11502 516 : nsString propertyValue;
11503 :
11504 258 : if (!mInSupportsCondition) {
11505 : // If we are in an @supports condition, we don't need to store the
11506 : // actual token stream on the nsCSSValue.
11507 258 : mScanner->StopRecording(propertyValue);
11508 258 : if (dropBackslash) {
11509 0 : MOZ_ASSERT(!propertyValue.IsEmpty() &&
11510 : propertyValue[propertyValue.Length() - 1] == '\\');
11511 0 : propertyValue.Truncate(propertyValue.Length() - 1);
11512 : }
11513 258 : propertyValue.Append(impliedCharacters);
11514 : }
11515 :
11516 258 : if (mHavePushBack) {
11517 : // If we came to the end of a property value that had a variable
11518 : // reference and a token was pushed back, then it would have been
11519 : // ended by '!', ')', ';', ']' or '}'. We should remove it from the
11520 : // recorded property value.
11521 258 : MOZ_ASSERT(mToken.IsSymbol('!') ||
11522 : mToken.IsSymbol(')') ||
11523 : mToken.IsSymbol(';') ||
11524 : mToken.IsSymbol(']') ||
11525 : mToken.IsSymbol('}'));
11526 258 : if (!mInSupportsCondition) {
11527 258 : MOZ_ASSERT(!propertyValue.IsEmpty());
11528 258 : MOZ_ASSERT(propertyValue[propertyValue.Length() - 1] ==
11529 : mToken.mSymbol);
11530 258 : propertyValue.Truncate(propertyValue.Length() - 1);
11531 : }
11532 : }
11533 :
11534 258 : if (!mInSupportsCondition) {
11535 258 : if (nsCSSProps::IsShorthand(aPropID)) {
11536 : // If this is a shorthand property, we store the token stream on each
11537 : // of its corresponding longhand properties.
11538 520 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID, EnabledState()) {
11539 434 : nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
11540 434 : tokenStream->mPropertyID = *p;
11541 434 : tokenStream->mShorthandPropertyID = aPropID;
11542 434 : tokenStream->mTokenStream = propertyValue;
11543 434 : tokenStream->mBaseURI = mBaseURI;
11544 434 : tokenStream->mSheetURI = mSheetURI;
11545 434 : tokenStream->mSheetPrincipal = mSheetPrincipal;
11546 : // XXX Should store sheet here (see bug 952338).
11547 : // tokenStream->mSheet = mSheet;
11548 434 : tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
11549 434 : tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
11550 434 : value.SetTokenStreamValue(tokenStream);
11551 434 : AppendValue(*p, value);
11552 : }
11553 : } else {
11554 172 : nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
11555 172 : tokenStream->mPropertyID = aPropID;
11556 172 : tokenStream->mTokenStream = propertyValue;
11557 172 : tokenStream->mBaseURI = mBaseURI;
11558 172 : tokenStream->mSheetURI = mSheetURI;
11559 172 : tokenStream->mSheetPrincipal = mSheetPrincipal;
11560 : // XXX Should store sheet here (see bug 952338).
11561 : // tokenStream->mSheet = mSheet;
11562 172 : tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
11563 172 : tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
11564 172 : value.SetTokenStreamValue(tokenStream);
11565 172 : AppendValue(aPropID, value);
11566 : }
11567 : }
11568 258 : result = true;
11569 : } else {
11570 0 : if (!mInSupportsCondition) {
11571 0 : mScanner->StopRecording();
11572 : }
11573 : }
11574 : }
11575 :
11576 7458 : if (mNavQuirkMode) {
11577 0 : mHashlessColorQuirk = false;
11578 0 : mUnitlessLengthQuirk = false;
11579 : }
11580 :
11581 14916 : return result;
11582 : }
11583 :
11584 : bool
11585 2594 : CSSParserImpl::ParsePropertyByFunction(nsCSSPropertyID aPropID)
11586 : {
11587 2594 : switch (aPropID) { // handle shorthand or multiple properties
11588 : case eCSSProperty_place_content:
11589 0 : return ParsePlaceContent();
11590 : case eCSSProperty_place_items:
11591 0 : return ParsePlaceItems();
11592 : case eCSSProperty_place_self:
11593 0 : return ParsePlaceSelf();
11594 : case eCSSProperty_background:
11595 65 : return ParseImageLayers(nsStyleImageLayers::kBackgroundLayerTable);
11596 : case eCSSProperty_background_repeat:
11597 38 : return ParseImageLayerRepeat(eCSSProperty_background_repeat);
11598 : case eCSSProperty_background_position:
11599 47 : return ParseImageLayerPosition(nsStyleImageLayers::kBackgroundLayerTable);
11600 : case eCSSProperty_background_position_x:
11601 : case eCSSProperty_background_position_y:
11602 0 : return ParseImageLayerPositionCoord(aPropID,
11603 0 : aPropID == eCSSProperty_background_position_x);
11604 : case eCSSProperty_background_size:
11605 36 : return ParseImageLayerSize(eCSSProperty_background_size);
11606 : case eCSSProperty_border:
11607 239 : return ParseBorderSide(kBorderTopIDs, true);
11608 : case eCSSProperty_border_color:
11609 42 : return ParseBorderColor();
11610 : case eCSSProperty_border_spacing:
11611 2 : return ParseBorderSpacing();
11612 : case eCSSProperty_border_style:
11613 44 : return ParseBorderStyle();
11614 : case eCSSProperty_border_block_end:
11615 0 : return ParseBorderSide(kBorderBlockEndIDs, false);
11616 : case eCSSProperty_border_block_start:
11617 0 : return ParseBorderSide(kBorderBlockStartIDs, false);
11618 : case eCSSProperty_border_bottom:
11619 26 : return ParseBorderSide(kBorderBottomIDs, false);
11620 : case eCSSProperty_border_inline_end:
11621 3 : return ParseBorderSide(kBorderInlineEndIDs, false);
11622 : case eCSSProperty_border_inline_start:
11623 6 : return ParseBorderSide(kBorderInlineStartIDs, false);
11624 : case eCSSProperty_border_left:
11625 24 : return ParseBorderSide(kBorderLeftIDs, false);
11626 : case eCSSProperty_border_right:
11627 20 : return ParseBorderSide(kBorderRightIDs, false);
11628 : case eCSSProperty_border_top:
11629 36 : return ParseBorderSide(kBorderTopIDs, false);
11630 : case eCSSProperty__moz_border_bottom_colors:
11631 : case eCSSProperty__moz_border_left_colors:
11632 : case eCSSProperty__moz_border_right_colors:
11633 : case eCSSProperty__moz_border_top_colors:
11634 34 : return ParseBorderColors(aPropID);
11635 : case eCSSProperty_border_image_slice:
11636 4 : return ParseBorderImageSlice(true, nullptr);
11637 : case eCSSProperty_border_image_width:
11638 0 : return ParseBorderImageWidth(true);
11639 : case eCSSProperty_border_image_outset:
11640 0 : return ParseBorderImageOutset(true);
11641 : case eCSSProperty_border_image_repeat:
11642 0 : return ParseBorderImageRepeat(true);
11643 : case eCSSProperty_border_image:
11644 6 : return ParseBorderImage();
11645 : case eCSSProperty_border_width:
11646 27 : return ParseBorderWidth();
11647 : case eCSSProperty_border_radius:
11648 354 : return ParseBoxCornerRadii(kBorderRadiusIDs);
11649 : case eCSSProperty__moz_outline_radius:
11650 1 : return ParseBoxCornerRadii(kOutlineRadiusIDs);
11651 :
11652 : case eCSSProperty_border_top_left_radius:
11653 : case eCSSProperty_border_top_right_radius:
11654 : case eCSSProperty_border_bottom_right_radius:
11655 : case eCSSProperty_border_bottom_left_radius:
11656 : case eCSSProperty__moz_outline_radius_topleft:
11657 : case eCSSProperty__moz_outline_radius_topright:
11658 : case eCSSProperty__moz_outline_radius_bottomright:
11659 : case eCSSProperty__moz_outline_radius_bottomleft:
11660 22 : return ParseBoxCornerRadius(aPropID);
11661 :
11662 : case eCSSProperty_box_shadow:
11663 : case eCSSProperty_text_shadow:
11664 69 : return ParseShadowList(aPropID);
11665 :
11666 : case eCSSProperty_clip:
11667 3 : return ParseRect(eCSSProperty_clip);
11668 : case eCSSProperty_columns:
11669 0 : return ParseColumns();
11670 : case eCSSProperty_column_rule:
11671 6 : return ParseBorderSide(kColumnRuleIDs, false);
11672 : case eCSSProperty_content:
11673 27 : return ParseContent();
11674 : case eCSSProperty__moz_context_properties:
11675 39 : return ParseContextProperties();
11676 : case eCSSProperty_counter_increment:
11677 : case eCSSProperty_counter_reset:
11678 0 : return ParseCounterData(aPropID);
11679 : case eCSSProperty_cursor:
11680 67 : return ParseCursor();
11681 : case eCSSProperty_filter:
11682 14 : return ParseFilter();
11683 : case eCSSProperty_flex:
11684 22 : return ParseFlex();
11685 : case eCSSProperty_flex_flow:
11686 0 : return ParseFlexFlow();
11687 : case eCSSProperty_font:
11688 31 : return ParseFont();
11689 : case eCSSProperty_font_variant:
11690 3 : return ParseFontVariant();
11691 : case eCSSProperty_grid_auto_flow:
11692 6 : return ParseGridAutoFlow();
11693 : case eCSSProperty_grid_auto_columns:
11694 : case eCSSProperty_grid_auto_rows:
11695 12 : return ParseGridAutoColumnsRows(aPropID);
11696 : case eCSSProperty_grid_template_areas:
11697 6 : return ParseGridTemplateAreas();
11698 : case eCSSProperty_grid_template_columns:
11699 : case eCSSProperty_grid_template_rows:
11700 12 : return ParseGridTemplateColumnsRows(aPropID);
11701 : case eCSSProperty_grid_template:
11702 0 : return ParseGridTemplate();
11703 : case eCSSProperty_grid:
11704 0 : return ParseGrid();
11705 : case eCSSProperty_grid_column_start:
11706 : case eCSSProperty_grid_column_end:
11707 : case eCSSProperty_grid_row_start:
11708 : case eCSSProperty_grid_row_end:
11709 8 : return ParseGridColumnRowStartEnd(aPropID);
11710 : case eCSSProperty_grid_column:
11711 : return ParseGridColumnRow(eCSSProperty_grid_column_start,
11712 0 : eCSSProperty_grid_column_end);
11713 : case eCSSProperty_grid_row:
11714 : return ParseGridColumnRow(eCSSProperty_grid_row_start,
11715 0 : eCSSProperty_grid_row_end);
11716 : case eCSSProperty_grid_area:
11717 0 : return ParseGridArea();
11718 : case eCSSProperty_grid_gap:
11719 0 : return ParseGridGap();
11720 : case eCSSProperty__moz_image_region:
11721 86 : return ParseRect(eCSSProperty__moz_image_region);
11722 : case eCSSProperty_align_content:
11723 : case eCSSProperty_justify_content:
11724 14 : return ParseAlignJustifyContent(aPropID);
11725 : case eCSSProperty_align_items:
11726 10 : return ParseAlignItems();
11727 : case eCSSProperty_align_self:
11728 : case eCSSProperty_justify_self:
11729 7 : return ParseAlignJustifySelf(aPropID);
11730 : case eCSSProperty_initial_letter:
11731 0 : return ParseInitialLetter();
11732 : case eCSSProperty_justify_items:
11733 6 : return ParseJustifyItems();
11734 : case eCSSProperty_list_style:
11735 2 : return ParseListStyle();
11736 : case eCSSProperty_margin:
11737 246 : return ParseMargin();
11738 : case eCSSProperty_object_position:
11739 2 : return ParseObjectPosition();
11740 : case eCSSProperty_outline:
11741 27 : return ParseOutline();
11742 : case eCSSProperty_overflow:
11743 71 : return ParseOverflow();
11744 : case eCSSProperty_padding:
11745 343 : return ParsePadding();
11746 : case eCSSProperty_quotes:
11747 0 : return ParseQuotes();
11748 : case eCSSProperty_text_decoration:
11749 16 : return ParseTextDecoration();
11750 : case eCSSProperty_text_emphasis:
11751 2 : return ParseTextEmphasis();
11752 : case eCSSProperty_will_change:
11753 1 : return ParseWillChange();
11754 : case eCSSProperty_transform:
11755 : case eCSSProperty__moz_window_transform:
11756 84 : return ParseTransform(false, aPropID);
11757 : case eCSSProperty__moz_transform:
11758 0 : return ParseTransform(true, eCSSProperty_transform);
11759 : case eCSSProperty_transform_origin:
11760 : case eCSSProperty_perspective_origin:
11761 : case eCSSProperty__moz_window_transform_origin:
11762 4 : return ParseTransformOrigin(aPropID);
11763 : case eCSSProperty_transition:
11764 106 : return ParseTransition();
11765 : case eCSSProperty_animation:
11766 11 : return ParseAnimation();
11767 : case eCSSProperty_transition_property:
11768 7 : return ParseTransitionProperty();
11769 : case eCSSProperty_fill:
11770 : case eCSSProperty_stroke:
11771 141 : return ParsePaint(aPropID);
11772 : case eCSSProperty_stroke_dasharray:
11773 2 : return ParseDasharray();
11774 : case eCSSProperty_marker:
11775 0 : return ParseMarker();
11776 : case eCSSProperty_paint_order:
11777 0 : return ParsePaintOrder();
11778 : case eCSSProperty_scroll_snap_type:
11779 0 : return ParseScrollSnapType();
11780 : #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
11781 : case eCSSProperty_mask:
11782 5 : return ParseImageLayers(nsStyleImageLayers::kMaskLayerTable);
11783 : case eCSSProperty_mask_repeat:
11784 0 : return ParseImageLayerRepeat(eCSSProperty_mask_repeat);
11785 : case eCSSProperty_mask_position:
11786 0 : return ParseImageLayerPosition(nsStyleImageLayers::kMaskLayerTable);
11787 : case eCSSProperty_mask_position_x:
11788 : case eCSSProperty_mask_position_y:
11789 0 : return ParseImageLayerPositionCoord(aPropID,
11790 0 : aPropID == eCSSProperty_mask_position_x);
11791 : case eCSSProperty_mask_size:
11792 0 : return ParseImageLayerSize(eCSSProperty_mask_size);
11793 : #endif
11794 : case eCSSProperty__webkit_text_stroke:
11795 0 : return ParseWebkitTextStroke();
11796 : case eCSSProperty_all:
11797 0 : return ParseAll();
11798 : default:
11799 0 : MOZ_ASSERT(false, "should not be called");
11800 : return false;
11801 : }
11802 : }
11803 :
11804 : // Bits used in determining which background position info we have
11805 : #define BG_CENTER NS_STYLE_IMAGELAYER_POSITION_CENTER
11806 : #define BG_TOP NS_STYLE_IMAGELAYER_POSITION_TOP
11807 : #define BG_BOTTOM NS_STYLE_IMAGELAYER_POSITION_BOTTOM
11808 : #define BG_LEFT NS_STYLE_IMAGELAYER_POSITION_LEFT
11809 : #define BG_RIGHT NS_STYLE_IMAGELAYER_POSITION_RIGHT
11810 : #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM)
11811 : #define BG_TB (BG_TOP | BG_BOTTOM)
11812 : #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT)
11813 : #define BG_LR (BG_LEFT | BG_RIGHT)
11814 :
11815 : CSSParseResult
11816 1722 : CSSParserImpl::ParseBoxProperty(nsCSSValue& aValue,
11817 : nsCSSPropertyID aPropID)
11818 : {
11819 1722 : if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
11820 0 : MOZ_ASSERT(false, "must only be called for longhand properties");
11821 : return CSSParseResult::NotFound;
11822 : }
11823 :
11824 1722 : MOZ_ASSERT(!nsCSSProps::PropHasFlags(aPropID,
11825 : CSS_PROPERTY_VALUE_PARSER_FUNCTION),
11826 : "must only be called for non-function-parsed properties");
11827 :
11828 1722 : uint32_t variant = nsCSSProps::ParserVariant(aPropID);
11829 1722 : if (variant == 0) {
11830 0 : MOZ_ASSERT(false, "must only be called for variant-parsed properties");
11831 : return CSSParseResult::NotFound;
11832 : }
11833 :
11834 1722 : if (variant & ~(VARIANT_AHKLP | VARIANT_COLOR | VARIANT_CALC)) {
11835 0 : MOZ_ASSERT(false, "must only be called for properties that take certain "
11836 : "variants");
11837 : return CSSParseResult::NotFound;
11838 : }
11839 :
11840 1722 : const KTableEntry* kwtable = nsCSSProps::kKeywordTableTable[aPropID];
11841 1722 : uint32_t restrictions = nsCSSProps::ValueRestrictions(aPropID);
11842 :
11843 1722 : return ParseVariantWithRestrictions(aValue, variant, kwtable, restrictions);
11844 : }
11845 :
11846 : bool
11847 186 : CSSParserImpl::ParseSingleValuePropertyByFunction(nsCSSValue& aValue,
11848 : nsCSSPropertyID aPropID)
11849 : {
11850 186 : switch (aPropID) {
11851 : case eCSSProperty_clip_path:
11852 8 : return ParseClipPath(aValue);
11853 : case eCSSProperty_contain:
11854 0 : return ParseContain(aValue);
11855 : case eCSSProperty_font_family:
11856 13 : return ParseFamily(aValue);
11857 : case eCSSProperty_font_synthesis:
11858 0 : return ParseFontSynthesis(aValue);
11859 : case eCSSProperty_font_variant_alternates:
11860 0 : return ParseFontVariantAlternates(aValue);
11861 : case eCSSProperty_font_variant_east_asian:
11862 2 : return ParseFontVariantEastAsian(aValue);
11863 : case eCSSProperty_font_variant_ligatures:
11864 0 : return ParseFontVariantLigatures(aValue);
11865 : case eCSSProperty_font_variant_numeric:
11866 2 : return ParseFontVariantNumeric(aValue);
11867 : case eCSSProperty_font_feature_settings:
11868 0 : return ParseFontFeatureSettings(aValue);
11869 : case eCSSProperty_font_variation_settings:
11870 0 : return ParseFontVariationSettings(aValue);
11871 : case eCSSProperty_font_weight:
11872 54 : return ParseFontWeight(aValue);
11873 : case eCSSProperty_image_orientation:
11874 0 : return ParseImageOrientation(aValue);
11875 : case eCSSProperty_list_style_type:
11876 12 : return ParseListStyleType(aValue);
11877 : case eCSSProperty_scroll_snap_points_x:
11878 0 : return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_x);
11879 : case eCSSProperty_scroll_snap_points_y:
11880 0 : return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_y);
11881 : case eCSSProperty_scroll_snap_destination:
11882 0 : return ParseScrollSnapDestination(aValue);
11883 : case eCSSProperty_scroll_snap_coordinate:
11884 0 : return ParseScrollSnapCoordinate(aValue);
11885 : case eCSSProperty_shape_outside:
11886 0 : return ParseShapeOutside(aValue);
11887 : case eCSSProperty_text_align:
11888 57 : return ParseTextAlign(aValue);
11889 : case eCSSProperty_text_align_last:
11890 0 : return ParseTextAlignLast(aValue);
11891 : case eCSSProperty_text_decoration_line:
11892 18 : return ParseTextDecorationLine(aValue);
11893 : case eCSSProperty_text_combine_upright:
11894 0 : return ParseTextCombineUpright(aValue);
11895 : case eCSSProperty_text_emphasis_position:
11896 0 : return ParseTextEmphasisPosition(aValue);
11897 : case eCSSProperty_text_emphasis_style:
11898 2 : return ParseTextEmphasisStyle(aValue);
11899 : case eCSSProperty_text_overflow:
11900 18 : return ParseTextOverflow(aValue);
11901 : case eCSSProperty_touch_action:
11902 0 : return ParseTouchAction(aValue);
11903 : default:
11904 0 : MOZ_ASSERT(false, "should not reach here");
11905 : return false;
11906 : }
11907 : }
11908 :
11909 : CSSParseResult
11910 7306 : CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
11911 : nsCSSPropertyID aPropID)
11912 : {
11913 7306 : if (aPropID == eCSSPropertyExtra_x_none_value) {
11914 6 : return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nullptr);
11915 : }
11916 :
11917 7300 : if (aPropID == eCSSPropertyExtra_x_auto_value) {
11918 0 : return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nullptr);
11919 : }
11920 :
11921 7300 : if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
11922 0 : MOZ_ASSERT(false, "not a single value property");
11923 : return CSSParseResult::NotFound;
11924 : }
11925 :
11926 7300 : if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) {
11927 : uint32_t lineBefore, colBefore;
11928 186 : if (!GetNextTokenLocation(true, &lineBefore, &colBefore)) {
11929 : // We're at EOF before parsing.
11930 0 : return CSSParseResult::NotFound;
11931 : }
11932 :
11933 186 : if (ParseSingleValuePropertyByFunction(aValue, aPropID)) {
11934 179 : return CSSParseResult::Ok;
11935 : }
11936 :
11937 : uint32_t lineAfter, colAfter;
11938 21 : if (!GetNextTokenLocation(true, &lineAfter, &colAfter) ||
11939 14 : lineAfter != lineBefore ||
11940 7 : colAfter != colBefore) {
11941 : // Any single token value that was invalid will have been pushed back,
11942 : // so GetNextTokenLocation encountering EOF means we failed while
11943 : // parsing a multi-token value.
11944 0 : return CSSParseResult::Error;
11945 : }
11946 :
11947 7 : return CSSParseResult::NotFound;
11948 : }
11949 :
11950 7114 : uint32_t variant = nsCSSProps::ParserVariant(aPropID);
11951 7114 : if (variant == 0) {
11952 0 : MOZ_ASSERT(false, "not a single value property");
11953 : return CSSParseResult::NotFound;
11954 : }
11955 :
11956 7114 : const KTableEntry* kwtable = nsCSSProps::kKeywordTableTable[aPropID];
11957 7114 : uint32_t restrictions = nsCSSProps::ValueRestrictions(aPropID);
11958 7114 : return ParseVariantWithRestrictions(aValue, variant, kwtable, restrictions);
11959 : }
11960 :
11961 : // font-descriptor: descriptor ':' value ';'
11962 : // caller has advanced mToken to point at the descriptor
11963 : bool
11964 0 : CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
11965 : nsCSSValue& aValue)
11966 : {
11967 0 : switch (aDescID) {
11968 : // These four are similar to the properties of the same name,
11969 : // possibly with more restrictions on the values they can take.
11970 : case eCSSFontDesc_Family: {
11971 0 : nsCSSValue value;
11972 0 : if (!ParseFamily(value) ||
11973 0 : value.GetUnit() != eCSSUnit_FontFamilyList)
11974 0 : return false;
11975 :
11976 : // name can only be a single, non-generic name
11977 0 : const FontFamilyList* f = value.GetFontFamilyListValue();
11978 0 : const nsTArray<FontFamilyName>& fontlist = f->GetFontlist();
11979 :
11980 0 : if (fontlist.Length() != 1 || !fontlist[0].IsNamed()) {
11981 0 : return false;
11982 : }
11983 :
11984 0 : aValue.SetStringValue(fontlist[0].mName, eCSSUnit_String);
11985 0 : return true;
11986 : }
11987 :
11988 : case eCSSFontDesc_Style:
11989 : // property is VARIANT_HMK|VARIANT_SYSFONT
11990 : return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
11991 0 : nsCSSProps::kFontStyleKTable);
11992 :
11993 : case eCSSFontDesc_Display:
11994 : return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD,
11995 0 : nsCSSProps::kFontDisplayKTable);
11996 :
11997 : case eCSSFontDesc_Weight:
11998 0 : return (ParseFontWeight(aValue) &&
11999 0 : aValue.GetUnit() != eCSSUnit_Inherit &&
12000 0 : aValue.GetUnit() != eCSSUnit_Initial &&
12001 0 : aValue.GetUnit() != eCSSUnit_Unset &&
12002 0 : (aValue.GetUnit() != eCSSUnit_Enumerated ||
12003 0 : (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
12004 0 : aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
12005 :
12006 : case eCSSFontDesc_Stretch:
12007 : // property is VARIANT_HK|VARIANT_SYSFONT
12008 : return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD,
12009 0 : nsCSSProps::kFontStretchKTable);
12010 :
12011 : // These two are unique to @font-face and have their own special grammar.
12012 : case eCSSFontDesc_Src:
12013 0 : return ParseFontSrc(aValue);
12014 :
12015 : case eCSSFontDesc_UnicodeRange:
12016 0 : return ParseFontRanges(aValue);
12017 :
12018 : case eCSSFontDesc_FontFeatureSettings:
12019 0 : return ParseFontFeatureSettings(aValue);
12020 :
12021 : case eCSSFontDesc_FontLanguageOverride:
12022 : return ParseSingleTokenVariant(aValue, VARIANT_NORMAL | VARIANT_STRING,
12023 0 : nullptr);
12024 :
12025 : case eCSSFontDesc_UNKNOWN:
12026 : case eCSSFontDesc_COUNT:
12027 0 : NS_NOTREACHED("bad nsCSSFontDesc code");
12028 : }
12029 : // explicitly do NOT have a default case to let the compiler
12030 : // help find missing descriptors
12031 0 : return false;
12032 : }
12033 :
12034 : static nsCSSValue
12035 8 : BoxPositionMaskToCSSValue(int32_t aMask, bool isX)
12036 : {
12037 8 : int32_t val = NS_STYLE_IMAGELAYER_POSITION_CENTER;
12038 8 : if (isX) {
12039 4 : if (aMask & BG_LEFT) {
12040 2 : val = NS_STYLE_IMAGELAYER_POSITION_LEFT;
12041 : }
12042 2 : else if (aMask & BG_RIGHT) {
12043 2 : val = NS_STYLE_IMAGELAYER_POSITION_RIGHT;
12044 : }
12045 : }
12046 : else {
12047 4 : if (aMask & BG_TOP) {
12048 0 : val = NS_STYLE_IMAGELAYER_POSITION_TOP;
12049 : }
12050 4 : else if (aMask & BG_BOTTOM) {
12051 0 : val = NS_STYLE_IMAGELAYER_POSITION_BOTTOM;
12052 : }
12053 : }
12054 :
12055 8 : return nsCSSValue(val, eCSSUnit_Enumerated);
12056 : }
12057 :
12058 : bool
12059 70 : CSSParserImpl::ParseImageLayers(const nsCSSPropertyID aTable[])
12060 : {
12061 140 : nsAutoParseCompoundProperty compound(this);
12062 :
12063 : // background-color can only be set once, so it's not a list.
12064 140 : nsCSSValue color;
12065 :
12066 : // Check first for inherit/initial/unset.
12067 70 : if (ParseSingleTokenVariant(color, VARIANT_INHERIT, nullptr)) {
12068 : // must be alone
12069 36 : for (const nsCSSPropertyID* subprops =
12070 4 : nsCSSProps::SubpropertyEntryFor(aTable[nsStyleImageLayers::shorthand]);
12071 40 : *subprops != eCSSProperty_UNKNOWN; ++subprops) {
12072 36 : AppendValue(*subprops, color);
12073 : }
12074 4 : return true;
12075 : }
12076 :
12077 132 : nsCSSValue image, repeat, attachment, clip, origin, positionX, positionY, size,
12078 132 : composite, maskMode;
12079 : ImageLayersShorthandParseState state(color, image.SetListValue(),
12080 : repeat.SetPairListValue(),
12081 : attachment.SetListValue(), clip.SetListValue(),
12082 : origin.SetListValue(),
12083 : positionX.SetListValue(), positionY.SetListValue(),
12084 : size.SetPairListValue(), composite.SetListValue(),
12085 66 : maskMode.SetListValue());
12086 :
12087 : for (;;) {
12088 66 : if (!ParseImageLayersItem(state, aTable)) {
12089 9 : return false;
12090 : }
12091 :
12092 : // If we saw a color, this must be the last item.
12093 57 : if (color.GetUnit() != eCSSUnit_Null) {
12094 34 : MOZ_ASSERT(aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN);
12095 34 : break;
12096 : }
12097 :
12098 : // If there's a comma, expect another item.
12099 23 : if (!ExpectSymbol(',', true)) {
12100 23 : break;
12101 : }
12102 :
12103 : #define APPENDNEXT(propID_, propMember_, propType_) \
12104 : if (aTable[propID_] != eCSSProperty_UNKNOWN) { \
12105 : propMember_->mNext = new propType_; \
12106 : propMember_ = propMember_->mNext; \
12107 : }
12108 : // Chain another entry on all the lists.
12109 0 : APPENDNEXT(nsStyleImageLayers::image, state.mImage,
12110 : nsCSSValueList);
12111 0 : APPENDNEXT(nsStyleImageLayers::repeat, state.mRepeat,
12112 : nsCSSValuePairList);
12113 0 : APPENDNEXT(nsStyleImageLayers::clip, state.mClip,
12114 : nsCSSValueList);
12115 0 : APPENDNEXT(nsStyleImageLayers::origin, state.mOrigin,
12116 : nsCSSValueList);
12117 0 : APPENDNEXT(nsStyleImageLayers::positionX, state.mPositionX,
12118 : nsCSSValueList);
12119 0 : APPENDNEXT(nsStyleImageLayers::positionY, state.mPositionY,
12120 : nsCSSValueList);
12121 0 : APPENDNEXT(nsStyleImageLayers::size, state.mSize,
12122 : nsCSSValuePairList);
12123 0 : APPENDNEXT(nsStyleImageLayers::attachment, state.mAttachment,
12124 : nsCSSValueList);
12125 0 : APPENDNEXT(nsStyleImageLayers::maskMode, state.mMode,
12126 : nsCSSValueList);
12127 0 : APPENDNEXT(nsStyleImageLayers::composite, state.mComposite,
12128 : nsCSSValueList);
12129 : #undef APPENDNEXT
12130 : }
12131 :
12132 : // If we get to this point without seeing a color, provide a default.
12133 57 : if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
12134 56 : if (color.GetUnit() == eCSSUnit_Null) {
12135 22 : color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor);
12136 : }
12137 : }
12138 :
12139 : #define APPENDVALUE(propID_, propValue_) \
12140 : if (propID_ != eCSSProperty_UNKNOWN) { \
12141 : AppendValue(propID_, propValue_); \
12142 : }
12143 :
12144 57 : APPENDVALUE(aTable[nsStyleImageLayers::image], image);
12145 57 : APPENDVALUE(aTable[nsStyleImageLayers::repeat], repeat);
12146 57 : APPENDVALUE(aTable[nsStyleImageLayers::clip], clip);
12147 57 : APPENDVALUE(aTable[nsStyleImageLayers::origin], origin);
12148 57 : APPENDVALUE(aTable[nsStyleImageLayers::positionX], positionX);
12149 57 : APPENDVALUE(aTable[nsStyleImageLayers::positionY], positionY);
12150 57 : APPENDVALUE(aTable[nsStyleImageLayers::size], size);
12151 57 : APPENDVALUE(aTable[nsStyleImageLayers::color], color);
12152 57 : APPENDVALUE(aTable[nsStyleImageLayers::attachment], attachment);
12153 57 : APPENDVALUE(aTable[nsStyleImageLayers::maskMode], maskMode);
12154 57 : APPENDVALUE(aTable[nsStyleImageLayers::composite], composite);
12155 :
12156 : #undef APPENDVALUE
12157 :
12158 57 : return true;
12159 : }
12160 :
12161 : // Helper for ParseImageLayersItem. Returns true if the passed-in nsCSSToken is
12162 : // a function which is accepted for background-image.
12163 : bool
12164 25 : CSSParserImpl::IsFunctionTokenValidForImageLayerImage(
12165 : const nsCSSToken& aToken) const
12166 : {
12167 25 : MOZ_ASSERT(aToken.mType == eCSSToken_Function,
12168 : "Should only be called for function-typed tokens");
12169 :
12170 25 : const nsAString& funcName = aToken.mIdent;
12171 :
12172 37 : return funcName.LowerCaseEqualsLiteral("linear-gradient") ||
12173 24 : funcName.LowerCaseEqualsLiteral("radial-gradient") ||
12174 23 : funcName.LowerCaseEqualsLiteral("repeating-linear-gradient") ||
12175 22 : funcName.LowerCaseEqualsLiteral("repeating-radial-gradient") ||
12176 22 : funcName.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
12177 22 : funcName.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
12178 22 : funcName.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
12179 22 : funcName.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
12180 22 : funcName.LowerCaseEqualsLiteral("-moz-image-rect") ||
12181 61 : funcName.LowerCaseEqualsLiteral("-moz-element") ||
12182 11 : (sWebkitPrefixedAliasesEnabled &&
12183 22 : (funcName.LowerCaseEqualsLiteral("-webkit-gradient") ||
12184 22 : funcName.LowerCaseEqualsLiteral("-webkit-linear-gradient") ||
12185 22 : funcName.LowerCaseEqualsLiteral("-webkit-radial-gradient") ||
12186 22 : funcName.LowerCaseEqualsLiteral("-webkit-repeating-linear-gradient") ||
12187 36 : funcName.LowerCaseEqualsLiteral("-webkit-repeating-radial-gradient")));
12188 : }
12189 :
12190 : // Parse one item of the background shorthand property.
12191 : bool
12192 66 : CSSParserImpl::ParseImageLayersItem(
12193 : CSSParserImpl::ImageLayersShorthandParseState& aState,
12194 : const nsCSSPropertyID aTable[])
12195 : {
12196 : // Fill in the values that the shorthand will set if we don't find
12197 : // other values.
12198 66 : aState.mImage->mValue.SetNoneValue();
12199 66 : aState.mAttachment->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL,
12200 66 : eCSSUnit_Enumerated);
12201 66 : aState.mClip->mValue.SetEnumValue(StyleGeometryBox::BorderBox);
12202 :
12203 66 : aState.mRepeat->mXValue.SetIntValue(uint8_t(StyleImageLayerRepeat::Repeat),
12204 66 : eCSSUnit_Enumerated);
12205 66 : aState.mRepeat->mYValue.Reset();
12206 :
12207 132 : RefPtr<nsCSSValue::Array> positionXArr = nsCSSValue::Array::Create(2);
12208 132 : RefPtr<nsCSSValue::Array> positionYArr = nsCSSValue::Array::Create(2);
12209 66 : aState.mPositionX->mValue.SetArrayValue(positionXArr, eCSSUnit_Array);
12210 66 : aState.mPositionY->mValue.SetArrayValue(positionYArr, eCSSUnit_Array);
12211 :
12212 66 : if (eCSSProperty_mask == aTable[nsStyleImageLayers::shorthand]) {
12213 1 : aState.mOrigin->mValue.SetEnumValue(StyleGeometryBox::BorderBox);
12214 : } else {
12215 65 : aState.mOrigin->mValue.SetEnumValue(StyleGeometryBox::PaddingBox);
12216 : }
12217 66 : positionXArr->Item(1).SetPercentValue(0.0f);
12218 66 : positionYArr->Item(1).SetPercentValue(0.0f);
12219 :
12220 66 : aState.mSize->mXValue.SetAutoValue();
12221 66 : aState.mSize->mYValue.SetAutoValue();
12222 66 : aState.mComposite->mValue.SetIntValue(NS_STYLE_MASK_COMPOSITE_ADD,
12223 66 : eCSSUnit_Enumerated);
12224 66 : aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_MATCH_SOURCE,
12225 66 : eCSSUnit_Enumerated);
12226 66 : bool haveColor = false,
12227 66 : haveImage = false,
12228 66 : haveRepeat = false,
12229 66 : haveAttach = false,
12230 66 : havePositionAndSize = false,
12231 66 : haveOrigin = false,
12232 66 : haveClip = false,
12233 66 : haveComposite = false,
12234 66 : haveMode = false,
12235 66 : haveSomething = false;
12236 :
12237 : const KTableEntry* originTable =
12238 66 : nsCSSProps::kKeywordTableTable[aTable[nsStyleImageLayers::origin]];
12239 : const KTableEntry* clipTable =
12240 66 : nsCSSProps::kKeywordTableTable[aTable[nsStyleImageLayers::clip]];
12241 :
12242 270 : while (GetToken(true)) {
12243 167 : nsCSSTokenType tt = mToken.mType;
12244 167 : UngetToken(); // ...but we'll still cheat and use mToken
12245 167 : if (tt == eCSSToken_Symbol) {
12246 : // ExpectEndProperty only looks for symbols, and nothing else will
12247 : // show up as one.
12248 56 : break;
12249 : }
12250 :
12251 111 : if (tt == eCSSToken_Ident) {
12252 57 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
12253 : int32_t dummy;
12254 57 : if (keyword == eCSSKeyword_inherit ||
12255 57 : keyword == eCSSKeyword_initial ||
12256 : keyword == eCSSKeyword_unset) {
12257 0 : return false;
12258 57 : } else if (!haveImage && keyword == eCSSKeyword_none) {
12259 4 : haveImage = true;
12260 8 : if (ParseSingleValueProperty(aState.mImage->mValue,
12261 4 : aTable[nsStyleImageLayers::image]) !=
12262 : CSSParseResult::Ok) {
12263 0 : NS_NOTREACHED("should be able to parse");
12264 0 : return false;
12265 : }
12266 159 : } else if (!haveAttach &&
12267 53 : aTable[nsStyleImageLayers::attachment] !=
12268 106 : eCSSProperty_UNKNOWN &&
12269 53 : nsCSSProps::FindKeyword(
12270 : keyword, nsCSSProps::kImageLayerAttachmentKTable, dummy)) {
12271 2 : haveAttach = true;
12272 2 : if (ParseSingleValueProperty(aState.mAttachment->mValue,
12273 2 : aTable[nsStyleImageLayers::attachment]) !=
12274 : CSSParseResult::Ok) {
12275 0 : NS_NOTREACHED("should be able to parse");
12276 0 : return false;
12277 : }
12278 93 : } else if (!haveRepeat &&
12279 42 : nsCSSProps::FindKeyword(
12280 : keyword, nsCSSProps::kImageLayerRepeatKTable, dummy)) {
12281 17 : haveRepeat = true;
12282 34 : nsCSSValuePair scratch;
12283 17 : if (!ParseImageLayerRepeatValues(scratch)) {
12284 0 : NS_NOTREACHED("should be able to parse");
12285 0 : return false;
12286 : }
12287 17 : aState.mRepeat->mXValue = scratch.mXValue;
12288 17 : aState.mRepeat->mYValue = scratch.mYValue;
12289 66 : } else if (!havePositionAndSize &&
12290 32 : nsCSSProps::FindKeyword(keyword,
12291 : nsCSSProps::kImageLayerPositionKTable, dummy)) {
12292 14 : havePositionAndSize = true;
12293 :
12294 14 : if (!ParsePositionValueSeparateCoords(aState.mPositionX->mValue,
12295 14 : aState.mPositionY->mValue)) {
12296 0 : return false;
12297 : }
12298 14 : if (ExpectSymbol('/', true)) {
12299 2 : nsCSSValuePair scratch;
12300 1 : if (!ParseImageLayerSizeValues(scratch)) {
12301 0 : return false;
12302 : }
12303 1 : aState.mSize->mXValue = scratch.mXValue;
12304 1 : aState.mSize->mYValue = scratch.mYValue;
12305 : }
12306 40 : } else if (!haveOrigin &&
12307 20 : nsCSSProps::FindKeyword(keyword, originTable, dummy)) {
12308 0 : haveOrigin = true;
12309 0 : if (ParseSingleValueProperty(aState.mOrigin->mValue,
12310 0 : aTable[nsStyleImageLayers::origin]) !=
12311 : CSSParseResult::Ok) {
12312 0 : NS_NOTREACHED("should be able to parse");
12313 0 : return false;
12314 : }
12315 : // Set clip value to origin if clip is not set yet.
12316 : // Note that we don't set haveClip here so that it can be
12317 : // overridden if we see it later.
12318 0 : if (!haveClip) {
12319 : #ifdef DEBUG
12320 0 : for (size_t i = 0; originTable[i].mValue != -1; i++) {
12321 : // For each keyword & value in kOriginKTable, ensure that
12322 : // kBackgroundKTable has a matching entry at the same position.
12323 0 : MOZ_ASSERT(originTable[i].mKeyword == clipTable[i].mKeyword);
12324 0 : MOZ_ASSERT(originTable[i].mValue == clipTable[i].mValue);
12325 : }
12326 : #endif
12327 0 : aState.mClip->mValue = aState.mOrigin->mValue;
12328 : }
12329 40 : } else if (!haveClip &&
12330 20 : nsCSSProps::FindKeyword(keyword, clipTable, dummy)) {
12331 : // It is important that we try parsing clip later than origin
12332 : // because if there are two <box> / <geometry-box> values, the
12333 : // first should be origin, and the second should be clip.
12334 0 : haveClip = true;
12335 0 : if (ParseSingleValueProperty(aState.mClip->mValue,
12336 0 : aTable[nsStyleImageLayers::clip]) !=
12337 : CSSParseResult::Ok) {
12338 0 : NS_NOTREACHED("should be able to parse");
12339 0 : return false;
12340 : }
12341 60 : } else if (!haveComposite &&
12342 20 : aTable[nsStyleImageLayers::composite] !=
12343 20 : eCSSProperty_UNKNOWN &&
12344 0 : nsCSSProps::FindKeyword(
12345 : keyword, nsCSSProps::kImageLayerCompositeKTable, dummy)) {
12346 0 : haveComposite = true;
12347 0 : if (ParseSingleValueProperty(aState.mComposite->mValue,
12348 0 : aTable[nsStyleImageLayers::composite]) !=
12349 : CSSParseResult::Ok) {
12350 0 : NS_NOTREACHED("should be able to parse");
12351 0 : return false;
12352 : }
12353 60 : } else if (!haveMode &&
12354 20 : aTable[nsStyleImageLayers::maskMode] != eCSSProperty_UNKNOWN &&
12355 0 : nsCSSProps::FindKeyword(
12356 : keyword, nsCSSProps::kImageLayerModeKTable, dummy)) {
12357 0 : haveMode = true;
12358 0 : if (ParseSingleValueProperty(aState.mMode->mValue,
12359 0 : aTable[nsStyleImageLayers::maskMode]) !=
12360 : CSSParseResult::Ok) {
12361 0 : NS_NOTREACHED("should be able to parse");
12362 0 : return false;
12363 : }
12364 40 : } else if (!haveColor &&
12365 20 : aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
12366 20 : haveColor = true;
12367 40 : if (ParseSingleValueProperty(aState.mColor,
12368 20 : aTable[nsStyleImageLayers::color]) !=
12369 : CSSParseResult::Ok) {
12370 0 : return false;
12371 : }
12372 : } else {
12373 0 : return false;
12374 : }
12375 85 : } else if (tt == eCSSToken_URL ||
12376 25 : (tt == eCSSToken_Function &&
12377 25 : IsFunctionTokenValidForImageLayerImage(mToken))) {
12378 31 : if (haveImage)
12379 0 : return false;
12380 31 : haveImage = true;
12381 31 : if (ParseSingleValueProperty(aState.mImage->mValue,
12382 31 : aTable[nsStyleImageLayers::image]) !=
12383 : CSSParseResult::Ok) {
12384 3 : return false;
12385 : }
12386 45 : } else if (tt == eCSSToken_Dimension ||
12387 20 : tt == eCSSToken_Number ||
12388 46 : tt == eCSSToken_Percentage ||
12389 11 : (tt == eCSSToken_Function &&
12390 11 : mToken.mIdent.LowerCaseEqualsLiteral("calc"))) {
12391 3 : if (havePositionAndSize)
12392 0 : return false;
12393 3 : havePositionAndSize = true;
12394 3 : if (!ParsePositionValueSeparateCoords(aState.mPositionX->mValue,
12395 3 : aState.mPositionY->mValue)) {
12396 0 : return false;
12397 : }
12398 3 : if (ExpectSymbol('/', true)) {
12399 0 : nsCSSValuePair scratch;
12400 0 : if (!ParseImageLayerSizeValues(scratch)) {
12401 0 : return false;
12402 : }
12403 0 : aState.mSize->mXValue = scratch.mXValue;
12404 0 : aState.mSize->mYValue = scratch.mYValue;
12405 : }
12406 20 : } else if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
12407 20 : if (haveColor)
12408 0 : return false;
12409 20 : haveColor = true;
12410 : // Note: This parses 'inherit', 'initial' and 'unset', but
12411 : // we've already checked for them, so it's ok.
12412 20 : if (ParseSingleValueProperty(aState.mColor,
12413 20 : aTable[nsStyleImageLayers::color]) !=
12414 : CSSParseResult::Ok) {
12415 6 : return false;
12416 : }
12417 : } else {
12418 0 : return false;
12419 : }
12420 :
12421 102 : haveSomething = true;
12422 : }
12423 :
12424 57 : return haveSomething;
12425 : }
12426 :
12427 : // This function is very similar to ParseScrollSnapCoordinate,
12428 : // ParseImageLayerPosition, and ParseImageLayersSize.
12429 : bool
12430 242 : CSSParserImpl::ParseValueList(nsCSSPropertyID aPropID)
12431 : {
12432 : // aPropID is a single value prop-id
12433 484 : nsCSSValue value;
12434 : // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
12435 242 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
12436 242 : nsCSSValueList* item = value.SetListValue();
12437 : for (;;) {
12438 308 : if (ParseSingleValueProperty(item->mValue, aPropID) !=
12439 : CSSParseResult::Ok) {
12440 7 : return false;
12441 : }
12442 268 : if (!ExpectSymbol(',', true)) {
12443 235 : break;
12444 : }
12445 33 : item->mNext = new nsCSSValueList;
12446 33 : item = item->mNext;
12447 : }
12448 : }
12449 235 : AppendValue(aPropID, value);
12450 235 : return true;
12451 : }
12452 :
12453 : bool
12454 38 : CSSParserImpl::ParseImageLayerRepeat(nsCSSPropertyID aPropID)
12455 : {
12456 76 : nsCSSValue value;
12457 : // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
12458 38 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
12459 75 : nsCSSValuePair valuePair;
12460 38 : if (!ParseImageLayerRepeatValues(valuePair)) {
12461 1 : return false;
12462 : }
12463 37 : nsCSSValuePairList* item = value.SetPairListValue();
12464 : for (;;) {
12465 45 : item->mXValue = valuePair.mXValue;
12466 41 : item->mYValue = valuePair.mYValue;
12467 41 : if (!ExpectSymbol(',', true)) {
12468 37 : break;
12469 : }
12470 4 : if (!ParseImageLayerRepeatValues(valuePair)) {
12471 0 : return false;
12472 : }
12473 4 : item->mNext = new nsCSSValuePairList;
12474 4 : item = item->mNext;
12475 : }
12476 : }
12477 :
12478 37 : AppendValue(aPropID, value);
12479 37 : return true;
12480 : }
12481 :
12482 : bool
12483 59 : CSSParserImpl::ParseImageLayerRepeatValues(nsCSSValuePair& aValue)
12484 : {
12485 59 : nsCSSValue& xValue = aValue.mXValue;
12486 59 : nsCSSValue& yValue = aValue.mYValue;
12487 :
12488 59 : if (ParseEnum(xValue, nsCSSProps::kImageLayerRepeatKTable)) {
12489 58 : int32_t value = xValue.GetIntValue();
12490 : // For single values set yValue as eCSSUnit_Null.
12491 112 : if (value == uint8_t(StyleImageLayerRepeat::RepeatX) ||
12492 112 : value == uint8_t(StyleImageLayerRepeat::RepeatY) ||
12493 54 : !ParseEnum(yValue, nsCSSProps::kImageLayerRepeatPartKTable)) {
12494 : // the caller will fail cases like "repeat-x no-repeat"
12495 : // by expecting a list separator or an end property.
12496 58 : yValue.Reset();
12497 : }
12498 58 : return true;
12499 : }
12500 :
12501 1 : return false;
12502 : }
12503 :
12504 : bool
12505 47 : CSSParserImpl::ParseImageLayerPosition(const nsCSSPropertyID aTable[])
12506 : {
12507 : // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
12508 94 : nsCSSValue position;
12509 47 : if (ParseSingleTokenVariant(position, VARIANT_INHERIT, nullptr)) {
12510 0 : AppendValue(aTable[nsStyleImageLayers::positionX], position);
12511 0 : AppendValue(aTable[nsStyleImageLayers::positionY], position);
12512 0 : return true;
12513 : }
12514 :
12515 94 : nsCSSValue itemValueX;
12516 94 : nsCSSValue itemValueY;
12517 47 : if (!ParsePositionValueSeparateCoords(itemValueX, itemValueY)) {
12518 3 : return false;
12519 : }
12520 :
12521 88 : nsCSSValue valueX;
12522 88 : nsCSSValue valueY;
12523 44 : nsCSSValueList* itemX = valueX.SetListValue();
12524 44 : nsCSSValueList* itemY = valueY.SetListValue();
12525 : for (;;) {
12526 54 : itemX->mValue = itemValueX;
12527 49 : itemY->mValue = itemValueY;
12528 49 : if (!ExpectSymbol(',', true)) {
12529 43 : break;
12530 : }
12531 6 : if (!ParsePositionValueSeparateCoords(itemValueX, itemValueY)) {
12532 1 : return false;
12533 : }
12534 5 : itemX->mNext = new nsCSSValueList;
12535 5 : itemY->mNext = new nsCSSValueList;
12536 5 : itemX = itemX->mNext;
12537 5 : itemY = itemY->mNext;
12538 : }
12539 43 : AppendValue(aTable[nsStyleImageLayers::positionX], valueX);
12540 43 : AppendValue(aTable[nsStyleImageLayers::positionY], valueY);
12541 43 : return true;
12542 : }
12543 :
12544 : bool
12545 0 : CSSParserImpl::ParseImageLayerPositionCoord(nsCSSPropertyID aPropID, bool aIsHorizontal)
12546 : {
12547 0 : nsCSSValue value;
12548 : // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
12549 0 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
12550 0 : nsCSSValue itemValue;
12551 0 : if (!ParseImageLayerPositionCoordItem(itemValue, aIsHorizontal)) {
12552 0 : return false;
12553 : }
12554 0 : nsCSSValueList* item = value.SetListValue();
12555 : for (;;) {
12556 0 : item->mValue = itemValue;
12557 0 : if (!ExpectSymbol(',', true)) {
12558 0 : break;
12559 : }
12560 0 : if (!ParseImageLayerPositionCoordItem(itemValue, aIsHorizontal)) {
12561 0 : return false;
12562 : }
12563 0 : item->mNext = new nsCSSValueList;
12564 0 : item = item->mNext;
12565 : }
12566 : }
12567 0 : AppendValue(aPropID, value);
12568 0 : return true;
12569 : }
12570 :
12571 : /**
12572 : * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used
12573 : * for parsing the CSS 2.1 background-position syntax (which has at
12574 : * most two values). (Compare to the css3-background syntax which
12575 : * takes up to four values.) Some current CSS specifications that
12576 : * use background-position-like syntax still use this old syntax.
12577 : **
12578 : * Parses two values that correspond to positions in a box. These can be
12579 : * values corresponding to percentages of the box, raw offsets, or keywords
12580 : * like "top," "left center," etc.
12581 : *
12582 : * @param aOut The nsCSSValuePair in which to place the result.
12583 : * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are
12584 : * legal values
12585 : * @param aAllowExplicitCenter If true, 'center' is a legal value
12586 : * @return Whether or not the operation succeeded.
12587 : */
12588 8 : bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
12589 : bool aAcceptsInherit,
12590 : bool aAllowExplicitCenter)
12591 : {
12592 : // First try a percentage or a length value
12593 8 : nsCSSValue &xValue = aOut.mXValue,
12594 8 : &yValue = aOut.mYValue;
12595 : int32_t variantMask =
12596 8 : (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC;
12597 8 : CSSParseResult result = ParseVariant(xValue, variantMask, nullptr);
12598 8 : if (result == CSSParseResult::Error) {
12599 0 : return false;
12600 8 : } else if (result == CSSParseResult::Ok) {
12601 10 : if (eCSSUnit_Inherit == xValue.GetUnit() ||
12602 6 : eCSSUnit_Initial == xValue.GetUnit() ||
12603 2 : eCSSUnit_Unset == xValue.GetUnit()) { // both are inherit, initial or unset
12604 2 : yValue = xValue;
12605 2 : return true;
12606 : }
12607 : // We have one percentage/length/calc. Get the optional second
12608 : // percentage/length/calc/keyword.
12609 2 : result = ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr);
12610 2 : if (result == CSSParseResult::Error) {
12611 0 : return false;
12612 2 : } else if (result == CSSParseResult::Ok) {
12613 : // We have two numbers
12614 2 : return true;
12615 : }
12616 :
12617 0 : if (ParseEnum(yValue, nsCSSProps::kImageLayerPositionKTable)) {
12618 0 : int32_t yVal = yValue.GetIntValue();
12619 0 : if (!(yVal & BG_CTB)) {
12620 : // The second keyword can only be 'center', 'top', or 'bottom'
12621 0 : return false;
12622 : }
12623 0 : yValue = BoxPositionMaskToCSSValue(yVal, false);
12624 0 : return true;
12625 : }
12626 :
12627 : // If only one percentage or length value is given, it sets the
12628 : // horizontal position only, and the vertical position will be 50%.
12629 0 : yValue.SetPercentValue(0.5f);
12630 0 : return true;
12631 : }
12632 :
12633 : // Now try keywords. We do this manually to allow for the first
12634 : // appearance of "center" to apply to the either the x or y
12635 : // position (it's ambiguous so we have to disambiguate). Each
12636 : // allowed keyword value is assigned it's own bit. We don't allow
12637 : // any duplicate keywords other than center. We try to get two
12638 : // keywords but it's okay if there is only one.
12639 4 : int32_t mask = 0;
12640 4 : if (ParseEnum(xValue, nsCSSProps::kImageLayerPositionKTable)) {
12641 4 : int32_t bit = xValue.GetIntValue();
12642 4 : mask |= bit;
12643 4 : if (ParseEnum(xValue, nsCSSProps::kImageLayerPositionKTable)) {
12644 0 : bit = xValue.GetIntValue();
12645 0 : if (mask & (bit & ~BG_CENTER)) {
12646 : // Only the 'center' keyword can be duplicated.
12647 0 : return false;
12648 : }
12649 0 : mask |= bit;
12650 : }
12651 : else {
12652 : // Only one keyword. See if we have a length, percentage, or calc.
12653 4 : result = ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr);
12654 4 : if (result == CSSParseResult::Error) {
12655 0 : return false;
12656 4 : } else if (result == CSSParseResult::Ok) {
12657 0 : if (!(mask & BG_CLR)) {
12658 : // The first keyword can only be 'center', 'left', or 'right'
12659 0 : return false;
12660 : }
12661 :
12662 0 : xValue = BoxPositionMaskToCSSValue(mask, true);
12663 0 : return true;
12664 : }
12665 : }
12666 : }
12667 :
12668 : // Check for bad input. Bad input consists of no matching keywords,
12669 : // or pairs of x keywords or pairs of y keywords.
12670 4 : if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
12671 4 : (mask == (BG_LEFT | BG_RIGHT)) ||
12672 8 : (!aAllowExplicitCenter && (mask & BG_CENTER))) {
12673 0 : return false;
12674 : }
12675 :
12676 : // Create style values
12677 4 : xValue = BoxPositionMaskToCSSValue(mask, true);
12678 4 : yValue = BoxPositionMaskToCSSValue(mask, false);
12679 4 : return true;
12680 : }
12681 :
12682 : // Parses a CSS <position> value, for e.g. the 'background-position' property.
12683 : // Spec reference: http://www.w3.org/TR/css3-background/#ltpositiongt
12684 : // Invariants:
12685 : // - Always produces a four-value array on a successful parse.
12686 : // - The values are: X edge, X offset, Y edge, Y offset.
12687 : // - Edges are always keywords or null.
12688 : // - A |center| edge will not have an offset.
12689 : bool
12690 70 : CSSParserImpl::ParsePositionValue(nsCSSValue& aOut)
12691 : {
12692 140 : RefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(4);
12693 70 : aOut.SetArrayValue(value, eCSSUnit_Array);
12694 :
12695 : // The following clarifies organisation of the array.
12696 70 : nsCSSValue &xEdge = value->Item(0),
12697 70 : &xOffset = value->Item(1),
12698 70 : &yEdge = value->Item(2),
12699 70 : &yOffset = value->Item(3);
12700 :
12701 : // Parse all the values into the array.
12702 70 : uint32_t valueCount = 0;
12703 194 : for (int32_t i = 0; i < 4; i++) {
12704 : CSSParseResult result =
12705 192 : ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD,
12706 192 : nsCSSProps::kImageLayerPositionKTable);
12707 192 : if (result == CSSParseResult::Error) {
12708 2 : return false;
12709 190 : } else if (result == CSSParseResult::NotFound) {
12710 66 : break;
12711 : }
12712 124 : ++valueCount;
12713 : }
12714 :
12715 68 : switch (valueCount) {
12716 : case 4:
12717 : // "If three or four values are given, then each <percentage> or <length>
12718 : // represents an offset and must be preceded by a keyword, which specifies
12719 : // from which edge the offset is given."
12720 6 : if (eCSSUnit_Enumerated != xEdge.GetUnit() ||
12721 4 : BG_CENTER == xEdge.GetIntValue() ||
12722 4 : eCSSUnit_Enumerated == xOffset.GetUnit() ||
12723 4 : eCSSUnit_Enumerated != yEdge.GetUnit() ||
12724 6 : BG_CENTER == yEdge.GetIntValue() ||
12725 2 : eCSSUnit_Enumerated == yOffset.GetUnit()) {
12726 0 : return false;
12727 : }
12728 2 : break;
12729 : case 3:
12730 : // "If three or four values are given, then each <percentage> or<length>
12731 : // represents an offset and must be preceded by a keyword, which specifies
12732 : // from which edge the offset is given." ... "If three values are given,
12733 : // the missing offset is assumed to be zero."
12734 7 : if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) {
12735 : // keyword offset keyword
12736 : // Second value is non-keyword, thus first value must be a non-center
12737 : // keyword.
12738 6 : if (eCSSUnit_Enumerated != value->Item(0).GetUnit() ||
12739 3 : BG_CENTER == value->Item(0).GetIntValue()) {
12740 0 : return false;
12741 : }
12742 :
12743 : // Remaining value must be a keyword.
12744 3 : if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
12745 0 : return false;
12746 : }
12747 :
12748 3 : yOffset.Reset(); // Everything else is in the correct position.
12749 4 : } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
12750 : // keyword keyword offset
12751 : // Third value is non-keyword, thus second value must be non-center
12752 : // keyword.
12753 4 : if (BG_CENTER == value->Item(1).GetIntValue()) {
12754 0 : return false;
12755 : }
12756 :
12757 : // Remaining value must be a keyword.
12758 4 : if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) {
12759 0 : return false;
12760 : }
12761 :
12762 : // Move the values to the correct position in the array.
12763 4 : value->Item(3) = value->Item(2); // yOffset
12764 4 : value->Item(2) = value->Item(1); // yEdge
12765 4 : value->Item(1).Reset(); // xOffset
12766 : } else {
12767 0 : return false;
12768 : }
12769 7 : break;
12770 : case 2:
12771 : // "If two values are given and at least one value is not a keyword, then
12772 : // the first value represents the horizontal position (or offset) and the
12773 : // second represents the vertical position (or offset)"
12774 36 : if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
12775 24 : if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
12776 : // keyword keyword
12777 24 : value->Item(2) = value->Item(1); // move yEdge to correct position
12778 24 : xOffset.Reset();
12779 24 : yOffset.Reset();
12780 : } else {
12781 : // keyword offset
12782 : // First value must represent horizontal position.
12783 0 : if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) {
12784 0 : return false;
12785 : }
12786 0 : value->Item(3) = value->Item(1); // move yOffset to correct position
12787 0 : xOffset.Reset();
12788 0 : yEdge.Reset();
12789 : }
12790 : } else {
12791 12 : if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
12792 : // offset keyword
12793 : // Second value must represent vertical position.
12794 2 : if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) {
12795 0 : return false;
12796 : }
12797 2 : value->Item(2) = value->Item(1); // move yEdge to correct position
12798 2 : value->Item(1) = value->Item(0); // move xOffset to correct position
12799 2 : xEdge.Reset();
12800 2 : yOffset.Reset();
12801 : } else {
12802 : // offset offset
12803 10 : value->Item(3) = value->Item(1); // move yOffset to correct position
12804 10 : value->Item(1) = value->Item(0); // move xOffset to correct position
12805 10 : xEdge.Reset();
12806 10 : yEdge.Reset();
12807 : }
12808 : }
12809 36 : break;
12810 : case 1:
12811 : // "If only one value is specified, the second value is assumed to be
12812 : // center."
12813 21 : if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
12814 18 : xOffset.Reset();
12815 : } else {
12816 3 : value->Item(1) = value->Item(0); // move xOffset to correct position
12817 3 : xEdge.Reset();
12818 : }
12819 21 : yEdge.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_CENTER, eCSSUnit_Enumerated);
12820 21 : yOffset.Reset();
12821 21 : break;
12822 : default:
12823 2 : return false;
12824 : }
12825 :
12826 : // For compatibility with CSS2.1 code the edges can be unspecified.
12827 : // Unspecified edges are recorded as nullptr.
12828 66 : NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() ||
12829 : eCSSUnit_Null == xEdge.GetUnit()) &&
12830 : (eCSSUnit_Enumerated == yEdge.GetUnit() ||
12831 : eCSSUnit_Null == yEdge.GetUnit()) &&
12832 : eCSSUnit_Enumerated != xOffset.GetUnit() &&
12833 : eCSSUnit_Enumerated != yOffset.GetUnit(),
12834 : "Unexpected units");
12835 :
12836 : // Keywords in first and second pairs can not both be vertical or
12837 : // horizontal keywords. (eg. left right, bottom top). Additionally,
12838 : // non-center keyword can not be duplicated (eg. left left).
12839 : int32_t xEdgeEnum =
12840 66 : xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0;
12841 : int32_t yEdgeEnum =
12842 66 : yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0;
12843 132 : if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) ||
12844 132 : (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) ||
12845 66 : (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) {
12846 0 : return false;
12847 : }
12848 :
12849 : // The values could be in an order that is different than expected.
12850 : // eg. x contains vertical information, y contains horizontal information.
12851 : // Swap if incorrect order.
12852 129 : if (xEdgeEnum & (BG_TOP | BG_BOTTOM) ||
12853 63 : yEdgeEnum & (BG_LEFT | BG_RIGHT)) {
12854 14 : nsCSSValue swapEdge = xEdge;
12855 14 : nsCSSValue swapOffset = xOffset;
12856 7 : xEdge = yEdge;
12857 7 : xOffset = yOffset;
12858 7 : yEdge = swapEdge;
12859 7 : yOffset = swapOffset;
12860 : }
12861 :
12862 66 : return true;
12863 : }
12864 :
12865 : static void
12866 0 : AdjustEdgeOffsetPairForBasicShape(nsCSSValue& aEdge,
12867 : nsCSSValue& aOffset,
12868 : uint8_t aDefaultEdge)
12869 : {
12870 : // 0 length offsets are 0%
12871 0 : if (aOffset.IsLengthUnit() && aOffset.GetFloatValue() == 0.0) {
12872 0 : aOffset.SetPercentValue(0);
12873 : }
12874 :
12875 : // Default edge is top/left in the 4-value case
12876 : // In case of 1 or 0 values, the default is center,
12877 : // but ParsePositionValue already handles this case
12878 0 : if (eCSSUnit_Null == aEdge.GetUnit()) {
12879 0 : aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
12880 : }
12881 : // Default offset is 0%
12882 0 : if (eCSSUnit_Null == aOffset.GetUnit()) {
12883 0 : aOffset.SetPercentValue(0.0);
12884 : }
12885 0 : if (eCSSUnit_Enumerated == aEdge.GetUnit() &&
12886 0 : eCSSUnit_Percent == aOffset.GetUnit()) {
12887 0 : switch (aEdge.GetIntValue()) {
12888 : case NS_STYLE_IMAGELAYER_POSITION_CENTER:
12889 0 : aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
12890 0 : MOZ_ASSERT(aOffset.GetPercentValue() == 0.0,
12891 : "center cannot be used with an offset");
12892 0 : aOffset.SetPercentValue(0.5);
12893 0 : break;
12894 : case NS_STYLE_IMAGELAYER_POSITION_BOTTOM:
12895 0 : MOZ_ASSERT(aDefaultEdge == NS_STYLE_IMAGELAYER_POSITION_TOP);
12896 0 : aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
12897 0 : aOffset.SetPercentValue(1 - aOffset.GetPercentValue());
12898 0 : break;
12899 : case NS_STYLE_IMAGELAYER_POSITION_RIGHT:
12900 0 : MOZ_ASSERT(aDefaultEdge == NS_STYLE_IMAGELAYER_POSITION_LEFT);
12901 0 : aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated);
12902 0 : aOffset.SetPercentValue(1 - aOffset.GetPercentValue());
12903 : }
12904 : }
12905 0 : }
12906 :
12907 : // https://drafts.csswg.org/css-shapes/#basic-shape-serialization
12908 : // We set values to defaults while parsing for basic shapes
12909 : // Invariants:
12910 : // - Always produces a four-value array on a successful parse.
12911 : // - The values are: X edge, X offset, Y edge, Y offset
12912 : // - Edges are always keywords (not including center)
12913 : // - Offsets are nonnull
12914 : // - Percentage offsets have keywords folded into them,
12915 : // so "bottom 40%" or "right 20%" will not exist.
12916 : bool
12917 0 : CSSParserImpl::ParsePositionValueForBasicShape(nsCSSValue& aOut)
12918 : {
12919 0 : if (!ParsePositionValue(aOut)) {
12920 0 : return false;
12921 : }
12922 0 : nsCSSValue::Array* value = aOut.GetArrayValue();
12923 0 : nsCSSValue& xEdge = value->Item(0);
12924 0 : nsCSSValue& xOffset = value->Item(1);
12925 0 : nsCSSValue& yEdge = value->Item(2);
12926 0 : nsCSSValue& yOffset = value->Item(3);
12927 : // A keyword edge + percent offset pair can be contracted
12928 : // into the percentage with the default value in the edge.
12929 : // Offset lengths which are 0 can also be rewritten as 0%
12930 : AdjustEdgeOffsetPairForBasicShape(xEdge, xOffset,
12931 0 : NS_STYLE_IMAGELAYER_POSITION_LEFT);
12932 : AdjustEdgeOffsetPairForBasicShape(yEdge, yOffset,
12933 0 : NS_STYLE_IMAGELAYER_POSITION_TOP);
12934 0 : return true;
12935 : }
12936 :
12937 : bool
12938 70 : CSSParserImpl::ParsePositionValueSeparateCoords(nsCSSValue& aOutX, nsCSSValue& aOutY)
12939 : {
12940 140 : nsCSSValue scratch;
12941 70 : if (!ParsePositionValue(scratch)) {
12942 4 : return false;
12943 : }
12944 :
12945 : // Separate the four values into two pairs of two values for X and Y.
12946 132 : RefPtr<nsCSSValue::Array> valueX = nsCSSValue::Array::Create(2);
12947 132 : RefPtr<nsCSSValue::Array> valueY = nsCSSValue::Array::Create(2);
12948 66 : aOutX.SetArrayValue(valueX, eCSSUnit_Array);
12949 66 : aOutY.SetArrayValue(valueY, eCSSUnit_Array);
12950 :
12951 132 : RefPtr<nsCSSValue::Array> value = scratch.GetArrayValue();
12952 66 : valueX->Item(0) = value->Item(0);
12953 66 : valueX->Item(1) = value->Item(1);
12954 66 : valueY->Item(0) = value->Item(2);
12955 66 : valueY->Item(1) = value->Item(3);
12956 66 : return true;
12957 : }
12958 :
12959 : // Parses one item in a list of values for the 'background-position-x' or
12960 : // 'background-position-y' property. Does not support the start/end keywords.
12961 : // Spec reference: https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-x
12962 : bool
12963 0 : CSSParserImpl::ParseImageLayerPositionCoordItem(nsCSSValue& aOut, bool aIsHorizontal)
12964 : {
12965 0 : RefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(2);
12966 0 : aOut.SetArrayValue(value, eCSSUnit_Array);
12967 :
12968 0 : nsCSSValue &edge = value->Item(0),
12969 0 : &offset = value->Item(1);
12970 :
12971 0 : nsCSSValue edgeOrOffset;
12972 : CSSParseResult result =
12973 : ParseVariant(edgeOrOffset, VARIANT_LPCALC | VARIANT_KEYWORD,
12974 0 : nsCSSProps::kImageLayerPositionKTable);
12975 0 : if (result != CSSParseResult::Ok) {
12976 0 : return false;
12977 : }
12978 :
12979 0 : if (edgeOrOffset.GetUnit() == eCSSUnit_Enumerated) {
12980 0 : edge = edgeOrOffset;
12981 :
12982 : // The edge can be followed by an optional offset.
12983 0 : result = ParseVariant(offset, VARIANT_LPCALC, nullptr);
12984 0 : if (result == CSSParseResult::Error) {
12985 0 : return false;
12986 : }
12987 : } else {
12988 0 : offset = edgeOrOffset;
12989 : }
12990 :
12991 : // Keywords for horizontal properties cannot be vertical keywords, and
12992 : // keywords for vertical properties cannot be horizontal keywords.
12993 : // Also, if an offset is specified, the edge cannot be center.
12994 : int32_t edgeEnum =
12995 0 : edge.GetUnit() == eCSSUnit_Enumerated ? edge.GetIntValue() : 0;
12996 : int32_t allowedKeywords =
12997 0 : (aIsHorizontal ? (BG_LEFT | BG_RIGHT) : (BG_TOP | BG_BOTTOM)) |
12998 0 : (offset.GetUnit() == eCSSUnit_Null ? BG_CENTER : 0);
12999 0 : if (edgeEnum & ~allowedKeywords) {
13000 0 : return false;
13001 : }
13002 :
13003 0 : NS_ASSERTION((eCSSUnit_Enumerated == edge.GetUnit() ||
13004 : eCSSUnit_Null == edge.GetUnit()) &&
13005 : eCSSUnit_Enumerated != offset.GetUnit(),
13006 : "Unexpected units");
13007 :
13008 0 : return true;
13009 : }
13010 :
13011 : // This function is very similar to ParseScrollSnapCoordinate,
13012 : // ParseImageLayers, and ParseImageLayerPosition.
13013 : bool
13014 36 : CSSParserImpl::ParseImageLayerSize(nsCSSPropertyID aPropID)
13015 : {
13016 72 : nsCSSValue value;
13017 : // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
13018 36 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13019 69 : nsCSSValuePair valuePair;
13020 36 : if (!ParseImageLayerSizeValues(valuePair)) {
13021 3 : return false;
13022 : }
13023 33 : nsCSSValuePairList* item = value.SetPairListValue();
13024 : for (;;) {
13025 47 : item->mXValue = valuePair.mXValue;
13026 40 : item->mYValue = valuePair.mYValue;
13027 40 : if (!ExpectSymbol(',', true)) {
13028 33 : break;
13029 : }
13030 7 : if (!ParseImageLayerSizeValues(valuePair)) {
13031 0 : return false;
13032 : }
13033 7 : item->mNext = new nsCSSValuePairList;
13034 7 : item = item->mNext;
13035 : }
13036 : }
13037 33 : AppendValue(aPropID, value);
13038 33 : return true;
13039 : }
13040 :
13041 : /**
13042 : * Parses two values that correspond to lengths for the background-size
13043 : * property. These can be one or two lengths (or the 'auto' keyword) or
13044 : * percentages corresponding to the element's dimensions or the single keywords
13045 : * 'contain' or 'cover'. 'initial', 'inherit' and 'unset' must be handled by
13046 : * the caller if desired.
13047 : *
13048 : * @param aOut The nsCSSValuePair in which to place the result.
13049 : * @return Whether or not the operation succeeded.
13050 : */
13051 : #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC)
13052 44 : bool CSSParserImpl::ParseImageLayerSizeValues(nsCSSValuePair &aOut)
13053 : {
13054 : // First try a percentage or a length value
13055 44 : nsCSSValue &xValue = aOut.mXValue,
13056 44 : &yValue = aOut.mYValue;
13057 : CSSParseResult result =
13058 44 : ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr);
13059 44 : if (result == CSSParseResult::Error) {
13060 0 : return false;
13061 44 : } else if (result == CSSParseResult::Ok) {
13062 : // We have one percentage/length/calc/auto. Get the optional second
13063 : // percentage/length/calc/keyword.
13064 37 : result = ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr);
13065 37 : if (result == CSSParseResult::Error) {
13066 0 : return false;
13067 37 : } else if (result == CSSParseResult::Ok) {
13068 : // We have a second percentage/length/calc/auto.
13069 25 : return true;
13070 : }
13071 :
13072 : // If only one percentage or length value is given, it sets the
13073 : // horizontal size only, and the vertical size will be as if by 'auto'.
13074 12 : yValue.SetAutoValue();
13075 12 : return true;
13076 : }
13077 :
13078 : // Now address 'contain' and 'cover'.
13079 7 : if (!ParseEnum(xValue, nsCSSProps::kImageLayerSizeKTable))
13080 3 : return false;
13081 4 : yValue.Reset();
13082 4 : return true;
13083 : }
13084 :
13085 : #undef BG_SIZE_VARIANT
13086 :
13087 : bool
13088 42 : CSSParserImpl::ParseBorderColor()
13089 : {
13090 42 : return ParseBoxProperties(kBorderColorIDs);
13091 : }
13092 :
13093 : void
13094 241 : CSSParserImpl::SetBorderImageInitialValues()
13095 : {
13096 : // border-image-source: none
13097 482 : nsCSSValue source;
13098 241 : source.SetNoneValue();
13099 241 : AppendValue(eCSSProperty_border_image_source, source);
13100 :
13101 : // border-image-slice: 100%
13102 482 : nsCSSValue sliceBoxValue;
13103 241 : nsCSSRect& sliceBox = sliceBoxValue.SetRectValue();
13104 241 : sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent));
13105 482 : nsCSSValue slice;
13106 241 : nsCSSValueList* sliceList = slice.SetListValue();
13107 241 : sliceList->mValue = sliceBoxValue;
13108 241 : AppendValue(eCSSProperty_border_image_slice, slice);
13109 :
13110 : // border-image-width: 1
13111 482 : nsCSSValue width;
13112 241 : nsCSSRect& widthBox = width.SetRectValue();
13113 241 : widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number));
13114 241 : AppendValue(eCSSProperty_border_image_width, width);
13115 :
13116 : // border-image-outset: 0
13117 482 : nsCSSValue outset;
13118 241 : nsCSSRect& outsetBox = outset.SetRectValue();
13119 241 : outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number));
13120 241 : AppendValue(eCSSProperty_border_image_outset, outset);
13121 :
13122 : // border-image-repeat: repeat
13123 482 : nsCSSValue repeat;
13124 482 : nsCSSValuePair repeatPair;
13125 482 : repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
13126 241 : eCSSUnit_Enumerated));
13127 241 : repeat.SetPairValue(&repeatPair);
13128 241 : AppendValue(eCSSProperty_border_image_repeat, repeat);
13129 241 : }
13130 :
13131 : bool
13132 4 : CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
13133 : bool* aConsumedTokens)
13134 : {
13135 : // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
13136 8 : nsCSSValue value;
13137 :
13138 4 : if (aConsumedTokens) {
13139 0 : *aConsumedTokens = true;
13140 : }
13141 :
13142 8 : if (aAcceptsInherit &&
13143 4 : ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13144 : // Keywords "inherit", "initial" and "unset" can not be mixed, so we
13145 : // are done.
13146 0 : AppendValue(eCSSProperty_border_image_slice, value);
13147 0 : return true;
13148 : }
13149 :
13150 : // Try parsing "fill" value.
13151 8 : nsCSSValue imageSliceFillValue;
13152 : bool hasFill = ParseEnum(imageSliceFillValue,
13153 4 : nsCSSProps::kBorderImageSliceKTable);
13154 :
13155 : // Parse the box dimensions.
13156 8 : nsCSSValue imageSliceBoxValue;
13157 4 : if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue,
13158 : CSS_PROPERTY_VALUE_NONNEGATIVE)) {
13159 0 : if (!hasFill && aConsumedTokens) {
13160 0 : *aConsumedTokens = false;
13161 : }
13162 :
13163 0 : return false;
13164 : }
13165 :
13166 : // Try parsing "fill" keyword again if the first time failed because keyword
13167 : // and slice dimensions can be in any order.
13168 4 : if (!hasFill) {
13169 : hasFill = ParseEnum(imageSliceFillValue,
13170 4 : nsCSSProps::kBorderImageSliceKTable);
13171 : }
13172 :
13173 4 : nsCSSValueList* borderImageSlice = value.SetListValue();
13174 : // Put the box value into the list.
13175 4 : borderImageSlice->mValue = imageSliceBoxValue;
13176 :
13177 4 : if (hasFill) {
13178 : // Put the "fill" value into the list.
13179 0 : borderImageSlice->mNext = new nsCSSValueList;
13180 0 : borderImageSlice->mNext->mValue = imageSliceFillValue;
13181 : }
13182 :
13183 4 : AppendValue(eCSSProperty_border_image_slice, value);
13184 4 : return true;
13185 : }
13186 :
13187 : bool
13188 0 : CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
13189 : {
13190 : // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
13191 0 : nsCSSValue value;
13192 :
13193 0 : if (aAcceptsInherit &&
13194 0 : ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13195 : // Keywords "inherit", "initial" and "unset" can not be mixed, so we
13196 : // are done.
13197 0 : AppendValue(eCSSProperty_border_image_width, value);
13198 0 : return true;
13199 : }
13200 :
13201 : // Parse the box dimensions.
13202 0 : if (!ParseGroupedBoxProperty(VARIANT_ALPN, value, CSS_PROPERTY_VALUE_NONNEGATIVE)) {
13203 0 : return false;
13204 : }
13205 :
13206 0 : AppendValue(eCSSProperty_border_image_width, value);
13207 0 : return true;
13208 : }
13209 :
13210 : bool
13211 0 : CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
13212 : {
13213 : // border-image-outset: initial | [<length>|<number>]{1,4}
13214 0 : nsCSSValue value;
13215 :
13216 0 : if (aAcceptsInherit &&
13217 0 : ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13218 : // Keywords "inherit", "initial" and "unset" can not be mixed, so we
13219 : // are done.
13220 0 : AppendValue(eCSSProperty_border_image_outset, value);
13221 0 : return true;
13222 : }
13223 :
13224 : // Parse the box dimensions.
13225 0 : if (!ParseGroupedBoxProperty(VARIANT_LN, value, CSS_PROPERTY_VALUE_NONNEGATIVE)) {
13226 0 : return false;
13227 : }
13228 :
13229 0 : AppendValue(eCSSProperty_border_image_outset, value);
13230 0 : return true;
13231 : }
13232 :
13233 : bool
13234 0 : CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit)
13235 : {
13236 0 : nsCSSValue value;
13237 0 : if (aAcceptsInherit &&
13238 0 : ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13239 : // Keywords "inherit", "initial" and "unset" can not be mixed, so we
13240 : // are done.
13241 0 : AppendValue(eCSSProperty_border_image_repeat, value);
13242 0 : return true;
13243 : }
13244 :
13245 0 : nsCSSValuePair result;
13246 0 : if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) {
13247 0 : return false;
13248 : }
13249 :
13250 : // optional second keyword, defaults to first
13251 0 : if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) {
13252 0 : result.mYValue = result.mXValue;
13253 : }
13254 :
13255 0 : value.SetPairValue(&result);
13256 0 : AppendValue(eCSSProperty_border_image_repeat, value);
13257 0 : return true;
13258 : }
13259 :
13260 : bool
13261 6 : CSSParserImpl::ParseBorderImage()
13262 : {
13263 12 : nsAutoParseCompoundProperty compound(this);
13264 :
13265 : // border-image: inherit | initial |
13266 : // <border-image-source> ||
13267 : // <border-image-slice>
13268 : // [ / <border-image-width> |
13269 : // / <border-image-width>? / <border-image-outset> ]? ||
13270 : // <border-image-repeat>
13271 :
13272 12 : nsCSSValue value;
13273 6 : if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13274 2 : AppendValue(eCSSProperty_border_image_source, value);
13275 2 : AppendValue(eCSSProperty_border_image_slice, value);
13276 2 : AppendValue(eCSSProperty_border_image_width, value);
13277 2 : AppendValue(eCSSProperty_border_image_outset, value);
13278 2 : AppendValue(eCSSProperty_border_image_repeat, value);
13279 : // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done.
13280 2 : return true;
13281 : }
13282 :
13283 : // No empty property.
13284 4 : if (CheckEndProperty()) {
13285 0 : return false;
13286 : }
13287 :
13288 : // Shorthand properties are required to set everything they can.
13289 4 : SetBorderImageInitialValues();
13290 :
13291 4 : bool foundSource = false;
13292 4 : bool foundSliceWidthOutset = false;
13293 4 : bool foundRepeat = false;
13294 :
13295 : // This loop is used to handle the parsing of border-image properties which
13296 : // can appear in any order.
13297 8 : nsCSSValue imageSourceValue;
13298 8 : while (!CheckEndProperty()) {
13299 : // <border-image-source>
13300 4 : if (!foundSource) {
13301 : CSSParseResult result =
13302 4 : ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr);
13303 4 : if (result == CSSParseResult::Error) {
13304 2 : return false;
13305 2 : } else if (result == CSSParseResult::Ok) {
13306 2 : AppendValue(eCSSProperty_border_image_source, imageSourceValue);
13307 2 : foundSource = true;
13308 2 : continue;
13309 : }
13310 : }
13311 :
13312 : // <border-image-slice>
13313 : // ParseBorderImageSlice is weird. It may consume tokens and then return
13314 : // false, because it parses a property with two required components that
13315 : // can appear in either order. Since the tokens that were consumed cannot
13316 : // parse as anything else we care about, this isn't a problem.
13317 0 : if (!foundSliceWidthOutset) {
13318 0 : bool sliceConsumedTokens = false;
13319 0 : if (ParseBorderImageSlice(false, &sliceConsumedTokens)) {
13320 0 : foundSliceWidthOutset = true;
13321 :
13322 : // [ / <border-image-width>?
13323 0 : if (ExpectSymbol('/', true)) {
13324 0 : bool foundBorderImageWidth = ParseBorderImageWidth(false);
13325 :
13326 : // [ / <border-image-outset>
13327 0 : if (ExpectSymbol('/', true)) {
13328 0 : if (!ParseBorderImageOutset(false)) {
13329 0 : return false;
13330 : }
13331 0 : } else if (!foundBorderImageWidth) {
13332 : // If this part has an trailing slash, the whole declaration is
13333 : // invalid.
13334 0 : return false;
13335 : }
13336 : }
13337 :
13338 0 : continue;
13339 : } else {
13340 : // If we consumed some tokens for <border-image-slice> but did not
13341 : // successfully parse it, we have an error.
13342 0 : if (sliceConsumedTokens) {
13343 0 : return false;
13344 : }
13345 : }
13346 : }
13347 :
13348 : // <border-image-repeat>
13349 0 : if (!foundRepeat && ParseBorderImageRepeat(false)) {
13350 0 : foundRepeat = true;
13351 0 : continue;
13352 : }
13353 :
13354 0 : return false;
13355 : }
13356 :
13357 2 : return true;
13358 : }
13359 :
13360 : bool
13361 2 : CSSParserImpl::ParseBorderSpacing()
13362 : {
13363 4 : nsCSSValue xValue, yValue;
13364 2 : if (ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr) !=
13365 : CSSParseResult::Ok) {
13366 0 : return false;
13367 : }
13368 :
13369 : // If we have one length, get the optional second length.
13370 : // set the second value equal to the first.
13371 2 : if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) {
13372 2 : if (ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC,
13373 : nullptr) == CSSParseResult::Error) {
13374 0 : return false;
13375 : }
13376 : }
13377 :
13378 2 : if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) {
13379 2 : AppendValue(eCSSProperty_border_spacing, xValue);
13380 : } else {
13381 0 : nsCSSValue pair;
13382 0 : pair.SetPairValue(xValue, yValue);
13383 0 : AppendValue(eCSSProperty_border_spacing, pair);
13384 : }
13385 2 : return true;
13386 : }
13387 :
13388 : bool
13389 360 : CSSParserImpl::ParseBorderSide(const nsCSSPropertyID aPropIDs[],
13390 : bool aSetAllSides)
13391 : {
13392 360 : const int32_t numProps = 3;
13393 720 : nsCSSValue values[numProps];
13394 :
13395 360 : int32_t found = ParseChoice(values, aPropIDs, numProps);
13396 360 : if (found < 1) {
13397 2 : return false;
13398 : }
13399 :
13400 358 : if ((found & 1) == 0) { // Provide default border-width
13401 56 : values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
13402 : }
13403 358 : if ((found & 2) == 0) { // Provide default border-style
13404 17 : values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
13405 : }
13406 358 : if ((found & 4) == 0) { // text color will be used
13407 134 : values[2].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
13408 : }
13409 :
13410 358 : if (aSetAllSides) {
13411 : // Parsing "border" shorthand; set all four sides to the same thing
13412 1195 : for (int32_t index = 0; index < 4; index++) {
13413 : NS_ASSERTION(numProps == 3, "This code needs updating");
13414 956 : AppendValue(kBorderWidthIDs[index], values[0]);
13415 956 : AppendValue(kBorderStyleIDs[index], values[1]);
13416 956 : AppendValue(kBorderColorIDs[index], values[2]);
13417 : }
13418 :
13419 : static const nsCSSPropertyID kBorderColorsProps[] = {
13420 : eCSSProperty__moz_border_top_colors,
13421 : eCSSProperty__moz_border_right_colors,
13422 : eCSSProperty__moz_border_bottom_colors,
13423 : eCSSProperty__moz_border_left_colors
13424 : };
13425 :
13426 : // Set the other properties that the border shorthand sets to their
13427 : // initial values.
13428 478 : nsCSSValue extraValue;
13429 239 : switch (values[0].GetUnit()) {
13430 : case eCSSUnit_Inherit:
13431 : case eCSSUnit_Initial:
13432 : case eCSSUnit_Unset:
13433 2 : extraValue = values[0];
13434 : // Set value of border-image properties to initial/inherit/unset
13435 2 : AppendValue(eCSSProperty_border_image_source, extraValue);
13436 2 : AppendValue(eCSSProperty_border_image_slice, extraValue);
13437 2 : AppendValue(eCSSProperty_border_image_width, extraValue);
13438 2 : AppendValue(eCSSProperty_border_image_outset, extraValue);
13439 2 : AppendValue(eCSSProperty_border_image_repeat, extraValue);
13440 2 : break;
13441 : default:
13442 237 : extraValue.SetNoneValue();
13443 237 : SetBorderImageInitialValues();
13444 237 : break;
13445 : }
13446 1195 : NS_FOR_CSS_SIDES(side) {
13447 956 : AppendValue(kBorderColorsProps[side], extraValue);
13448 : }
13449 : }
13450 : else {
13451 : // Just set our one side
13452 476 : for (int32_t index = 0; index < numProps; index++) {
13453 357 : AppendValue(aPropIDs[index], values[index]);
13454 : }
13455 : }
13456 358 : return true;
13457 : }
13458 :
13459 : bool
13460 44 : CSSParserImpl::ParseBorderStyle()
13461 : {
13462 44 : return ParseBoxProperties(kBorderStyleIDs);
13463 : }
13464 :
13465 : bool
13466 27 : CSSParserImpl::ParseBorderWidth()
13467 : {
13468 27 : return ParseBoxProperties(kBorderWidthIDs);
13469 : }
13470 :
13471 : bool
13472 34 : CSSParserImpl::ParseBorderColors(nsCSSPropertyID aProperty)
13473 : {
13474 68 : nsCSSValue value;
13475 : // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
13476 34 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
13477 : nullptr)) {
13478 30 : nsCSSValueList *cur = value.SetListValue();
13479 : for (;;) {
13480 82 : if (ParseVariant(cur->mValue, VARIANT_COLOR, nullptr) !=
13481 : CSSParseResult::Ok) {
13482 0 : return false;
13483 : }
13484 56 : if (CheckEndProperty()) {
13485 30 : break;
13486 : }
13487 26 : cur->mNext = new nsCSSValueList;
13488 26 : cur = cur->mNext;
13489 : }
13490 : }
13491 34 : AppendValue(aProperty, value);
13492 34 : return true;
13493 : }
13494 :
13495 : // Parse the top level of a calc() expression.
13496 : bool
13497 226 : CSSParserImpl::ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask)
13498 : {
13499 : // Parsing calc expressions requires, in a number of cases, looking
13500 : // for a token that is *either* a value of the property or a number.
13501 : // This can be done without lookahead when we assume that the property
13502 : // values cannot themselves be numbers.
13503 226 : MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
13504 226 : MOZ_ASSERT(!(aVariantMask & VARIANT_LPN) != !(aVariantMask & VARIANT_INTEGER),
13505 : "variant mask must intersect with exactly one of VARIANT_LPN "
13506 : "or VARIANT_INTEGER");
13507 :
13508 226 : bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk;
13509 226 : mUnitlessLengthQuirk = false;
13510 :
13511 : // One-iteration loop so we can break to the error-handling case.
13512 : do {
13513 : // The toplevel of a calc() is always an nsCSSValue::Array of length 1.
13514 226 : RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
13515 :
13516 226 : if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask))
13517 21 : break;
13518 :
13519 205 : if (!ExpectSymbol(')', true))
13520 0 : break;
13521 :
13522 205 : aValue.SetArrayValue(arr, eCSSUnit_Calc);
13523 205 : mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
13524 205 : return true;
13525 : } while (false);
13526 :
13527 21 : SkipUntil(')');
13528 21 : mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
13529 21 : return false;
13530 : }
13531 :
13532 : // We optimize away the <value-expression> production given that
13533 : // ParseVariant consumes initial whitespace and we call
13534 : // ExpectSymbol(')') with true for aSkipWS.
13535 : // * If aVariantMask is VARIANT_NUMBER, this function parses the
13536 : // <number-additive-expression> production.
13537 : // * If aVariantMask does not contain VARIANT_NUMBER, this function
13538 : // parses the <value-additive-expression> production.
13539 : // * Otherwise (VARIANT_NUMBER and other bits) this function parses
13540 : // whichever one of the productions matches ***and modifies
13541 : // aVariantMask*** to reflect which one it has parsed by either
13542 : // removing VARIANT_NUMBER or removing all other bits.
13543 : // It does so iteratively, but builds the correct recursive
13544 : // data structure.
13545 : bool
13546 251 : CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue,
13547 : uint32_t& aVariantMask)
13548 : {
13549 251 : MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
13550 251 : nsCSSValue *storage = &aValue;
13551 : for (;;) {
13552 : bool haveWS;
13553 428 : if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS))
13554 274 : return false;
13555 :
13556 405 : if (!haveWS || !GetToken(false))
13557 228 : return true;
13558 : nsCSSUnit unit;
13559 177 : if (mToken.IsSymbol('+')) {
13560 125 : unit = eCSSUnit_Calc_Plus;
13561 52 : } else if (mToken.IsSymbol('-')) {
13562 52 : unit = eCSSUnit_Calc_Minus;
13563 : } else {
13564 0 : UngetToken();
13565 0 : return true;
13566 : }
13567 177 : if (!RequireWhitespace())
13568 0 : return false;
13569 :
13570 354 : RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
13571 177 : arr->Item(0) = aValue;
13572 177 : storage = &arr->Item(1);
13573 177 : aValue.SetArrayValue(arr, unit);
13574 177 : }
13575 : }
13576 :
13577 : // * If aVariantMask is VARIANT_NUMBER, this function parses the
13578 : // <number-multiplicative-expression> production.
13579 : // * If aVariantMask does not contain VARIANT_NUMBER, this function
13580 : // parses the <value-multiplicative-expression> production.
13581 : // * Otherwise (VARIANT_NUMBER and other bits) this function parses
13582 : // whichever one of the productions matches ***and modifies
13583 : // aVariantMask*** to reflect which one it has parsed by either
13584 : // removing VARIANT_NUMBER or removing all other bits.
13585 : // It does so iteratively, but builds the correct recursive data
13586 : // structure.
13587 : // This function always consumes *trailing* whitespace when it returns
13588 : // true; whether there was any such whitespace is returned in the
13589 : // aHadFinalWS parameter.
13590 : bool
13591 428 : CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
13592 : uint32_t& aVariantMask,
13593 : bool *aHadFinalWS)
13594 : {
13595 428 : MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
13596 428 : bool gotValue = false; // already got the part with the unit
13597 428 : bool afterDivision = false;
13598 :
13599 428 : nsCSSValue *storage = &aValue;
13600 : for (;;) {
13601 : uint32_t variantMask;
13602 563 : if (aVariantMask & VARIANT_INTEGER) {
13603 0 : MOZ_ASSERT(aVariantMask == VARIANT_INTEGER,
13604 : "integers in calc expressions can't be mixed with anything "
13605 : "else.");
13606 0 : variantMask = aVariantMask;
13607 563 : } else if (afterDivision || gotValue) {
13608 : // At this point in the calc expression, we expect a coefficient or a
13609 : // divisor, which must be a number. (Not a length/%/etc.)
13610 26 : variantMask = VARIANT_NUMBER;
13611 : } else {
13612 : // At this point in the calc expression, we'll accept a coefficient
13613 : // (a number) or a value of whatever type |aVariantMask| specifies.
13614 537 : variantMask = aVariantMask | VARIANT_NUMBER;
13615 : }
13616 563 : if (!ParseCalcTerm(*storage, variantMask))
13617 46 : return false;
13618 540 : MOZ_ASSERT(variantMask != 0,
13619 : "ParseCalcTerm did not set variantMask appropriately");
13620 540 : MOZ_ASSERT(!(variantMask & VARIANT_NUMBER) ||
13621 : !(variantMask & ~int32_t(VARIANT_NUMBER)),
13622 : "ParseCalcTerm did not set variantMask appropriately");
13623 :
13624 540 : if (variantMask & VARIANT_NUMBER) {
13625 : // Simplify the value immediately so we can check for division by
13626 : // zero.
13627 : mozilla::css::ReduceNumberCalcOps ops;
13628 135 : float number = mozilla::css::ComputeCalc(*storage, ops);
13629 135 : if (number == 0.0 && afterDivision)
13630 0 : return false;
13631 135 : storage->SetFloatValue(number, eCSSUnit_Number);
13632 : } else {
13633 405 : gotValue = true;
13634 :
13635 405 : if (storage != &aValue) {
13636 : // Simplify any numbers in the Times_L position (which are
13637 : // not simplified by the check above).
13638 98 : MOZ_ASSERT(storage == &aValue.GetArrayValue()->Item(1),
13639 : "unexpected relationship to current storage");
13640 98 : nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
13641 98 : if (variantMask & VARIANT_INTEGER) {
13642 : mozilla::css::ReduceIntegerCalcOps ops;
13643 0 : int integer = mozilla::css::ComputeCalc(leftValue, ops);
13644 0 : leftValue.SetIntValue(integer, eCSSUnit_Integer);
13645 : } else {
13646 : mozilla::css::ReduceNumberCalcOps ops;
13647 98 : float number = mozilla::css::ComputeCalc(leftValue, ops);
13648 98 : leftValue.SetFloatValue(number, eCSSUnit_Number);
13649 : }
13650 : }
13651 : }
13652 :
13653 540 : bool hadWS = RequireWhitespace();
13654 540 : if (!GetToken(false)) {
13655 0 : *aHadFinalWS = hadWS;
13656 405 : break;
13657 : }
13658 : nsCSSUnit unit;
13659 540 : if (mToken.IsSymbol('*')) {
13660 109 : unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
13661 109 : afterDivision = false;
13662 431 : } else if (mToken.IsSymbol('/')) {
13663 26 : if (variantMask & VARIANT_INTEGER) {
13664 : // Integers aren't mixed with anything else (see the assert at the top
13665 : // of CSSParserImpl::ParseCalc).
13666 : // We don't allow division at all in calc()s for expressions where an
13667 : // integer is expected, because calc() division can't be resolved to
13668 : // an integer, as implied by spec text about '/' here:
13669 : // https://drafts.csswg.org/css-values-3/#calc-type-checking
13670 : // We've consumed the '/' token, but it doesn't matter as we're in an
13671 : // error-handling situation where we've already consumed a lot of
13672 : // other tokens (e.g. the token before the '/'). ParseVariant will
13673 : // indicate this with CSSParseResult::Error.
13674 0 : return false;
13675 : }
13676 26 : unit = eCSSUnit_Calc_Divided;
13677 26 : afterDivision = true;
13678 : } else {
13679 405 : UngetToken();
13680 405 : *aHadFinalWS = hadWS;
13681 405 : break;
13682 : }
13683 :
13684 270 : RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
13685 135 : arr->Item(0) = aValue;
13686 135 : storage = &arr->Item(1);
13687 135 : aValue.SetArrayValue(arr, unit);
13688 135 : }
13689 :
13690 : // Adjust aVariantMask (see comments above function) to reflect which
13691 : // option we took.
13692 405 : if (aVariantMask & VARIANT_NUMBER) {
13693 25 : if (gotValue) {
13694 25 : aVariantMask &= ~int32_t(VARIANT_NUMBER);
13695 : } else {
13696 0 : aVariantMask = VARIANT_NUMBER;
13697 : }
13698 : } else {
13699 380 : if (!gotValue) {
13700 : // We had to find a value, but we didn't.
13701 0 : return false;
13702 : }
13703 : }
13704 :
13705 405 : return true;
13706 : }
13707 :
13708 : // * If aVariantMask is VARIANT_NUMBER, this function parses the
13709 : // <number-term> production.
13710 : // * If aVariantMask does not contain VARIANT_NUMBER, this function
13711 : // parses the <value-term> production.
13712 : // * Otherwise (VARIANT_NUMBER and other bits) this function parses
13713 : // whichever one of the productions matches ***and modifies
13714 : // aVariantMask*** to reflect which one it has parsed by either
13715 : // removing VARIANT_NUMBER or removing all other bits.
13716 : bool
13717 563 : CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask)
13718 : {
13719 563 : MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
13720 563 : if (!GetToken(true))
13721 0 : return false;
13722 : // Either an additive expression in parentheses...
13723 1101 : if (mToken.IsSymbol('(') ||
13724 : // Treat nested calc() as plain parenthesis.
13725 538 : IsCSSTokenCalcFunction(mToken)) {
13726 48 : if (!ParseCalcAdditiveExpression(aValue, aVariantMask) ||
13727 23 : !ExpectSymbol(')', true)) {
13728 2 : SkipUntil(')');
13729 2 : return false;
13730 : }
13731 23 : return true;
13732 : }
13733 : // ... or just a value
13734 538 : UngetToken();
13735 538 : if (aVariantMask & VARIANT_INTEGER) {
13736 : // Integers aren't mixed with anything else (see the assert at the
13737 : // top of CSSParserImpl::ParseCalc).
13738 0 : if (ParseVariant(aValue, aVariantMask, nullptr) != CSSParseResult::Ok) {
13739 0 : return false;
13740 : }
13741 : } else {
13742 : // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
13743 : // always gets picked up (we want to catch unitless zeroes using
13744 : // VARIANT_NUMBER and then error out)
13745 538 : if (ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr) !=
13746 : CSSParseResult::Ok) {
13747 21 : return false;
13748 : }
13749 : // ...and do the VARIANT_NUMBER check ourselves.
13750 517 : if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
13751 0 : return false;
13752 : }
13753 : }
13754 : // If we did the value parsing, we need to adjust aVariantMask to
13755 : // reflect which option we took (see above).
13756 517 : if (aVariantMask & VARIANT_NUMBER) {
13757 517 : if (aValue.GetUnit() == eCSSUnit_Number) {
13758 135 : aVariantMask = VARIANT_NUMBER;
13759 : } else {
13760 382 : aVariantMask &= ~int32_t(VARIANT_NUMBER);
13761 : }
13762 : }
13763 517 : return true;
13764 : }
13765 :
13766 : // This function consumes all consecutive whitespace and returns whether
13767 : // there was any.
13768 : bool
13769 717 : CSSParserImpl::RequireWhitespace()
13770 : {
13771 717 : if (!GetToken(false))
13772 0 : return false;
13773 717 : if (mToken.mType != eCSSToken_Whitespace) {
13774 228 : UngetToken();
13775 228 : return false;
13776 : }
13777 : // Skip any additional whitespace tokens.
13778 489 : if (GetToken(true)) {
13779 489 : UngetToken();
13780 : }
13781 489 : return true;
13782 : }
13783 :
13784 : bool
13785 89 : CSSParserImpl::ParseRect(nsCSSPropertyID aPropID)
13786 : {
13787 178 : nsCSSValue val;
13788 89 : if (ParseSingleTokenVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) {
13789 10 : AppendValue(aPropID, val);
13790 10 : return true;
13791 : }
13792 :
13793 79 : if (! GetToken(true)) {
13794 0 : return false;
13795 : }
13796 :
13797 158 : if (mToken.mType == eCSSToken_Function &&
13798 79 : mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
13799 79 : nsCSSRect& rect = val.SetRectValue();
13800 : bool useCommas;
13801 395 : NS_FOR_CSS_SIDES(side) {
13802 316 : if (!ParseSingleTokenVariant(rect.*(nsCSSRect::sides[side]),
13803 : VARIANT_AL, nullptr)) {
13804 0 : return false;
13805 : }
13806 316 : if (side == 0) {
13807 79 : useCommas = ExpectSymbol(',', true);
13808 237 : } else if (useCommas && side < 3) {
13809 : // Skip optional commas between elements, but only if the first
13810 : // separator was a comma.
13811 158 : if (!ExpectSymbol(',', true)) {
13812 0 : return false;
13813 : }
13814 : }
13815 : }
13816 79 : if (!ExpectSymbol(')', true)) {
13817 0 : return false;
13818 : }
13819 : } else {
13820 0 : UngetToken();
13821 0 : return false;
13822 : }
13823 :
13824 79 : AppendValue(aPropID, val);
13825 79 : return true;
13826 : }
13827 :
13828 : bool
13829 0 : CSSParserImpl::ParseColumns()
13830 : {
13831 : // We use a similar "fake value" hack to ParseListStyle, because
13832 : // "auto" is acceptable for both column-count and column-width.
13833 : // If the fake "auto" value is found, and one of the real values isn't,
13834 : // that means the fake auto value is meant for the real value we didn't
13835 : // find.
13836 : static const nsCSSPropertyID columnIDs[] = {
13837 : eCSSPropertyExtra_x_auto_value,
13838 : eCSSProperty_column_count,
13839 : eCSSProperty_column_width
13840 : };
13841 0 : const int32_t numProps = MOZ_ARRAY_LENGTH(columnIDs);
13842 :
13843 0 : nsCSSValue values[numProps];
13844 0 : int32_t found = ParseChoice(values, columnIDs, numProps);
13845 0 : if (found < 1) {
13846 0 : return false;
13847 : }
13848 0 : if ((found & (1|2|4)) == (1|2|4) &&
13849 0 : values[0].GetUnit() == eCSSUnit_Auto) {
13850 : // We filled all 3 values, which is invalid
13851 0 : return false;
13852 : }
13853 :
13854 0 : if ((found & 2) == 0) {
13855 : // Provide auto column-count
13856 0 : values[1].SetAutoValue();
13857 : }
13858 0 : if ((found & 4) == 0) {
13859 : // Provide auto column-width
13860 0 : values[2].SetAutoValue();
13861 : }
13862 :
13863 : // Start at index 1 to skip the fake auto value.
13864 0 : for (int32_t index = 1; index < numProps; index++) {
13865 0 : AppendValue(columnIDs[index], values[index]);
13866 : }
13867 0 : return true;
13868 : }
13869 :
13870 : #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
13871 : VARIANT_KEYWORD)
13872 : bool
13873 27 : CSSParserImpl::ParseContent()
13874 : {
13875 : // We need to divide the 'content' keywords into two classes for
13876 : // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable.
13877 : static const KTableEntry kContentListKWs[] = {
13878 : { eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE },
13879 : { eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE },
13880 : { eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE },
13881 : { eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE },
13882 : { eCSSKeyword_UNKNOWN, -1 }
13883 : };
13884 :
13885 : static const KTableEntry kContentSolitaryKWs[] = {
13886 : { eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT },
13887 : { eCSSKeyword_UNKNOWN, -1 }
13888 : };
13889 :
13890 : // Verify that these two lists add up to the size of
13891 : // nsCSSProps::kContentKTable.
13892 27 : MOZ_ASSERT(nsCSSProps::kContentKTable[
13893 : ArrayLength(kContentListKWs) +
13894 : ArrayLength(kContentSolitaryKWs) - 2].mKeyword ==
13895 : eCSSKeyword_UNKNOWN &&
13896 : nsCSSProps::kContentKTable[
13897 : ArrayLength(kContentListKWs) +
13898 : ArrayLength(kContentSolitaryKWs) - 2].mValue == -1,
13899 : "content keyword tables out of sync");
13900 :
13901 54 : nsCSSValue value;
13902 : // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must
13903 : // be alone
13904 27 : if (!ParseSingleTokenVariant(value, VARIANT_HMK | VARIANT_NONE,
13905 : kContentSolitaryKWs)) {
13906 25 : nsCSSValueList* cur = value.SetListValue();
13907 : for (;;) {
13908 25 : if (ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs) !=
13909 : CSSParseResult::Ok) {
13910 0 : return false;
13911 : }
13912 25 : if (CheckEndProperty()) {
13913 25 : break;
13914 : }
13915 0 : cur->mNext = new nsCSSValueList;
13916 0 : cur = cur->mNext;
13917 : }
13918 : }
13919 27 : AppendValue(eCSSProperty_content, value);
13920 27 : return true;
13921 : }
13922 :
13923 : bool
13924 0 : CSSParserImpl::ParseCounterData(nsCSSPropertyID aPropID)
13925 : {
13926 : static const nsCSSKeyword kCounterDataKTable[] = {
13927 : eCSSKeyword_none,
13928 : eCSSKeyword_UNKNOWN
13929 : };
13930 0 : nsCSSValue value;
13931 0 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
13932 : nullptr)) {
13933 0 : if (!GetToken(true)) {
13934 0 : return false;
13935 : }
13936 0 : if (mToken.mType != eCSSToken_Ident) {
13937 0 : UngetToken();
13938 0 : return false;
13939 : }
13940 :
13941 0 : nsCSSValuePairList *cur = value.SetPairListValue();
13942 : for (;;) {
13943 0 : if (!ParseCustomIdent(cur->mXValue, mToken.mIdent, kCounterDataKTable)) {
13944 0 : return false;
13945 : }
13946 0 : int32_t value = aPropID == eCSSProperty_counter_increment ? 1 : 0;
13947 0 : if (GetToken(true)) {
13948 0 : if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
13949 0 : value = mToken.mInteger;
13950 : } else {
13951 0 : UngetToken();
13952 : }
13953 : }
13954 0 : cur->mYValue.SetIntValue(value, eCSSUnit_Integer);
13955 0 : if (!GetToken(true)) {
13956 0 : break;
13957 : }
13958 0 : if (mToken.mType != eCSSToken_Ident) {
13959 0 : UngetToken();
13960 0 : break;
13961 : }
13962 0 : cur->mNext = new nsCSSValuePairList;
13963 0 : cur = cur->mNext;
13964 0 : }
13965 : }
13966 0 : AppendValue(aPropID, value);
13967 0 : return true;
13968 : }
13969 :
13970 : bool
13971 67 : CSSParserImpl::ParseCursor()
13972 : {
13973 134 : nsCSSValue value;
13974 : // 'inherit', 'initial' and 'unset' must be alone
13975 67 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
13976 57 : nsCSSValueList* cur = value.SetListValue();
13977 : for (;;) {
13978 57 : if (!ParseSingleTokenVariant(cur->mValue, VARIANT_UK,
13979 : nsCSSProps::kCursorKTable)) {
13980 0 : return false;
13981 : }
13982 57 : if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last
13983 57 : break;
13984 : }
13985 :
13986 : // We have a URL, so make a value array with three values.
13987 0 : RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
13988 0 : val->Item(0) = cur->mValue;
13989 :
13990 : // Parse optional x and y position of cursor hotspot (css3-ui).
13991 0 : if (ParseSingleTokenVariant(val->Item(1), VARIANT_NUMBER, nullptr)) {
13992 : // If we have one number, we must have two.
13993 0 : if (!ParseSingleTokenVariant(val->Item(2), VARIANT_NUMBER, nullptr)) {
13994 0 : return false;
13995 : }
13996 : }
13997 0 : cur->mValue.SetArrayValue(val, eCSSUnit_Array);
13998 :
13999 0 : if (!ExpectSymbol(',', true)) { // url must not be last
14000 0 : return false;
14001 : }
14002 0 : cur->mNext = new nsCSSValueList;
14003 0 : cur = cur->mNext;
14004 0 : }
14005 : }
14006 67 : AppendValue(eCSSProperty_cursor, value);
14007 67 : return true;
14008 : }
14009 :
14010 :
14011 : bool
14012 31 : CSSParserImpl::ParseFont()
14013 : {
14014 62 : nsCSSValue family;
14015 31 : if (ParseSingleTokenVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
14016 76 : if (eCSSUnit_Inherit == family.GetUnit() ||
14017 50 : eCSSUnit_Initial == family.GetUnit() ||
14018 24 : eCSSUnit_Unset == family.GetUnit()) {
14019 2 : AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
14020 2 : AppendValue(eCSSProperty_font_family, family);
14021 2 : AppendValue(eCSSProperty_font_style, family);
14022 2 : AppendValue(eCSSProperty_font_weight, family);
14023 2 : AppendValue(eCSSProperty_font_size, family);
14024 2 : AppendValue(eCSSProperty_line_height, family);
14025 2 : AppendValue(eCSSProperty_font_stretch, family);
14026 2 : AppendValue(eCSSProperty_font_size_adjust, family);
14027 2 : AppendValue(eCSSProperty_font_feature_settings, family);
14028 2 : AppendValue(eCSSProperty_font_language_override, family);
14029 2 : AppendValue(eCSSProperty_font_kerning, family);
14030 2 : AppendValue(eCSSProperty_font_variant_alternates, family);
14031 2 : AppendValue(eCSSProperty_font_variant_caps, family);
14032 2 : AppendValue(eCSSProperty_font_variant_east_asian, family);
14033 2 : AppendValue(eCSSProperty_font_variant_ligatures, family);
14034 2 : AppendValue(eCSSProperty_font_variant_numeric, family);
14035 2 : AppendValue(eCSSProperty_font_variant_position, family);
14036 : }
14037 : else {
14038 24 : AppendValue(eCSSProperty__x_system_font, family);
14039 48 : nsCSSValue systemFont(eCSSUnit_System_Font);
14040 24 : AppendValue(eCSSProperty_font_family, systemFont);
14041 24 : AppendValue(eCSSProperty_font_style, systemFont);
14042 24 : AppendValue(eCSSProperty_font_weight, systemFont);
14043 24 : AppendValue(eCSSProperty_font_size, systemFont);
14044 24 : AppendValue(eCSSProperty_line_height, systemFont);
14045 24 : AppendValue(eCSSProperty_font_stretch, systemFont);
14046 24 : AppendValue(eCSSProperty_font_size_adjust, systemFont);
14047 24 : AppendValue(eCSSProperty_font_feature_settings, systemFont);
14048 24 : AppendValue(eCSSProperty_font_language_override, systemFont);
14049 24 : AppendValue(eCSSProperty_font_kerning, systemFont);
14050 24 : AppendValue(eCSSProperty_font_variant_alternates, systemFont);
14051 24 : AppendValue(eCSSProperty_font_variant_caps, systemFont);
14052 24 : AppendValue(eCSSProperty_font_variant_east_asian, systemFont);
14053 24 : AppendValue(eCSSProperty_font_variant_ligatures, systemFont);
14054 24 : AppendValue(eCSSProperty_font_variant_numeric, systemFont);
14055 24 : AppendValue(eCSSProperty_font_variant_position, systemFont);
14056 : }
14057 26 : return true;
14058 : }
14059 :
14060 : // Get optional font-style, font-variant, font-weight, font-stretch
14061 : // (in any order)
14062 :
14063 : // Indexes into fontIDs[] and values[] arrays.
14064 5 : const int kFontStyleIndex = 0;
14065 5 : const int kFontVariantIndex = 1;
14066 5 : const int kFontWeightIndex = 2;
14067 5 : const int kFontStretchIndex = 3;
14068 :
14069 : // The order of the initializers here must match the order of the indexes
14070 : // defined above!
14071 : static const nsCSSPropertyID fontIDs[] = {
14072 : eCSSProperty_font_style,
14073 : eCSSProperty_font_variant_caps,
14074 : eCSSProperty_font_weight,
14075 : eCSSProperty_font_stretch
14076 : };
14077 :
14078 5 : const int32_t numProps = MOZ_ARRAY_LENGTH(fontIDs);
14079 10 : nsCSSValue values[numProps];
14080 5 : int32_t found = ParseChoice(values, fontIDs, numProps);
14081 10 : if (found < 0 ||
14082 10 : eCSSUnit_Inherit == values[kFontStyleIndex].GetUnit() ||
14083 15 : eCSSUnit_Initial == values[kFontStyleIndex].GetUnit() ||
14084 5 : eCSSUnit_Unset == values[kFontStyleIndex].GetUnit()) { // illegal data
14085 0 : return false;
14086 : }
14087 5 : if ((found & (1 << kFontStyleIndex)) == 0) {
14088 : // Provide default font-style
14089 : values[kFontStyleIndex].SetIntValue(NS_FONT_STYLE_NORMAL,
14090 5 : eCSSUnit_Enumerated);
14091 : }
14092 5 : if ((found & (1 << kFontVariantIndex)) == 0) {
14093 : // Provide default font-variant
14094 5 : values[kFontVariantIndex].SetNormalValue();
14095 : } else {
14096 0 : if (values[kFontVariantIndex].GetUnit() == eCSSUnit_Enumerated &&
14097 0 : values[kFontVariantIndex].GetIntValue() !=
14098 : NS_FONT_VARIANT_CAPS_SMALLCAPS) {
14099 : // only normal or small-caps is allowed in font shorthand
14100 : // this also assumes other values for font-variant-caps never overlap
14101 : // possible values for style or weight
14102 0 : return false;
14103 : }
14104 : }
14105 5 : if ((found & (1 << kFontWeightIndex)) == 0) {
14106 : // Provide default font-weight
14107 : values[kFontWeightIndex].SetIntValue(NS_FONT_WEIGHT_NORMAL,
14108 5 : eCSSUnit_Enumerated);
14109 : }
14110 5 : if ((found & (1 << kFontStretchIndex)) == 0) {
14111 : // Provide default font-stretch
14112 : values[kFontStretchIndex].SetIntValue(NS_FONT_STRETCH_NORMAL,
14113 5 : eCSSUnit_Enumerated);
14114 : }
14115 :
14116 : // Get mandatory font-size
14117 10 : nsCSSValue size;
14118 5 : if (!ParseSingleTokenNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP,
14119 : nsCSSProps::kFontSizeKTable)) {
14120 2 : return false;
14121 : }
14122 :
14123 : // Get optional "/" line-height
14124 6 : nsCSSValue lineHeight;
14125 3 : if (ExpectSymbol('/', true)) {
14126 0 : if (ParseNonNegativeVariant(lineHeight,
14127 : VARIANT_NUMBER | VARIANT_LP |
14128 : VARIANT_NORMAL | VARIANT_CALC,
14129 : nullptr) != CSSParseResult::Ok) {
14130 0 : return false;
14131 : }
14132 : }
14133 : else {
14134 3 : lineHeight.SetNormalValue();
14135 : }
14136 :
14137 : // Get final mandatory font-family
14138 6 : nsAutoParseCompoundProperty compound(this);
14139 3 : if (ParseFamily(family)) {
14140 9 : if (eCSSUnit_Inherit != family.GetUnit() &&
14141 6 : eCSSUnit_Initial != family.GetUnit() &&
14142 3 : eCSSUnit_Unset != family.GetUnit()) {
14143 3 : AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
14144 3 : AppendValue(eCSSProperty_font_family, family);
14145 3 : AppendValue(eCSSProperty_font_style, values[kFontStyleIndex]);
14146 3 : AppendValue(eCSSProperty_font_variant_caps, values[kFontVariantIndex]);
14147 3 : AppendValue(eCSSProperty_font_weight, values[kFontWeightIndex]);
14148 3 : AppendValue(eCSSProperty_font_size, size);
14149 3 : AppendValue(eCSSProperty_line_height, lineHeight);
14150 3 : AppendValue(eCSSProperty_font_stretch, values[kFontStretchIndex]);
14151 3 : AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
14152 3 : AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal));
14153 3 : AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal));
14154 : AppendValue(eCSSProperty_font_kerning,
14155 3 : nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated));
14156 : AppendValue(eCSSProperty_font_variant_alternates,
14157 3 : nsCSSValue(eCSSUnit_Normal));
14158 : AppendValue(eCSSProperty_font_variant_east_asian,
14159 3 : nsCSSValue(eCSSUnit_Normal));
14160 : AppendValue(eCSSProperty_font_variant_ligatures,
14161 3 : nsCSSValue(eCSSUnit_Normal));
14162 : AppendValue(eCSSProperty_font_variant_numeric,
14163 3 : nsCSSValue(eCSSUnit_Normal));
14164 : AppendValue(eCSSProperty_font_variant_position,
14165 3 : nsCSSValue(eCSSUnit_Normal));
14166 3 : return true;
14167 : }
14168 : }
14169 0 : return false;
14170 : }
14171 :
14172 : bool
14173 0 : CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue)
14174 : {
14175 0 : if (!ParseSingleTokenVariant(aValue, VARIANT_HK | VARIANT_NONE,
14176 : nsCSSProps::kFontSynthesisKTable)) {
14177 0 : return false;
14178 : }
14179 :
14180 : // first value 'none' ==> done
14181 0 : if (eCSSUnit_None == aValue.GetUnit() ||
14182 0 : eCSSUnit_Initial == aValue.GetUnit() ||
14183 0 : eCSSUnit_Inherit == aValue.GetUnit() ||
14184 0 : eCSSUnit_Unset == aValue.GetUnit())
14185 : {
14186 0 : return true;
14187 : }
14188 :
14189 : // look for a second value
14190 0 : int32_t intValue = aValue.GetIntValue();
14191 0 : nsCSSValue nextValue;
14192 :
14193 0 : if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) {
14194 0 : int32_t nextIntValue = nextValue.GetIntValue();
14195 0 : if (nextIntValue & intValue) {
14196 0 : return false;
14197 : }
14198 0 : aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
14199 : }
14200 :
14201 0 : return true;
14202 : }
14203 :
14204 : // font-variant-alternates allows for a combination of multiple
14205 : // simple enumerated values and functional values. Functional values have
14206 : // parameter lists with one or more idents which are later resolved
14207 : // based on values defined in @font-feature-value rules.
14208 : //
14209 : // font-variant-alternates: swash(flowing) historical-forms styleset(alt-g, alt-m);
14210 : //
14211 : // So for this the nsCSSValue is set to a pair value, with one
14212 : // value for a bitmask of both simple and functional property values
14213 : // and another value containing a ValuePairList with lists of idents
14214 : // for each functional property value.
14215 : //
14216 : // pairValue
14217 : // o intValue
14218 : // NS_FONT_VARIANT_ALTERNATES_SWASH |
14219 : // NS_FONT_VARIANT_ALTERNATES_STYLESET
14220 : // o valuePairList, each element with
14221 : // - intValue - indicates which alternate
14222 : // - string or valueList of strings
14223 : //
14224 : // Note: when only 'historical-forms' is specified, there are no
14225 : // functional values to store, in which case the valuePairList is a
14226 : // single element dummy list. In all other cases, the length of the
14227 : // list will match the number of functional values.
14228 :
14229 : #define MAX_ALLOWED_FEATURES 512
14230 :
14231 : static uint16_t
14232 0 : MaxElementsForAlternateType(nsCSSKeyword keyword)
14233 : {
14234 0 : uint16_t maxElems = 1;
14235 0 : if (keyword == eCSSKeyword_styleset ||
14236 : keyword == eCSSKeyword_character_variant) {
14237 0 : maxElems = MAX_ALLOWED_FEATURES;
14238 : }
14239 0 : return maxElems;
14240 : }
14241 :
14242 : bool
14243 0 : CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature,
14244 : nsCSSValue& aValue)
14245 : {
14246 0 : if (!GetToken(true)) {
14247 0 : return false;
14248 : }
14249 :
14250 0 : bool isIdent = (mToken.mType == eCSSToken_Ident);
14251 0 : if (mToken.mType != eCSSToken_Function && !isIdent) {
14252 0 : UngetToken();
14253 0 : return false;
14254 : }
14255 :
14256 : // ident ==> simple enumerated prop val (e.g. historical-forms)
14257 : // function ==> e.g. swash(flowing) styleset(alt-g, alt-m)
14258 :
14259 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
14260 0 : if (!nsCSSProps::FindKeyword(keyword,
14261 : (isIdent ?
14262 : nsCSSProps::kFontVariantAlternatesKTable :
14263 : nsCSSProps::kFontVariantAlternatesFuncsKTable),
14264 : aWhichFeature))
14265 : {
14266 : // failed, pop token
14267 0 : UngetToken();
14268 0 : return false;
14269 : }
14270 :
14271 0 : if (isIdent) {
14272 0 : aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated);
14273 0 : return true;
14274 : }
14275 :
14276 0 : return ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER,
14277 0 : 1, MaxElementsForAlternateType(keyword), aValue);
14278 : }
14279 :
14280 : bool
14281 0 : CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue)
14282 : {
14283 0 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
14284 : nullptr)) {
14285 0 : return true;
14286 : }
14287 :
14288 : // iterate through parameters
14289 0 : nsCSSValue listValue;
14290 0 : int32_t feature, featureFlags = 0;
14291 :
14292 : // if no functional values, this may be a list with a single, unused element
14293 0 : listValue.SetListValue();
14294 :
14295 0 : nsCSSValueList* list = nullptr;
14296 0 : nsCSSValue value;
14297 0 : while (ParseSingleAlternate(feature, value)) {
14298 :
14299 : // check to make sure value not already set
14300 0 : if (feature == 0 ||
14301 0 : feature & featureFlags) {
14302 0 : return false;
14303 : }
14304 :
14305 0 : featureFlags |= feature;
14306 :
14307 : // if function, need to add to the list of functions
14308 0 : if (value.GetUnit() == eCSSUnit_Function) {
14309 0 : if (!list) {
14310 0 : list = listValue.GetListValue();
14311 : } else {
14312 0 : list->mNext = new nsCSSValueList;
14313 0 : list = list->mNext;
14314 : }
14315 0 : list->mValue = value;
14316 : }
14317 : }
14318 :
14319 0 : if (featureFlags == 0) {
14320 : // ParseSingleAlternate failed the first time through the loop.
14321 0 : return false;
14322 : }
14323 :
14324 0 : nsCSSValue featureValue;
14325 0 : featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated);
14326 0 : aValue.SetPairValue(featureValue, listValue);
14327 :
14328 0 : return true;
14329 : }
14330 :
14331 : bool
14332 0 : CSSParserImpl::MergeBitmaskValue(int32_t aNewValue,
14333 : const int32_t aMasks[],
14334 : int32_t& aMergedValue)
14335 : {
14336 : // check to make sure value not already set
14337 0 : if (aNewValue & aMergedValue) {
14338 0 : return false;
14339 : }
14340 :
14341 0 : const int32_t *m = aMasks;
14342 0 : int32_t c = 0;
14343 :
14344 0 : while (*m != MASK_END_VALUE) {
14345 0 : if (*m & aNewValue) {
14346 0 : c = aMergedValue & *m;
14347 0 : break;
14348 : }
14349 0 : m++;
14350 : }
14351 :
14352 0 : if (c) {
14353 0 : return false;
14354 : }
14355 :
14356 0 : aMergedValue |= aNewValue;
14357 0 : return true;
14358 : }
14359 :
14360 : // aMasks - array of masks for mutually-exclusive property values,
14361 : // e.g. proportial-nums, tabular-nums
14362 :
14363 : bool
14364 4 : CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue,
14365 : const KTableEntry aKeywordTable[],
14366 : const int32_t aMasks[])
14367 : {
14368 : // Parse at least one keyword
14369 4 : if (!ParseEnum(aValue, aKeywordTable)) {
14370 0 : return false;
14371 : }
14372 :
14373 : // look for more values
14374 8 : nsCSSValue nextValue;
14375 4 : int32_t mergedValue = aValue.GetIntValue();
14376 :
14377 4 : while (ParseEnum(nextValue, aKeywordTable))
14378 : {
14379 0 : if (!MergeBitmaskValue(nextValue.GetIntValue(), aMasks, mergedValue)) {
14380 0 : return false;
14381 : }
14382 : }
14383 :
14384 4 : aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated);
14385 :
14386 4 : return true;
14387 : }
14388 :
14389 : static const int32_t maskEastAsian[] = {
14390 : NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK,
14391 : NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK,
14392 : MASK_END_VALUE
14393 : };
14394 :
14395 : bool
14396 2 : CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue)
14397 : {
14398 2 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
14399 : nullptr)) {
14400 0 : return true;
14401 : }
14402 :
14403 2 : NS_ASSERTION(maskEastAsian[ArrayLength(maskEastAsian) - 1] ==
14404 : MASK_END_VALUE,
14405 : "incorrectly terminated array");
14406 :
14407 : return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable,
14408 2 : maskEastAsian);
14409 : }
14410 :
14411 : bool
14412 0 : CSSParserImpl::ParseContain(nsCSSValue& aValue)
14413 : {
14414 0 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE,
14415 : nullptr)) {
14416 0 : return true;
14417 : }
14418 : static const int32_t maskContain[] = { MASK_END_VALUE };
14419 0 : if (!ParseBitmaskValues(aValue, nsCSSProps::kContainKTable, maskContain)) {
14420 0 : return false;
14421 : }
14422 0 : if (aValue.GetIntValue() & NS_STYLE_CONTAIN_STRICT) {
14423 0 : if (aValue.GetIntValue() != NS_STYLE_CONTAIN_STRICT) {
14424 : // Disallow any other keywords in combination with 'strict'.
14425 0 : return false;
14426 : }
14427 : // Strict implies layout, style, and paint.
14428 : // However, for serialization purposes, we keep the strict bit around.
14429 : aValue.SetIntValue(NS_STYLE_CONTAIN_STRICT |
14430 0 : NS_STYLE_CONTAIN_ALL_BITS, eCSSUnit_Enumerated);
14431 : }
14432 0 : return true;
14433 : }
14434 :
14435 : static const int32_t maskLigatures[] = {
14436 : NS_FONT_VARIANT_LIGATURES_COMMON_MASK,
14437 : NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK,
14438 : NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK,
14439 : NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK,
14440 : MASK_END_VALUE
14441 : };
14442 :
14443 : bool
14444 0 : CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue)
14445 : {
14446 0 : if (ParseSingleTokenVariant(aValue,
14447 : VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE,
14448 : nullptr)) {
14449 0 : return true;
14450 : }
14451 :
14452 0 : NS_ASSERTION(maskLigatures[ArrayLength(maskLigatures) - 1] ==
14453 : MASK_END_VALUE,
14454 : "incorrectly terminated array");
14455 :
14456 : return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable,
14457 0 : maskLigatures);
14458 : }
14459 :
14460 : static const int32_t maskNumeric[] = {
14461 : NS_FONT_VARIANT_NUMERIC_FIGURE_MASK,
14462 : NS_FONT_VARIANT_NUMERIC_SPACING_MASK,
14463 : NS_FONT_VARIANT_NUMERIC_FRACTION_MASK,
14464 : MASK_END_VALUE
14465 : };
14466 :
14467 : bool
14468 2 : CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue)
14469 : {
14470 2 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
14471 : nullptr)) {
14472 0 : return true;
14473 : }
14474 :
14475 2 : NS_ASSERTION(maskNumeric[ArrayLength(maskNumeric) - 1] ==
14476 : MASK_END_VALUE,
14477 : "incorrectly terminated array");
14478 :
14479 : return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable,
14480 2 : maskNumeric);
14481 : }
14482 :
14483 : bool
14484 3 : CSSParserImpl::ParseFontVariant()
14485 : {
14486 : // parse single values - normal/inherit/none
14487 6 : nsCSSValue value;
14488 6 : nsCSSValue normal(eCSSUnit_Normal);
14489 :
14490 3 : if (ParseSingleTokenVariant(value,
14491 : VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE,
14492 : nullptr)) {
14493 2 : AppendValue(eCSSProperty_font_variant_ligatures, value);
14494 2 : if (eCSSUnit_None == value.GetUnit()) {
14495 : // 'none' applies the value 'normal' to all properties other
14496 : // than 'font-variant-ligatures'
14497 0 : value.SetNormalValue();
14498 : }
14499 2 : AppendValue(eCSSProperty_font_variant_alternates, value);
14500 2 : AppendValue(eCSSProperty_font_variant_caps, value);
14501 2 : AppendValue(eCSSProperty_font_variant_east_asian, value);
14502 2 : AppendValue(eCSSProperty_font_variant_numeric, value);
14503 2 : AppendValue(eCSSProperty_font_variant_position, value);
14504 2 : return true;
14505 : }
14506 :
14507 : // set each of the individual subproperties
14508 1 : int32_t altFeatures = 0, capsFeatures = 0, eastAsianFeatures = 0,
14509 1 : ligFeatures = 0, numericFeatures = 0, posFeatures = 0;
14510 2 : nsCSSValue altListValue;
14511 1 : nsCSSValueList* altList = nullptr;
14512 :
14513 : // if no functional values, this may be a list with a single, unused element
14514 1 : altListValue.SetListValue();
14515 :
14516 1 : bool foundValid = false; // found at least one proper value
14517 3 : while (GetToken(true)) {
14518 : // only an ident or a function at this point
14519 2 : bool isFunction = (mToken.mType == eCSSToken_Function);
14520 2 : if (mToken.mType != eCSSToken_Ident && !isFunction) {
14521 1 : UngetToken();
14522 1 : break;
14523 : }
14524 :
14525 1 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
14526 1 : if (keyword == eCSSKeyword_UNKNOWN) {
14527 0 : UngetToken();
14528 0 : return false;
14529 : }
14530 :
14531 : int32_t feature;
14532 :
14533 : // function? ==> font-variant-alternates
14534 1 : if (isFunction) {
14535 0 : if (!nsCSSProps::FindKeyword(keyword,
14536 : nsCSSProps::kFontVariantAlternatesFuncsKTable,
14537 0 : feature) ||
14538 0 : (feature & altFeatures)) {
14539 0 : UngetToken();
14540 0 : return false;
14541 : }
14542 :
14543 0 : altFeatures |= feature;
14544 0 : nsCSSValue funcValue;
14545 0 : if (!ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER, 1,
14546 0 : MaxElementsForAlternateType(keyword), funcValue) ||
14547 0 : funcValue.GetUnit() != eCSSUnit_Function) {
14548 0 : UngetToken();
14549 0 : return false;
14550 : }
14551 :
14552 0 : if (!altList) {
14553 0 : altList = altListValue.GetListValue();
14554 : } else {
14555 0 : altList->mNext = new nsCSSValueList;
14556 0 : altList = altList->mNext;
14557 : }
14558 0 : altList->mValue = funcValue;
14559 1 : } else if (nsCSSProps::FindKeyword(keyword,
14560 : nsCSSProps::kFontVariantCapsKTable,
14561 : feature)) {
14562 1 : if (capsFeatures != 0) {
14563 : // multiple values for font-variant-caps
14564 0 : UngetToken();
14565 0 : return false;
14566 : }
14567 1 : capsFeatures = feature;
14568 0 : } else if (nsCSSProps::FindKeyword(keyword,
14569 : nsCSSProps::kFontVariantAlternatesKTable,
14570 : feature)) {
14571 0 : if (feature & altFeatures) {
14572 : // same value repeated
14573 0 : UngetToken();
14574 0 : return false;
14575 : }
14576 0 : altFeatures |= feature;
14577 0 : } else if (nsCSSProps::FindKeyword(keyword,
14578 : nsCSSProps::kFontVariantEastAsianKTable,
14579 : feature)) {
14580 0 : if (!MergeBitmaskValue(feature, maskEastAsian, eastAsianFeatures)) {
14581 : // multiple mutually exclusive values
14582 0 : UngetToken();
14583 0 : return false;
14584 : }
14585 0 : } else if (nsCSSProps::FindKeyword(keyword,
14586 : nsCSSProps::kFontVariantLigaturesKTable,
14587 : feature)) {
14588 0 : if (keyword == eCSSKeyword_none ||
14589 0 : !MergeBitmaskValue(feature, maskLigatures, ligFeatures)) {
14590 : // none or multiple mutually exclusive values
14591 0 : UngetToken();
14592 0 : return false;
14593 : }
14594 0 : } else if (nsCSSProps::FindKeyword(keyword,
14595 : nsCSSProps::kFontVariantNumericKTable,
14596 : feature)) {
14597 0 : if (!MergeBitmaskValue(feature, maskNumeric, numericFeatures)) {
14598 : // multiple mutually exclusive values
14599 0 : UngetToken();
14600 0 : return false;
14601 : }
14602 0 : } else if (nsCSSProps::FindKeyword(keyword,
14603 : nsCSSProps::kFontVariantPositionKTable,
14604 : feature)) {
14605 0 : if (posFeatures != 0) {
14606 : // multiple values for font-variant-caps
14607 0 : UngetToken();
14608 0 : return false;
14609 : }
14610 0 : posFeatures = feature;
14611 : } else {
14612 : // bogus keyword, bail...
14613 0 : UngetToken();
14614 0 : return false;
14615 : }
14616 :
14617 1 : foundValid = true;
14618 : }
14619 :
14620 1 : if (!foundValid) {
14621 0 : return false;
14622 : }
14623 :
14624 1 : if (altFeatures) {
14625 0 : nsCSSValue featureValue;
14626 0 : featureValue.SetIntValue(altFeatures, eCSSUnit_Enumerated);
14627 0 : value.SetPairValue(featureValue, altListValue);
14628 0 : AppendValue(eCSSProperty_font_variant_alternates, value);
14629 : } else {
14630 1 : AppendValue(eCSSProperty_font_variant_alternates, normal);
14631 : }
14632 :
14633 1 : if (capsFeatures) {
14634 1 : value.SetIntValue(capsFeatures, eCSSUnit_Enumerated);
14635 1 : AppendValue(eCSSProperty_font_variant_caps, value);
14636 : } else {
14637 0 : AppendValue(eCSSProperty_font_variant_caps, normal);
14638 : }
14639 :
14640 1 : if (eastAsianFeatures) {
14641 0 : value.SetIntValue(eastAsianFeatures, eCSSUnit_Enumerated);
14642 0 : AppendValue(eCSSProperty_font_variant_east_asian, value);
14643 : } else {
14644 1 : AppendValue(eCSSProperty_font_variant_east_asian, normal);
14645 : }
14646 :
14647 1 : if (ligFeatures) {
14648 0 : value.SetIntValue(ligFeatures, eCSSUnit_Enumerated);
14649 0 : AppendValue(eCSSProperty_font_variant_ligatures, value);
14650 : } else {
14651 1 : AppendValue(eCSSProperty_font_variant_ligatures, normal);
14652 : }
14653 :
14654 1 : if (numericFeatures) {
14655 0 : value.SetIntValue(numericFeatures, eCSSUnit_Enumerated);
14656 0 : AppendValue(eCSSProperty_font_variant_numeric, value);
14657 : } else {
14658 1 : AppendValue(eCSSProperty_font_variant_numeric, normal);
14659 : }
14660 :
14661 1 : if (posFeatures) {
14662 0 : value.SetIntValue(posFeatures, eCSSUnit_Enumerated);
14663 0 : AppendValue(eCSSProperty_font_variant_position, value);
14664 : } else {
14665 1 : AppendValue(eCSSProperty_font_variant_position, normal);
14666 : }
14667 :
14668 1 : return true;
14669 : }
14670 :
14671 : bool
14672 54 : CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
14673 : {
14674 54 : if (ParseSingleTokenVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT,
14675 : nsCSSProps::kFontWeightKTable)) {
14676 49 : if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
14677 3 : int32_t intValue = aValue.GetIntValue();
14678 3 : if ((100 <= intValue) &&
14679 3 : (intValue <= 900) &&
14680 3 : (0 == (intValue % 100))) {
14681 3 : return true;
14682 : } else {
14683 0 : UngetToken();
14684 0 : return false;
14685 : }
14686 : }
14687 46 : return true;
14688 : }
14689 5 : return false;
14690 : }
14691 :
14692 : bool
14693 19 : CSSParserImpl::ParseOneFamily(nsAString& aFamily,
14694 : bool& aOneKeyword,
14695 : bool& aQuoted)
14696 : {
14697 19 : if (!GetToken(true))
14698 0 : return false;
14699 :
14700 19 : nsCSSToken* tk = &mToken;
14701 :
14702 19 : aOneKeyword = false;
14703 19 : aQuoted = false;
14704 19 : if (eCSSToken_Ident == tk->mType) {
14705 19 : aOneKeyword = true;
14706 19 : aFamily.Append(tk->mIdent);
14707 : for (;;) {
14708 19 : if (!GetToken(false))
14709 0 : break;
14710 :
14711 19 : if (eCSSToken_Ident == tk->mType) {
14712 0 : aOneKeyword = false;
14713 : // We had at least another keyword before.
14714 : // "If a sequence of identifiers is given as a font family name,
14715 : // the computed value is the name converted to a string by joining
14716 : // all the identifiers in the sequence by single spaces."
14717 : // -- CSS 2.1, section 15.3
14718 : // Whitespace tokens do not actually matter,
14719 : // identifier tokens can be separated by comments.
14720 0 : aFamily.Append(char16_t(' '));
14721 0 : aFamily.Append(tk->mIdent);
14722 19 : } else if (eCSSToken_Whitespace != tk->mType) {
14723 19 : UngetToken();
14724 19 : break;
14725 : }
14726 : }
14727 19 : return true;
14728 :
14729 0 : } else if (eCSSToken_String == tk->mType) {
14730 0 : aQuoted = true;
14731 0 : aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
14732 0 : return true;
14733 :
14734 : } else {
14735 0 : UngetToken();
14736 0 : return false;
14737 : }
14738 : }
14739 :
14740 :
14741 : static bool
14742 19 : AppendGeneric(nsCSSKeyword aKeyword, FontFamilyList *aFamilyList)
14743 : {
14744 19 : switch (aKeyword) {
14745 : case eCSSKeyword_serif:
14746 0 : aFamilyList->Append(FontFamilyName(eFamily_serif));
14747 0 : return true;
14748 : case eCSSKeyword_sans_serif:
14749 5 : aFamilyList->Append(FontFamilyName(eFamily_sans_serif));
14750 5 : return true;
14751 : case eCSSKeyword_monospace:
14752 1 : aFamilyList->Append(FontFamilyName(eFamily_monospace));
14753 1 : return true;
14754 : case eCSSKeyword_cursive:
14755 0 : aFamilyList->Append(FontFamilyName(eFamily_cursive));
14756 0 : return true;
14757 : case eCSSKeyword_fantasy:
14758 0 : aFamilyList->Append(FontFamilyName(eFamily_fantasy));
14759 0 : return true;
14760 : case eCSSKeyword__moz_fixed:
14761 10 : aFamilyList->Append(FontFamilyName(eFamily_moz_fixed));
14762 10 : return true;
14763 : default:
14764 3 : break;
14765 : }
14766 :
14767 3 : return false;
14768 : }
14769 :
14770 : bool
14771 16 : CSSParserImpl::ParseFamily(nsCSSValue& aValue)
14772 : {
14773 : RefPtr<css::FontFamilyListRefCnt> familyList =
14774 32 : new css::FontFamilyListRefCnt();
14775 32 : nsAutoString family;
14776 : bool single, quoted;
14777 :
14778 : // keywords only have meaning in the first position
14779 16 : if (!ParseOneFamily(family, single, quoted))
14780 0 : return false;
14781 :
14782 : // check for keywords, but only when keywords appear by themselves
14783 : // i.e. not in compounds such as font-family: default blah;
14784 16 : bool foundGeneric = false;
14785 16 : if (single) {
14786 16 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
14787 16 : switch (keyword) {
14788 : case eCSSKeyword_inherit:
14789 0 : aValue.SetInheritValue();
14790 0 : return true;
14791 : case eCSSKeyword_default:
14792 : // 605231 - don't parse unquoted 'default' reserved keyword
14793 0 : return false;
14794 : case eCSSKeyword_initial:
14795 0 : aValue.SetInitialValue();
14796 0 : return true;
14797 : case eCSSKeyword_unset:
14798 0 : if (nsLayoutUtils::UnsetValueEnabled()) {
14799 0 : aValue.SetUnsetValue();
14800 0 : return true;
14801 : }
14802 0 : break;
14803 : case eCSSKeyword__moz_use_system_font:
14804 0 : if (!IsParsingCompoundProperty()) {
14805 0 : aValue.SetSystemFontValue();
14806 0 : return true;
14807 : }
14808 0 : break;
14809 : default:
14810 16 : foundGeneric = AppendGeneric(keyword, familyList);
14811 : }
14812 : }
14813 :
14814 16 : if (!foundGeneric) {
14815 1 : familyList->Append(
14816 2 : FontFamilyName(family, (quoted ? eQuotedName : eUnquotedName)));
14817 : }
14818 :
14819 : for (;;) {
14820 19 : if (!ExpectSymbol(',', true))
14821 16 : break;
14822 :
14823 6 : nsAutoString nextFamily;
14824 3 : if (!ParseOneFamily(nextFamily, single, quoted))
14825 0 : return false;
14826 :
14827 : // at this point unquoted keywords are not allowed
14828 : // as font family names but can appear within names
14829 3 : foundGeneric = false;
14830 3 : if (single) {
14831 3 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily);
14832 3 : switch (keyword) {
14833 : case eCSSKeyword_inherit:
14834 : case eCSSKeyword_initial:
14835 : case eCSSKeyword_default:
14836 : case eCSSKeyword__moz_use_system_font:
14837 0 : return false;
14838 : case eCSSKeyword_unset:
14839 0 : if (nsLayoutUtils::UnsetValueEnabled()) {
14840 0 : return false;
14841 : }
14842 0 : break;
14843 : default:
14844 3 : foundGeneric = AppendGeneric(keyword, familyList);
14845 3 : break;
14846 : }
14847 : }
14848 :
14849 3 : if (!foundGeneric) {
14850 2 : familyList->Append(
14851 4 : FontFamilyName(nextFamily, (quoted ? eQuotedName : eUnquotedName)));
14852 : }
14853 3 : }
14854 :
14855 16 : if (familyList->IsEmpty()) {
14856 0 : return false;
14857 : }
14858 :
14859 16 : aValue.SetFontFamilyListValue(familyList);
14860 16 : return true;
14861 : }
14862 :
14863 : // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
14864 : // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
14865 : // local-src: 'local(' ( string | ident ) ')'
14866 :
14867 : bool
14868 0 : CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
14869 : {
14870 : // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>?
14871 0 : InfallibleTArray<nsCSSValue> values;
14872 0 : nsCSSValue cur;
14873 : for (;;) {
14874 0 : if (!GetToken(true))
14875 0 : break;
14876 :
14877 0 : if (mToken.mType == eCSSToken_URL) {
14878 0 : SetValueToURL(cur, mToken.mIdent);
14879 0 : values.AppendElement(cur);
14880 0 : if (!ParseFontSrcFormat(values))
14881 0 : return false;
14882 :
14883 0 : } else if (mToken.mType == eCSSToken_Function &&
14884 0 : mToken.mIdent.LowerCaseEqualsLiteral("local")) {
14885 : // css3-fonts does not specify a formal grammar for local().
14886 : // The text permits both unquoted identifiers and quoted
14887 : // strings. We resolve this ambiguity in the spec by
14888 : // assuming that the appropriate production is a single
14889 : // <family-name>, possibly surrounded by whitespace.
14890 :
14891 0 : nsAutoString family;
14892 : bool single, quoted;
14893 0 : if (!ParseOneFamily(family, single, quoted)) {
14894 0 : SkipUntil(')');
14895 0 : return false;
14896 : }
14897 0 : if (!ExpectSymbol(')', true)) {
14898 0 : SkipUntil(')');
14899 0 : return false;
14900 : }
14901 :
14902 : // reject generics
14903 0 : if (single) {
14904 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
14905 0 : switch (keyword) {
14906 : case eCSSKeyword_serif:
14907 : case eCSSKeyword_sans_serif:
14908 : case eCSSKeyword_monospace:
14909 : case eCSSKeyword_cursive:
14910 : case eCSSKeyword_fantasy:
14911 : case eCSSKeyword__moz_fixed:
14912 0 : return false;
14913 : default:
14914 0 : break;
14915 : }
14916 : }
14917 :
14918 0 : cur.SetStringValue(family, eCSSUnit_Local_Font);
14919 0 : values.AppendElement(cur);
14920 : } else {
14921 : // We don't know what to do with this token; unget it and error out
14922 0 : UngetToken();
14923 0 : return false;
14924 : }
14925 :
14926 0 : if (!ExpectSymbol(',', true))
14927 0 : break;
14928 0 : }
14929 :
14930 0 : if (values.Length() == 0)
14931 0 : return false;
14932 :
14933 : RefPtr<nsCSSValue::Array> srcVals
14934 0 : = nsCSSValue::Array::Create(values.Length());
14935 :
14936 : uint32_t i;
14937 0 : for (i = 0; i < values.Length(); i++)
14938 0 : srcVals->Item(i) = values[i];
14939 0 : aValue.SetArrayValue(srcVals, eCSSUnit_Array);
14940 0 : return true;
14941 : }
14942 :
14943 : bool
14944 0 : CSSParserImpl::ParseFontSrcFormat(InfallibleTArray<nsCSSValue> & values)
14945 : {
14946 0 : if (!GetToken(true))
14947 0 : return true; // EOF harmless here
14948 0 : if (mToken.mType != eCSSToken_Function ||
14949 0 : !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
14950 0 : UngetToken();
14951 0 : return true;
14952 : }
14953 :
14954 0 : do {
14955 0 : if (!GetToken(true))
14956 0 : return false; // EOF - no need for SkipUntil
14957 :
14958 0 : if (mToken.mType != eCSSToken_String) {
14959 0 : UngetToken();
14960 0 : SkipUntil(')');
14961 0 : return false;
14962 : }
14963 :
14964 0 : nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
14965 0 : values.AppendElement(cur);
14966 : } while (ExpectSymbol(',', true));
14967 :
14968 0 : if (!ExpectSymbol(')', true)) {
14969 0 : SkipUntil(')');
14970 0 : return false;
14971 : }
14972 :
14973 0 : return true;
14974 : }
14975 :
14976 : // font-ranges: urange ( ',' urange )*
14977 : bool
14978 0 : CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
14979 : {
14980 0 : InfallibleTArray<uint32_t> ranges;
14981 : for (;;) {
14982 0 : if (!GetToken(true))
14983 0 : break;
14984 :
14985 0 : if (mToken.mType != eCSSToken_URange) {
14986 0 : UngetToken();
14987 0 : break;
14988 : }
14989 :
14990 : // An invalid range token is a parsing error, causing the entire
14991 : // descriptor to be ignored.
14992 0 : if (!mToken.mIntegerValid)
14993 0 : return false;
14994 :
14995 0 : uint32_t low = mToken.mInteger;
14996 0 : uint32_t high = mToken.mInteger2;
14997 :
14998 : // A range that descends, or high end exceeds the current range of
14999 : // Unicode (U+0-10FFFF) invalidates the descriptor.
15000 0 : if (low > high || high > 0x10FFFF) {
15001 0 : return false;
15002 : }
15003 0 : ranges.AppendElement(low);
15004 0 : ranges.AppendElement(high);
15005 :
15006 0 : if (!ExpectSymbol(',', true))
15007 0 : break;
15008 0 : }
15009 :
15010 0 : if (ranges.Length() == 0)
15011 0 : return false;
15012 :
15013 : RefPtr<nsCSSValue::Array> srcVals
15014 0 : = nsCSSValue::Array::Create(ranges.Length());
15015 :
15016 0 : for (uint32_t i = 0; i < ranges.Length(); i++)
15017 0 : srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer);
15018 0 : aValue.SetArrayValue(srcVals, eCSSUnit_Array);
15019 0 : return true;
15020 : }
15021 :
15022 : // font-feature-settings: normal | <feature-tag-value> [, <feature-tag-value>]*
15023 : // <feature-tag-value> = <string> [ <integer> | on | off ]?
15024 :
15025 : // minimum - "tagx", "tagy", "tagz"
15026 : // edge error case - "tagx" on 1, "tagx" "tagy", "tagx" -1, "tagx" big
15027 :
15028 : // pair value is always x = string, y = int
15029 :
15030 : // font feature tags must be four ASCII characters
15031 : #define FEATURE_TAG_LENGTH 4
15032 :
15033 : static bool
15034 0 : ValidFontFeatureTag(const nsString& aTag)
15035 : {
15036 0 : if (aTag.Length() != FEATURE_TAG_LENGTH) {
15037 0 : return false;
15038 : }
15039 : uint32_t i;
15040 0 : for (i = 0; i < FEATURE_TAG_LENGTH; i++) {
15041 0 : uint32_t ch = aTag[i];
15042 0 : if (ch < 0x20 || ch > 0x7e) {
15043 0 : return false;
15044 : }
15045 : }
15046 0 : return true;
15047 : }
15048 :
15049 : bool
15050 0 : CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue)
15051 : {
15052 0 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
15053 : nullptr)) {
15054 0 : return true;
15055 : }
15056 :
15057 0 : auto resultHead = MakeUnique<nsCSSValuePairList>();
15058 0 : nsCSSValuePairList* cur = resultHead.get();
15059 :
15060 : for (;;) {
15061 : // feature tag
15062 0 : if (!GetToken(true)) {
15063 0 : return false;
15064 : }
15065 :
15066 0 : if (mToken.mType != eCSSToken_String ||
15067 0 : !ValidFontFeatureTag(mToken.mIdent)) {
15068 0 : UngetToken();
15069 0 : return false;
15070 : }
15071 0 : cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
15072 :
15073 0 : if (!GetToken(true)) {
15074 0 : cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
15075 0 : break;
15076 : }
15077 :
15078 : // optional value or on/off keyword
15079 0 : if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid &&
15080 0 : mToken.mInteger >= 0) {
15081 0 : cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
15082 0 : } else if (mToken.mType == eCSSToken_Ident &&
15083 0 : mToken.mIdent.LowerCaseEqualsLiteral("on")) {
15084 0 : cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
15085 0 : } else if (mToken.mType == eCSSToken_Ident &&
15086 0 : mToken.mIdent.LowerCaseEqualsLiteral("off")) {
15087 0 : cur->mYValue.SetIntValue(0, eCSSUnit_Integer);
15088 : } else {
15089 : // something other than value/on/off, set default value
15090 0 : cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
15091 0 : UngetToken();
15092 : }
15093 :
15094 0 : if (!ExpectSymbol(',', true)) {
15095 0 : break;
15096 : }
15097 :
15098 0 : cur->mNext = new nsCSSValuePairList;
15099 0 : cur = cur->mNext;
15100 : }
15101 :
15102 0 : aValue.AdoptPairListValue(Move(resultHead));
15103 :
15104 0 : return true;
15105 : }
15106 :
15107 : bool
15108 0 : CSSParserImpl::ParseFontVariationSettings(nsCSSValue& aValue)
15109 : {
15110 0 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
15111 : nullptr)) {
15112 0 : return true;
15113 : }
15114 :
15115 0 : auto resultHead = MakeUnique<nsCSSValuePairList>();
15116 0 : nsCSSValuePairList* cur = resultHead.get();
15117 :
15118 : for (;;) {
15119 : // variation tag
15120 0 : if (!GetToken(true)) {
15121 0 : return false;
15122 : }
15123 :
15124 : // variation tags are subject to the same validation as feature tags
15125 0 : if (mToken.mType != eCSSToken_String ||
15126 0 : !ValidFontFeatureTag(mToken.mIdent)) {
15127 0 : UngetToken();
15128 0 : return false;
15129 : }
15130 0 : cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
15131 :
15132 0 : if (!GetToken(true)) {
15133 0 : return false;
15134 : }
15135 :
15136 0 : if (mToken.mType == eCSSToken_Number) {
15137 0 : cur->mYValue.SetFloatValue(mToken.mNumber, eCSSUnit_Number);
15138 : } else {
15139 0 : UngetToken();
15140 0 : return false;
15141 : }
15142 :
15143 0 : if (!ExpectSymbol(',', true)) {
15144 0 : break;
15145 : }
15146 :
15147 0 : cur->mNext = new nsCSSValuePairList;
15148 0 : cur = cur->mNext;
15149 : }
15150 :
15151 0 : aValue.AdoptPairListValue(Move(resultHead));
15152 :
15153 0 : return true;
15154 : }
15155 :
15156 : bool
15157 2 : CSSParserImpl::ParseListStyle()
15158 : {
15159 : // 'list-style' can accept 'none' for two different subproperties,
15160 : // 'list-style-type' and 'list-style-image'. In order to accept
15161 : // 'none' as the value of either but still allow another value for
15162 : // either, we need to ensure that the first 'none' we find gets
15163 : // allocated to a dummy property instead. Since parse function for
15164 : // 'list-style-type' could accept values for 'list-style-position',
15165 : // we put position in front of type.
15166 : static const nsCSSPropertyID listStyleIDs[] = {
15167 : eCSSPropertyExtra_x_none_value,
15168 : eCSSProperty_list_style_position,
15169 : eCSSProperty_list_style_type,
15170 : eCSSProperty_list_style_image
15171 : };
15172 :
15173 4 : nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)];
15174 : int32_t found =
15175 2 : ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs));
15176 2 : if (found < 1) {
15177 0 : return false;
15178 : }
15179 :
15180 2 : if ((found & (1|4|8)) == (1|4|8)) {
15181 0 : if (values[0].GetUnit() == eCSSUnit_None) {
15182 : // We found a 'none' plus another value for both of
15183 : // 'list-style-type' and 'list-style-image'. This is a parse
15184 : // error, since the 'none' has to count for at least one of them.
15185 0 : return false;
15186 : } else {
15187 0 : NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] &&
15188 : values[0] == values[2] && values[0] == values[3],
15189 : "should be a special value");
15190 : }
15191 : }
15192 :
15193 2 : if ((found & 2) == 0) {
15194 : values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
15195 0 : eCSSUnit_Enumerated);
15196 : }
15197 2 : if ((found & 4) == 0) {
15198 : // Provide default values
15199 0 : nsIAtom* type = (found & 1) ? nsGkAtoms::none : nsGkAtoms::disc;
15200 0 : values[2].SetAtomIdentValue(do_AddRef(type));
15201 : }
15202 2 : if ((found & 8) == 0) {
15203 2 : values[3].SetNoneValue();
15204 : }
15205 :
15206 : // Start at 1 to avoid appending fake value.
15207 8 : for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) {
15208 6 : AppendValue(listStyleIDs[index], values[index]);
15209 : }
15210 2 : return true;
15211 : }
15212 :
15213 : bool
15214 12 : CSSParserImpl::ParseListStyleType(nsCSSValue& aValue)
15215 : {
15216 12 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_STRING,
15217 : nullptr)) {
15218 0 : return true;
15219 : }
15220 :
15221 12 : if (ParseCounterStyleNameValue(aValue) || ParseSymbols(aValue)) {
15222 12 : return true;
15223 : }
15224 :
15225 0 : return false;
15226 : }
15227 :
15228 : bool
15229 246 : CSSParserImpl::ParseMargin()
15230 : {
15231 : static const nsCSSPropertyID kMarginSideIDs[] = {
15232 : eCSSProperty_margin_top,
15233 : eCSSProperty_margin_right,
15234 : eCSSProperty_margin_bottom,
15235 : eCSSProperty_margin_left
15236 : };
15237 :
15238 246 : return ParseBoxProperties(kMarginSideIDs);
15239 : }
15240 :
15241 : bool
15242 2 : CSSParserImpl::ParseObjectPosition()
15243 : {
15244 4 : nsCSSValue value;
15245 2 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) &&
15246 0 : !ParsePositionValue(value)) {
15247 0 : return false;
15248 : }
15249 2 : AppendValue(eCSSProperty_object_position, value);
15250 2 : return true;
15251 : }
15252 :
15253 : bool
15254 27 : CSSParserImpl::ParseOutline()
15255 : {
15256 27 : const int32_t numProps = 3;
15257 : static const nsCSSPropertyID kOutlineIDs[] = {
15258 : eCSSProperty_outline_color,
15259 : eCSSProperty_outline_style,
15260 : eCSSProperty_outline_width
15261 : };
15262 :
15263 54 : nsCSSValue values[numProps];
15264 27 : int32_t found = ParseChoice(values, kOutlineIDs, numProps);
15265 27 : if (found < 1) {
15266 0 : return false;
15267 : }
15268 :
15269 : // Provide default values
15270 27 : if ((found & 1) == 0) { // Provide default outline-color
15271 20 : values[0].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
15272 : }
15273 27 : if ((found & 2) == 0) { // Provide default outline-style
15274 1 : values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
15275 : }
15276 27 : if ((found & 4) == 0) { // Provide default outline-width
15277 1 : values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
15278 : }
15279 :
15280 : int32_t index;
15281 108 : for (index = 0; index < numProps; index++) {
15282 81 : AppendValue(kOutlineIDs[index], values[index]);
15283 : }
15284 27 : return true;
15285 : }
15286 :
15287 : bool
15288 71 : CSSParserImpl::ParseOverflow()
15289 : {
15290 142 : nsCSSValue overflow;
15291 71 : if (!ParseSingleTokenVariant(overflow, VARIANT_HK,
15292 : nsCSSProps::kOverflowKTable)) {
15293 0 : return false;
15294 : }
15295 :
15296 142 : nsCSSValue overflowX(overflow);
15297 142 : nsCSSValue overflowY(overflow);
15298 71 : if (eCSSUnit_Enumerated == overflow.GetUnit())
15299 67 : switch(overflow.GetIntValue()) {
15300 : case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
15301 0 : overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
15302 0 : overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
15303 0 : break;
15304 : case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
15305 0 : overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
15306 0 : overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
15307 0 : break;
15308 : }
15309 71 : AppendValue(eCSSProperty_overflow_x, overflowX);
15310 71 : AppendValue(eCSSProperty_overflow_y, overflowY);
15311 71 : return true;
15312 : }
15313 :
15314 : bool
15315 343 : CSSParserImpl::ParsePadding()
15316 : {
15317 : static const nsCSSPropertyID kPaddingSideIDs[] = {
15318 : eCSSProperty_padding_top,
15319 : eCSSProperty_padding_right,
15320 : eCSSProperty_padding_bottom,
15321 : eCSSProperty_padding_left
15322 : };
15323 :
15324 343 : return ParseBoxProperties(kPaddingSideIDs);
15325 : }
15326 :
15327 : bool
15328 0 : CSSParserImpl::ParseQuotes()
15329 : {
15330 0 : nsCSSValue value;
15331 0 : if (!ParseSingleTokenVariant(value, VARIANT_HOS, nullptr)) {
15332 0 : return false;
15333 : }
15334 0 : if (value.GetUnit() == eCSSUnit_String) {
15335 0 : nsCSSValue open = value;
15336 0 : nsCSSValuePairList* quotes = value.SetPairListValue();
15337 : for (;;) {
15338 0 : quotes->mXValue = open;
15339 : // get mandatory close
15340 0 : if (!ParseSingleTokenVariant(quotes->mYValue, VARIANT_STRING, nullptr)) {
15341 0 : return false;
15342 : }
15343 : // look for another open
15344 0 : if (!ParseSingleTokenVariant(open, VARIANT_STRING, nullptr)) {
15345 0 : break;
15346 : }
15347 0 : quotes->mNext = new nsCSSValuePairList;
15348 0 : quotes = quotes->mNext;
15349 : }
15350 : }
15351 0 : AppendValue(eCSSProperty_quotes, value);
15352 0 : return true;
15353 : }
15354 :
15355 : bool
15356 16 : CSSParserImpl::ParseTextDecoration()
15357 : {
15358 : static const nsCSSPropertyID kTextDecorationIDs[] = {
15359 : eCSSProperty_text_decoration_line,
15360 : eCSSProperty_text_decoration_style,
15361 : eCSSProperty_text_decoration_color
15362 : };
15363 16 : const int32_t numProps = MOZ_ARRAY_LENGTH(kTextDecorationIDs);
15364 32 : nsCSSValue values[numProps];
15365 :
15366 16 : int32_t found = ParseChoice(values, kTextDecorationIDs, numProps);
15367 16 : if (found < 1) {
15368 0 : return false;
15369 : }
15370 :
15371 : // Provide default values
15372 16 : if ((found & 1) == 0) { // Provide default text-decoration-line
15373 : values[0].SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE,
15374 0 : eCSSUnit_Enumerated);
15375 : }
15376 16 : if ((found & 2) == 0) { // Provide default text-decoration-style
15377 : values[1].SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
15378 10 : eCSSUnit_Enumerated);
15379 : }
15380 16 : if ((found & 4) == 0) { // Provide default text-decoration-color
15381 12 : values[2].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
15382 : }
15383 :
15384 64 : for (int32_t index = 0; index < numProps; index++) {
15385 48 : AppendValue(kTextDecorationIDs[index], values[index]);
15386 : }
15387 16 : return true;
15388 : }
15389 :
15390 : bool
15391 2 : CSSParserImpl::ParseTextEmphasis()
15392 : {
15393 : static constexpr nsCSSPropertyID kTextEmphasisIDs[] = {
15394 : eCSSProperty_text_emphasis_style,
15395 : eCSSProperty_text_emphasis_color
15396 : };
15397 2 : constexpr int32_t numProps = MOZ_ARRAY_LENGTH(kTextEmphasisIDs);
15398 4 : nsCSSValue values[numProps];
15399 :
15400 2 : int32_t found = ParseChoice(values, kTextEmphasisIDs, numProps);
15401 2 : if (found < 1) {
15402 0 : return false;
15403 : }
15404 :
15405 2 : if (!(found & 1)) { // Provide default text-emphasis-style
15406 0 : values[0].SetNoneValue();
15407 : }
15408 2 : if (!(found & 2)) { // Provide default text-emphasis-color
15409 2 : values[1].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
15410 : }
15411 :
15412 6 : for (int32_t index = 0; index < numProps; index++) {
15413 4 : AppendValue(kTextEmphasisIDs[index], values[index]);
15414 : }
15415 2 : return true;
15416 : }
15417 :
15418 : bool
15419 0 : CSSParserImpl::ParseTextEmphasisPosition(nsCSSValue& aValue)
15420 : {
15421 : static_assert((NS_STYLE_TEXT_EMPHASIS_POSITION_OVER ^
15422 : NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER ^
15423 : NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT ^
15424 : NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT) ==
15425 : (NS_STYLE_TEXT_EMPHASIS_POSITION_OVER |
15426 : NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER |
15427 : NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT |
15428 : NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT),
15429 : "text-emphasis-position constants should be bitmasks");
15430 :
15431 0 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
15432 0 : return true;
15433 : }
15434 :
15435 0 : nsCSSValue first, second;
15436 0 : const auto& kTable = nsCSSProps::kTextEmphasisPositionKTable;
15437 0 : if (!ParseSingleTokenVariant(first, VARIANT_KEYWORD, kTable) ||
15438 0 : !ParseSingleTokenVariant(second, VARIANT_KEYWORD, kTable)) {
15439 0 : return false;
15440 : }
15441 :
15442 0 : auto firstValue = first.GetIntValue();
15443 0 : auto secondValue = second.GetIntValue();
15444 0 : if ((firstValue == NS_STYLE_TEXT_EMPHASIS_POSITION_OVER ||
15445 0 : firstValue == NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER) ==
15446 0 : (secondValue == NS_STYLE_TEXT_EMPHASIS_POSITION_OVER ||
15447 : secondValue == NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER)) {
15448 0 : return false;
15449 : }
15450 :
15451 0 : aValue.SetIntValue(firstValue | secondValue, eCSSUnit_Enumerated);
15452 0 : return true;
15453 : }
15454 :
15455 : bool
15456 2 : CSSParserImpl::ParseTextEmphasisStyle(nsCSSValue& aValue)
15457 : {
15458 : static_assert((NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK ^
15459 : NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK) ==
15460 : (NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK |
15461 : NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK),
15462 : "text-emphasis-style shape and fill constants "
15463 : "should not intersect");
15464 : static_assert(NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED == 0,
15465 : "Making 'filled' zero ensures that if neither 'filled' nor "
15466 : "'open' is specified, we compute it to 'filled' per spec");
15467 :
15468 2 : if (ParseSingleTokenVariant(aValue, VARIANT_HOS, nullptr)) {
15469 2 : return true;
15470 : }
15471 :
15472 0 : nsCSSValue first, second;
15473 0 : const auto& fillKTable = nsCSSProps::kTextEmphasisStyleFillKTable;
15474 0 : const auto& shapeKTable = nsCSSProps::kTextEmphasisStyleShapeKTable;
15475 :
15476 : // Parse a fill value and/or a shape value, in either order.
15477 : // (Require at least one of them, and treat the second as optional.)
15478 0 : if (ParseSingleTokenVariant(first, VARIANT_KEYWORD, fillKTable)) {
15479 0 : Unused << ParseSingleTokenVariant(second, VARIANT_KEYWORD, shapeKTable);
15480 0 : } else if (ParseSingleTokenVariant(first, VARIANT_KEYWORD, shapeKTable)) {
15481 0 : Unused << ParseSingleTokenVariant(second, VARIANT_KEYWORD, fillKTable);
15482 : } else {
15483 0 : return false;
15484 : }
15485 :
15486 0 : auto value = first.GetIntValue();
15487 0 : if (second.GetUnit() == eCSSUnit_Enumerated) {
15488 0 : value |= second.GetIntValue();
15489 : }
15490 0 : aValue.SetIntValue(value, eCSSUnit_Enumerated);
15491 0 : return true;
15492 : }
15493 :
15494 : bool
15495 57 : CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableEntry aTable[])
15496 : {
15497 57 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
15498 : // 'inherit', 'initial' and 'unset' must be alone
15499 7 : return true;
15500 : }
15501 :
15502 100 : nsCSSValue left;
15503 50 : if (!ParseSingleTokenVariant(left, VARIANT_KEYWORD, aTable)) {
15504 0 : return false;
15505 : }
15506 :
15507 50 : if (!nsLayoutUtils::IsTextAlignUnsafeValueEnabled()) {
15508 50 : aValue = left;
15509 50 : return true;
15510 : }
15511 :
15512 0 : nsCSSValue right;
15513 0 : if (ParseSingleTokenVariant(right, VARIANT_KEYWORD, aTable)) {
15514 : // 'true' must be combined with some other value than 'true'.
15515 0 : if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE &&
15516 0 : right.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) {
15517 0 : return false;
15518 : }
15519 0 : aValue.SetPairValue(left, right);
15520 : } else {
15521 : // Single value 'true' is not allowed.
15522 0 : if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) {
15523 0 : return false;
15524 : }
15525 0 : aValue = left;
15526 : }
15527 0 : return true;
15528 : }
15529 :
15530 : bool
15531 57 : CSSParserImpl::ParseTextAlign(nsCSSValue& aValue)
15532 : {
15533 57 : return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable);
15534 : }
15535 :
15536 : bool
15537 0 : CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue)
15538 : {
15539 0 : return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable);
15540 : }
15541 :
15542 : bool
15543 18 : CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
15544 : {
15545 : static_assert((NS_STYLE_TEXT_DECORATION_LINE_NONE ^
15546 : NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE ^
15547 : NS_STYLE_TEXT_DECORATION_LINE_OVERLINE ^
15548 : NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH ^
15549 : NS_STYLE_TEXT_DECORATION_LINE_BLINK) ==
15550 : (NS_STYLE_TEXT_DECORATION_LINE_NONE |
15551 : NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE |
15552 : NS_STYLE_TEXT_DECORATION_LINE_OVERLINE |
15553 : NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH |
15554 : NS_STYLE_TEXT_DECORATION_LINE_BLINK),
15555 : "text decoration constants need to be bitmasks");
15556 18 : if (ParseSingleTokenVariant(aValue, VARIANT_HK,
15557 : nsCSSProps::kTextDecorationLineKTable)) {
15558 16 : if (eCSSUnit_Enumerated == aValue.GetUnit()) {
15559 12 : int32_t intValue = aValue.GetIntValue();
15560 12 : if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
15561 : // look for more keywords
15562 24 : nsCSSValue keyword;
15563 : int32_t index;
15564 12 : for (index = 0; index < 3; index++) {
15565 12 : if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) {
15566 0 : int32_t newValue = keyword.GetIntValue();
15567 0 : if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE ||
15568 0 : newValue & intValue) {
15569 : // 'none' keyword in conjuction with others is not allowed, and
15570 : // duplicate keyword is not allowed.
15571 0 : return false;
15572 : }
15573 0 : intValue |= newValue;
15574 : }
15575 : else {
15576 12 : break;
15577 : }
15578 : }
15579 12 : aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
15580 : }
15581 : }
15582 16 : return true;
15583 : }
15584 2 : return false;
15585 : }
15586 :
15587 : bool
15588 18 : CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
15589 : {
15590 18 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
15591 : // 'inherit', 'initial' and 'unset' must be alone
15592 15 : return true;
15593 : }
15594 :
15595 6 : nsCSSValue left;
15596 3 : if (!ParseSingleTokenVariant(left, VARIANT_KEYWORD | VARIANT_STRING,
15597 : nsCSSProps::kTextOverflowKTable))
15598 0 : return false;
15599 :
15600 6 : nsCSSValue right;
15601 3 : if (ParseSingleTokenVariant(right, VARIANT_KEYWORD | VARIANT_STRING,
15602 : nsCSSProps::kTextOverflowKTable))
15603 0 : aValue.SetPairValue(left, right);
15604 : else {
15605 3 : aValue = left;
15606 : }
15607 3 : return true;
15608 : }
15609 :
15610 : bool
15611 0 : CSSParserImpl::ParseTouchAction(nsCSSValue& aValue)
15612 : {
15613 : // Avaliable values of property touch-action:
15614 : // auto | none | [pan-x || pan-y] | manipulation
15615 :
15616 0 : if (!ParseSingleTokenVariant(aValue, VARIANT_HK,
15617 : nsCSSProps::kTouchActionKTable)) {
15618 0 : return false;
15619 : }
15620 :
15621 : // Auto and None keywords aren't allowed in conjunction with others.
15622 : // Also inherit, initial and unset values are available.
15623 0 : if (eCSSUnit_Enumerated != aValue.GetUnit()) {
15624 0 : return true;
15625 : }
15626 :
15627 0 : int32_t intValue = aValue.GetIntValue();
15628 0 : nsCSSValue nextValue;
15629 0 : if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) {
15630 0 : int32_t nextIntValue = nextValue.GetIntValue();
15631 :
15632 : // duplicates aren't allowed.
15633 0 : if (nextIntValue & intValue) {
15634 0 : return false;
15635 : }
15636 :
15637 : // Auto and None and Manipulation is not allowed in conjunction with others.
15638 0 : if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE |
15639 : NS_STYLE_TOUCH_ACTION_AUTO |
15640 : NS_STYLE_TOUCH_ACTION_MANIPULATION)) {
15641 0 : return false;
15642 : }
15643 :
15644 0 : aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
15645 : }
15646 :
15647 0 : return true;
15648 : }
15649 :
15650 : bool
15651 0 : CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue)
15652 : {
15653 0 : if (!ParseSingleTokenVariant(aValue, VARIANT_HK,
15654 : nsCSSProps::kTextCombineUprightKTable)) {
15655 0 : return false;
15656 : }
15657 :
15658 : // if 'digits', need to check for an explicit number [2, 3, 4]
15659 0 : if (eCSSUnit_Enumerated == aValue.GetUnit() &&
15660 0 : aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) {
15661 0 : if (!nsLayoutUtils::TextCombineUprightDigitsEnabled()) {
15662 0 : return false;
15663 : }
15664 0 : if (!GetToken(true)) {
15665 0 : return true;
15666 : }
15667 0 : if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
15668 0 : switch (mToken.mInteger) {
15669 : case 2: // already set, nothing to do
15670 0 : break;
15671 : case 3:
15672 : aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3,
15673 0 : eCSSUnit_Enumerated);
15674 0 : break;
15675 : case 4:
15676 : aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4,
15677 0 : eCSSUnit_Enumerated);
15678 0 : break;
15679 : default:
15680 : // invalid digits value
15681 0 : return false;
15682 : }
15683 : } else {
15684 0 : UngetToken();
15685 : }
15686 : }
15687 0 : return true;
15688 : }
15689 :
15690 : ///////////////////////////////////////////////////////
15691 : // transform Parsing Implementation
15692 :
15693 : /* Reads a function list of arguments and consumes the closing parenthesis.
15694 : * Do not call this function directly; it's meant to be called from
15695 : * ParseFunction.
15696 : */
15697 : bool
15698 121 : CSSParserImpl::ParseFunctionInternals(const uint32_t aVariantMask[],
15699 : uint32_t aVariantMaskAll,
15700 : uint16_t aMinElems,
15701 : uint16_t aMaxElems,
15702 : InfallibleTArray<nsCSSValue> &aOutput)
15703 : {
15704 121 : NS_ASSERTION((aVariantMask && !aVariantMaskAll) ||
15705 : (!aVariantMask && aVariantMaskAll),
15706 : "only one of the two variant mask parameters can be set");
15707 :
15708 127 : for (uint16_t index = 0; index < aMaxElems; ++index) {
15709 127 : nsCSSValue newValue;
15710 127 : uint32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index];
15711 127 : if (ParseVariant(newValue, m, nullptr) != CSSParseResult::Ok) {
15712 0 : break;
15713 : }
15714 :
15715 127 : if (nsCSSValue::IsFloatUnit(newValue.GetUnit())) {
15716 : // Clamp infinity or -infinity values to max float or -max float to avoid
15717 : // calculations with infinity.
15718 214 : newValue.SetFloatValue(
15719 214 : mozilla::clamped(newValue.GetFloatValue(),
15720 214 : -std::numeric_limits<float>::max(),
15721 321 : std::numeric_limits<float>::max()),
15722 107 : newValue.GetUnit());
15723 : }
15724 :
15725 127 : aOutput.AppendElement(newValue);
15726 :
15727 127 : if (ExpectSymbol(',', true)) {
15728 : // Move on to the next argument if we see a comma.
15729 6 : continue;
15730 : }
15731 :
15732 121 : if (ExpectSymbol(')', true)) {
15733 : // Make sure we've read enough symbols if we see a closing parenthesis.
15734 121 : return (index + 1) >= aMinElems;
15735 : }
15736 :
15737 : // Only a comma or a closing parenthesis is valid after an argument.
15738 0 : break;
15739 : }
15740 :
15741 : // If we're here, we've hit an error without seeing a closing parenthesis or
15742 : // we've read too many elements without seeing a closing parenthesis.
15743 0 : SkipUntil(')');
15744 0 : return false;
15745 : }
15746 :
15747 : /* Parses a function [ input of the form (a [, b]*) ] and stores it
15748 : * as an nsCSSValue that holds a function of the form
15749 : * function-name arg1 arg2 ... argN
15750 : *
15751 : * On error, the return value is false.
15752 : *
15753 : * @param aFunction The name of the function that we're reading.
15754 : * @param aAllowedTypes An array of values corresponding to the legal
15755 : * types for each element in the function. The zeroth element in the
15756 : * array corresponds to the first function parameter, etc. The length
15757 : * of this array _must_ be greater than or equal to aMaxElems or the
15758 : * behavior is undefined. If not null, aAllowTypesAll must be 0.
15759 : * @param aAllowedTypesAll If set, every element tested for these types
15760 : * @param aMinElems Minimum number of elements to read. Reading fewer than
15761 : * this many elements will result in the function failing.
15762 : * @param aMaxElems Maximum number of elements to read. Reading more than
15763 : * this many elements will result in the function failing.
15764 : * @param aValue (out) The value that was parsed.
15765 : */
15766 : bool
15767 121 : CSSParserImpl::ParseFunction(nsCSSKeyword aFunction,
15768 : const uint32_t aAllowedTypes[],
15769 : uint32_t aAllowedTypesAll,
15770 : uint16_t aMinElems, uint16_t aMaxElems,
15771 : nsCSSValue &aValue)
15772 : {
15773 121 : NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) ||
15774 : (!aAllowedTypes && aAllowedTypesAll),
15775 : "only one of the two allowed type parameter can be set");
15776 : typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
15777 :
15778 : /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
15779 : * elements stored in the the nsCSSValue::Array.
15780 : */
15781 : static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
15782 :
15783 : /* Read in a list of values as an array, failing if we can't or if
15784 : * it's out of bounds.
15785 : *
15786 : * We reserve 16 entries in the foundValues array in order to avoid
15787 : * having to resize the array dynamically when parsing some well-formed
15788 : * functions. The number 16 is coming from the number of arguments that
15789 : * matrix3d() accepts.
15790 : */
15791 242 : AutoTArray<nsCSSValue, 16> foundValues;
15792 121 : if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems,
15793 : aMaxElems, foundValues)) {
15794 0 : return false;
15795 : }
15796 :
15797 : /*
15798 : * In case the user has given us more than 2^16 - 2 arguments,
15799 : * we'll truncate them at 2^16 - 2 arguments.
15800 : */
15801 121 : uint16_t numArgs = std::min(foundValues.Length(), MAX_ALLOWED_ELEMS);
15802 : RefPtr<nsCSSValue::Array> convertedArray =
15803 242 : aValue.InitFunction(aFunction, numArgs);
15804 :
15805 : /* Copy things over. */
15806 248 : for (uint16_t index = 0; index < numArgs; ++index)
15807 127 : convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
15808 :
15809 : /* Return it! */
15810 121 : return true;
15811 : }
15812 :
15813 : /**
15814 : * Given a token, determines the minimum and maximum number of function
15815 : * parameters to read, along with the mask that should be used to read
15816 : * those function parameters. If the token isn't a transform function,
15817 : * returns an error.
15818 : *
15819 : * @param aToken The token identifying the function.
15820 : * @param aIsPrefixed If true, parse matrices using the matrix syntax
15821 : * for -moz-transform.
15822 : * @param aDisallowRelativeValues If true, only allow variants that are
15823 : * numbers or have non-relative dimensions.
15824 : * @param aMinElems [out] The minimum number of elements to read.
15825 : * @param aMaxElems [out] The maximum number of elements to read
15826 : * @param aVariantMask [out] The variant mask to use during parsing
15827 : * @return Whether the information was loaded successfully.
15828 : */
15829 108 : static bool GetFunctionParseInformation(nsCSSKeyword aToken,
15830 : bool aIsPrefixed,
15831 : bool aDisallowRelativeValues,
15832 : uint16_t &aMinElems,
15833 : uint16_t &aMaxElems,
15834 : const uint32_t *& aVariantMask)
15835 : {
15836 : /* These types represent the common variant masks that will be used to
15837 : * parse out the individual functions. The order in the enumeration
15838 : * must match the order in which the masks are declared.
15839 : */
15840 : enum { eLengthPercentCalc,
15841 : eLengthCalc,
15842 : eAbsoluteLengthCalc,
15843 : eTwoLengthPercentCalcs,
15844 : eTwoAbsoluteLengthCalcs,
15845 : eTwoLengthPercentCalcsOneLengthCalc,
15846 : eThreeAbsoluteLengthCalc,
15847 : eAngle,
15848 : eTwoAngles,
15849 : eNumber,
15850 : eNonNegativeLength,
15851 : eNonNegativeAbsoluteLength,
15852 : eTwoNumbers,
15853 : eThreeNumbers,
15854 : eThreeNumbersOneAngle,
15855 : eMatrix,
15856 : eMatrixPrefixed,
15857 : eMatrix3d,
15858 : eMatrix3dPrefixed,
15859 : eNumVariantMasks };
15860 : static const int32_t kMaxElemsPerFunction = 16;
15861 : static const uint32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
15862 : {VARIANT_LPCALC},
15863 : {VARIANT_LCALC},
15864 : {VARIANT_LB},
15865 : {VARIANT_LPCALC, VARIANT_LPCALC},
15866 : {VARIANT_LBCALC, VARIANT_LBCALC},
15867 : {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LCALC},
15868 : {VARIANT_LBCALC, VARIANT_LBCALC, VARIANT_LBCALC},
15869 : {VARIANT_ANGLE_OR_ZERO},
15870 : {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
15871 : {VARIANT_NUMBER},
15872 : {VARIANT_LENGTH|VARIANT_NONNEGATIVE_DIMENSION},
15873 : {VARIANT_LB|VARIANT_NONNEGATIVE_DIMENSION},
15874 : {VARIANT_NUMBER, VARIANT_NUMBER},
15875 : {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
15876 : {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
15877 : {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
15878 : VARIANT_NUMBER, VARIANT_NUMBER},
15879 : {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
15880 : VARIANT_LPNCALC, VARIANT_LPNCALC},
15881 : {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
15882 : VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
15883 : VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
15884 : VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
15885 : {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
15886 : VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
15887 : VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
15888 : VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
15889 : // Map from a mask to a congruent mask that excludes relative variants.
15890 : static const int32_t kNonRelativeVariantMap[eNumVariantMasks] = {
15891 : eAbsoluteLengthCalc,
15892 : eAbsoluteLengthCalc,
15893 : eAbsoluteLengthCalc,
15894 : eTwoAbsoluteLengthCalcs,
15895 : eTwoAbsoluteLengthCalcs,
15896 : eThreeAbsoluteLengthCalc,
15897 : eThreeAbsoluteLengthCalc,
15898 : eAngle,
15899 : eTwoAngles,
15900 : eNumber,
15901 : eNonNegativeAbsoluteLength,
15902 : eNonNegativeAbsoluteLength,
15903 : eTwoNumbers,
15904 : eThreeNumbers,
15905 : eThreeNumbersOneAngle,
15906 : eMatrix,
15907 : eMatrix,
15908 : eMatrix3d,
15909 : eMatrix3d };
15910 :
15911 : #ifdef DEBUG
15912 : static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
15913 : {1, 1, 1, 2, 2, 3, 3, 1, 2, 1, 1, 1, 2, 3, 4, 6, 6, 16, 16};
15914 : #endif
15915 :
15916 108 : int32_t variantIndex = eNumVariantMasks;
15917 :
15918 108 : switch (aToken) {
15919 : case eCSSKeyword_translatex:
15920 : case eCSSKeyword_translatey:
15921 : /* Exactly one length or percent. */
15922 33 : variantIndex = eLengthPercentCalc;
15923 33 : aMinElems = 1U;
15924 33 : aMaxElems = 1U;
15925 33 : break;
15926 : case eCSSKeyword_translatez:
15927 : /* Exactly one length */
15928 0 : variantIndex = eLengthCalc;
15929 0 : aMinElems = 1U;
15930 0 : aMaxElems = 1U;
15931 0 : break;
15932 : case eCSSKeyword_translate3d:
15933 : /* Exactly two lengthds or percents and a number */
15934 0 : variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
15935 0 : aMinElems = 3U;
15936 0 : aMaxElems = 3U;
15937 0 : break;
15938 : case eCSSKeyword_scalez:
15939 : case eCSSKeyword_scalex:
15940 : case eCSSKeyword_scaley:
15941 : /* Exactly one scale factor. */
15942 23 : variantIndex = eNumber;
15943 23 : aMinElems = 1U;
15944 23 : aMaxElems = 1U;
15945 23 : break;
15946 : case eCSSKeyword_scale3d:
15947 : /* Exactly three scale factors. */
15948 0 : variantIndex = eThreeNumbers;
15949 0 : aMinElems = 3U;
15950 0 : aMaxElems = 3U;
15951 0 : break;
15952 : case eCSSKeyword_rotatex:
15953 : case eCSSKeyword_rotatey:
15954 : case eCSSKeyword_rotate:
15955 : case eCSSKeyword_rotatez:
15956 : /* Exactly one angle. */
15957 13 : variantIndex = eAngle;
15958 13 : aMinElems = 1U;
15959 13 : aMaxElems = 1U;
15960 13 : break;
15961 : case eCSSKeyword_rotate3d:
15962 0 : variantIndex = eThreeNumbersOneAngle;
15963 0 : aMinElems = 4U;
15964 0 : aMaxElems = 4U;
15965 0 : break;
15966 : case eCSSKeyword_translate:
15967 : /* One or two lengths or percents. */
15968 12 : variantIndex = eTwoLengthPercentCalcs;
15969 12 : aMinElems = 1U;
15970 12 : aMaxElems = 2U;
15971 12 : break;
15972 : case eCSSKeyword_skew:
15973 : /* Exactly one or two angles. */
15974 0 : variantIndex = eTwoAngles;
15975 0 : aMinElems = 1U;
15976 0 : aMaxElems = 2U;
15977 0 : break;
15978 : case eCSSKeyword_scale:
15979 : /* One or two scale factors. */
15980 26 : variantIndex = eTwoNumbers;
15981 26 : aMinElems = 1U;
15982 26 : aMaxElems = 2U;
15983 26 : break;
15984 : case eCSSKeyword_skewx:
15985 : /* Exactly one angle. */
15986 0 : variantIndex = eAngle;
15987 0 : aMinElems = 1U;
15988 0 : aMaxElems = 1U;
15989 0 : break;
15990 : case eCSSKeyword_skewy:
15991 : /* Exactly one angle. */
15992 0 : variantIndex = eAngle;
15993 0 : aMinElems = 1U;
15994 0 : aMaxElems = 1U;
15995 0 : break;
15996 : case eCSSKeyword_matrix:
15997 : /* Six values, all numbers. */
15998 0 : variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
15999 0 : aMinElems = 6U;
16000 0 : aMaxElems = 6U;
16001 0 : break;
16002 : case eCSSKeyword_matrix3d:
16003 : /* 16 matrix values, all numbers */
16004 0 : variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
16005 0 : aMinElems = 16U;
16006 0 : aMaxElems = 16U;
16007 0 : break;
16008 : case eCSSKeyword_perspective:
16009 : /* Exactly one scale number. */
16010 1 : variantIndex = eNonNegativeLength;
16011 1 : aMinElems = 1U;
16012 1 : aMaxElems = 1U;
16013 1 : break;
16014 : default:
16015 : /* Oh dear, we didn't match. Report an error. */
16016 0 : return false;
16017 : }
16018 :
16019 108 : if (aDisallowRelativeValues) {
16020 0 : variantIndex = kNonRelativeVariantMap[variantIndex];
16021 : }
16022 :
16023 108 : NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
16024 108 : NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
16025 108 : NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
16026 108 : NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
16027 108 : NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
16028 : #ifdef DEBUG
16029 108 : NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
16030 : "Invalid aMaxElems for this variant mask.");
16031 : #endif
16032 :
16033 : // Convert the index into a mask.
16034 108 : aVariantMask = kVariantMasks[variantIndex];
16035 :
16036 108 : return true;
16037 : }
16038 :
16039 1 : bool CSSParserImpl::ParseWillChange()
16040 : {
16041 2 : nsCSSValue listValue;
16042 1 : nsCSSValueList* currentListValue = listValue.SetListValue();
16043 1 : bool first = true;
16044 : for (;;) {
16045 : const uint32_t variantMask = VARIANT_IDENTIFIER |
16046 : VARIANT_INHERIT |
16047 : VARIANT_NONE |
16048 : VARIANT_ALL |
16049 1 : VARIANT_AUTO;
16050 1 : nsCSSValue value;
16051 1 : if (!ParseSingleTokenVariant(value, variantMask, nullptr)) {
16052 0 : return false;
16053 : }
16054 :
16055 2 : if (value.GetUnit() == eCSSUnit_None ||
16056 1 : value.GetUnit() == eCSSUnit_All)
16057 : {
16058 0 : return false;
16059 : }
16060 :
16061 1 : if (value.GetUnit() != eCSSUnit_Ident) {
16062 0 : if (first) {
16063 0 : AppendValue(eCSSProperty_will_change, value);
16064 0 : return true;
16065 : } else {
16066 0 : return false;
16067 : }
16068 : }
16069 :
16070 1 : value.AtomizeIdentValue();
16071 1 : nsIAtom* atom = value.GetAtomValue();
16072 1 : if (atom == nsGkAtoms::_default || atom == nsGkAtoms::willChange) {
16073 0 : return false;
16074 : }
16075 :
16076 1 : currentListValue->mValue = Move(value);
16077 :
16078 1 : if (!ExpectSymbol(',', true)) {
16079 1 : break;
16080 : }
16081 0 : currentListValue->mNext = new nsCSSValueList;
16082 0 : currentListValue = currentListValue->mNext;
16083 0 : first = false;
16084 0 : }
16085 :
16086 1 : AppendValue(eCSSProperty_will_change, listValue);
16087 1 : return true;
16088 : }
16089 :
16090 : /* Reads a single transform function from the tokenizer stream, reporting an
16091 : * error if something goes wrong.
16092 : */
16093 : bool
16094 108 : CSSParserImpl::ParseSingleTransform(bool aIsPrefixed,
16095 : bool aDisallowRelativeValues,
16096 : nsCSSValue& aValue)
16097 : {
16098 108 : if (!GetToken(true))
16099 0 : return false;
16100 :
16101 108 : if (mToken.mType != eCSSToken_Function) {
16102 0 : UngetToken();
16103 0 : return false;
16104 : }
16105 :
16106 : const uint32_t* variantMask;
16107 : uint16_t minElems, maxElems;
16108 108 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
16109 :
16110 108 : if (!GetFunctionParseInformation(keyword, aIsPrefixed,
16111 : aDisallowRelativeValues,
16112 : minElems, maxElems,
16113 : variantMask))
16114 0 : return false;
16115 :
16116 108 : return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue);
16117 : }
16118 :
16119 : /* Parses a transform property list by continuously reading in properties
16120 : * and constructing a matrix from it.
16121 : * aProperty can be transform or -moz-window-transform.
16122 : * FIXME: For -moz-window-transform, it would be nice to reject non-2d
16123 : * transforms at parse time, because the implementation only supports 2d
16124 : * transforms. Instead, at the moment, non-2d transforms are treated as the
16125 : * identity transform very late in the pipeline.
16126 : */
16127 : bool
16128 84 : CSSParserImpl::ParseTransform(bool aIsPrefixed, nsCSSPropertyID aProperty,
16129 : bool aDisallowRelativeValues)
16130 : {
16131 168 : nsCSSValue value;
16132 : // 'inherit', 'initial', 'unset' and 'none' must be alone
16133 84 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
16134 : nullptr)) {
16135 80 : nsCSSValueSharedList* list = new nsCSSValueSharedList;
16136 80 : value.SetSharedListValue(list);
16137 80 : list->mHead = new nsCSSValueList;
16138 80 : nsCSSValueList* cur = list->mHead;
16139 : for (;;) {
16140 136 : if (!ParseSingleTransform(aIsPrefixed, aDisallowRelativeValues,
16141 : cur->mValue)) {
16142 0 : return false;
16143 : }
16144 108 : if (CheckEndProperty()) {
16145 80 : break;
16146 : }
16147 28 : cur->mNext = new nsCSSValueList;
16148 28 : cur = cur->mNext;
16149 : }
16150 : }
16151 84 : AppendValue(aProperty, value);
16152 84 : return true;
16153 : }
16154 :
16155 : /* Reads a polygon function's argument list.
16156 : */
16157 : bool
16158 0 : CSSParserImpl::ParsePolygonFunction(nsCSSValue& aValue)
16159 : {
16160 0 : uint16_t numArgs = 1;
16161 :
16162 0 : nsCSSValue fillRuleValue;
16163 0 : if (ParseEnum(fillRuleValue, nsCSSProps::kFillRuleKTable)) {
16164 0 : numArgs++;
16165 :
16166 : // The fill-rule must be comma separated from the polygon points.
16167 0 : if (!ExpectSymbol(',', true)) {
16168 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
16169 0 : SkipUntil(')');
16170 0 : return false;
16171 : }
16172 : }
16173 :
16174 0 : nsCSSValue coordinates;
16175 0 : nsCSSValuePairList* item = coordinates.SetPairListValue();
16176 : for (;;) {
16177 0 : nsCSSValue xValue, yValue;
16178 0 : if (ParseVariant(xValue, VARIANT_LPCALC, nullptr) != CSSParseResult::Ok ||
16179 0 : ParseVariant(yValue, VARIANT_LPCALC, nullptr) != CSSParseResult::Ok) {
16180 0 : REPORT_UNEXPECTED_TOKEN(PECoordinatePair);
16181 0 : SkipUntil(')');
16182 0 : return false;
16183 : }
16184 0 : item->mXValue = xValue;
16185 0 : item->mYValue = yValue;
16186 :
16187 : // See whether to continue or whether to look for end of function.
16188 0 : if (!ExpectSymbol(',', true)) {
16189 : // We need to read the closing parenthesis.
16190 0 : if (!ExpectSymbol(')', true)) {
16191 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
16192 0 : SkipUntil(')');
16193 0 : return false;
16194 : }
16195 0 : break;
16196 : }
16197 0 : item->mNext = new nsCSSValuePairList;
16198 0 : item = item->mNext;
16199 0 : }
16200 :
16201 : RefPtr<nsCSSValue::Array> functionArray =
16202 0 : aValue.InitFunction(eCSSKeyword_polygon, numArgs);
16203 0 : functionArray->Item(numArgs) = coordinates;
16204 0 : if (numArgs > 1) {
16205 0 : functionArray->Item(1) = fillRuleValue;
16206 : }
16207 :
16208 0 : return true;
16209 : }
16210 :
16211 : bool
16212 0 : CSSParserImpl::ParseCircleOrEllipseFunction(nsCSSKeyword aKeyword,
16213 : nsCSSValue& aValue)
16214 : {
16215 0 : nsCSSValue radiusX, radiusY, position;
16216 0 : bool hasRadius = false, hasPosition = false;
16217 :
16218 : int32_t mask = VARIANT_LPCALC | VARIANT_NONNEGATIVE_DIMENSION |
16219 0 : VARIANT_KEYWORD;
16220 : CSSParseResult result =
16221 0 : ParseVariant(radiusX, mask, nsCSSProps::kShapeRadiusKTable);
16222 0 : if (result == CSSParseResult::Error) {
16223 0 : return false;
16224 0 : } else if (result == CSSParseResult::Ok) {
16225 0 : if (aKeyword == eCSSKeyword_ellipse) {
16226 0 : if (ParseVariant(radiusY, mask, nsCSSProps::kShapeRadiusKTable) !=
16227 : CSSParseResult::Ok) {
16228 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedRadius);
16229 0 : SkipUntil(')');
16230 0 : return false;
16231 : }
16232 : }
16233 0 : hasRadius = true;
16234 : }
16235 :
16236 0 : if (!ExpectSymbol(')', true)) {
16237 0 : if (!GetToken(true)) {
16238 0 : REPORT_UNEXPECTED_EOF(PEPositionEOF);
16239 0 : return false;
16240 : }
16241 :
16242 0 : if (mToken.mType != eCSSToken_Ident ||
16243 0 : !mToken.mIdent.LowerCaseEqualsLiteral("at") ||
16244 0 : !ParsePositionValueForBasicShape(position)) {
16245 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
16246 0 : SkipUntil(')');
16247 0 : return false;
16248 : }
16249 0 : if (!ExpectSymbol(')', true)) {
16250 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
16251 0 : SkipUntil(')');
16252 0 : return false;
16253 : }
16254 0 : hasPosition = true;
16255 : }
16256 :
16257 0 : size_t count = aKeyword == eCSSKeyword_circle ? 2 : 3;
16258 : RefPtr<nsCSSValue::Array> functionArray =
16259 0 : aValue.InitFunction(aKeyword, count);
16260 0 : if (hasRadius) {
16261 0 : functionArray->Item(1) = radiusX;
16262 0 : if (aKeyword == eCSSKeyword_ellipse) {
16263 0 : functionArray->Item(2) = radiusY;
16264 : }
16265 : }
16266 0 : if (hasPosition) {
16267 0 : functionArray->Item(count) = position;
16268 : }
16269 :
16270 0 : return true;
16271 : }
16272 :
16273 : bool
16274 0 : CSSParserImpl::ParseInsetFunction(nsCSSValue& aValue)
16275 : {
16276 : RefPtr<nsCSSValue::Array> functionArray =
16277 0 : aValue.InitFunction(eCSSKeyword_inset, 5);
16278 :
16279 0 : int count = 0;
16280 0 : while (count < 4) {
16281 : CSSParseResult result =
16282 0 : ParseVariant(functionArray->Item(count + 1), VARIANT_LPCALC, nullptr);
16283 0 : if (result == CSSParseResult::Error) {
16284 0 : count = 0;
16285 0 : break;
16286 0 : } else if (result == CSSParseResult::NotFound) {
16287 0 : break;
16288 : }
16289 0 : ++count;
16290 : }
16291 :
16292 0 : if (count == 0) {
16293 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedShapeArg);
16294 0 : SkipUntil(')');
16295 0 : return false;
16296 : }
16297 :
16298 0 : if (!ExpectSymbol(')', true)) {
16299 0 : if (!GetToken(true)) {
16300 0 : NS_NOTREACHED("ExpectSymbol should have returned true");
16301 0 : return false;
16302 : }
16303 :
16304 0 : RefPtr<nsCSSValue::Array> radiusArray = nsCSSValue::Array::Create(4);
16305 0 : functionArray->Item(5).SetArrayValue(radiusArray, eCSSUnit_Array);
16306 0 : if (mToken.mType != eCSSToken_Ident ||
16307 0 : !mToken.mIdent.LowerCaseEqualsLiteral("round") ||
16308 0 : !ParseBoxCornerRadiiInternals(radiusArray->ItemStorage())) {
16309 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedRadius);
16310 0 : SkipUntil(')');
16311 0 : return false;
16312 : }
16313 :
16314 0 : if (!ExpectSymbol(')', true)) {
16315 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
16316 0 : SkipUntil(')');
16317 0 : return false;
16318 : }
16319 : }
16320 :
16321 0 : return true;
16322 : }
16323 :
16324 : bool
16325 0 : CSSParserImpl::ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens)
16326 : {
16327 0 : if (!GetToken(true)) {
16328 0 : return false;
16329 : }
16330 :
16331 0 : if (mToken.mType != eCSSToken_Function) {
16332 0 : UngetToken();
16333 0 : return false;
16334 : }
16335 :
16336 : // Specific shape function parsing always consumes tokens.
16337 0 : *aConsumedTokens = true;
16338 0 : nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
16339 0 : switch (keyword) {
16340 : case eCSSKeyword_polygon:
16341 0 : return ParsePolygonFunction(aValue);
16342 : case eCSSKeyword_circle:
16343 : case eCSSKeyword_ellipse:
16344 0 : return ParseCircleOrEllipseFunction(keyword, aValue);
16345 : case eCSSKeyword_inset:
16346 0 : return ParseInsetFunction(aValue);
16347 : default:
16348 0 : return false;
16349 : }
16350 : }
16351 :
16352 : bool
16353 0 : CSSParserImpl::ParseReferenceBoxAndBasicShape(
16354 : nsCSSValue& aValue,
16355 : const KTableEntry aBoxKeywordTable[])
16356 : {
16357 0 : nsCSSValue referenceBox;
16358 0 : bool hasBox = ParseEnum(referenceBox, aBoxKeywordTable);
16359 :
16360 0 : const bool boxCameFirst = hasBox;
16361 :
16362 0 : nsCSSValue basicShape;
16363 0 : bool basicShapeConsumedTokens = false;
16364 0 : bool hasShape = ParseBasicShape(basicShape, &basicShapeConsumedTokens);
16365 :
16366 : // Parsing wasn't successful if ParseBasicShape consumed tokens but failed
16367 : // or if the token was neither a reference box nor a basic shape.
16368 0 : if ((!hasShape && basicShapeConsumedTokens) || (!hasBox && !hasShape)) {
16369 0 : return false;
16370 : }
16371 :
16372 : // Check if the second argument is a reference box if the first wasn't.
16373 0 : if (!hasBox) {
16374 0 : hasBox = ParseEnum(referenceBox, aBoxKeywordTable);
16375 : }
16376 :
16377 : RefPtr<nsCSSValue::Array> fullValue =
16378 0 : nsCSSValue::Array::Create((hasBox && hasShape) ? 2 : 1);
16379 :
16380 0 : if (hasBox && hasShape) {
16381 0 : fullValue->Item(boxCameFirst ? 0 : 1) = referenceBox;
16382 0 : fullValue->Item(boxCameFirst ? 1 : 0) = basicShape;
16383 0 : } else if (hasBox) {
16384 0 : fullValue->Item(0) = referenceBox;
16385 : } else {
16386 0 : MOZ_ASSERT(hasShape, "should've bailed if we got neither box nor shape");
16387 0 : fullValue->Item(0) = basicShape;
16388 : }
16389 :
16390 0 : aValue.SetArrayValue(fullValue, eCSSUnit_Array);
16391 0 : return true;
16392 : }
16393 :
16394 : // Parse a clip-path url to a <clipPath> element or a basic shape.
16395 : bool
16396 8 : CSSParserImpl::ParseClipPath(nsCSSValue& aValue)
16397 : {
16398 8 : if (ParseSingleTokenVariant(aValue, VARIANT_HUO, nullptr)) {
16399 8 : return true;
16400 : }
16401 :
16402 0 : if (!nsLayoutUtils::CSSClipPathShapesEnabled()) {
16403 : // With CSS Clip Path Shapes disabled, we should only accept
16404 : // SVG clipPath reference and none.
16405 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
16406 0 : return false;
16407 : }
16408 :
16409 : return ParseReferenceBoxAndBasicShape(
16410 0 : aValue, nsCSSProps::kClipPathGeometryBoxKTable);
16411 : }
16412 :
16413 : // none | [ <basic-shape> || <shape-box> ] | <image>
16414 : bool
16415 0 : CSSParserImpl::ParseShapeOutside(nsCSSValue& aValue)
16416 : {
16417 0 : if (ParseSingleTokenVariant(aValue, VARIANT_HUO, nullptr)) {
16418 : // 'inherit', 'initial', 'unset', 'none', and <image> url must be alone.
16419 0 : return true;
16420 : }
16421 :
16422 : return ParseReferenceBoxAndBasicShape(
16423 0 : aValue, nsCSSProps::kShapeOutsideShapeBoxKTable);
16424 : }
16425 :
16426 4 : bool CSSParserImpl::ParseTransformOrigin(nsCSSPropertyID aProperty)
16427 : {
16428 8 : nsCSSValuePair position;
16429 4 : if (!ParseBoxPositionValues(position, true))
16430 0 : return false;
16431 :
16432 : // Unlike many other uses of pairs, this position should always be stored
16433 : // as a pair, even if the values are the same, so it always serializes as
16434 : // a pair, and to keep the computation code simple.
16435 10 : if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
16436 6 : position.mXValue.GetUnit() == eCSSUnit_Initial ||
16437 2 : position.mXValue.GetUnit() == eCSSUnit_Unset) {
16438 2 : MOZ_ASSERT(position.mXValue == position.mYValue,
16439 : "inherit/initial/unset only half?");
16440 2 : AppendValue(aProperty, position.mXValue);
16441 : } else {
16442 4 : nsCSSValue value;
16443 2 : if (aProperty != eCSSProperty_transform_origin) {
16444 0 : value.SetPairValue(position.mXValue, position.mYValue);
16445 : } else {
16446 4 : nsCSSValue depth;
16447 : CSSParseResult result =
16448 2 : ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr);
16449 2 : if (result == CSSParseResult::Error) {
16450 0 : return false;
16451 2 : } else if (result == CSSParseResult::NotFound) {
16452 2 : depth.SetFloatValue(0.0f, eCSSUnit_Pixel);
16453 : }
16454 2 : value.SetTripletValue(position.mXValue, position.mYValue, depth);
16455 : }
16456 :
16457 2 : AppendValue(aProperty, value);
16458 : }
16459 4 : return true;
16460 : }
16461 :
16462 : /**
16463 : * Reads a drop-shadow value. At the moment the Filter Effects specification
16464 : * just expects one shadow item. Should this ever change to a list of shadow
16465 : * items, use ParseShadowList instead.
16466 : */
16467 : bool
16468 4 : CSSParserImpl::ParseDropShadow(nsCSSValue* aValue)
16469 : {
16470 : // Use nsCSSValueList to reuse the shadow resolving code in
16471 : // nsRuleNode and nsComputedDOMStyle.
16472 8 : nsCSSValue shadow;
16473 4 : nsCSSValueList* cur = shadow.SetListValue();
16474 4 : if (!ParseShadowItem(cur->mValue, false))
16475 0 : return false;
16476 :
16477 4 : if (!ExpectSymbol(')', true))
16478 0 : return false;
16479 :
16480 4 : nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1);
16481 :
16482 : // Copy things over.
16483 4 : dropShadow->Item(1) = shadow;
16484 :
16485 4 : return true;
16486 : }
16487 :
16488 : /**
16489 : * Reads a single url or filter function from the tokenizer stream, reporting an
16490 : * error if something goes wrong.
16491 : */
16492 : bool
16493 18 : CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
16494 : {
16495 18 : if (ParseSingleTokenVariant(*aValue, VARIANT_URL, nullptr)) {
16496 1 : return true;
16497 : }
16498 :
16499 17 : if (!nsLayoutUtils::CSSFiltersEnabled()) {
16500 : // With CSS Filters disabled, we should only accept an SVG reference filter.
16501 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
16502 0 : return false;
16503 : }
16504 :
16505 17 : if (!GetToken(true)) {
16506 0 : REPORT_UNEXPECTED_EOF(PEFilterEOF);
16507 0 : return false;
16508 : }
16509 :
16510 17 : if (mToken.mType != eCSSToken_Function) {
16511 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
16512 0 : UngetToken();
16513 0 : return false;
16514 : }
16515 :
16516 17 : nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
16517 : // Parse drop-shadow independently of the other filter functions
16518 : // because of its more complex characteristics.
16519 17 : if (functionName == eCSSKeyword_drop_shadow) {
16520 4 : if (ParseDropShadow(aValue)) {
16521 4 : return true;
16522 : } else {
16523 : // Unrecognized filter function.
16524 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
16525 0 : SkipUntil(')');
16526 0 : return false;
16527 : }
16528 : }
16529 :
16530 : // Set up the parsing rules based on the filter function.
16531 13 : uint32_t variantMask = VARIANT_PN;
16532 13 : bool rejectNegativeArgument = true;
16533 13 : bool clampArgumentToOne = false;
16534 13 : switch (functionName) {
16535 : case eCSSKeyword_blur:
16536 0 : variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
16537 : // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
16538 0 : rejectNegativeArgument = false;
16539 0 : break;
16540 : case eCSSKeyword_brightness:
16541 : case eCSSKeyword_contrast:
16542 : case eCSSKeyword_saturate:
16543 6 : break;
16544 : case eCSSKeyword_grayscale:
16545 : case eCSSKeyword_invert:
16546 : case eCSSKeyword_sepia:
16547 : case eCSSKeyword_opacity:
16548 7 : clampArgumentToOne = true;
16549 7 : break;
16550 : case eCSSKeyword_hue_rotate:
16551 0 : variantMask = VARIANT_ANGLE;
16552 0 : rejectNegativeArgument = false;
16553 0 : break;
16554 : default:
16555 : // Unrecognized filter function.
16556 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
16557 0 : SkipUntil(')');
16558 0 : return false;
16559 : }
16560 :
16561 : // Parse the function.
16562 13 : uint16_t minElems = 1U;
16563 13 : uint16_t maxElems = 1U;
16564 13 : uint32_t allVariants = 0;
16565 13 : if (!ParseFunction(functionName, &variantMask, allVariants,
16566 : minElems, maxElems, *aValue)) {
16567 0 : REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError);
16568 0 : return false;
16569 : }
16570 :
16571 : // Get the first and only argument to the filter function.
16572 13 : MOZ_ASSERT(aValue->GetUnit() == eCSSUnit_Function,
16573 : "expected a filter function");
16574 13 : MOZ_ASSERT(aValue->UnitHasArrayValue(),
16575 : "filter function should be an array");
16576 13 : MOZ_ASSERT(aValue->GetArrayValue()->Count() == 2,
16577 : "filter function should have exactly one argument");
16578 13 : nsCSSValue& arg = aValue->GetArrayValue()->Item(1);
16579 :
16580 26 : if (rejectNegativeArgument &&
16581 39 : ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) ||
16582 13 : (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) {
16583 0 : REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
16584 0 : return false;
16585 : }
16586 :
16587 13 : if (clampArgumentToOne) {
16588 7 : if (arg.GetUnit() == eCSSUnit_Number &&
16589 0 : arg.GetFloatValue() > 1.0f) {
16590 0 : arg.SetFloatValue(1.0f, arg.GetUnit());
16591 14 : } else if (arg.GetUnit() == eCSSUnit_Percent &&
16592 7 : arg.GetPercentValue() > 1.0f) {
16593 0 : arg.SetPercentValue(1.0f);
16594 : }
16595 : }
16596 :
16597 13 : return true;
16598 : }
16599 :
16600 : /**
16601 : * Parses a filter property value by continuously reading in urls and/or filter
16602 : * functions and constructing a list.
16603 : *
16604 : * When CSS Filters are enabled, the filter property accepts one or more SVG
16605 : * reference filters and/or CSS filter functions.
16606 : * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%);
16607 : *
16608 : * When CSS Filters are disabled, the filter property only accepts one SVG
16609 : * reference filter.
16610 : * e.g. filter: url(#my-filter);
16611 : */
16612 : bool
16613 14 : CSSParserImpl::ParseFilter()
16614 : {
16615 28 : nsCSSValue value;
16616 : // 'inherit', 'initial', 'unset' and 'none' must be alone
16617 14 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
16618 : nullptr)) {
16619 8 : nsCSSValueList* cur = value.SetListValue();
16620 28 : while (cur) {
16621 18 : if (!ParseSingleFilter(&cur->mValue)) {
16622 0 : return false;
16623 : }
16624 18 : if (CheckEndProperty()) {
16625 8 : break;
16626 : }
16627 10 : if (!nsLayoutUtils::CSSFiltersEnabled()) {
16628 : // With CSS Filters disabled, we should only accept one SVG reference
16629 : // filter.
16630 0 : REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
16631 0 : return false;
16632 : }
16633 10 : cur->mNext = new nsCSSValueList;
16634 10 : cur = cur->mNext;
16635 : }
16636 : }
16637 14 : AppendValue(eCSSProperty_filter, value);
16638 14 : return true;
16639 : }
16640 :
16641 : bool
16642 7 : CSSParserImpl::ParseTransitionProperty()
16643 : {
16644 14 : nsCSSValue value;
16645 : // 'inherit', 'initial', 'unset' and 'none' must be alone
16646 7 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
16647 : nullptr)) {
16648 : // Accept a list of arbitrary identifiers. They should be
16649 : // CSS properties, but we want to accept any so that we
16650 : // accept properties that we don't know about yet, e.g.
16651 : // transition-property: invalid-property, left, opacity;
16652 7 : nsCSSValueList* cur = value.SetListValue();
16653 : for (;;) {
16654 18 : if (!ParseSingleTokenVariant(cur->mValue,
16655 : VARIANT_IDENTIFIER | VARIANT_ALL,
16656 : nullptr)) {
16657 0 : return false;
16658 : }
16659 18 : if (cur->mValue.GetUnit() == eCSSUnit_Ident) {
16660 36 : nsDependentString str(cur->mValue.GetStringBufferValue());
16661 : // Exclude 'none', 'inherit', 'initial' and 'unset' according to the
16662 : // same rules as for 'counter-reset' in CSS 2.1.
16663 54 : if (str.LowerCaseEqualsLiteral("none") ||
16664 36 : str.LowerCaseEqualsLiteral("inherit") ||
16665 54 : str.LowerCaseEqualsLiteral("initial") ||
16666 18 : (str.LowerCaseEqualsLiteral("unset") &&
16667 0 : nsLayoutUtils::UnsetValueEnabled())) {
16668 0 : return false;
16669 : }
16670 : }
16671 18 : if (!ExpectSymbol(',', true)) {
16672 7 : break;
16673 : }
16674 11 : cur->mNext = new nsCSSValueList;
16675 11 : cur = cur->mNext;
16676 11 : }
16677 : }
16678 7 : AppendValue(eCSSProperty_transition_property, value);
16679 7 : return true;
16680 : }
16681 :
16682 : bool
16683 0 : CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue)
16684 : {
16685 0 : NS_ASSERTION(!mHavePushBack &&
16686 : mToken.mType == eCSSToken_Function &&
16687 : mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"),
16688 : "unexpected initial state");
16689 :
16690 0 : RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(4);
16691 :
16692 : float x1, x2, y1, y2;
16693 0 : if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) ||
16694 0 : !ParseTransitionTimingFunctionValueComponent(y1, ',', false) ||
16695 0 : !ParseTransitionTimingFunctionValueComponent(x2, ',', true) ||
16696 0 : !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) {
16697 0 : return false;
16698 : }
16699 :
16700 0 : val->Item(0).SetFloatValue(x1, eCSSUnit_Number);
16701 0 : val->Item(1).SetFloatValue(y1, eCSSUnit_Number);
16702 0 : val->Item(2).SetFloatValue(x2, eCSSUnit_Number);
16703 0 : val->Item(3).SetFloatValue(y2, eCSSUnit_Number);
16704 :
16705 0 : aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier);
16706 :
16707 0 : return true;
16708 : }
16709 :
16710 : bool
16711 0 : CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent,
16712 : char aStop,
16713 : bool aIsXPoint)
16714 : {
16715 0 : if (!GetToken(true)) {
16716 0 : return false;
16717 : }
16718 0 : nsCSSToken* tk = &mToken;
16719 0 : if (tk->mType == eCSSToken_Number) {
16720 0 : float num = tk->mNumber;
16721 :
16722 : // Clamp infinity or -infinity values to max float or -max float to avoid
16723 : // calculations with infinity.
16724 0 : num = mozilla::clamped(num, -std::numeric_limits<float>::max(),
16725 0 : std::numeric_limits<float>::max());
16726 :
16727 : // X control point should be inside [0, 1] range.
16728 0 : if (aIsXPoint && (num < 0.0 || num > 1.0)) {
16729 0 : return false;
16730 : }
16731 0 : aComponent = num;
16732 0 : if (ExpectSymbol(aStop, true)) {
16733 0 : return true;
16734 : }
16735 : }
16736 0 : return false;
16737 : }
16738 :
16739 : bool
16740 3 : CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
16741 : {
16742 3 : NS_ASSERTION(!mHavePushBack &&
16743 : mToken.mType == eCSSToken_Function &&
16744 : mToken.mIdent.LowerCaseEqualsLiteral("steps"),
16745 : "unexpected initial state");
16746 :
16747 6 : RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
16748 :
16749 3 : if (!ParseSingleTokenOneOrLargerVariant(val->Item(0), VARIANT_INTEGER,
16750 : nullptr)) {
16751 0 : return false;
16752 : }
16753 :
16754 3 : int32_t type = -1; // indicates an implicit end value
16755 3 : if (ExpectSymbol(',', true)) {
16756 0 : if (!GetToken(true)) {
16757 0 : return false;
16758 : }
16759 0 : if (mToken.mType == eCSSToken_Ident) {
16760 0 : if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
16761 0 : type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
16762 0 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) {
16763 0 : type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
16764 : }
16765 : }
16766 0 : if (type == -1) {
16767 0 : UngetToken();
16768 0 : return false;
16769 : }
16770 : }
16771 3 : val->Item(1).SetIntValue(type, eCSSUnit_Enumerated);
16772 :
16773 3 : if (!ExpectSymbol(')', true)) {
16774 0 : return false;
16775 : }
16776 :
16777 3 : aValue.SetArrayValue(val, eCSSUnit_Steps);
16778 3 : return true;
16779 : }
16780 :
16781 : bool
16782 0 : CSSParserImpl::ParseTransitionFramesTimingFunctionValues(nsCSSValue& aValue)
16783 : {
16784 0 : NS_ASSERTION(!mHavePushBack &&
16785 : mToken.mType == eCSSToken_Function &&
16786 : mToken.mIdent.LowerCaseEqualsLiteral("frames"),
16787 : "unexpected initial state");
16788 :
16789 0 : nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
16790 0 : MOZ_ASSERT(functionName == eCSSKeyword_frames);
16791 :
16792 0 : nsCSSValue frameNumber;
16793 0 : if (!ParseSingleTokenOneOrLargerVariant(frameNumber, VARIANT_INTEGER,
16794 : nullptr)) {
16795 0 : return false;
16796 : }
16797 0 : MOZ_ASSERT(frameNumber.GetIntValue() >= 1,
16798 : "Parsing function should've enforced OneOrLarger, per its name");
16799 :
16800 : // The number of frames must be a positive integer greater than one.
16801 0 : if (frameNumber.GetIntValue() == 1) {
16802 0 : return false;
16803 : }
16804 :
16805 0 : if (!ExpectSymbol(')', true)) {
16806 0 : return false;
16807 : }
16808 :
16809 0 : RefPtr<nsCSSValue::Array> val = aValue.InitFunction(functionName, 1);
16810 0 : val->Item(1) = frameNumber;
16811 0 : return true;
16812 : }
16813 :
16814 : static nsCSSValueList*
16815 564 : AppendValueToList(nsCSSValue& aContainer,
16816 : nsCSSValueList* aTail,
16817 : const nsCSSValue& aValue)
16818 : {
16819 : nsCSSValueList* entry;
16820 564 : if (aContainer.GetUnit() == eCSSUnit_Null) {
16821 496 : MOZ_ASSERT(!aTail, "should not have an entry");
16822 496 : entry = aContainer.SetListValue();
16823 : } else {
16824 68 : MOZ_ASSERT(!aTail->mNext, "should not have a next entry");
16825 68 : MOZ_ASSERT(aContainer.GetUnit() == eCSSUnit_List, "not a list");
16826 68 : entry = new nsCSSValueList;
16827 68 : aTail->mNext = entry;
16828 : }
16829 564 : entry->mValue = aValue;
16830 564 : return entry;
16831 : }
16832 :
16833 : CSSParserImpl::ParseAnimationOrTransitionShorthandResult
16834 117 : CSSParserImpl::ParseAnimationOrTransitionShorthand(
16835 : const nsCSSPropertyID* aProperties,
16836 : const nsCSSValue* aInitialValues,
16837 : nsCSSValue* aValues,
16838 : size_t aNumProperties)
16839 : {
16840 234 : nsCSSValue tempValue;
16841 : // first see if 'inherit', 'initial' or 'unset' is specified. If one is,
16842 : // it can be the only thing specified, so don't attempt to parse any
16843 : // additional properties
16844 117 : if (ParseSingleTokenVariant(tempValue, VARIANT_INHERIT, nullptr)) {
16845 0 : for (uint32_t i = 0; i < aNumProperties; ++i) {
16846 0 : AppendValue(aProperties[i], tempValue);
16847 : }
16848 0 : return eParseAnimationOrTransitionShorthand_Inherit;
16849 : }
16850 :
16851 : static const size_t maxNumProperties = 8;
16852 117 : MOZ_ASSERT(aNumProperties <= maxNumProperties,
16853 : "can't handle this many properties");
16854 : nsCSSValueList *cur[maxNumProperties];
16855 : bool parsedProperty[maxNumProperties];
16856 :
16857 629 : for (size_t i = 0; i < aNumProperties; ++i) {
16858 512 : cur[i] = nullptr;
16859 : }
16860 117 : bool atEOP = false; // at end of property?
16861 : for (;;) { // loop over comma-separated transitions or animations
16862 : // whether a particular subproperty was specified for this
16863 : // transition or animation
16864 128 : bool haveAnyProperty = false;
16865 708 : for (size_t i = 0; i < aNumProperties; ++i) {
16866 580 : parsedProperty[i] = false;
16867 : }
16868 : for (;;) { // loop over values within a transition or animation
16869 591 : bool foundProperty = false;
16870 : // check to see if we're at the end of one full transition or
16871 : // animation definition (either because we hit a comma or because
16872 : // we hit the end of the property definition)
16873 591 : if (ExpectSymbol(',', true))
16874 11 : break;
16875 580 : if (CheckEndProperty()) {
16876 109 : atEOP = true;
16877 109 : break;
16878 : }
16879 :
16880 : // else, try to parse the next transition or animation sub-property
16881 1340 : for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) {
16882 1332 : if (!parsedProperty[i]) {
16883 : // if we haven't found this property yet, try to parse it
16884 : CSSParseResult result =
16885 957 : ParseSingleValueProperty(tempValue, aProperties[i]);
16886 957 : if (result == CSSParseResult::Error) {
16887 0 : return eParseAnimationOrTransitionShorthand_Error;
16888 : }
16889 957 : if (result == CSSParseResult::Ok) {
16890 463 : parsedProperty[i] = true;
16891 463 : cur[i] = AppendValueToList(aValues[i], cur[i], tempValue);
16892 463 : foundProperty = true;
16893 463 : haveAnyProperty = true;
16894 463 : break; // out of inner loop; continue looking for next sub-property
16895 : }
16896 : }
16897 : }
16898 471 : if (!foundProperty) {
16899 : // We're not at a ',' or at the end of the property, but we couldn't
16900 : // parse any of the sub-properties, so the declaration is invalid.
16901 8 : return eParseAnimationOrTransitionShorthand_Error;
16902 : }
16903 463 : }
16904 :
16905 120 : if (!haveAnyProperty) {
16906 : // Got an empty item.
16907 0 : return eParseAnimationOrTransitionShorthand_Error;
16908 : }
16909 :
16910 : // We hit the end of the property or the end of one transition
16911 : // or animation definition, add its components to the list.
16912 668 : for (uint32_t i = 0; i < aNumProperties; ++i) {
16913 : // If all of the subproperties were not explicitly specified, fill
16914 : // in the missing ones with initial values.
16915 548 : if (!parsedProperty[i]) {
16916 101 : cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]);
16917 : }
16918 : }
16919 :
16920 120 : if (atEOP)
16921 109 : break;
16922 : // else we just hit a ',' so continue parsing the next compound transition
16923 11 : }
16924 :
16925 109 : return eParseAnimationOrTransitionShorthand_Values;
16926 : }
16927 :
16928 : bool
16929 106 : CSSParserImpl::ParseTransition()
16930 : {
16931 : static const nsCSSPropertyID kTransitionProperties[] = {
16932 : eCSSProperty_transition_duration,
16933 : eCSSProperty_transition_timing_function,
16934 : // Must check 'transition-delay' after 'transition-duration', since
16935 : // that's our assumption about what the spec means for the shorthand
16936 : // syntax (the first time given is the duration, and the second
16937 : // given is the delay).
16938 : eCSSProperty_transition_delay,
16939 : // Must check 'transition-property' after
16940 : // 'transition-timing-function' since 'transition-property' accepts
16941 : // any keyword.
16942 : eCSSProperty_transition_property
16943 : };
16944 : static const uint32_t numProps = MOZ_ARRAY_LENGTH(kTransitionProperties);
16945 : // this is a shorthand property that accepts -property, -delay,
16946 : // -duration, and -timing-function with some components missing.
16947 : // there can be multiple transitions, separated with commas
16948 :
16949 212 : nsCSSValue initialValues[numProps];
16950 106 : initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
16951 : initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
16952 106 : eCSSUnit_Enumerated);
16953 106 : initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
16954 106 : initialValues[3].SetAllValue();
16955 :
16956 212 : nsCSSValue values[numProps];
16957 :
16958 : ParseAnimationOrTransitionShorthandResult spres =
16959 : ParseAnimationOrTransitionShorthand(kTransitionProperties,
16960 106 : initialValues, values, numProps);
16961 106 : if (spres != eParseAnimationOrTransitionShorthand_Values) {
16962 8 : return spres != eParseAnimationOrTransitionShorthand_Error;
16963 : }
16964 :
16965 : // Make two checks on the list for 'transition-property':
16966 : // + If there is more than one item, then none of the items can be
16967 : // 'none'.
16968 : // + None of the items can be 'inherit', 'initial' or 'unset'.
16969 : {
16970 98 : MOZ_ASSERT(kTransitionProperties[3] == eCSSProperty_transition_property,
16971 : "array index mismatch");
16972 98 : nsCSSValueList *l = values[3].GetListValue();
16973 98 : bool multipleItems = !!l->mNext;
16974 97 : do {
16975 103 : const nsCSSValue& val = l->mValue;
16976 103 : if (val.GetUnit() == eCSSUnit_None) {
16977 6 : if (multipleItems) {
16978 : // This is a syntax error.
16979 0 : return false;
16980 : }
16981 :
16982 : // Unbox a solitary 'none'.
16983 6 : values[3].SetNoneValue();
16984 6 : break;
16985 : }
16986 97 : if (val.GetUnit() == eCSSUnit_Ident) {
16987 194 : nsDependentString str(val.GetStringBufferValue());
16988 291 : if (str.EqualsLiteral("inherit") ||
16989 194 : str.EqualsLiteral("initial") ||
16990 97 : (str.EqualsLiteral("unset") &&
16991 0 : nsLayoutUtils::UnsetValueEnabled())) {
16992 0 : return false;
16993 : }
16994 : }
16995 97 : } while ((l = l->mNext));
16996 : }
16997 :
16998 : // Save all parsed transition sub-properties in mTempData
16999 490 : for (uint32_t i = 0; i < numProps; ++i) {
17000 392 : AppendValue(kTransitionProperties[i], values[i]);
17001 : }
17002 98 : return true;
17003 : }
17004 :
17005 : bool
17006 11 : CSSParserImpl::ParseAnimation()
17007 : {
17008 : static const nsCSSPropertyID kAnimationProperties[] = {
17009 : eCSSProperty_animation_duration,
17010 : eCSSProperty_animation_timing_function,
17011 : // Must check 'animation-delay' after 'animation-duration', since
17012 : // that's our assumption about what the spec means for the shorthand
17013 : // syntax (the first time given is the duration, and the second
17014 : // given is the delay).
17015 : eCSSProperty_animation_delay,
17016 : eCSSProperty_animation_direction,
17017 : eCSSProperty_animation_fill_mode,
17018 : eCSSProperty_animation_iteration_count,
17019 : eCSSProperty_animation_play_state,
17020 : // Must check 'animation-name' after 'animation-timing-function',
17021 : // 'animation-direction', 'animation-fill-mode',
17022 : // 'animation-iteration-count', and 'animation-play-state' since
17023 : // 'animation-name' accepts any keyword.
17024 : eCSSProperty_animation_name
17025 : };
17026 : static const uint32_t numProps = MOZ_ARRAY_LENGTH(kAnimationProperties);
17027 : // this is a shorthand property that accepts -property, -delay,
17028 : // -duration, and -timing-function with some components missing.
17029 : // there can be multiple animations, separated with commas
17030 :
17031 22 : nsCSSValue initialValues[numProps];
17032 11 : initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
17033 : initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
17034 11 : eCSSUnit_Enumerated);
17035 11 : initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
17036 : initialValues[3].SetIntValue(static_cast<int32_t>(dom::PlaybackDirection::Normal),
17037 11 : eCSSUnit_Enumerated);
17038 : initialValues[4].SetIntValue(static_cast<int32_t>(dom::FillMode::None),
17039 11 : eCSSUnit_Enumerated);
17040 11 : initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
17041 11 : initialValues[6].SetIntValue(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING, eCSSUnit_Enumerated);
17042 11 : initialValues[7].SetNoneValue();
17043 :
17044 22 : nsCSSValue values[numProps];
17045 :
17046 : ParseAnimationOrTransitionShorthandResult spres =
17047 : ParseAnimationOrTransitionShorthand(kAnimationProperties,
17048 11 : initialValues, values, numProps);
17049 11 : if (spres != eParseAnimationOrTransitionShorthand_Values) {
17050 0 : return spres != eParseAnimationOrTransitionShorthand_Error;
17051 : }
17052 :
17053 : // Save all parsed animation sub-properties in mTempData
17054 99 : for (uint32_t i = 0; i < numProps; ++i) {
17055 88 : AppendValue(kAnimationProperties[i], values[i]);
17056 : }
17057 11 : return true;
17058 : }
17059 :
17060 : bool
17061 43 : CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow)
17062 : {
17063 : // A shadow list item is an array, with entries in this sequence:
17064 : enum {
17065 : IndexX,
17066 : IndexY,
17067 : IndexRadius,
17068 : IndexSpread, // only for box-shadow
17069 : IndexColor,
17070 : IndexInset // only for box-shadow
17071 : };
17072 :
17073 86 : RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6);
17074 :
17075 43 : if (aIsBoxShadow) {
17076 : // Optional inset keyword (ignore errors)
17077 35 : Unused << ParseSingleTokenVariant(val->Item(IndexInset), VARIANT_KEYWORD,
17078 : nsCSSProps::kBoxShadowTypeKTable);
17079 : }
17080 :
17081 86 : nsCSSValue xOrColor;
17082 43 : bool haveColor = false;
17083 43 : if (ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC,
17084 : nullptr) != CSSParseResult::Ok) {
17085 0 : return false;
17086 : }
17087 43 : if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) {
17088 43 : val->Item(IndexX) = xOrColor;
17089 : } else {
17090 : // Must be a color (as string or color value)
17091 0 : NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident ||
17092 : xOrColor.GetUnit() == eCSSUnit_EnumColor ||
17093 : xOrColor.IsNumericColorUnit(),
17094 : "Must be a color value");
17095 0 : val->Item(IndexColor) = xOrColor;
17096 0 : haveColor = true;
17097 :
17098 : // X coordinate mandatory after color
17099 0 : if (ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC,
17100 : nullptr) != CSSParseResult::Ok) {
17101 0 : return false;
17102 : }
17103 : }
17104 :
17105 : // Y coordinate; mandatory
17106 43 : if (ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC,
17107 : nullptr) != CSSParseResult::Ok) {
17108 0 : return false;
17109 : }
17110 :
17111 : // Optional radius. Ignore errors except if they pass a negative
17112 : // value which we must reject. If we use ParseNonNegativeVariant
17113 : // we can't tell the difference between an unspecified radius
17114 : // and a negative radius.
17115 : CSSParseResult result =
17116 43 : ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC,
17117 43 : nullptr);
17118 43 : if (result == CSSParseResult::Error) {
17119 0 : return false;
17120 43 : } else if (result == CSSParseResult::Ok) {
17121 80 : if (val->Item(IndexRadius).IsLengthUnit() &&
17122 40 : val->Item(IndexRadius).GetFloatValue() < 0) {
17123 0 : return false;
17124 : }
17125 : }
17126 :
17127 43 : if (aIsBoxShadow) {
17128 : // Optional spread
17129 35 : if (ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC,
17130 : nullptr) == CSSParseResult::Error) {
17131 0 : return false;
17132 : }
17133 : }
17134 :
17135 43 : if (!haveColor) {
17136 : // Optional color
17137 43 : if (ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr) ==
17138 : CSSParseResult::Error) {
17139 0 : return false;
17140 : }
17141 : }
17142 :
17143 43 : if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) {
17144 : // Optional inset keyword (ignore errors)
17145 34 : Unused << ParseSingleTokenVariant(val->Item(IndexInset), VARIANT_KEYWORD,
17146 : nsCSSProps::kBoxShadowTypeKTable);
17147 : }
17148 :
17149 43 : aValue.SetArrayValue(val, eCSSUnit_Array);
17150 43 : return true;
17151 : }
17152 :
17153 : bool
17154 69 : CSSParserImpl::ParseShadowList(nsCSSPropertyID aProperty)
17155 : {
17156 138 : nsAutoParseCompoundProperty compound(this);
17157 69 : bool isBoxShadow = aProperty == eCSSProperty_box_shadow;
17158 :
17159 138 : nsCSSValue value;
17160 : // 'inherit', 'initial', 'unset' and 'none' must be alone
17161 69 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE,
17162 : nullptr)) {
17163 33 : nsCSSValueList* cur = value.SetListValue();
17164 : for (;;) {
17165 45 : if (!ParseShadowItem(cur->mValue, isBoxShadow)) {
17166 0 : return false;
17167 : }
17168 39 : if (!ExpectSymbol(',', true)) {
17169 33 : break;
17170 : }
17171 6 : cur->mNext = new nsCSSValueList;
17172 6 : cur = cur->mNext;
17173 : }
17174 : }
17175 69 : AppendValue(aProperty, value);
17176 69 : return true;
17177 : }
17178 :
17179 : int32_t
17180 78 : CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix)
17181 : {
17182 78 : NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
17183 :
17184 78 : int32_t nameSpaceID = kNameSpaceID_Unknown;
17185 78 : if (mNameSpaceMap) {
17186 : // user-specified identifiers are case-sensitive (bug 416106)
17187 156 : nsCOMPtr<nsIAtom> prefix = NS_Atomize(aPrefix);
17188 78 : nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
17189 : }
17190 : // else no declared namespaces
17191 :
17192 78 : if (nameSpaceID == kNameSpaceID_Unknown) { // unknown prefix, dump it
17193 0 : REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, aPrefix);
17194 : }
17195 :
17196 78 : return nameSpaceID;
17197 : }
17198 :
17199 : void
17200 8187 : CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
17201 : {
17202 8187 : if (mNameSpaceMap) {
17203 6353 : aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nullptr));
17204 : } else {
17205 1834 : aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
17206 : }
17207 8187 : }
17208 :
17209 : bool
17210 141 : CSSParserImpl::ParsePaint(nsCSSPropertyID aPropID)
17211 : {
17212 282 : nsCSSValue x, y;
17213 :
17214 141 : if (ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL | VARIANT_KEYWORD,
17215 : nsCSSProps::kContextPatternKTable) != CSSParseResult::Ok) {
17216 14 : return false;
17217 : }
17218 :
17219 127 : bool hasFallback = false;
17220 254 : bool canHaveFallback = x.GetUnit() == eCSSUnit_URL ||
17221 254 : x.GetUnit() == eCSSUnit_Enumerated;
17222 127 : if (canHaveFallback) {
17223 : CSSParseResult result =
17224 30 : ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr);
17225 30 : if (result == CSSParseResult::Error) {
17226 0 : return false;
17227 : }
17228 30 : hasFallback = (result != CSSParseResult::NotFound);
17229 : }
17230 :
17231 127 : if (hasFallback) {
17232 0 : nsCSSValue val;
17233 0 : val.SetPairValue(x, y);
17234 0 : AppendValue(aPropID, val);
17235 : } else {
17236 127 : AppendValue(aPropID, x);
17237 : }
17238 127 : return true;
17239 : }
17240 :
17241 : bool
17242 2 : CSSParserImpl::ParseDasharray()
17243 : {
17244 4 : nsCSSValue value;
17245 :
17246 : // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
17247 2 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE |
17248 : VARIANT_OPENTYPE_SVG_KEYWORD,
17249 : nsCSSProps::kStrokeContextValueKTable)) {
17250 0 : nsCSSValueList *cur = value.SetListValue();
17251 : for (;;) {
17252 0 : if (!ParseSingleTokenNonNegativeVariant(cur->mValue, VARIANT_LPN,
17253 : nullptr)) {
17254 0 : return false;
17255 : }
17256 0 : if (CheckEndProperty()) {
17257 0 : break;
17258 : }
17259 : // skip optional commas between elements
17260 0 : (void)ExpectSymbol(',', true);
17261 :
17262 0 : cur->mNext = new nsCSSValueList;
17263 0 : cur = cur->mNext;
17264 : }
17265 : }
17266 2 : AppendValue(eCSSProperty_stroke_dasharray, value);
17267 2 : return true;
17268 : }
17269 :
17270 : bool
17271 0 : CSSParserImpl::ParseMarker()
17272 : {
17273 0 : nsCSSValue marker;
17274 0 : if (ParseSingleValueProperty(marker, eCSSProperty_marker_end) ==
17275 : CSSParseResult::Ok) {
17276 0 : AppendValue(eCSSProperty_marker_end, marker);
17277 0 : AppendValue(eCSSProperty_marker_mid, marker);
17278 0 : AppendValue(eCSSProperty_marker_start, marker);
17279 0 : return true;
17280 : }
17281 0 : return false;
17282 : }
17283 :
17284 : bool
17285 0 : CSSParserImpl::ParsePaintOrder()
17286 : {
17287 : static_assert
17288 : ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) > NS_STYLE_PAINT_ORDER_LAST_VALUE,
17289 : "bitfield width insufficient for paint-order constants");
17290 :
17291 : static const KTableEntry kPaintOrderKTable[] = {
17292 : { eCSSKeyword_normal, NS_STYLE_PAINT_ORDER_NORMAL },
17293 : { eCSSKeyword_fill, NS_STYLE_PAINT_ORDER_FILL },
17294 : { eCSSKeyword_stroke, NS_STYLE_PAINT_ORDER_STROKE },
17295 : { eCSSKeyword_markers, NS_STYLE_PAINT_ORDER_MARKERS },
17296 : { eCSSKeyword_UNKNOWN, -1 }
17297 : };
17298 :
17299 : static_assert(MOZ_ARRAY_LENGTH(kPaintOrderKTable) ==
17300 : NS_STYLE_PAINT_ORDER_LAST_VALUE + 2,
17301 : "missing paint-order values in kPaintOrderKTable");
17302 :
17303 0 : nsCSSValue value;
17304 0 : if (!ParseSingleTokenVariant(value, VARIANT_HK, kPaintOrderKTable)) {
17305 0 : return false;
17306 : }
17307 :
17308 0 : uint32_t seen = 0;
17309 0 : uint32_t order = 0;
17310 0 : uint32_t position = 0;
17311 :
17312 : // Ensure that even cast to a signed int32_t when stored in CSSValue,
17313 : // we have enough space for the entire paint-order value.
17314 : static_assert
17315 : (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE < 32,
17316 : "seen and order not big enough");
17317 :
17318 0 : if (value.GetUnit() == eCSSUnit_Enumerated) {
17319 0 : uint32_t component = static_cast<uint32_t>(value.GetIntValue());
17320 0 : if (component != NS_STYLE_PAINT_ORDER_NORMAL) {
17321 0 : bool parsedOK = true;
17322 : for (;;) {
17323 0 : if (seen & (1 << component)) {
17324 : // Already seen this component.
17325 0 : UngetToken();
17326 0 : parsedOK = false;
17327 0 : break;
17328 : }
17329 0 : seen |= (1 << component);
17330 0 : order |= (component << position);
17331 0 : position += NS_STYLE_PAINT_ORDER_BITWIDTH;
17332 0 : if (!ParseEnum(value, kPaintOrderKTable)) {
17333 0 : break;
17334 : }
17335 0 : component = value.GetIntValue();
17336 0 : if (component == NS_STYLE_PAINT_ORDER_NORMAL) {
17337 : // Can't have "normal" in the middle of the list of paint components.
17338 0 : UngetToken();
17339 0 : parsedOK = false;
17340 0 : break;
17341 : }
17342 : }
17343 :
17344 : // Fill in the remaining paint-order components in the order of their
17345 : // constant values.
17346 0 : if (parsedOK) {
17347 0 : for (component = 1;
17348 0 : component <= NS_STYLE_PAINT_ORDER_LAST_VALUE;
17349 : component++) {
17350 0 : if (!(seen & (1 << component))) {
17351 0 : order |= (component << position);
17352 0 : position += NS_STYLE_PAINT_ORDER_BITWIDTH;
17353 : }
17354 : }
17355 : }
17356 : }
17357 :
17358 : static_assert(NS_STYLE_PAINT_ORDER_NORMAL == 0,
17359 : "unexpected value for NS_STYLE_PAINT_ORDER_NORMAL");
17360 0 : value.SetIntValue(static_cast<int32_t>(order), eCSSUnit_Enumerated);
17361 : }
17362 :
17363 0 : AppendValue(eCSSProperty_paint_order, value);
17364 0 : return true;
17365 : }
17366 :
17367 : bool
17368 473 : CSSParserImpl::BackslashDropped()
17369 : {
17370 473 : return mScanner->GetEOFCharacters() &
17371 473 : nsCSSScanner::eEOFCharacters_DropBackslash;
17372 : }
17373 :
17374 : void
17375 141 : CSSParserImpl::AppendImpliedEOFCharacters(nsAString& aResult)
17376 : {
17377 141 : nsCSSScanner::AppendImpliedEOFCharacters(mScanner->GetEOFCharacters(),
17378 141 : aResult);
17379 141 : }
17380 :
17381 : bool
17382 0 : CSSParserImpl::ParseAll()
17383 : {
17384 0 : nsCSSValue value;
17385 0 : if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
17386 0 : return false;
17387 : }
17388 :
17389 : // It's unlikely we'll want to use 'all' from within a UA style sheet, so
17390 : // instead of computing the correct EnabledState value we just expand out
17391 : // to all content-visible properties.
17392 0 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all,
17393 : CSSEnabledState::eForAllContent) {
17394 0 : AppendValue(*p, value);
17395 : }
17396 0 : return true;
17397 : }
17398 :
17399 : bool
17400 74 : CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
17401 : nsString& aValue)
17402 : {
17403 : CSSVariableDeclarations::Type type;
17404 148 : nsString variableValue;
17405 : bool dropBackslash;
17406 148 : nsString impliedCharacters;
17407 :
17408 : // Record the token stream while parsing a variable value.
17409 74 : if (!mInSupportsCondition) {
17410 74 : mScanner->StartRecording();
17411 : }
17412 74 : if (!ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
17413 : nullptr, nullptr)) {
17414 0 : if (!mInSupportsCondition) {
17415 0 : mScanner->StopRecording();
17416 : }
17417 0 : return false;
17418 : }
17419 :
17420 74 : if (!mInSupportsCondition) {
17421 74 : if (type == CSSVariableDeclarations::eTokenStream) {
17422 : // This was indeed a token stream value, so store it in variableValue.
17423 74 : mScanner->StopRecording(variableValue);
17424 74 : if (dropBackslash) {
17425 0 : MOZ_ASSERT(!variableValue.IsEmpty() &&
17426 : variableValue[variableValue.Length() - 1] == '\\');
17427 0 : variableValue.Truncate(variableValue.Length() - 1);
17428 : }
17429 74 : variableValue.Append(impliedCharacters);
17430 : } else {
17431 : // This was either 'inherit' or 'initial'; we don't need the recorded
17432 : // input.
17433 0 : mScanner->StopRecording();
17434 : }
17435 : }
17436 :
17437 74 : if (mHavePushBack && type == CSSVariableDeclarations::eTokenStream) {
17438 : // If we came to the end of a valid variable declaration and a token was
17439 : // pushed back, then it would have been ended by '!', ')', ';', ']' or '}'.
17440 : // We need to remove it from the recorded variable value.
17441 74 : MOZ_ASSERT(mToken.IsSymbol('!') ||
17442 : mToken.IsSymbol(')') ||
17443 : mToken.IsSymbol(';') ||
17444 : mToken.IsSymbol(']') ||
17445 : mToken.IsSymbol('}'));
17446 74 : if (!mInSupportsCondition) {
17447 74 : MOZ_ASSERT(!variableValue.IsEmpty());
17448 74 : MOZ_ASSERT(variableValue[variableValue.Length() - 1] == mToken.mSymbol);
17449 74 : variableValue.Truncate(variableValue.Length() - 1);
17450 : }
17451 : }
17452 :
17453 74 : *aType = type;
17454 74 : aValue = variableValue;
17455 74 : return true;
17456 : }
17457 :
17458 : bool
17459 0 : CSSParserImpl::ParseScrollSnapType()
17460 : {
17461 0 : nsCSSValue value;
17462 0 : if (!ParseSingleTokenVariant(value, VARIANT_HK,
17463 : nsCSSProps::kScrollSnapTypeKTable)) {
17464 0 : return false;
17465 : }
17466 0 : AppendValue(eCSSProperty_scroll_snap_type_x, value);
17467 0 : AppendValue(eCSSProperty_scroll_snap_type_y, value);
17468 0 : return true;
17469 : }
17470 :
17471 : bool
17472 0 : CSSParserImpl::ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSPropertyID aPropID)
17473 : {
17474 0 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE,
17475 : nullptr)) {
17476 0 : return true;
17477 : }
17478 0 : if (!GetToken(true)) {
17479 0 : return false;
17480 : }
17481 0 : if (mToken.mType == eCSSToken_Function &&
17482 0 : nsCSSKeywords::LookupKeyword(mToken.mIdent) == eCSSKeyword_repeat) {
17483 0 : nsCSSValue lengthValue;
17484 0 : if (ParseNonNegativeVariant(lengthValue,
17485 : VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_CALC,
17486 : nullptr) != CSSParseResult::Ok) {
17487 0 : REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
17488 0 : SkipUntil(')');
17489 0 : return false;
17490 : }
17491 0 : if (!ExpectSymbol(')', true)) {
17492 0 : REPORT_UNEXPECTED(PEExpectedCloseParen);
17493 0 : SkipUntil(')');
17494 0 : return false;
17495 : }
17496 : RefPtr<nsCSSValue::Array> functionArray =
17497 0 : aValue.InitFunction(eCSSKeyword_repeat, 1);
17498 0 : functionArray->Item(1) = lengthValue;
17499 0 : return true;
17500 : }
17501 0 : UngetToken();
17502 0 : return false;
17503 : }
17504 :
17505 :
17506 : bool
17507 0 : CSSParserImpl::ParseScrollSnapDestination(nsCSSValue& aValue)
17508 : {
17509 0 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) {
17510 0 : return true;
17511 : }
17512 0 : nsCSSValue itemValue;
17513 0 : if (!ParsePositionValue(aValue)) {
17514 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
17515 0 : return false;
17516 : }
17517 0 : return true;
17518 : }
17519 :
17520 : // This function is very similar to ParseImageLayerPosition, and ParseImageLayerSize.
17521 : bool
17522 0 : CSSParserImpl::ParseScrollSnapCoordinate(nsCSSValue& aValue)
17523 : {
17524 0 : if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE,
17525 : nullptr)) {
17526 0 : return true;
17527 : }
17528 0 : nsCSSValue itemValue;
17529 0 : if (!ParsePositionValue(itemValue)) {
17530 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
17531 0 : return false;
17532 : }
17533 0 : nsCSSValueList* item = aValue.SetListValue();
17534 : for (;;) {
17535 0 : item->mValue = itemValue;
17536 0 : if (!ExpectSymbol(',', true)) {
17537 0 : break;
17538 : }
17539 0 : if (!ParsePositionValue(itemValue)) {
17540 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedPosition);
17541 0 : return false;
17542 : }
17543 0 : item->mNext = new nsCSSValueList;
17544 0 : item = item->mNext;
17545 : }
17546 0 : return true;
17547 : }
17548 :
17549 : bool
17550 473 : CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
17551 : bool* aDropBackslash,
17552 : nsString& aImpliedCharacters,
17553 : void (*aFunc)(const nsAString&, void*),
17554 : void* aData)
17555 : {
17556 : // A property value is invalid if it contains variable references and also:
17557 : //
17558 : // * has unbalanced parens, brackets or braces
17559 : // * has any BAD_STRING or BAD_URL tokens
17560 : // * has any ';' or '!' tokens at the top level of a variable reference's
17561 : // fallback
17562 : //
17563 : // If the property is a custom property (i.e. a variable declaration), then
17564 : // it is also invalid if it consists of no tokens, such as:
17565 : //
17566 : // --invalid:;
17567 : //
17568 : // Note that is valid for a custom property to have a value that consists
17569 : // solely of white space, such as:
17570 : //
17571 : // --valid: ;
17572 :
17573 : // Stack of closing characters for currently open constructs.
17574 946 : StopSymbolCharStack stack;
17575 :
17576 : // Indexes into ')' characters in |stack| that correspond to "var(". This
17577 : // is used to stop parsing when we encounter a '!' or ';' at the top level
17578 : // of a variable reference's fallback.
17579 946 : AutoTArray<uint32_t, 16> references;
17580 :
17581 473 : if (!GetToken(false)) {
17582 : // Variable value was empty since we reached EOF.
17583 0 : REPORT_UNEXPECTED_EOF(PEVariableEOF);
17584 0 : return false;
17585 : }
17586 :
17587 473 : if (mToken.mType == eCSSToken_Symbol &&
17588 0 : (mToken.mSymbol == '!' ||
17589 0 : mToken.mSymbol == ')' ||
17590 0 : mToken.mSymbol == ';' ||
17591 0 : mToken.mSymbol == ']' ||
17592 0 : mToken.mSymbol == '}')) {
17593 : // Variable value was empty since we reached the end of the construct.
17594 0 : UngetToken();
17595 0 : REPORT_UNEXPECTED_TOKEN(PEVariableEmpty);
17596 0 : return false;
17597 : }
17598 :
17599 473 : if (mToken.mType == eCSSToken_Whitespace) {
17600 473 : if (!GetToken(true)) {
17601 : // Variable value was white space only. This is valid.
17602 0 : MOZ_ASSERT(!BackslashDropped());
17603 0 : *aType = CSSVariableDeclarations::eTokenStream;
17604 0 : *aDropBackslash = false;
17605 0 : AppendImpliedEOFCharacters(aImpliedCharacters);
17606 0 : return true;
17607 : }
17608 : }
17609 :
17610 : // Look for 'initial', 'inherit' or 'unset' as the first non-white space
17611 : // token.
17612 473 : CSSVariableDeclarations::Type type = CSSVariableDeclarations::eTokenStream;
17613 473 : if (mToken.mType == eCSSToken_Ident) {
17614 59 : if (mToken.mIdent.LowerCaseEqualsLiteral("initial")) {
17615 0 : type = CSSVariableDeclarations::eInitial;
17616 59 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("inherit")) {
17617 0 : type = CSSVariableDeclarations::eInherit;
17618 59 : } else if (mToken.mIdent.LowerCaseEqualsLiteral("unset")) {
17619 0 : type = CSSVariableDeclarations::eUnset;
17620 : }
17621 : }
17622 :
17623 473 : if (type != CSSVariableDeclarations::eTokenStream) {
17624 0 : if (!GetToken(true)) {
17625 : // Variable value was 'initial' or 'inherit' followed by EOF.
17626 0 : MOZ_ASSERT(!BackslashDropped());
17627 0 : *aType = type;
17628 0 : *aDropBackslash = false;
17629 0 : AppendImpliedEOFCharacters(aImpliedCharacters);
17630 0 : return true;
17631 : }
17632 0 : UngetToken();
17633 0 : if (mToken.mType == eCSSToken_Symbol &&
17634 0 : (mToken.mSymbol == '!' ||
17635 0 : mToken.mSymbol == ')' ||
17636 0 : mToken.mSymbol == ';' ||
17637 0 : mToken.mSymbol == ']' ||
17638 0 : mToken.mSymbol == '}')) {
17639 : // Variable value was 'initial' or 'inherit' followed by the end
17640 : // of the declaration.
17641 0 : MOZ_ASSERT(!BackslashDropped());
17642 0 : *aType = type;
17643 0 : *aDropBackslash = false;
17644 0 : return true;
17645 : }
17646 : }
17647 :
17648 1323 : do {
17649 1655 : switch (mToken.mType) {
17650 : case eCSSToken_Symbol:
17651 677 : if (mToken.mSymbol == '(') {
17652 5 : stack.AppendElement(')');
17653 672 : } else if (mToken.mSymbol == '[') {
17654 0 : stack.AppendElement(']');
17655 672 : } else if (mToken.mSymbol == '{') {
17656 0 : stack.AppendElement('}');
17657 1056 : } else if (mToken.mSymbol == ';' ||
17658 384 : mToken.mSymbol == '!') {
17659 331 : if (stack.IsEmpty()) {
17660 331 : UngetToken();
17661 331 : MOZ_ASSERT(!BackslashDropped());
17662 331 : *aType = CSSVariableDeclarations::eTokenStream;
17663 331 : *aDropBackslash = false;
17664 331 : return true;
17665 0 : } else if (!references.IsEmpty() &&
17666 0 : references.LastElement() == stack.Length() - 1) {
17667 0 : REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback);
17668 0 : SkipUntilAllOf(stack);
17669 0 : return false;
17670 : }
17671 574 : } else if (mToken.mSymbol == ')' ||
17672 466 : mToken.mSymbol == ']' ||
17673 233 : mToken.mSymbol == '}') {
17674 : for (;;) {
17675 109 : if (stack.IsEmpty()) {
17676 1 : UngetToken();
17677 1 : MOZ_ASSERT(!BackslashDropped());
17678 1 : *aType = CSSVariableDeclarations::eTokenStream;
17679 1 : *aDropBackslash = false;
17680 1 : return true;
17681 : }
17682 108 : char16_t c = stack.LastElement();
17683 108 : stack.TruncateLength(stack.Length() - 1);
17684 126 : if (!references.IsEmpty() &&
17685 18 : references.LastElement() == stack.Length()) {
17686 18 : references.TruncateLength(references.Length() - 1);
17687 : }
17688 108 : if (mToken.mSymbol == c) {
17689 108 : break;
17690 : }
17691 0 : }
17692 : }
17693 345 : break;
17694 :
17695 : case eCSSToken_Function:
17696 370 : if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
17697 285 : if (!GetToken(true)) {
17698 : // EOF directly after "var(".
17699 0 : REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF);
17700 0 : return false;
17701 : }
17702 570 : if (mToken.mType != eCSSToken_Ident ||
17703 285 : !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
17704 : // There must be an identifier directly after the "var(" and
17705 : // it must be a custom property name.
17706 0 : UngetToken();
17707 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName);
17708 0 : SkipUntil(')');
17709 0 : SkipUntilAllOf(stack);
17710 0 : return false;
17711 : }
17712 285 : if (aFunc) {
17713 11 : MOZ_ASSERT(Substring(mToken.mIdent, 0,
17714 : CSS_CUSTOM_NAME_PREFIX_LENGTH).
17715 : EqualsLiteral("--"));
17716 : // remove '--'
17717 : const nsDependentSubstring varName =
17718 22 : Substring(mToken.mIdent, CSS_CUSTOM_NAME_PREFIX_LENGTH);
17719 11 : aFunc(varName, aData);
17720 : }
17721 285 : if (!GetToken(true)) {
17722 : // EOF right after "var(<ident>".
17723 0 : stack.AppendElement(')');
17724 285 : } else if (mToken.IsSymbol(',')) {
17725 : // Variable reference with fallback.
17726 18 : if (!GetToken(false) || mToken.IsSymbol(')')) {
17727 : // Comma must be followed by at least one fallback token.
17728 0 : REPORT_UNEXPECTED(PEExpectedVariableFallback);
17729 0 : SkipUntilAllOf(stack);
17730 0 : return false;
17731 : }
17732 18 : UngetToken();
17733 18 : references.AppendElement(stack.Length());
17734 18 : stack.AppendElement(')');
17735 267 : } else if (mToken.IsSymbol(')')) {
17736 : // Correctly closed variable reference.
17737 : } else {
17738 : // Malformed variable reference.
17739 0 : REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen);
17740 0 : SkipUntil(')');
17741 0 : SkipUntilAllOf(stack);
17742 0 : return false;
17743 : }
17744 : } else {
17745 85 : stack.AppendElement(')');
17746 : }
17747 370 : break;
17748 :
17749 : case eCSSToken_Bad_String:
17750 0 : SkipUntilAllOf(stack);
17751 0 : return false;
17752 :
17753 : case eCSSToken_Bad_URL:
17754 0 : SkipUntil(')');
17755 0 : SkipUntilAllOf(stack);
17756 0 : return false;
17757 :
17758 : default:
17759 608 : break;
17760 : }
17761 : } while (GetToken(true));
17762 :
17763 : // Append any implied closing characters.
17764 141 : *aDropBackslash = BackslashDropped();
17765 141 : AppendImpliedEOFCharacters(aImpliedCharacters);
17766 141 : uint32_t i = stack.Length();
17767 141 : while (i--) {
17768 0 : aImpliedCharacters.Append(stack[i]);
17769 : }
17770 :
17771 141 : *aType = type;
17772 141 : return true;
17773 : }
17774 :
17775 : bool
17776 0 : CSSParserImpl::IsValueValidForProperty(const nsCSSPropertyID aPropID,
17777 : const nsAString& aPropValue)
17778 : {
17779 0 : mData.AssertInitialState();
17780 0 : mTempData.AssertInitialState();
17781 :
17782 0 : nsCSSScanner scanner(aPropValue, 0);
17783 0 : css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr);
17784 0 : InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
17785 :
17786 : // We normally would need to pass in a sheet principal to InitScanner,
17787 : // because we might parse a URL value. However, we will never use the
17788 : // parsed nsCSSValue (and so whether we have a sheet principal or not
17789 : // doesn't really matter), so to avoid failing the assertion in
17790 : // SetValueToURL, we set mSheetPrincipalRequired to false to declare
17791 : // that it's safe to skip the assertion.
17792 0 : AutoRestore<bool> autoRestore(mSheetPrincipalRequired);
17793 0 : mSheetPrincipalRequired = false;
17794 :
17795 0 : nsAutoSuppressErrors suppressErrors(this);
17796 :
17797 0 : mSection = eCSSSection_General;
17798 :
17799 : // Check for unknown properties
17800 0 : if (eCSSProperty_UNKNOWN == aPropID) {
17801 0 : ReleaseScanner();
17802 0 : return false;
17803 : }
17804 :
17805 : // Check that the property and value parse successfully
17806 0 : bool parsedOK = ParseProperty(aPropID);
17807 :
17808 : // Check for priority
17809 0 : parsedOK = parsedOK && ParsePriority() != ePriority_Error;
17810 :
17811 : // We should now be at EOF
17812 0 : parsedOK = parsedOK && !GetToken(true);
17813 :
17814 0 : mTempData.ClearProperty(aPropID);
17815 0 : mTempData.AssertInitialState();
17816 0 : mData.AssertInitialState();
17817 :
17818 0 : CLEAR_ERROR();
17819 0 : ReleaseScanner();
17820 :
17821 0 : return parsedOK;
17822 : }
17823 :
17824 : } // namespace
17825 :
17826 : // Recycling of parser implementation objects
17827 :
17828 : static CSSParserImpl* gFreeList = nullptr;
17829 :
17830 : /* static */ void
17831 3 : nsCSSParser::Startup()
17832 : {
17833 : Preferences::AddBoolVarCache(&sOpentypeSVGEnabled,
17834 3 : "gfx.font_rendering.opentype_svg.enabled");
17835 : Preferences::AddBoolVarCache(&sWebkitPrefixedAliasesEnabled,
17836 3 : "layout.css.prefixes.webkit");
17837 : Preferences::AddBoolVarCache(&sWebkitDevicePixelRatioEnabled,
17838 3 : "layout.css.prefixes.device-pixel-ratio-webkit");
17839 : Preferences::AddBoolVarCache(&sMozGradientsEnabled,
17840 3 : "layout.css.prefixes.gradients");
17841 : Preferences::AddBoolVarCache(&sControlCharVisibility,
17842 3 : "layout.css.control-characters.visible");
17843 : Preferences::AddBoolVarCache(&sFramesTimingFunctionEnabled,
17844 3 : "layout.css.frames-timing.enabled");
17845 3 : }
17846 :
17847 12025 : nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
17848 12025 : CSSStyleSheet* aSheet)
17849 : {
17850 12025 : CSSParserImpl *impl = gFreeList;
17851 12025 : if (impl) {
17852 12021 : gFreeList = impl->mNextFree;
17853 12021 : impl->mNextFree = nullptr;
17854 : } else {
17855 4 : impl = new CSSParserImpl();
17856 : }
17857 :
17858 12025 : if (aLoader) {
17859 300 : impl->SetChildLoader(aLoader);
17860 300 : impl->SetQuirkMode(aLoader->GetCompatibilityMode() ==
17861 300 : eCompatibility_NavQuirks);
17862 : }
17863 12025 : if (aSheet) {
17864 60 : impl->SetStyleSheet(aSheet);
17865 : }
17866 :
17867 12025 : mImpl = static_cast<void*>(impl);
17868 12025 : }
17869 :
17870 24050 : nsCSSParser::~nsCSSParser()
17871 : {
17872 12025 : CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl);
17873 12025 : impl->Reset();
17874 12025 : impl->mNextFree = gFreeList;
17875 12025 : gFreeList = impl;
17876 12025 : }
17877 :
17878 : /* static */ void
17879 0 : nsCSSParser::Shutdown()
17880 : {
17881 0 : CSSParserImpl *tofree = gFreeList;
17882 : CSSParserImpl *next;
17883 0 : while (tofree)
17884 : {
17885 0 : next = tofree->mNextFree;
17886 0 : delete tofree;
17887 0 : tofree = next;
17888 : }
17889 0 : }
17890 :
17891 : // Wrapper methods
17892 :
17893 : nsresult
17894 60 : nsCSSParser::ParseSheet(const nsAString& aInput,
17895 : nsIURI* aSheetURI,
17896 : nsIURI* aBaseURI,
17897 : nsIPrincipal* aSheetPrincipal,
17898 : uint32_t aLineNumber,
17899 : css::LoaderReusableStyleSheets* aReusableSheets)
17900 : {
17901 60 : return static_cast<CSSParserImpl*>(mImpl)->
17902 : ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
17903 60 : aReusableSheets);
17904 : }
17905 :
17906 : already_AddRefed<css::Declaration>
17907 7 : nsCSSParser::ParseStyleAttribute(const nsAString& aAttributeValue,
17908 : nsIURI* aDocURI,
17909 : nsIURI* aBaseURI,
17910 : nsIPrincipal* aNodePrincipal)
17911 : {
17912 7 : return static_cast<CSSParserImpl*>(mImpl)->
17913 7 : ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI, aNodePrincipal);
17914 : }
17915 :
17916 : nsresult
17917 0 : nsCSSParser::ParseDeclarations(const nsAString& aBuffer,
17918 : nsIURI* aSheetURI,
17919 : nsIURI* aBaseURI,
17920 : nsIPrincipal* aSheetPrincipal,
17921 : css::Declaration* aDeclaration,
17922 : bool* aChanged)
17923 : {
17924 0 : return static_cast<CSSParserImpl*>(mImpl)->
17925 : ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal,
17926 0 : aDeclaration, aChanged);
17927 : }
17928 :
17929 : nsresult
17930 0 : nsCSSParser::ParseRule(const nsAString& aRule,
17931 : nsIURI* aSheetURI,
17932 : nsIURI* aBaseURI,
17933 : nsIPrincipal* aSheetPrincipal,
17934 : css::Rule** aResult)
17935 : {
17936 0 : return static_cast<CSSParserImpl*>(mImpl)->
17937 0 : ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult);
17938 : }
17939 :
17940 : void
17941 103 : nsCSSParser::ParseProperty(const nsCSSPropertyID aPropID,
17942 : const nsAString& aPropValue,
17943 : nsIURI* aSheetURI,
17944 : nsIURI* aBaseURI,
17945 : nsIPrincipal* aSheetPrincipal,
17946 : css::Declaration* aDeclaration,
17947 : bool* aChanged,
17948 : bool aIsImportant,
17949 : bool aIsSVGMode)
17950 : {
17951 103 : static_cast<CSSParserImpl*>(mImpl)->
17952 103 : ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
17953 : aSheetPrincipal, aDeclaration, aChanged,
17954 103 : aIsImportant, aIsSVGMode);
17955 103 : }
17956 :
17957 : void
17958 0 : nsCSSParser::ParseLonghandProperty(const nsCSSPropertyID aPropID,
17959 : const nsAString& aPropValue,
17960 : nsIURI* aSheetURI,
17961 : nsIURI* aBaseURI,
17962 : nsIPrincipal* aSheetPrincipal,
17963 : nsCSSValue& aResult)
17964 : {
17965 0 : static_cast<CSSParserImpl*>(mImpl)->
17966 : ParseLonghandProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
17967 0 : aSheetPrincipal, aResult);
17968 0 : }
17969 :
17970 : bool
17971 0 : nsCSSParser::ParseTransformProperty(const nsAString& aPropValue,
17972 : bool aDisallowRelativeValues,
17973 : nsCSSValue& aResult)
17974 : {
17975 0 : return static_cast<CSSParserImpl*>(mImpl)->
17976 0 : ParseTransformProperty(aPropValue, aDisallowRelativeValues, aResult);
17977 : }
17978 :
17979 : void
17980 0 : nsCSSParser::ParseVariable(const nsAString& aVariableName,
17981 : const nsAString& aPropValue,
17982 : nsIURI* aSheetURI,
17983 : nsIURI* aBaseURI,
17984 : nsIPrincipal* aSheetPrincipal,
17985 : css::Declaration* aDeclaration,
17986 : bool* aChanged,
17987 : bool aIsImportant)
17988 : {
17989 0 : static_cast<CSSParserImpl*>(mImpl)->
17990 0 : ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI,
17991 0 : aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
17992 0 : }
17993 :
17994 : void
17995 3 : nsCSSParser::ParseMediaList(const nsAString& aBuffer,
17996 : nsIURI* aURI,
17997 : uint32_t aLineNumber,
17998 : nsMediaList* aMediaList)
17999 : {
18000 3 : static_cast<CSSParserImpl*>(mImpl)->
18001 3 : ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList);
18002 3 : }
18003 :
18004 : bool
18005 0 : nsCSSParser::ParseSourceSizeList(const nsAString& aBuffer,
18006 : nsIURI* aURI,
18007 : uint32_t aLineNumber,
18008 : InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
18009 : InfallibleTArray<nsCSSValue>& aValues)
18010 : {
18011 0 : return static_cast<CSSParserImpl*>(mImpl)->
18012 0 : ParseSourceSizeList(aBuffer, aURI, aLineNumber, aQueries, aValues);
18013 : }
18014 :
18015 : bool
18016 0 : nsCSSParser::ParseFontFamilyListString(const nsAString& aBuffer,
18017 : nsIURI* aURI,
18018 : uint32_t aLineNumber,
18019 : nsCSSValue& aValue)
18020 : {
18021 0 : return static_cast<CSSParserImpl*>(mImpl)->
18022 0 : ParseFontFamilyListString(aBuffer, aURI, aLineNumber, aValue);
18023 : }
18024 :
18025 : bool
18026 152 : nsCSSParser::ParseColorString(const nsAString& aBuffer,
18027 : nsIURI* aURI,
18028 : uint32_t aLineNumber,
18029 : nsCSSValue& aValue,
18030 : bool aSuppressErrors /* false */)
18031 : {
18032 152 : return static_cast<CSSParserImpl*>(mImpl)->
18033 304 : ParseColorString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors);
18034 : }
18035 :
18036 : bool
18037 0 : nsCSSParser::ParseMarginString(const nsAString& aBuffer,
18038 : nsIURI* aURI,
18039 : uint32_t aLineNumber,
18040 : nsCSSValue& aValue,
18041 : bool aSuppressErrors /* false */)
18042 : {
18043 0 : return static_cast<CSSParserImpl*>(mImpl)->
18044 0 : ParseMarginString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors);
18045 : }
18046 :
18047 : nsresult
18048 7 : nsCSSParser::ParseSelectorString(const nsAString& aSelectorString,
18049 : nsIURI* aURI,
18050 : uint32_t aLineNumber,
18051 : nsCSSSelectorList** aSelectorList)
18052 : {
18053 7 : return static_cast<CSSParserImpl*>(mImpl)->
18054 7 : ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList);
18055 : }
18056 :
18057 : already_AddRefed<nsCSSKeyframeRule>
18058 0 : nsCSSParser::ParseKeyframeRule(const nsAString& aBuffer,
18059 : nsIURI* aURI,
18060 : uint32_t aLineNumber)
18061 : {
18062 0 : return static_cast<CSSParserImpl*>(mImpl)->
18063 0 : ParseKeyframeRule(aBuffer, aURI, aLineNumber);
18064 : }
18065 :
18066 : bool
18067 0 : nsCSSParser::ParseKeyframeSelectorString(const nsAString& aSelectorString,
18068 : nsIURI* aURI,
18069 : uint32_t aLineNumber,
18070 : InfallibleTArray<float>& aSelectorList)
18071 : {
18072 0 : return static_cast<CSSParserImpl*>(mImpl)->
18073 : ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber,
18074 0 : aSelectorList);
18075 : }
18076 :
18077 : bool
18078 0 : nsCSSParser::EvaluateSupportsDeclaration(const nsAString& aProperty,
18079 : const nsAString& aValue,
18080 : nsIURI* aDocURL,
18081 : nsIURI* aBaseURL,
18082 : nsIPrincipal* aDocPrincipal)
18083 : {
18084 0 : return static_cast<CSSParserImpl*>(mImpl)->
18085 : EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL,
18086 0 : aDocPrincipal);
18087 : }
18088 :
18089 : bool
18090 0 : nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition,
18091 : nsIURI* aDocURL,
18092 : nsIURI* aBaseURL,
18093 : nsIPrincipal* aDocPrincipal,
18094 : SupportsParsingSettings aSettings)
18095 : {
18096 0 : return static_cast<CSSParserImpl*>(mImpl)->
18097 : EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL,
18098 0 : aDocPrincipal, aSettings);
18099 : }
18100 :
18101 : bool
18102 141 : nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue,
18103 : VariableEnumFunc aFunc,
18104 : void* aData)
18105 : {
18106 141 : return static_cast<CSSParserImpl*>(mImpl)->
18107 141 : EnumerateVariableReferences(aPropertyValue, aFunc, aData);
18108 : }
18109 :
18110 : bool
18111 141 : nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue,
18112 : const CSSVariableValues* aVariables,
18113 : nsString& aResult,
18114 : nsCSSTokenSerializationType& aFirstToken,
18115 : nsCSSTokenSerializationType& aLastToken)
18116 : {
18117 141 : return static_cast<CSSParserImpl*>(mImpl)->
18118 : ResolveVariableValue(aPropertyValue, aVariables,
18119 141 : aResult, aFirstToken, aLastToken);
18120 : }
18121 :
18122 : void
18123 883 : nsCSSParser::ParsePropertyWithVariableReferences(
18124 : nsCSSPropertyID aPropertyID,
18125 : nsCSSPropertyID aShorthandPropertyID,
18126 : const nsAString& aValue,
18127 : const CSSVariableValues* aVariables,
18128 : nsRuleData* aRuleData,
18129 : nsIURI* aDocURL,
18130 : nsIURI* aBaseURL,
18131 : nsIPrincipal* aDocPrincipal,
18132 : CSSStyleSheet* aSheet,
18133 : uint32_t aLineNumber,
18134 : uint32_t aLineOffset)
18135 : {
18136 883 : static_cast<CSSParserImpl*>(mImpl)->
18137 : ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID,
18138 : aValue, aVariables, aRuleData, aDocURL,
18139 : aBaseURL, aDocPrincipal, aSheet,
18140 883 : aLineNumber, aLineOffset);
18141 883 : }
18142 :
18143 : already_AddRefed<nsIAtom>
18144 0 : nsCSSParser::ParseCounterStyleName(const nsAString& aBuffer, nsIURI* aURL)
18145 : {
18146 0 : return static_cast<CSSParserImpl*>(mImpl)->
18147 0 : ParseCounterStyleName(aBuffer, aURL);
18148 : }
18149 :
18150 : bool
18151 0 : nsCSSParser::ParseCounterDescriptor(nsCSSCounterDesc aDescID,
18152 : const nsAString& aBuffer,
18153 : nsIURI* aSheetURL,
18154 : nsIURI* aBaseURL,
18155 : nsIPrincipal* aSheetPrincipal,
18156 : nsCSSValue& aValue)
18157 : {
18158 0 : return static_cast<CSSParserImpl*>(mImpl)->
18159 : ParseCounterDescriptor(aDescID, aBuffer,
18160 0 : aSheetURL, aBaseURL, aSheetPrincipal, aValue);
18161 : }
18162 :
18163 : bool
18164 0 : nsCSSParser::ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
18165 : const nsAString& aBuffer,
18166 : nsIURI* aSheetURL,
18167 : nsIURI* aBaseURL,
18168 : nsIPrincipal* aSheetPrincipal,
18169 : nsCSSValue& aValue)
18170 : {
18171 0 : return static_cast<CSSParserImpl*>(mImpl)->
18172 : ParseFontFaceDescriptor(aDescID, aBuffer,
18173 0 : aSheetURL, aBaseURL, aSheetPrincipal, aValue);
18174 : }
18175 :
18176 : bool
18177 0 : nsCSSParser::IsValueValidForProperty(const nsCSSPropertyID aPropID,
18178 : const nsAString& aPropValue)
18179 : {
18180 0 : return static_cast<CSSParserImpl*>(mImpl)->
18181 0 : IsValueValidForProperty(aPropID, aPropValue);
18182 : }
18183 :
18184 : /* static */
18185 : uint8_t
18186 101 : nsCSSParser::ControlCharVisibilityDefault()
18187 : {
18188 : return sControlCharVisibility
18189 : ? NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE
18190 101 : : NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN;
18191 : }
|