|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=78: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */ |
|
8 |
|
9 #include "mozilla/ArrayUtils.h" |
|
10 #include "mozilla/DebugOnly.h" |
|
11 |
|
12 #include "nsCSSParser.h" |
|
13 #include "nsCSSProps.h" |
|
14 #include "nsCSSKeywords.h" |
|
15 #include "nsCSSScanner.h" |
|
16 #include "mozilla/css/ErrorReporter.h" |
|
17 #include "mozilla/css/Loader.h" |
|
18 #include "mozilla/css/StyleRule.h" |
|
19 #include "mozilla/css/ImportRule.h" |
|
20 #include "nsCSSRules.h" |
|
21 #include "mozilla/css/NameSpaceRule.h" |
|
22 #include "nsTArray.h" |
|
23 #include "nsCSSStyleSheet.h" |
|
24 #include "mozilla/css/Declaration.h" |
|
25 #include "nsStyleConsts.h" |
|
26 #include "nsNetUtil.h" |
|
27 #include "nsCOMPtr.h" |
|
28 #include "nsString.h" |
|
29 #include "nsIAtom.h" |
|
30 #include "nsColor.h" |
|
31 #include "nsCSSPseudoClasses.h" |
|
32 #include "nsCSSPseudoElements.h" |
|
33 #include "nsNameSpaceManager.h" |
|
34 #include "nsXMLNameSpaceMap.h" |
|
35 #include "nsError.h" |
|
36 #include "nsIMediaList.h" |
|
37 #include "nsStyleUtil.h" |
|
38 #include "nsIPrincipal.h" |
|
39 #include "prprf.h" |
|
40 #include "nsContentUtils.h" |
|
41 #include "nsAutoPtr.h" |
|
42 #include "CSSCalc.h" |
|
43 #include "nsMediaFeatures.h" |
|
44 #include "nsLayoutUtils.h" |
|
45 #include "mozilla/Preferences.h" |
|
46 #include "nsRuleData.h" |
|
47 #include "mozilla/CSSVariableValues.h" |
|
48 #include "mozilla/dom/URL.h" |
|
49 |
|
50 using namespace mozilla; |
|
51 |
|
52 typedef nsCSSProps::KTableValue KTableValue; |
|
53 |
|
54 const uint32_t |
|
55 nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = { |
|
56 #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \ |
|
57 stylestruct_, stylestructoffset_, animtype_) \ |
|
58 parsevariant_, |
|
59 #include "nsCSSPropList.h" |
|
60 #undef CSS_PROP |
|
61 }; |
|
62 |
|
63 // Maximum number of repetitions for the repeat() function |
|
64 // in the grid-template-columns and grid-template-rows properties, |
|
65 // to limit high memory usage from small stylesheets. |
|
66 // Must be a positive integer. Should be large-ish. |
|
67 #define GRID_TEMPLATE_MAX_REPETITIONS 10000 |
|
68 |
|
69 // End-of-array marker for mask arguments to ParseBitmaskValues |
|
70 #define MASK_END_VALUE (-1) |
|
71 |
|
72 MOZ_BEGIN_ENUM_CLASS(CSSParseResult, int32_t) |
|
73 // Parsed something successfully: |
|
74 Ok, |
|
75 // Did not find what we were looking for, but did not consume any token: |
|
76 NotFound, |
|
77 // Unexpected token or token value, too late for UngetToken() to be enough: |
|
78 Error |
|
79 MOZ_END_ENUM_CLASS(CSSParseResult) |
|
80 |
|
81 namespace { |
|
82 |
|
83 // Rule processing function |
|
84 typedef void (* RuleAppendFunc) (css::Rule* aRule, void* aData); |
|
85 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer); |
|
86 static void AppendRuleToSheet(css::Rule* aRule, void* aParser); |
|
87 |
|
88 struct CSSParserInputState { |
|
89 nsCSSScannerPosition mPosition; |
|
90 nsCSSToken mToken; |
|
91 bool mHavePushBack; |
|
92 }; |
|
93 |
|
94 // Your basic top-down recursive descent style parser |
|
95 // The exposed methods and members of this class are precisely those |
|
96 // needed by nsCSSParser, far below. |
|
97 class CSSParserImpl { |
|
98 public: |
|
99 CSSParserImpl(); |
|
100 ~CSSParserImpl(); |
|
101 |
|
102 nsresult SetStyleSheet(nsCSSStyleSheet* aSheet); |
|
103 |
|
104 nsresult SetQuirkMode(bool aQuirkMode); |
|
105 |
|
106 nsresult SetChildLoader(mozilla::css::Loader* aChildLoader); |
|
107 |
|
108 // Clears everything set by the above Set*() functions. |
|
109 void Reset(); |
|
110 |
|
111 nsresult ParseSheet(const nsAString& aInput, |
|
112 nsIURI* aSheetURI, |
|
113 nsIURI* aBaseURI, |
|
114 nsIPrincipal* aSheetPrincipal, |
|
115 uint32_t aLineNumber, |
|
116 bool aAllowUnsafeRules); |
|
117 |
|
118 nsresult ParseStyleAttribute(const nsAString& aAttributeValue, |
|
119 nsIURI* aDocURL, |
|
120 nsIURI* aBaseURL, |
|
121 nsIPrincipal* aNodePrincipal, |
|
122 css::StyleRule** aResult); |
|
123 |
|
124 nsresult ParseDeclarations(const nsAString& aBuffer, |
|
125 nsIURI* aSheetURL, |
|
126 nsIURI* aBaseURL, |
|
127 nsIPrincipal* aSheetPrincipal, |
|
128 css::Declaration* aDeclaration, |
|
129 bool* aChanged); |
|
130 |
|
131 nsresult ParseRule(const nsAString& aRule, |
|
132 nsIURI* aSheetURL, |
|
133 nsIURI* aBaseURL, |
|
134 nsIPrincipal* aSheetPrincipal, |
|
135 css::Rule** aResult); |
|
136 |
|
137 nsresult ParseProperty(const nsCSSProperty aPropID, |
|
138 const nsAString& aPropValue, |
|
139 nsIURI* aSheetURL, |
|
140 nsIURI* aBaseURL, |
|
141 nsIPrincipal* aSheetPrincipal, |
|
142 css::Declaration* aDeclaration, |
|
143 bool* aChanged, |
|
144 bool aIsImportant, |
|
145 bool aIsSVGMode); |
|
146 |
|
147 void ParseMediaList(const nsSubstring& aBuffer, |
|
148 nsIURI* aURL, // for error reporting |
|
149 uint32_t aLineNumber, // for error reporting |
|
150 nsMediaList* aMediaList, |
|
151 bool aHTMLMode); |
|
152 |
|
153 nsresult ParseVariable(const nsAString& aVariableName, |
|
154 const nsAString& aPropValue, |
|
155 nsIURI* aSheetURL, |
|
156 nsIURI* aBaseURL, |
|
157 nsIPrincipal* aSheetPrincipal, |
|
158 css::Declaration* aDeclaration, |
|
159 bool* aChanged, |
|
160 bool aIsImportant); |
|
161 |
|
162 bool ParseColorString(const nsSubstring& aBuffer, |
|
163 nsIURI* aURL, // for error reporting |
|
164 uint32_t aLineNumber, // for error reporting |
|
165 nsCSSValue& aValue); |
|
166 |
|
167 nsresult ParseSelectorString(const nsSubstring& aSelectorString, |
|
168 nsIURI* aURL, // for error reporting |
|
169 uint32_t aLineNumber, // for error reporting |
|
170 nsCSSSelectorList **aSelectorList); |
|
171 |
|
172 already_AddRefed<nsCSSKeyframeRule> |
|
173 ParseKeyframeRule(const nsSubstring& aBuffer, |
|
174 nsIURI* aURL, |
|
175 uint32_t aLineNumber); |
|
176 |
|
177 bool ParseKeyframeSelectorString(const nsSubstring& aSelectorString, |
|
178 nsIURI* aURL, // for error reporting |
|
179 uint32_t aLineNumber, // for error reporting |
|
180 InfallibleTArray<float>& aSelectorList); |
|
181 |
|
182 bool EvaluateSupportsDeclaration(const nsAString& aProperty, |
|
183 const nsAString& aValue, |
|
184 nsIURI* aDocURL, |
|
185 nsIURI* aBaseURL, |
|
186 nsIPrincipal* aDocPrincipal); |
|
187 |
|
188 bool EvaluateSupportsCondition(const nsAString& aCondition, |
|
189 nsIURI* aDocURL, |
|
190 nsIURI* aBaseURL, |
|
191 nsIPrincipal* aDocPrincipal); |
|
192 |
|
193 typedef nsCSSParser::VariableEnumFunc VariableEnumFunc; |
|
194 |
|
195 /** |
|
196 * Parses a CSS token stream value and invokes a callback function for each |
|
197 * variable reference that is encountered. |
|
198 * |
|
199 * @param aPropertyValue The CSS token stream value. |
|
200 * @param aFunc The callback function to invoke; its parameters are the |
|
201 * variable name found and the aData argument passed in to this function. |
|
202 * @param aData User data to pass in to the callback. |
|
203 * @return Whether aPropertyValue could be parsed as a valid CSS token stream |
|
204 * value (e.g., without syntactic errors in variable references). |
|
205 */ |
|
206 bool EnumerateVariableReferences(const nsAString& aPropertyValue, |
|
207 VariableEnumFunc aFunc, |
|
208 void* aData); |
|
209 |
|
210 /** |
|
211 * Parses aPropertyValue as a CSS token stream value and resolves any |
|
212 * variable references using the variables in aVariables. |
|
213 * |
|
214 * @param aPropertyValue The CSS token stream value. |
|
215 * @param aVariables The set of variable values to use when resolving variable |
|
216 * references. |
|
217 * @param aResult Out parameter that gets the resolved value. |
|
218 * @param aFirstToken Out parameter that gets the type of the first token in |
|
219 * aResult. |
|
220 * @param aLastToken Out parameter that gets the type of the last token in |
|
221 * aResult. |
|
222 * @return Whether aResult could be parsed successfully and variable reference |
|
223 * substitution succeeded. |
|
224 */ |
|
225 bool ResolveVariableValue(const nsAString& aPropertyValue, |
|
226 const CSSVariableValues* aVariables, |
|
227 nsString& aResult, |
|
228 nsCSSTokenSerializationType& aFirstToken, |
|
229 nsCSSTokenSerializationType& aLastToken); |
|
230 |
|
231 /** |
|
232 * Parses a string as a CSS token stream value for particular property, |
|
233 * resolving any variable references. The parsed property value is stored |
|
234 * in the specified nsRuleData object. If aShorthandPropertyID has a value |
|
235 * other than eCSSProperty_UNKNOWN, this is the property that will be parsed; |
|
236 * otherwise, aPropertyID will be parsed. Either way, only aPropertyID, |
|
237 * a longhand property, will be copied over to the rule data. |
|
238 * |
|
239 * If the property cannot be parsed, it will be treated as if 'initial' or |
|
240 * 'inherit' were specified, for non-inherited and inherited properties |
|
241 * respectively. |
|
242 * |
|
243 * @param aPropertyID The ID of the longhand property whose value is to be |
|
244 * copied to the rule data. |
|
245 * @param aShorthandPropertyID The ID of the shorthand property to be parsed. |
|
246 * If a longhand property is to be parsed, aPropertyID is that property, |
|
247 * and aShorthandPropertyID must be eCSSProperty_UNKNOWN. |
|
248 * @param aValue The CSS token stream value. |
|
249 * @param aVariables The set of variable values to use when resolving variable |
|
250 * references. |
|
251 * @param aRuleData The rule data object into which parsed property value for |
|
252 * aPropertyID will be stored. |
|
253 */ |
|
254 void ParsePropertyWithVariableReferences(nsCSSProperty aPropertyID, |
|
255 nsCSSProperty aShorthandPropertyID, |
|
256 const nsAString& aValue, |
|
257 const CSSVariableValues* aVariables, |
|
258 nsRuleData* aRuleData, |
|
259 nsIURI* aDocURL, |
|
260 nsIURI* aBaseURL, |
|
261 nsIPrincipal* aDocPrincipal, |
|
262 nsCSSStyleSheet* aSheet, |
|
263 uint32_t aLineNumber, |
|
264 uint32_t aLineOffset); |
|
265 |
|
266 nsCSSProperty LookupEnabledProperty(const nsAString& aProperty) { |
|
267 static_assert(nsCSSProps::eEnabledForAllContent == 0, |
|
268 "nsCSSProps::eEnabledForAllContent should be zero for " |
|
269 "this bitfield to work"); |
|
270 nsCSSProps::EnabledState enabledState = nsCSSProps::eEnabledForAllContent; |
|
271 if (mUnsafeRulesEnabled) { |
|
272 enabledState |= nsCSSProps::eEnabledInUASheets; |
|
273 } |
|
274 if (mIsChromeOrCertifiedApp) { |
|
275 enabledState |= nsCSSProps::eEnabledInChromeOrCertifiedApp; |
|
276 } |
|
277 return nsCSSProps::LookupProperty(aProperty, enabledState); |
|
278 } |
|
279 |
|
280 protected: |
|
281 class nsAutoParseCompoundProperty; |
|
282 friend class nsAutoParseCompoundProperty; |
|
283 |
|
284 class nsAutoFailingSupportsRule; |
|
285 friend class nsAutoFailingSupportsRule; |
|
286 |
|
287 class nsAutoSuppressErrors; |
|
288 friend class nsAutoSuppressErrors; |
|
289 |
|
290 void AppendRule(css::Rule* aRule); |
|
291 friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule |
|
292 |
|
293 /** |
|
294 * This helper class automatically calls SetParsingCompoundProperty in its |
|
295 * constructor and takes care of resetting it to false in its destructor. |
|
296 */ |
|
297 class nsAutoParseCompoundProperty { |
|
298 public: |
|
299 nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser) |
|
300 { |
|
301 NS_ASSERTION(!aParser->IsParsingCompoundProperty(), |
|
302 "already parsing compound property"); |
|
303 NS_ASSERTION(aParser, "Null parser?"); |
|
304 aParser->SetParsingCompoundProperty(true); |
|
305 } |
|
306 |
|
307 ~nsAutoParseCompoundProperty() |
|
308 { |
|
309 mParser->SetParsingCompoundProperty(false); |
|
310 } |
|
311 private: |
|
312 CSSParserImpl* mParser; |
|
313 }; |
|
314 |
|
315 /** |
|
316 * This helper class conditionally sets mInFailingSupportsRule to |
|
317 * true if aCondition = false, and resets it to its original value in its |
|
318 * destructor. If we are already somewhere within a failing @supports |
|
319 * rule, passing in aCondition = true does not change mInFailingSupportsRule. |
|
320 */ |
|
321 class nsAutoFailingSupportsRule { |
|
322 public: |
|
323 nsAutoFailingSupportsRule(CSSParserImpl* aParser, |
|
324 bool aCondition) |
|
325 : mParser(aParser), |
|
326 mOriginalValue(aParser->mInFailingSupportsRule) |
|
327 { |
|
328 if (!aCondition) { |
|
329 mParser->mInFailingSupportsRule = true; |
|
330 } |
|
331 } |
|
332 |
|
333 ~nsAutoFailingSupportsRule() |
|
334 { |
|
335 mParser->mInFailingSupportsRule = mOriginalValue; |
|
336 } |
|
337 |
|
338 private: |
|
339 CSSParserImpl* mParser; |
|
340 bool mOriginalValue; |
|
341 }; |
|
342 |
|
343 /** |
|
344 * Auto class to set aParser->mSuppressErrors to the specified value |
|
345 * and restore it to its original value later. |
|
346 */ |
|
347 class nsAutoSuppressErrors { |
|
348 public: |
|
349 nsAutoSuppressErrors(CSSParserImpl* aParser, |
|
350 bool aSuppressErrors = true) |
|
351 : mParser(aParser), |
|
352 mOriginalValue(aParser->mSuppressErrors) |
|
353 { |
|
354 mParser->mSuppressErrors = aSuppressErrors; |
|
355 } |
|
356 |
|
357 ~nsAutoSuppressErrors() |
|
358 { |
|
359 mParser->mSuppressErrors = mOriginalValue; |
|
360 } |
|
361 |
|
362 private: |
|
363 CSSParserImpl* mParser; |
|
364 bool mOriginalValue; |
|
365 }; |
|
366 |
|
367 // the caller must hold on to aString until parsing is done |
|
368 void InitScanner(nsCSSScanner& aScanner, |
|
369 css::ErrorReporter& aReporter, |
|
370 nsIURI* aSheetURI, nsIURI* aBaseURI, |
|
371 nsIPrincipal* aSheetPrincipal); |
|
372 void ReleaseScanner(void); |
|
373 bool IsSVGMode() const { |
|
374 return mScanner->IsSVGMode(); |
|
375 } |
|
376 |
|
377 /** |
|
378 * Saves the current input state, which includes any currently pushed |
|
379 * back token, and the current position of the scanner. |
|
380 */ |
|
381 void SaveInputState(CSSParserInputState& aState); |
|
382 |
|
383 /** |
|
384 * Restores the saved input state by pushing back any saved pushback |
|
385 * token and calling RestoreSavedPosition on the scanner. |
|
386 */ |
|
387 void RestoreSavedInputState(const CSSParserInputState& aState); |
|
388 |
|
389 bool GetToken(bool aSkipWS); |
|
390 void UngetToken(); |
|
391 bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum); |
|
392 |
|
393 bool ExpectSymbol(char16_t aSymbol, bool aSkipWS); |
|
394 bool ExpectEndProperty(); |
|
395 bool CheckEndProperty(); |
|
396 nsSubstring* NextIdent(); |
|
397 |
|
398 // returns true when the stop symbol is found, and false for EOF |
|
399 bool SkipUntil(char16_t aStopSymbol); |
|
400 void SkipUntilOneOf(const char16_t* aStopSymbolChars); |
|
401 // For each character in aStopSymbolChars from the end of the array |
|
402 // to the start, calls SkipUntil with that character. |
|
403 typedef nsAutoTArray<char16_t, 16> StopSymbolCharStack; |
|
404 void SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars); |
|
405 // returns true if the stop symbol or EOF is found, and false for an |
|
406 // unexpected ')', ']' or '}'; this not safe to call outside variable |
|
407 // resolution, as it doesn't handle mismatched content |
|
408 bool SkipBalancedContentUntil(char16_t aStopSymbol); |
|
409 |
|
410 void SkipRuleSet(bool aInsideBraces); |
|
411 bool SkipAtRule(bool aInsideBlock); |
|
412 bool SkipDeclaration(bool aCheckForBraces); |
|
413 |
|
414 void PushGroup(css::GroupRule* aRule); |
|
415 void PopGroup(); |
|
416 |
|
417 bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData, |
|
418 bool aInsideBraces = false); |
|
419 bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData, |
|
420 bool aInAtRule); |
|
421 bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData); |
|
422 bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData); |
|
423 bool ParseURLOrString(nsString& aURL); |
|
424 bool GatherMedia(nsMediaList* aMedia, |
|
425 bool aInAtRule); |
|
426 bool ParseMediaQuery(bool aInAtRule, nsMediaQuery **aQuery, |
|
427 bool *aHitStop); |
|
428 bool ParseMediaQueryExpression(nsMediaQuery* aQuery); |
|
429 void ProcessImport(const nsString& aURLSpec, |
|
430 nsMediaList* aMedia, |
|
431 RuleAppendFunc aAppendFunc, |
|
432 void* aProcessData); |
|
433 bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc, |
|
434 void* aProcessData); |
|
435 bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData); |
|
436 bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData); |
|
437 bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData); |
|
438 void ProcessNameSpace(const nsString& aPrefix, |
|
439 const nsString& aURLSpec, RuleAppendFunc aAppendFunc, |
|
440 void* aProcessData); |
|
441 |
|
442 bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData); |
|
443 bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc, |
|
444 void* aProcessData); |
|
445 bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule); |
|
446 bool ParseFontDescriptor(nsCSSFontFaceRule* aRule); |
|
447 bool ParseFontDescriptorValue(nsCSSFontDesc aDescID, |
|
448 nsCSSValue& aValue); |
|
449 |
|
450 bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData); |
|
451 bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData); |
|
452 already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule(); |
|
453 bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList); |
|
454 |
|
455 bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData); |
|
456 bool ParseSupportsCondition(bool& aConditionMet); |
|
457 bool ParseSupportsConditionNegation(bool& aConditionMet); |
|
458 bool ParseSupportsConditionInParens(bool& aConditionMet); |
|
459 bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet); |
|
460 bool ParseSupportsConditionTerms(bool& aConditionMet); |
|
461 enum SupportsConditionTermOperator { eAnd, eOr }; |
|
462 bool ParseSupportsConditionTermsAfterOperator( |
|
463 bool& aConditionMet, |
|
464 SupportsConditionTermOperator aOperator); |
|
465 |
|
466 /** |
|
467 * Parses the current input stream for a CSS token stream value and resolves |
|
468 * any variable references using the variables in aVariables. |
|
469 * |
|
470 * @param aVariables The set of variable values to use when resolving variable |
|
471 * references. |
|
472 * @param aResult Out parameter that, if the function returns true, will be |
|
473 * replaced with the resolved value. |
|
474 * @return Whether aResult could be parsed successfully and variable reference |
|
475 * substitution succeeded. |
|
476 */ |
|
477 bool ResolveValueWithVariableReferences( |
|
478 const CSSVariableValues* aVariables, |
|
479 nsString& aResult, |
|
480 nsCSSTokenSerializationType& aResultFirstToken, |
|
481 nsCSSTokenSerializationType& aResultLastToken); |
|
482 // Helper function for ResolveValueWithVariableReferences. |
|
483 bool ResolveValueWithVariableReferencesRec( |
|
484 nsString& aResult, |
|
485 nsCSSTokenSerializationType& aResultFirstToken, |
|
486 nsCSSTokenSerializationType& aResultLastToken, |
|
487 const CSSVariableValues* aVariables); |
|
488 |
|
489 enum nsSelectorParsingStatus { |
|
490 // we have parsed a selector and we saw a token that cannot be |
|
491 // part of a selector: |
|
492 eSelectorParsingStatus_Done, |
|
493 // we should continue parsing the selector: |
|
494 eSelectorParsingStatus_Continue, |
|
495 // we saw an unexpected token or token value, |
|
496 // or we saw end-of-file with an unfinished selector: |
|
497 eSelectorParsingStatus_Error |
|
498 }; |
|
499 nsSelectorParsingStatus ParseIDSelector(int32_t& aDataMask, |
|
500 nsCSSSelector& aSelector); |
|
501 |
|
502 nsSelectorParsingStatus ParseClassSelector(int32_t& aDataMask, |
|
503 nsCSSSelector& aSelector); |
|
504 |
|
505 // aPseudoElement and aPseudoElementArgs are the location where |
|
506 // pseudo-elements (as opposed to pseudo-classes) are stored; |
|
507 // pseudo-classes are stored on aSelector. aPseudoElement and |
|
508 // aPseudoElementArgs must be non-null iff !aIsNegated. |
|
509 nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask, |
|
510 nsCSSSelector& aSelector, |
|
511 bool aIsNegated, |
|
512 nsIAtom** aPseudoElement, |
|
513 nsAtomList** aPseudoElementArgs, |
|
514 nsCSSPseudoElements::Type* aPseudoElementType); |
|
515 |
|
516 nsSelectorParsingStatus ParseAttributeSelector(int32_t& aDataMask, |
|
517 nsCSSSelector& aSelector); |
|
518 |
|
519 nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask, |
|
520 nsCSSSelector& aSelector, |
|
521 bool aIsNegated); |
|
522 |
|
523 nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector, |
|
524 nsCSSPseudoClasses::Type aType); |
|
525 |
|
526 nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector, |
|
527 nsCSSPseudoClasses::Type aType); |
|
528 |
|
529 nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, |
|
530 nsCSSPseudoClasses::Type aType); |
|
531 |
|
532 nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask, |
|
533 nsCSSSelector& aSelector); |
|
534 |
|
535 // If aStopChar is non-zero, the selector list is done when we hit |
|
536 // aStopChar. Otherwise, it's done when we hit EOF. |
|
537 bool ParseSelectorList(nsCSSSelectorList*& aListHead, |
|
538 char16_t aStopChar); |
|
539 bool ParseSelectorGroup(nsCSSSelectorList*& aListHead); |
|
540 bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator); |
|
541 |
|
542 enum { |
|
543 eParseDeclaration_InBraces = 1 << 0, |
|
544 eParseDeclaration_AllowImportant = 1 << 1 |
|
545 }; |
|
546 enum nsCSSContextType { |
|
547 eCSSContext_General, |
|
548 eCSSContext_Page |
|
549 }; |
|
550 |
|
551 css::Declaration* ParseDeclarationBlock(uint32_t aFlags, |
|
552 nsCSSContextType aContext = eCSSContext_General); |
|
553 bool ParseDeclaration(css::Declaration* aDeclaration, |
|
554 uint32_t aFlags, |
|
555 bool aMustCallValueAppended, |
|
556 bool* aChanged, |
|
557 nsCSSContextType aContext = eCSSContext_General); |
|
558 |
|
559 bool ParseProperty(nsCSSProperty aPropID); |
|
560 bool ParsePropertyByFunction(nsCSSProperty aPropID); |
|
561 bool ParseSingleValueProperty(nsCSSValue& aValue, |
|
562 nsCSSProperty aPropID); |
|
563 |
|
564 enum PriorityParsingStatus { |
|
565 ePriority_None, |
|
566 ePriority_Important, |
|
567 ePriority_Error |
|
568 }; |
|
569 PriorityParsingStatus ParsePriority(); |
|
570 |
|
571 #ifdef MOZ_XUL |
|
572 bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs); |
|
573 #endif |
|
574 |
|
575 void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties); |
|
576 |
|
577 // Property specific parsing routines |
|
578 bool ParseBackground(); |
|
579 |
|
580 struct BackgroundParseState { |
|
581 nsCSSValue& mColor; |
|
582 nsCSSValueList* mImage; |
|
583 nsCSSValuePairList* mRepeat; |
|
584 nsCSSValueList* mAttachment; |
|
585 nsCSSValueList* mClip; |
|
586 nsCSSValueList* mOrigin; |
|
587 nsCSSValueList* mPosition; |
|
588 nsCSSValuePairList* mSize; |
|
589 BackgroundParseState( |
|
590 nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat, |
|
591 nsCSSValueList* aAttachment, nsCSSValueList* aClip, |
|
592 nsCSSValueList* aOrigin, nsCSSValueList* aPosition, |
|
593 nsCSSValuePairList* aSize) : |
|
594 mColor(aColor), mImage(aImage), mRepeat(aRepeat), |
|
595 mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin), |
|
596 mPosition(aPosition), mSize(aSize) {}; |
|
597 }; |
|
598 |
|
599 bool ParseBackgroundItem(BackgroundParseState& aState); |
|
600 |
|
601 bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id |
|
602 bool ParseBackgroundRepeat(); |
|
603 bool ParseBackgroundRepeatValues(nsCSSValuePair& aValue); |
|
604 bool ParseBackgroundPosition(); |
|
605 |
|
606 // ParseBoxPositionValues parses the CSS 2.1 background-position syntax, |
|
607 // which is still used by some properties. See ParseBackgroundPositionValues |
|
608 // for the css3-background syntax. |
|
609 bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit, |
|
610 bool aAllowExplicitCenter = true); // deprecated |
|
611 bool ParseBackgroundPositionValues(nsCSSValue& aOut, bool aAcceptsInherit); |
|
612 |
|
613 bool ParseBackgroundSize(); |
|
614 bool ParseBackgroundSizeValues(nsCSSValuePair& aOut); |
|
615 bool ParseBorderColor(); |
|
616 bool ParseBorderColors(nsCSSProperty aProperty); |
|
617 void SetBorderImageInitialValues(); |
|
618 bool ParseBorderImageRepeat(bool aAcceptsInherit); |
|
619 // If ParseBorderImageSlice returns false, aConsumedTokens indicates |
|
620 // whether or not any tokens were consumed (in other words, was the property |
|
621 // in error or just not present). If ParseBorderImageSlice returns true |
|
622 // aConsumedTokens is always true. |
|
623 bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens); |
|
624 bool ParseBorderImageWidth(bool aAcceptsInherit); |
|
625 bool ParseBorderImageOutset(bool aAcceptsInherit); |
|
626 bool ParseBorderImage(); |
|
627 bool ParseBorderSpacing(); |
|
628 bool ParseBorderSide(const nsCSSProperty aPropIDs[], |
|
629 bool aSetAllSides); |
|
630 bool ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[], |
|
631 int32_t aSourceType); |
|
632 bool ParseBorderStyle(); |
|
633 bool ParseBorderWidth(); |
|
634 |
|
635 bool ParseCalc(nsCSSValue &aValue, int32_t aVariantMask); |
|
636 bool ParseCalcAdditiveExpression(nsCSSValue& aValue, |
|
637 int32_t& aVariantMask); |
|
638 bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue, |
|
639 int32_t& aVariantMask, |
|
640 bool *aHadFinalWS); |
|
641 bool ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask); |
|
642 bool RequireWhitespace(); |
|
643 |
|
644 // For "flex" shorthand property, defined in CSS Flexbox spec |
|
645 bool ParseFlex(); |
|
646 // For "flex-flow" shorthand property, defined in CSS Flexbox spec |
|
647 bool ParseFlexFlow(); |
|
648 |
|
649 // CSS Grid |
|
650 bool ParseGridAutoFlow(); |
|
651 |
|
652 // Parse a <line-names> expression. |
|
653 // If successful, either leave aValue untouched, |
|
654 // to indicate that we parsed the empty list, |
|
655 // or set it to a eCSSUnit_List of eCSSUnit_Ident. |
|
656 // |
|
657 // To parse an optional <line-names> (ie. if not finding an open paren |
|
658 // is considered the same as an empty list), |
|
659 // treat CSSParseResult::NotFound the same as CSSParseResult::Ok. |
|
660 // |
|
661 // If aValue is already a eCSSUnit_List, append to that list. |
|
662 CSSParseResult ParseGridLineNames(nsCSSValue& aValue); |
|
663 bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr); |
|
664 bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue); |
|
665 bool ParseGridTrackBreadth(nsCSSValue& aValue); |
|
666 CSSParseResult ParseGridTrackSize(nsCSSValue& aValue); |
|
667 bool ParseGridAutoColumnsRows(nsCSSProperty aPropID); |
|
668 bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr); |
|
669 |
|
670 // Assuming a [ <line-names>? ] has already been parsed, |
|
671 // parse the rest of a <track-list>. |
|
672 // |
|
673 // This exists because [ <line-names>? ] is ambiguous in the |
|
674 // 'grid-template' shorthand: it can be either the start of a <track-list>, |
|
675 // or of the intertwined syntax that sets both |
|
676 // grid-template-rows and grid-template-areas. |
|
677 // |
|
678 // On success, |aValue| will be a list of odd length >= 3, |
|
679 // starting with a <line-names> (which is itself a list) |
|
680 // and alternating between that and <track-size>. |
|
681 bool ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue, |
|
682 const nsCSSValue& aFirstLineNames); |
|
683 bool ParseGridTemplateColumnsRows(nsCSSProperty aPropID); |
|
684 |
|
685 // |aAreaIndices| is a lookup table to help us parse faster, |
|
686 // mapping area names to indices in |aResult.mNamedAreas|. |
|
687 bool ParseGridTemplateAreasLine(const nsAutoString& aInput, |
|
688 css::GridTemplateAreasValue* aResult, |
|
689 nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices); |
|
690 bool ParseGridTemplateAreas(); |
|
691 bool ParseGridTemplate(); |
|
692 bool ParseGridTemplateAfterSlash(bool aColumnsIsTrackList); |
|
693 bool ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames); |
|
694 bool ParseGrid(); |
|
695 bool ParseGridShorthandAutoProps(); |
|
696 bool ParseGridLine(nsCSSValue& aValue); |
|
697 bool ParseGridAutoPosition(); |
|
698 bool ParseGridColumnRowStartEnd(nsCSSProperty aPropID); |
|
699 bool ParseGridColumnRow(nsCSSProperty aStartPropID, |
|
700 nsCSSProperty aEndPropID); |
|
701 bool ParseGridArea(); |
|
702 |
|
703 // for 'clip' and '-moz-image-region' |
|
704 bool ParseRect(nsCSSProperty aPropID); |
|
705 bool ParseColumns(); |
|
706 bool ParseContent(); |
|
707 bool ParseCounterData(nsCSSProperty aPropID); |
|
708 bool ParseCursor(); |
|
709 bool ParseFont(); |
|
710 bool ParseFontSynthesis(nsCSSValue& aValue); |
|
711 bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue); |
|
712 bool ParseFontVariantAlternates(nsCSSValue& aValue); |
|
713 bool ParseBitmaskValues(nsCSSValue& aValue, |
|
714 const KTableValue aKeywordTable[], |
|
715 const int32_t aMasks[]); |
|
716 bool ParseFontVariantEastAsian(nsCSSValue& aValue); |
|
717 bool ParseFontVariantLigatures(nsCSSValue& aValue); |
|
718 bool ParseFontVariantNumeric(nsCSSValue& aValue); |
|
719 bool ParseFontWeight(nsCSSValue& aValue); |
|
720 bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword); |
|
721 bool ParseFamily(nsCSSValue& aValue); |
|
722 bool ParseFontFeatureSettings(nsCSSValue& aValue); |
|
723 bool ParseFontSrc(nsCSSValue& aValue); |
|
724 bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values); |
|
725 bool ParseFontRanges(nsCSSValue& aValue); |
|
726 bool ParseListStyle(); |
|
727 bool ParseMargin(); |
|
728 bool ParseMarks(nsCSSValue& aValue); |
|
729 bool ParseTransform(bool aIsPrefixed); |
|
730 bool ParseOutline(); |
|
731 bool ParseOverflow(); |
|
732 bool ParsePadding(); |
|
733 bool ParseQuotes(); |
|
734 bool ParseSize(); |
|
735 bool ParseTextAlign(nsCSSValue& aValue, |
|
736 const KTableValue aTable[]); |
|
737 bool ParseTextAlign(nsCSSValue& aValue); |
|
738 bool ParseTextAlignLast(nsCSSValue& aValue); |
|
739 bool ParseTextDecoration(); |
|
740 bool ParseTextDecorationLine(nsCSSValue& aValue); |
|
741 bool ParseTextCombineUpright(nsCSSValue& aValue); |
|
742 bool ParseTextOverflow(nsCSSValue& aValue); |
|
743 bool ParseTouchAction(nsCSSValue& aValue); |
|
744 |
|
745 bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow); |
|
746 bool ParseShadowList(nsCSSProperty aProperty); |
|
747 bool ParseTransitionProperty(); |
|
748 bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue); |
|
749 bool ParseTransitionTimingFunctionValueComponent(float& aComponent, |
|
750 char aStop, |
|
751 bool aCheckRange); |
|
752 bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue); |
|
753 enum ParseAnimationOrTransitionShorthandResult { |
|
754 eParseAnimationOrTransitionShorthand_Values, |
|
755 eParseAnimationOrTransitionShorthand_Inherit, |
|
756 eParseAnimationOrTransitionShorthand_Error |
|
757 }; |
|
758 ParseAnimationOrTransitionShorthandResult |
|
759 ParseAnimationOrTransitionShorthand(const nsCSSProperty* aProperties, |
|
760 const nsCSSValue* aInitialValues, |
|
761 nsCSSValue* aValues, |
|
762 size_t aNumProperties); |
|
763 bool ParseTransition(); |
|
764 bool ParseAnimation(); |
|
765 bool ParseWillChange(); |
|
766 |
|
767 bool ParsePaint(nsCSSProperty aPropID); |
|
768 bool ParseDasharray(); |
|
769 bool ParseMarker(); |
|
770 bool ParsePaintOrder(); |
|
771 bool ParseAll(); |
|
772 |
|
773 /** |
|
774 * Parses a variable value from a custom property declaration. |
|
775 * |
|
776 * @param aType Out parameter into which will be stored the type of variable |
|
777 * value, indicating whether the parsed value was a token stream or one of |
|
778 * the CSS-wide keywords. |
|
779 * @param aValue Out parameter into which will be stored the token stream |
|
780 * as a string, if the parsed custom property did take a token stream. |
|
781 * @return Whether parsing succeeded. |
|
782 */ |
|
783 bool ParseVariableDeclaration(CSSVariableDeclarations::Type* aType, |
|
784 nsString& aValue); |
|
785 |
|
786 /** |
|
787 * Parses a CSS variable value. This could be 'initial', 'inherit', 'unset' |
|
788 * or a token stream, which may or may not include variable references. |
|
789 * |
|
790 * @param aType Out parameter into which the type of the variable value |
|
791 * will be stored. |
|
792 * @param aDropBackslash Out parameter indicating whether during variable |
|
793 * value parsing there was a trailing backslash before EOF that must |
|
794 * be dropped when serializing the variable value. |
|
795 * @param aImpliedCharacters Out parameter appended to which will be any |
|
796 * characters that were implied when encountering EOF and which |
|
797 * must be included at the end of the serialized variable value. |
|
798 * @param aFunc A callback function to invoke when a variable reference |
|
799 * is encountered. May be null. Arguments are the variable name |
|
800 * and the aData argument passed in to this function. |
|
801 * @param User data to pass in to the callback. |
|
802 * @return Whether parsing succeeded. |
|
803 */ |
|
804 bool ParseValueWithVariables(CSSVariableDeclarations::Type* aType, |
|
805 bool* aDropBackslash, |
|
806 nsString& aImpliedCharacters, |
|
807 void (*aFunc)(const nsAString&, void*), |
|
808 void* aData); |
|
809 |
|
810 /** |
|
811 * Returns whether the scanner dropped a backslash just before EOF. |
|
812 */ |
|
813 bool BackslashDropped(); |
|
814 |
|
815 /** |
|
816 * Calls AppendImpliedEOFCharacters on mScanner. |
|
817 */ |
|
818 void AppendImpliedEOFCharacters(nsAString& aResult); |
|
819 |
|
820 // Reused utility parsing routines |
|
821 void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue); |
|
822 bool ParseBoxProperties(const nsCSSProperty aPropIDs[]); |
|
823 bool ParseGroupedBoxProperty(int32_t aVariantMask, |
|
824 nsCSSValue& aValue); |
|
825 bool ParseDirectionalBoxProperty(nsCSSProperty aProperty, |
|
826 int32_t aSourceType); |
|
827 bool ParseBoxCornerRadius(const nsCSSProperty aPropID); |
|
828 bool ParseBoxCornerRadii(const nsCSSProperty aPropIDs[]); |
|
829 int32_t ParseChoice(nsCSSValue aValues[], |
|
830 const nsCSSProperty aPropIDs[], int32_t aNumIDs); |
|
831 bool ParseColor(nsCSSValue& aValue); |
|
832 bool ParseNumberColorComponent(uint8_t& aComponent, char aStop); |
|
833 bool ParsePercentageColorComponent(float& aComponent, char aStop); |
|
834 // ParseHSLColor parses everything starting with the opening '(' |
|
835 // up through and including the aStop char. |
|
836 bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness, |
|
837 char aStop); |
|
838 // ParseColorOpacity will enforce that the color ends with a ')' |
|
839 // after the opacity |
|
840 bool ParseColorOpacity(uint8_t& aOpacity); |
|
841 bool ParseColorOpacity(float& aOpacity); |
|
842 bool ParseEnum(nsCSSValue& aValue, |
|
843 const KTableValue aKeywordTable[]); |
|
844 bool ParseVariant(nsCSSValue& aValue, |
|
845 int32_t aVariantMask, |
|
846 const KTableValue aKeywordTable[]); |
|
847 bool ParseNonNegativeVariant(nsCSSValue& aValue, |
|
848 int32_t aVariantMask, |
|
849 const KTableValue aKeywordTable[]); |
|
850 bool ParseOneOrLargerVariant(nsCSSValue& aValue, |
|
851 int32_t aVariantMask, |
|
852 const KTableValue aKeywordTable[]); |
|
853 |
|
854 // http://dev.w3.org/csswg/css-values/#custom-idents |
|
855 // Parse an identifier that is none of: |
|
856 // * a CSS-wide keyword |
|
857 // * "default" |
|
858 // * a keyword in |aExcludedKeywords| |
|
859 // * a keyword in |aPropertyKTable| |
|
860 // |
|
861 // |aExcludedKeywords| is an array of nsCSSKeyword |
|
862 // that ends with a eCSSKeyword_UNKNOWN marker. |
|
863 // |
|
864 // |aPropertyKTable| can be used if some of the keywords to exclude |
|
865 // also appear in an existing nsCSSProps::KTableValue, |
|
866 // to avoid duplicating them. |
|
867 bool ParseCustomIdent(nsCSSValue& aValue, |
|
868 const nsAutoString& aIdentValue, |
|
869 const nsCSSKeyword aExcludedKeywords[] = nullptr, |
|
870 const nsCSSProps::KTableValue aPropertyKTable[] = nullptr); |
|
871 bool ParseCounter(nsCSSValue& aValue); |
|
872 bool ParseAttr(nsCSSValue& aValue); |
|
873 bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL); |
|
874 bool TranslateDimension(nsCSSValue& aValue, int32_t aVariantMask, |
|
875 float aNumber, const nsString& aUnit); |
|
876 bool ParseImageOrientation(nsCSSValue& aAngle); |
|
877 bool ParseImageRect(nsCSSValue& aImage); |
|
878 bool ParseElement(nsCSSValue& aValue); |
|
879 bool ParseColorStop(nsCSSValueGradient* aGradient); |
|
880 bool ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating, |
|
881 bool aIsLegacy); |
|
882 bool ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating, |
|
883 bool aIsLegacy); |
|
884 bool IsLegacyGradientLine(const nsCSSTokenType& aType, |
|
885 const nsString& aId); |
|
886 bool ParseGradientColorStops(nsCSSValueGradient* aGradient, |
|
887 nsCSSValue& aValue); |
|
888 |
|
889 void SetParsingCompoundProperty(bool aBool) { |
|
890 mParsingCompoundProperty = aBool; |
|
891 } |
|
892 bool IsParsingCompoundProperty(void) const { |
|
893 return mParsingCompoundProperty; |
|
894 } |
|
895 |
|
896 /* Functions for transform Parsing */ |
|
897 bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue); |
|
898 bool ParseFunction(nsCSSKeyword aFunction, const int32_t aAllowedTypes[], |
|
899 int32_t aVariantMaskAll, uint16_t aMinElems, |
|
900 uint16_t aMaxElems, nsCSSValue &aValue); |
|
901 bool ParseFunctionInternals(const int32_t aVariantMask[], |
|
902 int32_t aVariantMaskAll, |
|
903 uint16_t aMinElems, |
|
904 uint16_t aMaxElems, |
|
905 InfallibleTArray<nsCSSValue>& aOutput); |
|
906 |
|
907 /* Functions for transform-origin/perspective-origin Parsing */ |
|
908 bool ParseTransformOrigin(bool aPerspective); |
|
909 |
|
910 /* Functions for filter parsing */ |
|
911 bool ParseFilter(); |
|
912 bool ParseSingleFilter(nsCSSValue* aValue); |
|
913 bool ParseDropShadow(nsCSSValue* aValue); |
|
914 |
|
915 /* Find and return the namespace ID associated with aPrefix. |
|
916 If aPrefix has not been declared in an @namespace rule, returns |
|
917 kNameSpaceID_Unknown. */ |
|
918 int32_t GetNamespaceIdForPrefix(const nsString& aPrefix); |
|
919 |
|
920 /* Find the correct default namespace, and set it on aSelector. */ |
|
921 void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector); |
|
922 |
|
923 // Current token. The value is valid after calling GetToken and invalidated |
|
924 // by UngetToken. |
|
925 nsCSSToken mToken; |
|
926 |
|
927 // Our scanner. |
|
928 nsCSSScanner* mScanner; |
|
929 |
|
930 // Our error reporter. |
|
931 css::ErrorReporter* mReporter; |
|
932 |
|
933 // The URI to be used as a base for relative URIs. |
|
934 nsCOMPtr<nsIURI> mBaseURI; |
|
935 |
|
936 // The URI to be used as an HTTP "Referer" and for error reporting. |
|
937 nsCOMPtr<nsIURI> mSheetURI; |
|
938 |
|
939 // The principal of the sheet involved |
|
940 nsCOMPtr<nsIPrincipal> mSheetPrincipal; |
|
941 |
|
942 // The sheet we're parsing into |
|
943 nsRefPtr<nsCSSStyleSheet> mSheet; |
|
944 |
|
945 // Used for @import rules |
|
946 mozilla::css::Loader* mChildLoader; // not ref counted, it owns us |
|
947 |
|
948 // Sheet section we're in. This is used to enforce correct ordering of the |
|
949 // various rule types (eg the fact that a @charset rule must come before |
|
950 // anything else). Note that there are checks of similar things in various |
|
951 // places in nsCSSStyleSheet.cpp (e.g in insertRule, RebuildChildList). |
|
952 enum nsCSSSection { |
|
953 eCSSSection_Charset, |
|
954 eCSSSection_Import, |
|
955 eCSSSection_NameSpace, |
|
956 eCSSSection_General |
|
957 }; |
|
958 nsCSSSection mSection; |
|
959 |
|
960 nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it |
|
961 |
|
962 // After an UngetToken is done this flag is true. The next call to |
|
963 // GetToken clears the flag. |
|
964 bool mHavePushBack : 1; |
|
965 |
|
966 // True if we are in quirks mode; false in standards or almost standards mode |
|
967 bool mNavQuirkMode : 1; |
|
968 |
|
969 // True when the hashless color quirk applies. |
|
970 bool mHashlessColorQuirk : 1; |
|
971 |
|
972 // True when the unitless length quirk applies. |
|
973 bool mUnitlessLengthQuirk : 1; |
|
974 |
|
975 // True if unsafe rules should be allowed |
|
976 bool mUnsafeRulesEnabled : 1; |
|
977 |
|
978 // True if we are in parsing rules for Chrome or Certified App content, |
|
979 // in which case CSS properties with the |
|
980 // CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP |
|
981 // flag should be allowed. |
|
982 bool mIsChromeOrCertifiedApp : 1; |
|
983 |
|
984 // True if viewport units should be allowed. |
|
985 bool mViewportUnitsEnabled : 1; |
|
986 |
|
987 // True for parsing media lists for HTML attributes, where we have to |
|
988 // ignore CSS comments. |
|
989 bool mHTMLMediaMode : 1; |
|
990 |
|
991 // This flag is set when parsing a non-box shorthand; it's used to not apply |
|
992 // some quirks during shorthand parsing |
|
993 bool mParsingCompoundProperty : 1; |
|
994 |
|
995 // True if we are in the middle of parsing an @supports condition. |
|
996 // This is used to avoid recording the input stream when variable references |
|
997 // are encountered in a property declaration in the @supports condition. |
|
998 bool mInSupportsCondition : 1; |
|
999 |
|
1000 // True if we are somewhere within a @supports rule whose condition is |
|
1001 // false. |
|
1002 bool mInFailingSupportsRule : 1; |
|
1003 |
|
1004 // True if we will suppress all parse errors (except unexpected EOFs). |
|
1005 // This is used to prevent errors for declarations inside a failing |
|
1006 // @supports rule. |
|
1007 bool mSuppressErrors : 1; |
|
1008 |
|
1009 // Stack of rule groups; used for @media and such. |
|
1010 InfallibleTArray<nsRefPtr<css::GroupRule> > mGroupStack; |
|
1011 |
|
1012 // During the parsing of a property (which may be a shorthand), the data |
|
1013 // are stored in |mTempData|. (It is needed to ensure that parser |
|
1014 // errors cause the data to be ignored, and to ensure that a |
|
1015 // non-'!important' declaration does not override an '!important' |
|
1016 // one.) |
|
1017 nsCSSExpandedDataBlock mTempData; |
|
1018 |
|
1019 // All data from successfully parsed properties are placed into |mData|. |
|
1020 nsCSSExpandedDataBlock mData; |
|
1021 |
|
1022 public: |
|
1023 // Used from nsCSSParser constructors and destructors |
|
1024 CSSParserImpl* mNextFree; |
|
1025 }; |
|
1026 |
|
1027 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer) |
|
1028 { |
|
1029 css::Rule **pointer = static_cast<css::Rule**>(aPointer); |
|
1030 NS_ADDREF(*pointer = aRule); |
|
1031 } |
|
1032 |
|
1033 static void AppendRuleToSheet(css::Rule* aRule, void* aParser) |
|
1034 { |
|
1035 CSSParserImpl* parser = (CSSParserImpl*) aParser; |
|
1036 parser->AppendRule(aRule); |
|
1037 } |
|
1038 |
|
1039 #define REPORT_UNEXPECTED(msg_) \ |
|
1040 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_); } |
|
1041 |
|
1042 #define REPORT_UNEXPECTED_P(msg_, param_) \ |
|
1043 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_); } |
|
1044 |
|
1045 #define REPORT_UNEXPECTED_TOKEN(msg_) \ |
|
1046 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken); } |
|
1047 |
|
1048 #define REPORT_UNEXPECTED_TOKEN_CHAR(msg_, ch_) \ |
|
1049 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken, ch_); } |
|
1050 |
|
1051 #define REPORT_UNEXPECTED_EOF(lf_) \ |
|
1052 mReporter->ReportUnexpectedEOF(#lf_) |
|
1053 |
|
1054 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \ |
|
1055 mReporter->ReportUnexpectedEOF(ch_) |
|
1056 |
|
1057 #define OUTPUT_ERROR() \ |
|
1058 mReporter->OutputError() |
|
1059 |
|
1060 #define OUTPUT_ERROR_WITH_POSITION(linenum_, lineoff_) \ |
|
1061 mReporter->OutputError(linenum_, lineoff_) |
|
1062 |
|
1063 #define CLEAR_ERROR() \ |
|
1064 mReporter->ClearError() |
|
1065 |
|
1066 CSSParserImpl::CSSParserImpl() |
|
1067 : mToken(), |
|
1068 mScanner(nullptr), |
|
1069 mReporter(nullptr), |
|
1070 mChildLoader(nullptr), |
|
1071 mSection(eCSSSection_Charset), |
|
1072 mNameSpaceMap(nullptr), |
|
1073 mHavePushBack(false), |
|
1074 mNavQuirkMode(false), |
|
1075 mHashlessColorQuirk(false), |
|
1076 mUnitlessLengthQuirk(false), |
|
1077 mUnsafeRulesEnabled(false), |
|
1078 mIsChromeOrCertifiedApp(false), |
|
1079 mViewportUnitsEnabled(true), |
|
1080 mHTMLMediaMode(false), |
|
1081 mParsingCompoundProperty(false), |
|
1082 mInSupportsCondition(false), |
|
1083 mInFailingSupportsRule(false), |
|
1084 mSuppressErrors(false), |
|
1085 mNextFree(nullptr) |
|
1086 { |
|
1087 } |
|
1088 |
|
1089 CSSParserImpl::~CSSParserImpl() |
|
1090 { |
|
1091 mData.AssertInitialState(); |
|
1092 mTempData.AssertInitialState(); |
|
1093 } |
|
1094 |
|
1095 nsresult |
|
1096 CSSParserImpl::SetStyleSheet(nsCSSStyleSheet* aSheet) |
|
1097 { |
|
1098 if (aSheet != mSheet) { |
|
1099 // Switch to using the new sheet, if any |
|
1100 mGroupStack.Clear(); |
|
1101 mSheet = aSheet; |
|
1102 if (mSheet) { |
|
1103 mNameSpaceMap = mSheet->GetNameSpaceMap(); |
|
1104 } else { |
|
1105 mNameSpaceMap = nullptr; |
|
1106 } |
|
1107 } else if (mSheet) { |
|
1108 mNameSpaceMap = mSheet->GetNameSpaceMap(); |
|
1109 } |
|
1110 |
|
1111 return NS_OK; |
|
1112 } |
|
1113 |
|
1114 nsresult |
|
1115 CSSParserImpl::SetQuirkMode(bool aQuirkMode) |
|
1116 { |
|
1117 mNavQuirkMode = aQuirkMode; |
|
1118 return NS_OK; |
|
1119 } |
|
1120 |
|
1121 nsresult |
|
1122 CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader) |
|
1123 { |
|
1124 mChildLoader = aChildLoader; // not ref counted, it owns us |
|
1125 return NS_OK; |
|
1126 } |
|
1127 |
|
1128 void |
|
1129 CSSParserImpl::Reset() |
|
1130 { |
|
1131 NS_ASSERTION(!mScanner, "resetting with scanner active"); |
|
1132 SetStyleSheet(nullptr); |
|
1133 SetQuirkMode(false); |
|
1134 SetChildLoader(nullptr); |
|
1135 } |
|
1136 |
|
1137 void |
|
1138 CSSParserImpl::InitScanner(nsCSSScanner& aScanner, |
|
1139 css::ErrorReporter& aReporter, |
|
1140 nsIURI* aSheetURI, nsIURI* aBaseURI, |
|
1141 nsIPrincipal* aSheetPrincipal) |
|
1142 { |
|
1143 NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state"); |
|
1144 NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state"); |
|
1145 NS_PRECONDITION(!mScanner, "already have scanner"); |
|
1146 |
|
1147 mScanner = &aScanner; |
|
1148 mReporter = &aReporter; |
|
1149 mScanner->SetErrorReporter(mReporter); |
|
1150 |
|
1151 mBaseURI = aBaseURI; |
|
1152 mSheetURI = aSheetURI; |
|
1153 mSheetPrincipal = aSheetPrincipal; |
|
1154 mHavePushBack = false; |
|
1155 } |
|
1156 |
|
1157 void |
|
1158 CSSParserImpl::ReleaseScanner() |
|
1159 { |
|
1160 mScanner = nullptr; |
|
1161 mReporter = nullptr; |
|
1162 mBaseURI = nullptr; |
|
1163 mSheetURI = nullptr; |
|
1164 mSheetPrincipal = nullptr; |
|
1165 } |
|
1166 |
|
1167 nsresult |
|
1168 CSSParserImpl::ParseSheet(const nsAString& aInput, |
|
1169 nsIURI* aSheetURI, |
|
1170 nsIURI* aBaseURI, |
|
1171 nsIPrincipal* aSheetPrincipal, |
|
1172 uint32_t aLineNumber, |
|
1173 bool aAllowUnsafeRules) |
|
1174 { |
|
1175 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); |
|
1176 NS_PRECONDITION(aBaseURI, "need base URI"); |
|
1177 NS_PRECONDITION(aSheetURI, "need sheet URI"); |
|
1178 NS_PRECONDITION(mSheet, "Must have sheet to parse into"); |
|
1179 NS_ENSURE_STATE(mSheet); |
|
1180 |
|
1181 #ifdef DEBUG |
|
1182 nsIURI* uri = mSheet->GetSheetURI(); |
|
1183 bool equal; |
|
1184 NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal, |
|
1185 "Sheet URI does not match passed URI"); |
|
1186 NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal, |
|
1187 &equal)) && |
|
1188 equal, |
|
1189 "Sheet principal does not match passed principal"); |
|
1190 #endif |
|
1191 |
|
1192 nsCSSScanner scanner(aInput, aLineNumber); |
|
1193 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI); |
|
1194 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal); |
|
1195 |
|
1196 int32_t ruleCount = mSheet->StyleRuleCount(); |
|
1197 if (0 < ruleCount) { |
|
1198 const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1); |
|
1199 if (lastRule) { |
|
1200 switch (lastRule->GetType()) { |
|
1201 case css::Rule::CHARSET_RULE: |
|
1202 case css::Rule::IMPORT_RULE: |
|
1203 mSection = eCSSSection_Import; |
|
1204 break; |
|
1205 case css::Rule::NAMESPACE_RULE: |
|
1206 mSection = eCSSSection_NameSpace; |
|
1207 break; |
|
1208 default: |
|
1209 mSection = eCSSSection_General; |
|
1210 break; |
|
1211 } |
|
1212 } |
|
1213 } |
|
1214 else { |
|
1215 mSection = eCSSSection_Charset; // sheet is empty, any rules are fair |
|
1216 } |
|
1217 |
|
1218 mUnsafeRulesEnabled = aAllowUnsafeRules; |
|
1219 mIsChromeOrCertifiedApp = |
|
1220 dom::IsChromeURI(aSheetURI) || |
|
1221 aSheetPrincipal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED; |
|
1222 |
|
1223 nsCSSToken* tk = &mToken; |
|
1224 for (;;) { |
|
1225 // Get next non-whitespace token |
|
1226 if (!GetToken(true)) { |
|
1227 OUTPUT_ERROR(); |
|
1228 break; |
|
1229 } |
|
1230 if (eCSSToken_HTMLComment == tk->mType) { |
|
1231 continue; // legal here only |
|
1232 } |
|
1233 if (eCSSToken_AtKeyword == tk->mType) { |
|
1234 ParseAtRule(AppendRuleToSheet, this, false); |
|
1235 continue; |
|
1236 } |
|
1237 UngetToken(); |
|
1238 if (ParseRuleSet(AppendRuleToSheet, this)) { |
|
1239 mSection = eCSSSection_General; |
|
1240 } |
|
1241 } |
|
1242 ReleaseScanner(); |
|
1243 |
|
1244 mUnsafeRulesEnabled = false; |
|
1245 mIsChromeOrCertifiedApp = false; |
|
1246 |
|
1247 // XXX check for low level errors |
|
1248 return NS_OK; |
|
1249 } |
|
1250 |
|
1251 /** |
|
1252 * Determines whether the identifier contained in the given string is a |
|
1253 * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1. |
|
1254 */ |
|
1255 static bool |
|
1256 NonMozillaVendorIdentifier(const nsAString& ident) |
|
1257 { |
|
1258 return (ident.First() == char16_t('-') && |
|
1259 !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) || |
|
1260 ident.First() == char16_t('_'); |
|
1261 |
|
1262 } |
|
1263 |
|
1264 nsresult |
|
1265 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue, |
|
1266 nsIURI* aDocURI, |
|
1267 nsIURI* aBaseURI, |
|
1268 nsIPrincipal* aNodePrincipal, |
|
1269 css::StyleRule** aResult) |
|
1270 { |
|
1271 NS_PRECONDITION(aNodePrincipal, "Must have principal here!"); |
|
1272 NS_PRECONDITION(aBaseURI, "need base URI"); |
|
1273 |
|
1274 // XXX line number? |
|
1275 nsCSSScanner scanner(aAttributeValue, 0); |
|
1276 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI); |
|
1277 InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal); |
|
1278 |
|
1279 mSection = eCSSSection_General; |
|
1280 |
|
1281 uint32_t parseFlags = eParseDeclaration_AllowImportant; |
|
1282 |
|
1283 css::Declaration* declaration = ParseDeclarationBlock(parseFlags); |
|
1284 if (declaration) { |
|
1285 // Create a style rule for the declaration |
|
1286 NS_ADDREF(*aResult = new css::StyleRule(nullptr, declaration)); |
|
1287 } else { |
|
1288 *aResult = nullptr; |
|
1289 } |
|
1290 |
|
1291 ReleaseScanner(); |
|
1292 |
|
1293 // XXX check for low level errors |
|
1294 return NS_OK; |
|
1295 } |
|
1296 |
|
1297 nsresult |
|
1298 CSSParserImpl::ParseDeclarations(const nsAString& aBuffer, |
|
1299 nsIURI* aSheetURI, |
|
1300 nsIURI* aBaseURI, |
|
1301 nsIPrincipal* aSheetPrincipal, |
|
1302 css::Declaration* aDeclaration, |
|
1303 bool* aChanged) |
|
1304 { |
|
1305 *aChanged = false; |
|
1306 |
|
1307 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); |
|
1308 |
|
1309 nsCSSScanner scanner(aBuffer, 0); |
|
1310 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI); |
|
1311 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal); |
|
1312 |
|
1313 mSection = eCSSSection_General; |
|
1314 |
|
1315 mData.AssertInitialState(); |
|
1316 aDeclaration->ClearData(); |
|
1317 // We could check if it was already empty, but... |
|
1318 *aChanged = true; |
|
1319 |
|
1320 for (;;) { |
|
1321 // If we cleared the old decl, then we want to be calling |
|
1322 // ValueAppended as we parse. |
|
1323 if (!ParseDeclaration(aDeclaration, eParseDeclaration_AllowImportant, |
|
1324 true, aChanged)) { |
|
1325 if (!SkipDeclaration(false)) { |
|
1326 break; |
|
1327 } |
|
1328 } |
|
1329 } |
|
1330 |
|
1331 aDeclaration->CompressFrom(&mData); |
|
1332 ReleaseScanner(); |
|
1333 return NS_OK; |
|
1334 } |
|
1335 |
|
1336 nsresult |
|
1337 CSSParserImpl::ParseRule(const nsAString& aRule, |
|
1338 nsIURI* aSheetURI, |
|
1339 nsIURI* aBaseURI, |
|
1340 nsIPrincipal* aSheetPrincipal, |
|
1341 css::Rule** aResult) |
|
1342 { |
|
1343 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); |
|
1344 NS_PRECONDITION(aBaseURI, "need base URI"); |
|
1345 |
|
1346 *aResult = nullptr; |
|
1347 |
|
1348 nsCSSScanner scanner(aRule, 0); |
|
1349 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI); |
|
1350 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal); |
|
1351 |
|
1352 mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules. |
|
1353 |
|
1354 nsCSSToken* tk = &mToken; |
|
1355 // Get first non-whitespace token |
|
1356 nsresult rv = NS_OK; |
|
1357 if (!GetToken(true)) { |
|
1358 REPORT_UNEXPECTED(PEParseRuleWSOnly); |
|
1359 OUTPUT_ERROR(); |
|
1360 rv = NS_ERROR_DOM_SYNTAX_ERR; |
|
1361 } else { |
|
1362 if (eCSSToken_AtKeyword == tk->mType) { |
|
1363 // FIXME: perhaps aInsideBlock should be true when we are? |
|
1364 ParseAtRule(AssignRuleToPointer, aResult, false); |
|
1365 } else { |
|
1366 UngetToken(); |
|
1367 ParseRuleSet(AssignRuleToPointer, aResult); |
|
1368 } |
|
1369 |
|
1370 if (*aResult && GetToken(true)) { |
|
1371 // garbage after rule |
|
1372 REPORT_UNEXPECTED_TOKEN(PERuleTrailing); |
|
1373 NS_RELEASE(*aResult); |
|
1374 } |
|
1375 |
|
1376 if (!*aResult) { |
|
1377 rv = NS_ERROR_DOM_SYNTAX_ERR; |
|
1378 OUTPUT_ERROR(); |
|
1379 } |
|
1380 } |
|
1381 |
|
1382 ReleaseScanner(); |
|
1383 return rv; |
|
1384 } |
|
1385 |
|
1386 // See Bug 723197 |
|
1387 #ifdef _MSC_VER |
|
1388 #pragma optimize( "", off ) |
|
1389 #pragma warning( push ) |
|
1390 #pragma warning( disable : 4748 ) |
|
1391 #endif |
|
1392 |
|
1393 nsresult |
|
1394 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID, |
|
1395 const nsAString& aPropValue, |
|
1396 nsIURI* aSheetURI, |
|
1397 nsIURI* aBaseURI, |
|
1398 nsIPrincipal* aSheetPrincipal, |
|
1399 css::Declaration* aDeclaration, |
|
1400 bool* aChanged, |
|
1401 bool aIsImportant, |
|
1402 bool aIsSVGMode) |
|
1403 { |
|
1404 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); |
|
1405 NS_PRECONDITION(aBaseURI, "need base URI"); |
|
1406 NS_PRECONDITION(aDeclaration, "Need declaration to parse into!"); |
|
1407 |
|
1408 mData.AssertInitialState(); |
|
1409 mTempData.AssertInitialState(); |
|
1410 aDeclaration->AssertMutable(); |
|
1411 |
|
1412 nsCSSScanner scanner(aPropValue, 0); |
|
1413 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI); |
|
1414 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal); |
|
1415 mSection = eCSSSection_General; |
|
1416 scanner.SetSVGMode(aIsSVGMode); |
|
1417 |
|
1418 *aChanged = false; |
|
1419 |
|
1420 // Check for unknown or preffed off properties |
|
1421 if (eCSSProperty_UNKNOWN == aPropID || |
|
1422 !(nsCSSProps::IsEnabled(aPropID) || |
|
1423 (mUnsafeRulesEnabled && |
|
1424 nsCSSProps::PropHasFlags(aPropID, |
|
1425 CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS)))) { |
|
1426 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID)); |
|
1427 REPORT_UNEXPECTED_P(PEUnknownProperty, propName); |
|
1428 REPORT_UNEXPECTED(PEDeclDropped); |
|
1429 OUTPUT_ERROR(); |
|
1430 ReleaseScanner(); |
|
1431 return NS_OK; |
|
1432 } |
|
1433 |
|
1434 bool parsedOK = ParseProperty(aPropID); |
|
1435 // We should now be at EOF |
|
1436 if (parsedOK && GetToken(true)) { |
|
1437 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); |
|
1438 parsedOK = false; |
|
1439 } |
|
1440 |
|
1441 if (!parsedOK) { |
|
1442 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID)); |
|
1443 REPORT_UNEXPECTED_P(PEValueParsingError, propName); |
|
1444 REPORT_UNEXPECTED(PEDeclDropped); |
|
1445 OUTPUT_ERROR(); |
|
1446 mTempData.ClearProperty(aPropID); |
|
1447 } else { |
|
1448 |
|
1449 // We know we don't need to force a ValueAppended call for the new |
|
1450 // value. So if we are not processing a shorthand, and there's |
|
1451 // already a value for this property in the declaration at the |
|
1452 // same importance level, then we can just copy our parsed value |
|
1453 // directly into the declaration without going through the whole |
|
1454 // expand/compress thing. |
|
1455 if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData, |
|
1456 aChanged)) { |
|
1457 // Do it the slow way |
|
1458 aDeclaration->ExpandTo(&mData); |
|
1459 *aChanged = mData.TransferFromBlock(mTempData, aPropID, aIsImportant, |
|
1460 true, false, aDeclaration); |
|
1461 aDeclaration->CompressFrom(&mData); |
|
1462 } |
|
1463 CLEAR_ERROR(); |
|
1464 } |
|
1465 |
|
1466 mTempData.AssertInitialState(); |
|
1467 |
|
1468 ReleaseScanner(); |
|
1469 return NS_OK; |
|
1470 } |
|
1471 |
|
1472 nsresult |
|
1473 CSSParserImpl::ParseVariable(const nsAString& aVariableName, |
|
1474 const nsAString& aPropValue, |
|
1475 nsIURI* aSheetURI, |
|
1476 nsIURI* aBaseURI, |
|
1477 nsIPrincipal* aSheetPrincipal, |
|
1478 css::Declaration* aDeclaration, |
|
1479 bool* aChanged, |
|
1480 bool aIsImportant) |
|
1481 { |
|
1482 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); |
|
1483 NS_PRECONDITION(aBaseURI, "need base URI"); |
|
1484 NS_PRECONDITION(aDeclaration, "Need declaration to parse into!"); |
|
1485 NS_PRECONDITION(nsLayoutUtils::CSSVariablesEnabled(), |
|
1486 "expected Variables to be enabled"); |
|
1487 |
|
1488 mData.AssertInitialState(); |
|
1489 mTempData.AssertInitialState(); |
|
1490 aDeclaration->AssertMutable(); |
|
1491 |
|
1492 nsCSSScanner scanner(aPropValue, 0); |
|
1493 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI); |
|
1494 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal); |
|
1495 mSection = eCSSSection_General; |
|
1496 |
|
1497 *aChanged = false; |
|
1498 |
|
1499 CSSVariableDeclarations::Type variableType; |
|
1500 nsString variableValue; |
|
1501 |
|
1502 bool parsedOK = ParseVariableDeclaration(&variableType, variableValue); |
|
1503 |
|
1504 // We should now be at EOF |
|
1505 if (parsedOK && GetToken(true)) { |
|
1506 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); |
|
1507 parsedOK = false; |
|
1508 } |
|
1509 |
|
1510 if (!parsedOK) { |
|
1511 REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("--") + |
|
1512 aVariableName); |
|
1513 REPORT_UNEXPECTED(PEDeclDropped); |
|
1514 OUTPUT_ERROR(); |
|
1515 } else { |
|
1516 CLEAR_ERROR(); |
|
1517 aDeclaration->AddVariableDeclaration(aVariableName, variableType, |
|
1518 variableValue, aIsImportant, true); |
|
1519 *aChanged = true; |
|
1520 } |
|
1521 |
|
1522 mTempData.AssertInitialState(); |
|
1523 |
|
1524 ReleaseScanner(); |
|
1525 return NS_OK; |
|
1526 } |
|
1527 |
|
1528 #ifdef _MSC_VER |
|
1529 #pragma warning( pop ) |
|
1530 #pragma optimize( "", on ) |
|
1531 #endif |
|
1532 |
|
1533 void |
|
1534 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer, |
|
1535 nsIURI* aURI, // for error reporting |
|
1536 uint32_t aLineNumber, // for error reporting |
|
1537 nsMediaList* aMediaList, |
|
1538 bool aHTMLMode) |
|
1539 { |
|
1540 // XXX Are there cases where the caller wants to keep what it already |
|
1541 // has in case of parser error? If GatherMedia ever changes to return |
|
1542 // a value other than true, we probably should avoid modifying aMediaList. |
|
1543 aMediaList->Clear(); |
|
1544 |
|
1545 // fake base URI since media lists don't have URIs in them |
|
1546 nsCSSScanner scanner(aBuffer, aLineNumber); |
|
1547 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); |
|
1548 InitScanner(scanner, reporter, aURI, aURI, nullptr); |
|
1549 |
|
1550 mHTMLMediaMode = aHTMLMode; |
|
1551 |
|
1552 // XXXldb We need to make the scanner not skip CSS comments! (Or |
|
1553 // should we?) |
|
1554 |
|
1555 // For aHTMLMode, we used to follow the parsing rules in |
|
1556 // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors |
|
1557 // which wouldn't work for media queries since they remove all but the |
|
1558 // first word. However, they're changed in |
|
1559 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2 |
|
1560 // (as of 2008-05-29) which says that the media attribute just points |
|
1561 // to a media query. (The main substative difference is the relative |
|
1562 // precedence of commas and paretheses.) |
|
1563 |
|
1564 DebugOnly<bool> parsedOK = GatherMedia(aMediaList, false); |
|
1565 NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid " |
|
1566 "trashing aMediaList"); |
|
1567 |
|
1568 CLEAR_ERROR(); |
|
1569 ReleaseScanner(); |
|
1570 mHTMLMediaMode = false; |
|
1571 } |
|
1572 |
|
1573 bool |
|
1574 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer, |
|
1575 nsIURI* aURI, // for error reporting |
|
1576 uint32_t aLineNumber, // for error reporting |
|
1577 nsCSSValue& aValue) |
|
1578 { |
|
1579 nsCSSScanner scanner(aBuffer, aLineNumber); |
|
1580 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); |
|
1581 InitScanner(scanner, reporter, aURI, aURI, nullptr); |
|
1582 |
|
1583 // Parse a color, and check that there's nothing else after it. |
|
1584 bool colorParsed = ParseColor(aValue) && !GetToken(true); |
|
1585 OUTPUT_ERROR(); |
|
1586 ReleaseScanner(); |
|
1587 return colorParsed; |
|
1588 } |
|
1589 |
|
1590 nsresult |
|
1591 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString, |
|
1592 nsIURI* aURI, // for error reporting |
|
1593 uint32_t aLineNumber, // for error reporting |
|
1594 nsCSSSelectorList **aSelectorList) |
|
1595 { |
|
1596 nsCSSScanner scanner(aSelectorString, aLineNumber); |
|
1597 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); |
|
1598 InitScanner(scanner, reporter, aURI, aURI, nullptr); |
|
1599 |
|
1600 bool success = ParseSelectorList(*aSelectorList, char16_t(0)); |
|
1601 |
|
1602 // We deliberately do not call OUTPUT_ERROR here, because all our |
|
1603 // callers map a failure return to a JS exception, and if that JS |
|
1604 // exception is caught, people don't want to see parser diagnostics; |
|
1605 // see e.g. http://bugs.jquery.com/ticket/7535 |
|
1606 // It would be nice to be able to save the parser diagnostics into |
|
1607 // the exception, so that if it _isn't_ caught we can report them |
|
1608 // along with the usual uncaught-exception message, but we don't |
|
1609 // have any way to do that at present; see bug 631621. |
|
1610 CLEAR_ERROR(); |
|
1611 ReleaseScanner(); |
|
1612 |
|
1613 if (success) { |
|
1614 NS_ASSERTION(*aSelectorList, "Should have list!"); |
|
1615 return NS_OK; |
|
1616 } |
|
1617 |
|
1618 NS_ASSERTION(!*aSelectorList, "Shouldn't have list!"); |
|
1619 |
|
1620 return NS_ERROR_DOM_SYNTAX_ERR; |
|
1621 } |
|
1622 |
|
1623 |
|
1624 already_AddRefed<nsCSSKeyframeRule> |
|
1625 CSSParserImpl::ParseKeyframeRule(const nsSubstring& aBuffer, |
|
1626 nsIURI* aURI, |
|
1627 uint32_t aLineNumber) |
|
1628 { |
|
1629 nsCSSScanner scanner(aBuffer, aLineNumber); |
|
1630 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); |
|
1631 InitScanner(scanner, reporter, aURI, aURI, nullptr); |
|
1632 |
|
1633 nsRefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule(); |
|
1634 if (GetToken(true)) { |
|
1635 // extra garbage at the end |
|
1636 result = nullptr; |
|
1637 } |
|
1638 |
|
1639 OUTPUT_ERROR(); |
|
1640 ReleaseScanner(); |
|
1641 |
|
1642 return result.forget(); |
|
1643 } |
|
1644 |
|
1645 bool |
|
1646 CSSParserImpl::ParseKeyframeSelectorString(const nsSubstring& aSelectorString, |
|
1647 nsIURI* aURI, // for error reporting |
|
1648 uint32_t aLineNumber, // for error reporting |
|
1649 InfallibleTArray<float>& aSelectorList) |
|
1650 { |
|
1651 NS_ABORT_IF_FALSE(aSelectorList.IsEmpty(), "given list should start empty"); |
|
1652 |
|
1653 nsCSSScanner scanner(aSelectorString, aLineNumber); |
|
1654 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); |
|
1655 InitScanner(scanner, reporter, aURI, aURI, nullptr); |
|
1656 |
|
1657 bool success = ParseKeyframeSelectorList(aSelectorList) && |
|
1658 // must consume entire input string |
|
1659 !GetToken(true); |
|
1660 |
|
1661 OUTPUT_ERROR(); |
|
1662 ReleaseScanner(); |
|
1663 |
|
1664 if (success) { |
|
1665 NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty"); |
|
1666 } else { |
|
1667 aSelectorList.Clear(); |
|
1668 } |
|
1669 |
|
1670 return success; |
|
1671 } |
|
1672 |
|
1673 bool |
|
1674 CSSParserImpl::EvaluateSupportsDeclaration(const nsAString& aProperty, |
|
1675 const nsAString& aValue, |
|
1676 nsIURI* aDocURL, |
|
1677 nsIURI* aBaseURL, |
|
1678 nsIPrincipal* aDocPrincipal) |
|
1679 { |
|
1680 nsCSSProperty propID = LookupEnabledProperty(aProperty); |
|
1681 if (propID == eCSSProperty_UNKNOWN) { |
|
1682 return false; |
|
1683 } |
|
1684 |
|
1685 nsCSSScanner scanner(aValue, 0); |
|
1686 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL); |
|
1687 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal); |
|
1688 nsAutoSuppressErrors suppressErrors(this); |
|
1689 |
|
1690 bool parsedOK; |
|
1691 |
|
1692 if (propID == eCSSPropertyExtra_variable) { |
|
1693 MOZ_ASSERT(Substring(aProperty, 0, |
|
1694 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--")); |
|
1695 const nsDependentSubstring varName = |
|
1696 Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH); // remove '--' |
|
1697 CSSVariableDeclarations::Type variableType; |
|
1698 nsString variableValue; |
|
1699 parsedOK = ParseVariableDeclaration(&variableType, variableValue) && |
|
1700 !GetToken(true); |
|
1701 } else { |
|
1702 parsedOK = ParseProperty(propID) && !GetToken(true); |
|
1703 |
|
1704 mTempData.ClearProperty(propID); |
|
1705 mTempData.AssertInitialState(); |
|
1706 } |
|
1707 |
|
1708 CLEAR_ERROR(); |
|
1709 ReleaseScanner(); |
|
1710 |
|
1711 return parsedOK; |
|
1712 } |
|
1713 |
|
1714 bool |
|
1715 CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration, |
|
1716 nsIURI* aDocURL, |
|
1717 nsIURI* aBaseURL, |
|
1718 nsIPrincipal* aDocPrincipal) |
|
1719 { |
|
1720 nsCSSScanner scanner(aDeclaration, 0); |
|
1721 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL); |
|
1722 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal); |
|
1723 nsAutoSuppressErrors suppressErrors(this); |
|
1724 |
|
1725 bool conditionMet; |
|
1726 bool parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true); |
|
1727 |
|
1728 CLEAR_ERROR(); |
|
1729 ReleaseScanner(); |
|
1730 |
|
1731 return parsedOK && conditionMet; |
|
1732 } |
|
1733 |
|
1734 bool |
|
1735 CSSParserImpl::EnumerateVariableReferences(const nsAString& aPropertyValue, |
|
1736 VariableEnumFunc aFunc, |
|
1737 void* aData) |
|
1738 { |
|
1739 nsCSSScanner scanner(aPropertyValue, 0); |
|
1740 css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr); |
|
1741 InitScanner(scanner, reporter, nullptr, nullptr, nullptr); |
|
1742 nsAutoSuppressErrors suppressErrors(this); |
|
1743 |
|
1744 CSSVariableDeclarations::Type type; |
|
1745 bool dropBackslash; |
|
1746 nsString impliedCharacters; |
|
1747 bool result = ParseValueWithVariables(&type, &dropBackslash, |
|
1748 impliedCharacters, aFunc, aData) && |
|
1749 !GetToken(true); |
|
1750 |
|
1751 ReleaseScanner(); |
|
1752 |
|
1753 return result; |
|
1754 } |
|
1755 |
|
1756 static bool |
|
1757 SeparatorRequiredBetweenTokens(nsCSSTokenSerializationType aToken1, |
|
1758 nsCSSTokenSerializationType aToken2) |
|
1759 { |
|
1760 // The two lines marked with (*) do not correspond to entries in |
|
1761 // the table in the css-syntax spec but which we need to handle, |
|
1762 // as we treat them as whole tokens. |
|
1763 switch (aToken1) { |
|
1764 case eCSSTokenSerialization_Ident: |
|
1765 return aToken2 == eCSSTokenSerialization_Ident || |
|
1766 aToken2 == eCSSTokenSerialization_Function || |
|
1767 aToken2 == eCSSTokenSerialization_URL_or_BadURL || |
|
1768 aToken2 == eCSSTokenSerialization_Symbol_Minus || |
|
1769 aToken2 == eCSSTokenSerialization_Number || |
|
1770 aToken2 == eCSSTokenSerialization_Percentage || |
|
1771 aToken2 == eCSSTokenSerialization_Dimension || |
|
1772 aToken2 == eCSSTokenSerialization_URange || |
|
1773 aToken2 == eCSSTokenSerialization_CDC || |
|
1774 aToken2 == eCSSTokenSerialization_Symbol_OpenParen; |
|
1775 case eCSSTokenSerialization_AtKeyword_or_Hash: |
|
1776 case eCSSTokenSerialization_Dimension: |
|
1777 return aToken2 == eCSSTokenSerialization_Ident || |
|
1778 aToken2 == eCSSTokenSerialization_Function || |
|
1779 aToken2 == eCSSTokenSerialization_URL_or_BadURL || |
|
1780 aToken2 == eCSSTokenSerialization_Symbol_Minus || |
|
1781 aToken2 == eCSSTokenSerialization_Number || |
|
1782 aToken2 == eCSSTokenSerialization_Percentage || |
|
1783 aToken2 == eCSSTokenSerialization_Dimension || |
|
1784 aToken2 == eCSSTokenSerialization_URange || |
|
1785 aToken2 == eCSSTokenSerialization_CDC; |
|
1786 case eCSSTokenSerialization_Symbol_Hash: |
|
1787 return aToken2 == eCSSTokenSerialization_Ident || |
|
1788 aToken2 == eCSSTokenSerialization_Function || |
|
1789 aToken2 == eCSSTokenSerialization_URL_or_BadURL || |
|
1790 aToken2 == eCSSTokenSerialization_Symbol_Minus || |
|
1791 aToken2 == eCSSTokenSerialization_Number || |
|
1792 aToken2 == eCSSTokenSerialization_Percentage || |
|
1793 aToken2 == eCSSTokenSerialization_Dimension || |
|
1794 aToken2 == eCSSTokenSerialization_URange; |
|
1795 case eCSSTokenSerialization_Symbol_Minus: |
|
1796 case eCSSTokenSerialization_Number: |
|
1797 return aToken2 == eCSSTokenSerialization_Ident || |
|
1798 aToken2 == eCSSTokenSerialization_Function || |
|
1799 aToken2 == eCSSTokenSerialization_URL_or_BadURL || |
|
1800 aToken2 == eCSSTokenSerialization_Number || |
|
1801 aToken2 == eCSSTokenSerialization_Percentage || |
|
1802 aToken2 == eCSSTokenSerialization_Dimension || |
|
1803 aToken2 == eCSSTokenSerialization_URange; |
|
1804 case eCSSTokenSerialization_Symbol_At: |
|
1805 return aToken2 == eCSSTokenSerialization_Ident || |
|
1806 aToken2 == eCSSTokenSerialization_Function || |
|
1807 aToken2 == eCSSTokenSerialization_URL_or_BadURL || |
|
1808 aToken2 == eCSSTokenSerialization_Symbol_Minus || |
|
1809 aToken2 == eCSSTokenSerialization_URange; |
|
1810 case eCSSTokenSerialization_URange: |
|
1811 return aToken2 == eCSSTokenSerialization_Ident || |
|
1812 aToken2 == eCSSTokenSerialization_Function || |
|
1813 aToken2 == eCSSTokenSerialization_Number || |
|
1814 aToken2 == eCSSTokenSerialization_Percentage || |
|
1815 aToken2 == eCSSTokenSerialization_Dimension || |
|
1816 aToken2 == eCSSTokenSerialization_Symbol_Question; |
|
1817 case eCSSTokenSerialization_Symbol_Dot_or_Plus: |
|
1818 return aToken2 == eCSSTokenSerialization_Number || |
|
1819 aToken2 == eCSSTokenSerialization_Percentage || |
|
1820 aToken2 == eCSSTokenSerialization_Dimension; |
|
1821 case eCSSTokenSerialization_Symbol_Assorted: |
|
1822 case eCSSTokenSerialization_Symbol_Asterisk: |
|
1823 return aToken2 == eCSSTokenSerialization_Symbol_Equals; |
|
1824 case eCSSTokenSerialization_Symbol_Bar: |
|
1825 return aToken2 == eCSSTokenSerialization_Symbol_Equals || |
|
1826 aToken2 == eCSSTokenSerialization_Symbol_Bar || |
|
1827 aToken2 == eCSSTokenSerialization_DashMatch; // (*) |
|
1828 case eCSSTokenSerialization_Symbol_Slash: |
|
1829 return aToken2 == eCSSTokenSerialization_Symbol_Asterisk || |
|
1830 aToken2 == eCSSTokenSerialization_ContainsMatch; // (*) |
|
1831 default: |
|
1832 MOZ_ASSERT(aToken1 == eCSSTokenSerialization_Nothing || |
|
1833 aToken1 == eCSSTokenSerialization_Whitespace || |
|
1834 aToken1 == eCSSTokenSerialization_Percentage || |
|
1835 aToken1 == eCSSTokenSerialization_URL_or_BadURL || |
|
1836 aToken1 == eCSSTokenSerialization_Function || |
|
1837 aToken1 == eCSSTokenSerialization_CDC || |
|
1838 aToken1 == eCSSTokenSerialization_Symbol_OpenParen || |
|
1839 aToken1 == eCSSTokenSerialization_Symbol_Question || |
|
1840 aToken1 == eCSSTokenSerialization_Symbol_Assorted || |
|
1841 aToken1 == eCSSTokenSerialization_Symbol_Asterisk || |
|
1842 aToken1 == eCSSTokenSerialization_Symbol_Equals || |
|
1843 aToken1 == eCSSTokenSerialization_Symbol_Bar || |
|
1844 aToken1 == eCSSTokenSerialization_Symbol_Slash || |
|
1845 aToken1 == eCSSTokenSerialization_Other || |
|
1846 "unexpected nsCSSTokenSerializationType value"); |
|
1847 return false; |
|
1848 } |
|
1849 } |
|
1850 |
|
1851 /** |
|
1852 * Appends aValue to aResult, possibly inserting an empty CSS |
|
1853 * comment between the two to ensure that tokens from both strings |
|
1854 * remain separated. |
|
1855 */ |
|
1856 static void |
|
1857 AppendTokens(nsAString& aResult, |
|
1858 nsCSSTokenSerializationType& aResultFirstToken, |
|
1859 nsCSSTokenSerializationType& aResultLastToken, |
|
1860 nsCSSTokenSerializationType aValueFirstToken, |
|
1861 nsCSSTokenSerializationType aValueLastToken, |
|
1862 const nsAString& aValue) |
|
1863 { |
|
1864 if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) { |
|
1865 aResult.AppendLiteral("/**/"); |
|
1866 } |
|
1867 aResult.Append(aValue); |
|
1868 if (aResultFirstToken == eCSSTokenSerialization_Nothing) { |
|
1869 aResultFirstToken = aValueFirstToken; |
|
1870 } |
|
1871 if (aValueLastToken != eCSSTokenSerialization_Nothing) { |
|
1872 aResultLastToken = aValueLastToken; |
|
1873 } |
|
1874 } |
|
1875 |
|
1876 /** |
|
1877 * Stops the given scanner recording, and appends the recorded result |
|
1878 * to aResult, possibly inserting an empty CSS comment between the two to |
|
1879 * ensure that tokens from both strings remain separated. |
|
1880 */ |
|
1881 static void |
|
1882 StopRecordingAndAppendTokens(nsString& aResult, |
|
1883 nsCSSTokenSerializationType& aResultFirstToken, |
|
1884 nsCSSTokenSerializationType& aResultLastToken, |
|
1885 nsCSSTokenSerializationType aValueFirstToken, |
|
1886 nsCSSTokenSerializationType aValueLastToken, |
|
1887 nsCSSScanner* aScanner) |
|
1888 { |
|
1889 if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) { |
|
1890 aResult.AppendLiteral("/**/"); |
|
1891 } |
|
1892 aScanner->StopRecording(aResult); |
|
1893 if (aResultFirstToken == eCSSTokenSerialization_Nothing) { |
|
1894 aResultFirstToken = aValueFirstToken; |
|
1895 } |
|
1896 if (aValueLastToken != eCSSTokenSerialization_Nothing) { |
|
1897 aResultLastToken = aValueLastToken; |
|
1898 } |
|
1899 } |
|
1900 |
|
1901 bool |
|
1902 CSSParserImpl::ResolveValueWithVariableReferencesRec( |
|
1903 nsString& aResult, |
|
1904 nsCSSTokenSerializationType& aResultFirstToken, |
|
1905 nsCSSTokenSerializationType& aResultLastToken, |
|
1906 const CSSVariableValues* aVariables) |
|
1907 { |
|
1908 // This function assumes we are already recording, and will leave the scanner |
|
1909 // recording when it returns. |
|
1910 MOZ_ASSERT(mScanner->IsRecording()); |
|
1911 MOZ_ASSERT(aResult.IsEmpty()); |
|
1912 |
|
1913 // Stack of closing characters for currently open constructs. |
|
1914 nsAutoTArray<char16_t, 16> stack; |
|
1915 |
|
1916 // The resolved value for this ResolveValueWithVariableReferencesRec call. |
|
1917 nsString value; |
|
1918 |
|
1919 // The length of the scanner's recording before the currently parsed token. |
|
1920 // This is used so that when we encounter a "var(" token, we can strip |
|
1921 // it off the end of the recording, regardless of how long the token was. |
|
1922 // (With escapes, it could be longer than four characters.) |
|
1923 uint32_t lengthBeforeVar = 0; |
|
1924 |
|
1925 // Tracking the type of token that appears at the start and end of |value| |
|
1926 // and that appears at the start and end of the scanner recording. These are |
|
1927 // used to determine whether we need to insert "/**/" when pasting token |
|
1928 // streams together. |
|
1929 nsCSSTokenSerializationType valueFirstToken = eCSSTokenSerialization_Nothing, |
|
1930 valueLastToken = eCSSTokenSerialization_Nothing, |
|
1931 recFirstToken = eCSSTokenSerialization_Nothing, |
|
1932 recLastToken = eCSSTokenSerialization_Nothing; |
|
1933 |
|
1934 #define UPDATE_RECORDING_TOKENS(type) \ |
|
1935 if (recFirstToken == eCSSTokenSerialization_Nothing) { \ |
|
1936 recFirstToken = type; \ |
|
1937 } \ |
|
1938 recLastToken = type; |
|
1939 |
|
1940 while (GetToken(false)) { |
|
1941 switch (mToken.mType) { |
|
1942 case eCSSToken_Symbol: { |
|
1943 nsCSSTokenSerializationType type = eCSSTokenSerialization_Other; |
|
1944 if (mToken.mSymbol == '(') { |
|
1945 stack.AppendElement(')'); |
|
1946 type = eCSSTokenSerialization_Symbol_OpenParen; |
|
1947 } else if (mToken.mSymbol == '[') { |
|
1948 stack.AppendElement(']'); |
|
1949 } else if (mToken.mSymbol == '{') { |
|
1950 stack.AppendElement('}'); |
|
1951 } else if (mToken.mSymbol == ';') { |
|
1952 if (stack.IsEmpty()) { |
|
1953 // A ';' that is at the top level of the value or at the top level |
|
1954 // of a variable reference's fallback is invalid. |
|
1955 return false; |
|
1956 } |
|
1957 } else if (mToken.mSymbol == '!') { |
|
1958 if (stack.IsEmpty()) { |
|
1959 // An '!' that is at the top level of the value or at the top level |
|
1960 // of a variable reference's fallback is invalid. |
|
1961 return false; |
|
1962 } |
|
1963 } else if (mToken.mSymbol == ')' && |
|
1964 stack.IsEmpty()) { |
|
1965 // We're closing a "var(". |
|
1966 nsString finalTokens; |
|
1967 mScanner->StopRecording(finalTokens); |
|
1968 MOZ_ASSERT(finalTokens[finalTokens.Length() - 1] == ')'); |
|
1969 finalTokens.Truncate(finalTokens.Length() - 1); |
|
1970 aResult.Append(value); |
|
1971 |
|
1972 AppendTokens(aResult, valueFirstToken, valueLastToken, |
|
1973 recFirstToken, recLastToken, finalTokens); |
|
1974 |
|
1975 mScanner->StartRecording(); |
|
1976 UngetToken(); |
|
1977 aResultFirstToken = valueFirstToken; |
|
1978 aResultLastToken = valueLastToken; |
|
1979 return true; |
|
1980 } else if (mToken.mSymbol == ')' || |
|
1981 mToken.mSymbol == ']' || |
|
1982 mToken.mSymbol == '}') { |
|
1983 if (stack.IsEmpty() || |
|
1984 stack.LastElement() != mToken.mSymbol) { |
|
1985 // A mismatched closing bracket is invalid. |
|
1986 return false; |
|
1987 } |
|
1988 stack.TruncateLength(stack.Length() - 1); |
|
1989 } else if (mToken.mSymbol == '#') { |
|
1990 type = eCSSTokenSerialization_Symbol_Hash; |
|
1991 } else if (mToken.mSymbol == '@') { |
|
1992 type = eCSSTokenSerialization_Symbol_At; |
|
1993 } else if (mToken.mSymbol == '.' || |
|
1994 mToken.mSymbol == '+') { |
|
1995 type = eCSSTokenSerialization_Symbol_Dot_or_Plus; |
|
1996 } else if (mToken.mSymbol == '-') { |
|
1997 type = eCSSTokenSerialization_Symbol_Minus; |
|
1998 } else if (mToken.mSymbol == '?') { |
|
1999 type = eCSSTokenSerialization_Symbol_Question; |
|
2000 } else if (mToken.mSymbol == '$' || |
|
2001 mToken.mSymbol == '^' || |
|
2002 mToken.mSymbol == '~') { |
|
2003 type = eCSSTokenSerialization_Symbol_Assorted; |
|
2004 } else if (mToken.mSymbol == '=') { |
|
2005 type = eCSSTokenSerialization_Symbol_Equals; |
|
2006 } else if (mToken.mSymbol == '|') { |
|
2007 type = eCSSTokenSerialization_Symbol_Bar; |
|
2008 } else if (mToken.mSymbol == '/') { |
|
2009 type = eCSSTokenSerialization_Symbol_Slash; |
|
2010 } else if (mToken.mSymbol == '*') { |
|
2011 type = eCSSTokenSerialization_Symbol_Asterisk; |
|
2012 } |
|
2013 UPDATE_RECORDING_TOKENS(type); |
|
2014 break; |
|
2015 } |
|
2016 |
|
2017 case eCSSToken_Function: |
|
2018 if (mToken.mIdent.LowerCaseEqualsLiteral("var")) { |
|
2019 // Save the tokens before the "var(" to our resolved value. |
|
2020 nsString recording; |
|
2021 mScanner->StopRecording(recording); |
|
2022 recording.Truncate(lengthBeforeVar); |
|
2023 AppendTokens(value, valueFirstToken, valueLastToken, |
|
2024 recFirstToken, recLastToken, recording); |
|
2025 recFirstToken = eCSSTokenSerialization_Nothing; |
|
2026 recLastToken = eCSSTokenSerialization_Nothing; |
|
2027 |
|
2028 if (!GetToken(true) || |
|
2029 mToken.mType != eCSSToken_Ident || |
|
2030 !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) { |
|
2031 // "var(" must be followed by an identifier, and it must be a |
|
2032 // custom property name. |
|
2033 return false; |
|
2034 } |
|
2035 |
|
2036 // Turn the custom property name into a variable name by removing the |
|
2037 // '--' prefix. |
|
2038 MOZ_ASSERT(Substring(mToken.mIdent, 0, |
|
2039 CSS_CUSTOM_NAME_PREFIX_LENGTH). |
|
2040 EqualsLiteral("--")); |
|
2041 nsDependentString variableName(mToken.mIdent, |
|
2042 CSS_CUSTOM_NAME_PREFIX_LENGTH); |
|
2043 |
|
2044 // Get the value of the identified variable. Note that we |
|
2045 // check if the variable value is the empty string, as that means |
|
2046 // that the variable was invalid at computed value time due to |
|
2047 // unresolveable variable references or cycles. |
|
2048 nsString variableValue; |
|
2049 nsCSSTokenSerializationType varFirstToken, varLastToken; |
|
2050 bool valid = aVariables->Get(variableName, variableValue, |
|
2051 varFirstToken, varLastToken) && |
|
2052 !variableValue.IsEmpty(); |
|
2053 |
|
2054 if (!GetToken(true) || |
|
2055 mToken.IsSymbol(')')) { |
|
2056 mScanner->StartRecording(); |
|
2057 if (!valid) { |
|
2058 // Invalid variable with no fallback. |
|
2059 return false; |
|
2060 } |
|
2061 // Valid variable with no fallback. |
|
2062 AppendTokens(value, valueFirstToken, valueLastToken, |
|
2063 varFirstToken, varLastToken, variableValue); |
|
2064 } else if (mToken.IsSymbol(',')) { |
|
2065 mScanner->StartRecording(); |
|
2066 if (!GetToken(false) || |
|
2067 mToken.IsSymbol(')')) { |
|
2068 // Comma must be followed by at least one fallback token. |
|
2069 return false; |
|
2070 } |
|
2071 UngetToken(); |
|
2072 if (valid) { |
|
2073 // Valid variable with ignored fallback. |
|
2074 mScanner->StopRecording(); |
|
2075 AppendTokens(value, valueFirstToken, valueLastToken, |
|
2076 varFirstToken, varLastToken, variableValue); |
|
2077 bool ok = SkipBalancedContentUntil(')'); |
|
2078 mScanner->StartRecording(); |
|
2079 if (!ok) { |
|
2080 return false; |
|
2081 } |
|
2082 } else { |
|
2083 nsString fallback; |
|
2084 if (!ResolveValueWithVariableReferencesRec(fallback, |
|
2085 varFirstToken, |
|
2086 varLastToken, |
|
2087 aVariables)) { |
|
2088 // Fallback value had invalid tokens or an invalid variable reference |
|
2089 // that itself had no fallback. |
|
2090 return false; |
|
2091 } |
|
2092 AppendTokens(value, valueFirstToken, valueLastToken, |
|
2093 varFirstToken, varLastToken, fallback); |
|
2094 // Now we're either at the pushed back ')' that finished the |
|
2095 // fallback or at EOF. |
|
2096 DebugOnly<bool> gotToken = GetToken(false); |
|
2097 MOZ_ASSERT(!gotToken || mToken.IsSymbol(')')); |
|
2098 } |
|
2099 } else { |
|
2100 // Expected ',' or ')' after the variable name. |
|
2101 mScanner->StartRecording(); |
|
2102 return false; |
|
2103 } |
|
2104 } else { |
|
2105 stack.AppendElement(')'); |
|
2106 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function); |
|
2107 } |
|
2108 break; |
|
2109 |
|
2110 case eCSSToken_Bad_String: |
|
2111 case eCSSToken_Bad_URL: |
|
2112 return false; |
|
2113 |
|
2114 case eCSSToken_Whitespace: |
|
2115 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace); |
|
2116 break; |
|
2117 |
|
2118 case eCSSToken_AtKeyword: |
|
2119 case eCSSToken_Hash: |
|
2120 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash); |
|
2121 break; |
|
2122 |
|
2123 case eCSSToken_Number: |
|
2124 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number); |
|
2125 break; |
|
2126 |
|
2127 case eCSSToken_Dimension: |
|
2128 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension); |
|
2129 break; |
|
2130 |
|
2131 case eCSSToken_Ident: |
|
2132 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident); |
|
2133 break; |
|
2134 |
|
2135 case eCSSToken_Percentage: |
|
2136 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage); |
|
2137 break; |
|
2138 |
|
2139 case eCSSToken_URange: |
|
2140 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange); |
|
2141 break; |
|
2142 |
|
2143 case eCSSToken_URL: |
|
2144 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL); |
|
2145 break; |
|
2146 |
|
2147 case eCSSToken_HTMLComment: |
|
2148 if (mToken.mIdent[0] == '-') { |
|
2149 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC); |
|
2150 } else { |
|
2151 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other); |
|
2152 } |
|
2153 break; |
|
2154 |
|
2155 case eCSSToken_Dashmatch: |
|
2156 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch); |
|
2157 break; |
|
2158 |
|
2159 case eCSSToken_Containsmatch: |
|
2160 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch); |
|
2161 break; |
|
2162 |
|
2163 default: |
|
2164 NS_NOTREACHED("unexpected token type"); |
|
2165 // fall through |
|
2166 case eCSSToken_ID: |
|
2167 case eCSSToken_String: |
|
2168 case eCSSToken_Includes: |
|
2169 case eCSSToken_Beginsmatch: |
|
2170 case eCSSToken_Endsmatch: |
|
2171 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other); |
|
2172 break; |
|
2173 } |
|
2174 |
|
2175 lengthBeforeVar = mScanner->RecordingLength(); |
|
2176 } |
|
2177 |
|
2178 #undef UPDATE_RECORDING_TOKENS |
|
2179 |
|
2180 aResult.Append(value); |
|
2181 StopRecordingAndAppendTokens(aResult, valueFirstToken, valueLastToken, |
|
2182 recFirstToken, recLastToken, mScanner); |
|
2183 |
|
2184 // Append any implicitly closed brackets. |
|
2185 if (!stack.IsEmpty()) { |
|
2186 do { |
|
2187 aResult.Append(stack.LastElement()); |
|
2188 stack.TruncateLength(stack.Length() - 1); |
|
2189 } while (!stack.IsEmpty()); |
|
2190 valueLastToken = eCSSTokenSerialization_Other; |
|
2191 } |
|
2192 |
|
2193 mScanner->StartRecording(); |
|
2194 aResultFirstToken = valueFirstToken; |
|
2195 aResultLastToken = valueLastToken; |
|
2196 return true; |
|
2197 } |
|
2198 |
|
2199 bool |
|
2200 CSSParserImpl::ResolveValueWithVariableReferences( |
|
2201 const CSSVariableValues* aVariables, |
|
2202 nsString& aResult, |
|
2203 nsCSSTokenSerializationType& aFirstToken, |
|
2204 nsCSSTokenSerializationType& aLastToken) |
|
2205 { |
|
2206 aResult.Truncate(0); |
|
2207 |
|
2208 // Start recording before we read the first token. |
|
2209 mScanner->StartRecording(); |
|
2210 |
|
2211 if (!GetToken(false)) { |
|
2212 // Value was empty since we reached EOF. |
|
2213 mScanner->StopRecording(); |
|
2214 return false; |
|
2215 } |
|
2216 |
|
2217 UngetToken(); |
|
2218 |
|
2219 nsString value; |
|
2220 nsCSSTokenSerializationType firstToken, lastToken; |
|
2221 bool ok = ResolveValueWithVariableReferencesRec(value, firstToken, lastToken, aVariables) && |
|
2222 !GetToken(true); |
|
2223 |
|
2224 mScanner->StopRecording(); |
|
2225 |
|
2226 if (ok) { |
|
2227 aResult = value; |
|
2228 aFirstToken = firstToken; |
|
2229 aLastToken = lastToken; |
|
2230 } |
|
2231 return ok; |
|
2232 } |
|
2233 |
|
2234 bool |
|
2235 CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue, |
|
2236 const CSSVariableValues* aVariables, |
|
2237 nsString& aResult, |
|
2238 nsCSSTokenSerializationType& aFirstToken, |
|
2239 nsCSSTokenSerializationType& aLastToken) |
|
2240 { |
|
2241 nsCSSScanner scanner(aPropertyValue, 0); |
|
2242 |
|
2243 // At this point, we know that aPropertyValue is syntactically correct |
|
2244 // for a token stream that has variable references. We also won't be |
|
2245 // interpreting any of the stream as we parse it, apart from expanding |
|
2246 // var() references, so we don't need a base URL etc. or any useful |
|
2247 // error reporting. |
|
2248 css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr); |
|
2249 InitScanner(scanner, reporter, nullptr, nullptr, nullptr); |
|
2250 |
|
2251 bool valid = ResolveValueWithVariableReferences(aVariables, aResult, |
|
2252 aFirstToken, aLastToken); |
|
2253 |
|
2254 ReleaseScanner(); |
|
2255 return valid; |
|
2256 } |
|
2257 |
|
2258 void |
|
2259 CSSParserImpl::ParsePropertyWithVariableReferences( |
|
2260 nsCSSProperty aPropertyID, |
|
2261 nsCSSProperty aShorthandPropertyID, |
|
2262 const nsAString& aValue, |
|
2263 const CSSVariableValues* aVariables, |
|
2264 nsRuleData* aRuleData, |
|
2265 nsIURI* aDocURL, |
|
2266 nsIURI* aBaseURL, |
|
2267 nsIPrincipal* aDocPrincipal, |
|
2268 nsCSSStyleSheet* aSheet, |
|
2269 uint32_t aLineNumber, |
|
2270 uint32_t aLineOffset) |
|
2271 { |
|
2272 mTempData.AssertInitialState(); |
|
2273 |
|
2274 bool valid; |
|
2275 nsString expandedValue; |
|
2276 |
|
2277 // Resolve any variable references in the property value. |
|
2278 { |
|
2279 nsCSSScanner scanner(aValue, 0); |
|
2280 css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL); |
|
2281 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal); |
|
2282 |
|
2283 nsCSSTokenSerializationType firstToken, lastToken; |
|
2284 valid = ResolveValueWithVariableReferences(aVariables, expandedValue, |
|
2285 firstToken, lastToken); |
|
2286 if (!valid) { |
|
2287 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropertyID)); |
|
2288 REPORT_UNEXPECTED(PEInvalidVariableReference); |
|
2289 REPORT_UNEXPECTED_P(PEValueParsingError, propName); |
|
2290 if (nsCSSProps::IsInherited(aPropertyID)) { |
|
2291 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit); |
|
2292 } else { |
|
2293 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial); |
|
2294 } |
|
2295 OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset); |
|
2296 } |
|
2297 ReleaseScanner(); |
|
2298 } |
|
2299 |
|
2300 nsCSSProperty propertyToParse = |
|
2301 aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID : |
|
2302 aPropertyID; |
|
2303 |
|
2304 // Parse the property with that resolved value. |
|
2305 if (valid) { |
|
2306 nsCSSScanner scanner(expandedValue, 0); |
|
2307 css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL); |
|
2308 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal); |
|
2309 valid = ParseProperty(propertyToParse); |
|
2310 if (valid && GetToken(true)) { |
|
2311 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); |
|
2312 valid = false; |
|
2313 } |
|
2314 if (!valid) { |
|
2315 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue( |
|
2316 propertyToParse)); |
|
2317 REPORT_UNEXPECTED_P(PEValueWithVariablesParsingError, propName); |
|
2318 if (nsCSSProps::IsInherited(aPropertyID)) { |
|
2319 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit); |
|
2320 } else { |
|
2321 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial); |
|
2322 } |
|
2323 OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset); |
|
2324 } |
|
2325 ReleaseScanner(); |
|
2326 } |
|
2327 |
|
2328 // If the property could not be parsed with the resolved value, then we |
|
2329 // treat it as if the value were 'initial' or 'inherit', depending on whether |
|
2330 // the property is an inherited property. |
|
2331 if (!valid) { |
|
2332 nsCSSValue defaultValue; |
|
2333 if (nsCSSProps::IsInherited(aPropertyID)) { |
|
2334 defaultValue.SetInheritValue(); |
|
2335 } else { |
|
2336 defaultValue.SetInitialValue(); |
|
2337 } |
|
2338 mTempData.AddLonghandProperty(aPropertyID, defaultValue); |
|
2339 } |
|
2340 |
|
2341 // Copy the property value into the rule data. |
|
2342 mTempData.MapRuleInfoInto(aPropertyID, aRuleData); |
|
2343 |
|
2344 mTempData.ClearProperty(propertyToParse); |
|
2345 mTempData.AssertInitialState(); |
|
2346 } |
|
2347 |
|
2348 //---------------------------------------------------------------------- |
|
2349 |
|
2350 bool |
|
2351 CSSParserImpl::GetToken(bool aSkipWS) |
|
2352 { |
|
2353 if (mHavePushBack) { |
|
2354 mHavePushBack = false; |
|
2355 if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) { |
|
2356 return true; |
|
2357 } |
|
2358 } |
|
2359 return mScanner->Next(mToken, aSkipWS); |
|
2360 } |
|
2361 |
|
2362 void |
|
2363 CSSParserImpl::UngetToken() |
|
2364 { |
|
2365 NS_PRECONDITION(!mHavePushBack, "double pushback"); |
|
2366 mHavePushBack = true; |
|
2367 } |
|
2368 |
|
2369 bool |
|
2370 CSSParserImpl::GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum) |
|
2371 { |
|
2372 // Peek at next token so that mScanner updates line and column vals |
|
2373 if (!GetToken(aSkipWS)) { |
|
2374 return false; |
|
2375 } |
|
2376 UngetToken(); |
|
2377 // The scanner uses one-indexing for line numbers but zero-indexing |
|
2378 // for column numbers. |
|
2379 *linenum = mScanner->GetLineNumber(); |
|
2380 *colnum = 1 + mScanner->GetColumnNumber(); |
|
2381 return true; |
|
2382 } |
|
2383 |
|
2384 bool |
|
2385 CSSParserImpl::ExpectSymbol(char16_t aSymbol, |
|
2386 bool aSkipWS) |
|
2387 { |
|
2388 if (!GetToken(aSkipWS)) { |
|
2389 // CSS2.1 specifies that all "open constructs" are to be closed at |
|
2390 // EOF. It simplifies higher layers if we claim to have found an |
|
2391 // ), ], }, or ; if we encounter EOF while looking for one of them. |
|
2392 // Do still issue a diagnostic, to aid debugging. |
|
2393 if (aSymbol == ')' || aSymbol == ']' || |
|
2394 aSymbol == '}' || aSymbol == ';') { |
|
2395 REPORT_UNEXPECTED_EOF_CHAR(aSymbol); |
|
2396 return true; |
|
2397 } |
|
2398 else |
|
2399 return false; |
|
2400 } |
|
2401 if (mToken.IsSymbol(aSymbol)) { |
|
2402 return true; |
|
2403 } |
|
2404 UngetToken(); |
|
2405 return false; |
|
2406 } |
|
2407 |
|
2408 // Checks to see if we're at the end of a property. If an error occurs during |
|
2409 // the check, does not signal a parse error. |
|
2410 bool |
|
2411 CSSParserImpl::CheckEndProperty() |
|
2412 { |
|
2413 if (!GetToken(true)) { |
|
2414 return true; // properties may end with eof |
|
2415 } |
|
2416 if ((eCSSToken_Symbol == mToken.mType) && |
|
2417 ((';' == mToken.mSymbol) || |
|
2418 ('!' == mToken.mSymbol) || |
|
2419 ('}' == mToken.mSymbol) || |
|
2420 (')' == mToken.mSymbol))) { |
|
2421 // XXX need to verify that ! is only followed by "important [;|}] |
|
2422 // XXX this requires a multi-token pushback buffer |
|
2423 UngetToken(); |
|
2424 return true; |
|
2425 } |
|
2426 UngetToken(); |
|
2427 return false; |
|
2428 } |
|
2429 |
|
2430 // Checks if we're at the end of a property, raising an error if we're not. |
|
2431 bool |
|
2432 CSSParserImpl::ExpectEndProperty() |
|
2433 { |
|
2434 if (CheckEndProperty()) |
|
2435 return true; |
|
2436 |
|
2437 // If we're here, we read something incorrect, so we should report it. |
|
2438 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); |
|
2439 return false; |
|
2440 } |
|
2441 |
|
2442 // Parses the priority suffix on a property, which at present may be |
|
2443 // either '!important' or nothing. |
|
2444 CSSParserImpl::PriorityParsingStatus |
|
2445 CSSParserImpl::ParsePriority() |
|
2446 { |
|
2447 if (!GetToken(true)) { |
|
2448 return ePriority_None; // properties may end with EOF |
|
2449 } |
|
2450 if (!mToken.IsSymbol('!')) { |
|
2451 UngetToken(); |
|
2452 return ePriority_None; // dunno what it is, but it's not a priority |
|
2453 } |
|
2454 |
|
2455 if (!GetToken(true)) { |
|
2456 // EOF is not ok after ! |
|
2457 REPORT_UNEXPECTED_EOF(PEImportantEOF); |
|
2458 return ePriority_Error; |
|
2459 } |
|
2460 |
|
2461 if (mToken.mType != eCSSToken_Ident || |
|
2462 !mToken.mIdent.LowerCaseEqualsLiteral("important")) { |
|
2463 REPORT_UNEXPECTED_TOKEN(PEExpectedImportant); |
|
2464 UngetToken(); |
|
2465 return ePriority_Error; |
|
2466 } |
|
2467 |
|
2468 return ePriority_Important; |
|
2469 } |
|
2470 |
|
2471 nsSubstring* |
|
2472 CSSParserImpl::NextIdent() |
|
2473 { |
|
2474 // XXX Error reporting? |
|
2475 if (!GetToken(true)) { |
|
2476 return nullptr; |
|
2477 } |
|
2478 if (eCSSToken_Ident != mToken.mType) { |
|
2479 UngetToken(); |
|
2480 return nullptr; |
|
2481 } |
|
2482 return &mToken.mIdent; |
|
2483 } |
|
2484 |
|
2485 bool |
|
2486 CSSParserImpl::SkipAtRule(bool aInsideBlock) |
|
2487 { |
|
2488 for (;;) { |
|
2489 if (!GetToken(true)) { |
|
2490 REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2); |
|
2491 return false; |
|
2492 } |
|
2493 if (eCSSToken_Symbol == mToken.mType) { |
|
2494 char16_t symbol = mToken.mSymbol; |
|
2495 if (symbol == ';') { |
|
2496 break; |
|
2497 } |
|
2498 if (aInsideBlock && symbol == '}') { |
|
2499 // The closing } doesn't belong to us. |
|
2500 UngetToken(); |
|
2501 break; |
|
2502 } |
|
2503 if (symbol == '{') { |
|
2504 SkipUntil('}'); |
|
2505 break; |
|
2506 } else if (symbol == '(') { |
|
2507 SkipUntil(')'); |
|
2508 } else if (symbol == '[') { |
|
2509 SkipUntil(']'); |
|
2510 } |
|
2511 } else if (eCSSToken_Function == mToken.mType || |
|
2512 eCSSToken_Bad_URL == mToken.mType) { |
|
2513 SkipUntil(')'); |
|
2514 } |
|
2515 } |
|
2516 return true; |
|
2517 } |
|
2518 |
|
2519 bool |
|
2520 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc, |
|
2521 void* aData, |
|
2522 bool aInAtRule) |
|
2523 { |
|
2524 |
|
2525 nsCSSSection newSection; |
|
2526 bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*); |
|
2527 |
|
2528 if ((mSection <= eCSSSection_Charset) && |
|
2529 (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) { |
|
2530 parseFunc = &CSSParserImpl::ParseCharsetRule; |
|
2531 newSection = eCSSSection_Import; // only one charset allowed |
|
2532 |
|
2533 } else if ((mSection <= eCSSSection_Import) && |
|
2534 mToken.mIdent.LowerCaseEqualsLiteral("import")) { |
|
2535 parseFunc = &CSSParserImpl::ParseImportRule; |
|
2536 newSection = eCSSSection_Import; |
|
2537 |
|
2538 } else if ((mSection <= eCSSSection_NameSpace) && |
|
2539 mToken.mIdent.LowerCaseEqualsLiteral("namespace")) { |
|
2540 parseFunc = &CSSParserImpl::ParseNameSpaceRule; |
|
2541 newSection = eCSSSection_NameSpace; |
|
2542 |
|
2543 } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) { |
|
2544 parseFunc = &CSSParserImpl::ParseMediaRule; |
|
2545 newSection = eCSSSection_General; |
|
2546 |
|
2547 } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) { |
|
2548 parseFunc = &CSSParserImpl::ParseMozDocumentRule; |
|
2549 newSection = eCSSSection_General; |
|
2550 |
|
2551 } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) { |
|
2552 parseFunc = &CSSParserImpl::ParseFontFaceRule; |
|
2553 newSection = eCSSSection_General; |
|
2554 |
|
2555 } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values") && |
|
2556 nsCSSFontFeatureValuesRule::PrefEnabled()) { |
|
2557 parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule; |
|
2558 newSection = eCSSSection_General; |
|
2559 |
|
2560 } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) { |
|
2561 parseFunc = &CSSParserImpl::ParsePageRule; |
|
2562 newSection = eCSSSection_General; |
|
2563 |
|
2564 } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation) && |
|
2565 mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) || |
|
2566 mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) { |
|
2567 parseFunc = &CSSParserImpl::ParseKeyframesRule; |
|
2568 newSection = eCSSSection_General; |
|
2569 |
|
2570 } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports") && |
|
2571 CSSSupportsRule::PrefEnabled()) { |
|
2572 parseFunc = &CSSParserImpl::ParseSupportsRule; |
|
2573 newSection = eCSSSection_General; |
|
2574 |
|
2575 } else { |
|
2576 if (!NonMozillaVendorIdentifier(mToken.mIdent)) { |
|
2577 REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule); |
|
2578 OUTPUT_ERROR(); |
|
2579 } |
|
2580 // Skip over unsupported at rule, don't advance section |
|
2581 return SkipAtRule(aInAtRule); |
|
2582 } |
|
2583 |
|
2584 // Inside of @-rules, only the rules that can occur anywhere |
|
2585 // are allowed. |
|
2586 bool unnestable = aInAtRule && newSection != eCSSSection_General; |
|
2587 if (unnestable) { |
|
2588 REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule); |
|
2589 } |
|
2590 |
|
2591 if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) { |
|
2592 // Skip over invalid at rule, don't advance section |
|
2593 OUTPUT_ERROR(); |
|
2594 return SkipAtRule(aInAtRule); |
|
2595 } |
|
2596 |
|
2597 // Nested @-rules don't affect the top-level rule chain requirement |
|
2598 if (!aInAtRule) { |
|
2599 mSection = newSection; |
|
2600 } |
|
2601 |
|
2602 return true; |
|
2603 } |
|
2604 |
|
2605 bool |
|
2606 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc, |
|
2607 void* aData) |
|
2608 { |
|
2609 if (!GetToken(true)) { |
|
2610 REPORT_UNEXPECTED_EOF(PECharsetRuleEOF); |
|
2611 return false; |
|
2612 } |
|
2613 |
|
2614 if (eCSSToken_String != mToken.mType) { |
|
2615 UngetToken(); |
|
2616 REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString); |
|
2617 return false; |
|
2618 } |
|
2619 |
|
2620 nsAutoString charset = mToken.mIdent; |
|
2621 |
|
2622 if (!ExpectSymbol(';', true)) { |
|
2623 return false; |
|
2624 } |
|
2625 |
|
2626 nsRefPtr<css::CharsetRule> rule = new css::CharsetRule(charset); |
|
2627 (*aAppendFunc)(rule, aData); |
|
2628 |
|
2629 return true; |
|
2630 } |
|
2631 |
|
2632 bool |
|
2633 CSSParserImpl::ParseURLOrString(nsString& aURL) |
|
2634 { |
|
2635 if (!GetToken(true)) { |
|
2636 return false; |
|
2637 } |
|
2638 if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) { |
|
2639 aURL = mToken.mIdent; |
|
2640 return true; |
|
2641 } |
|
2642 UngetToken(); |
|
2643 return false; |
|
2644 } |
|
2645 |
|
2646 bool |
|
2647 CSSParserImpl::ParseMediaQuery(bool aInAtRule, |
|
2648 nsMediaQuery **aQuery, |
|
2649 bool *aHitStop) |
|
2650 { |
|
2651 *aQuery = nullptr; |
|
2652 *aHitStop = false; |
|
2653 |
|
2654 // "If the comma-separated list is the empty list it is assumed to |
|
2655 // specify the media query 'all'." (css3-mediaqueries, section |
|
2656 // "Media Queries") |
|
2657 if (!GetToken(true)) { |
|
2658 *aHitStop = true; |
|
2659 // expected termination by EOF |
|
2660 if (!aInAtRule) |
|
2661 return true; |
|
2662 |
|
2663 // unexpected termination by EOF |
|
2664 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF); |
|
2665 return true; |
|
2666 } |
|
2667 |
|
2668 if (eCSSToken_Symbol == mToken.mType && aInAtRule && |
|
2669 (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) { |
|
2670 *aHitStop = true; |
|
2671 UngetToken(); |
|
2672 return true; |
|
2673 } |
|
2674 UngetToken(); |
|
2675 |
|
2676 nsMediaQuery* query = new nsMediaQuery; |
|
2677 *aQuery = query; |
|
2678 |
|
2679 if (ExpectSymbol('(', true)) { |
|
2680 // we got an expression without a media type |
|
2681 UngetToken(); // so ParseMediaQueryExpression can handle it |
|
2682 query->SetType(nsGkAtoms::all); |
|
2683 query->SetTypeOmitted(); |
|
2684 // Just parse the first expression here. |
|
2685 if (!ParseMediaQueryExpression(query)) { |
|
2686 OUTPUT_ERROR(); |
|
2687 query->SetHadUnknownExpression(); |
|
2688 } |
|
2689 } else { |
|
2690 nsCOMPtr<nsIAtom> mediaType; |
|
2691 bool gotNotOrOnly = false; |
|
2692 for (;;) { |
|
2693 if (!GetToken(true)) { |
|
2694 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF); |
|
2695 return false; |
|
2696 } |
|
2697 if (eCSSToken_Ident != mToken.mType) { |
|
2698 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent); |
|
2699 UngetToken(); |
|
2700 return false; |
|
2701 } |
|
2702 // case insensitive from CSS - must be lower cased |
|
2703 nsContentUtils::ASCIIToLower(mToken.mIdent); |
|
2704 mediaType = do_GetAtom(mToken.mIdent); |
|
2705 if (!mediaType) { |
|
2706 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?"); |
|
2707 } |
|
2708 if (!gotNotOrOnly && mediaType == nsGkAtoms::_not) { |
|
2709 gotNotOrOnly = true; |
|
2710 query->SetNegated(); |
|
2711 } else if (!gotNotOrOnly && mediaType == nsGkAtoms::only) { |
|
2712 gotNotOrOnly = true; |
|
2713 query->SetHasOnly(); |
|
2714 } else if (mediaType == nsGkAtoms::_not || |
|
2715 mediaType == nsGkAtoms::only || |
|
2716 mediaType == nsGkAtoms::_and || |
|
2717 mediaType == nsGkAtoms::_or) { |
|
2718 REPORT_UNEXPECTED_TOKEN(PEGatherMediaReservedMediaType); |
|
2719 UngetToken(); |
|
2720 return false; |
|
2721 } else { |
|
2722 // valid media type |
|
2723 break; |
|
2724 } |
|
2725 } |
|
2726 query->SetType(mediaType); |
|
2727 } |
|
2728 |
|
2729 for (;;) { |
|
2730 if (!GetToken(true)) { |
|
2731 *aHitStop = true; |
|
2732 // expected termination by EOF |
|
2733 if (!aInAtRule) |
|
2734 break; |
|
2735 |
|
2736 // unexpected termination by EOF |
|
2737 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF); |
|
2738 break; |
|
2739 } |
|
2740 |
|
2741 if (eCSSToken_Symbol == mToken.mType && aInAtRule && |
|
2742 (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) { |
|
2743 *aHitStop = true; |
|
2744 UngetToken(); |
|
2745 break; |
|
2746 } |
|
2747 if (eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') { |
|
2748 // Done with the expressions for this query |
|
2749 break; |
|
2750 } |
|
2751 if (eCSSToken_Ident != mToken.mType || |
|
2752 !mToken.mIdent.LowerCaseEqualsLiteral("and")) { |
|
2753 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma); |
|
2754 UngetToken(); |
|
2755 return false; |
|
2756 } |
|
2757 if (!ParseMediaQueryExpression(query)) { |
|
2758 OUTPUT_ERROR(); |
|
2759 query->SetHadUnknownExpression(); |
|
2760 } |
|
2761 } |
|
2762 return true; |
|
2763 } |
|
2764 |
|
2765 // Returns false only when there is a low-level error in the scanner |
|
2766 // (out-of-memory). |
|
2767 bool |
|
2768 CSSParserImpl::GatherMedia(nsMediaList* aMedia, |
|
2769 bool aInAtRule) |
|
2770 { |
|
2771 for (;;) { |
|
2772 nsAutoPtr<nsMediaQuery> query; |
|
2773 bool hitStop; |
|
2774 if (!ParseMediaQuery(aInAtRule, getter_Transfers(query), |
|
2775 &hitStop)) { |
|
2776 NS_ASSERTION(!hitStop, "should return true when hit stop"); |
|
2777 OUTPUT_ERROR(); |
|
2778 if (query) { |
|
2779 query->SetHadUnknownExpression(); |
|
2780 } |
|
2781 if (aInAtRule) { |
|
2782 const char16_t stopChars[] = |
|
2783 { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) }; |
|
2784 SkipUntilOneOf(stopChars); |
|
2785 } else { |
|
2786 SkipUntil(','); |
|
2787 } |
|
2788 // Rely on SkipUntilOneOf leaving mToken around as the last token read. |
|
2789 if (mToken.mType == eCSSToken_Symbol && aInAtRule && |
|
2790 (mToken.mSymbol == '{' || mToken.mSymbol == ';' || mToken.mSymbol == '}')) { |
|
2791 UngetToken(); |
|
2792 hitStop = true; |
|
2793 } |
|
2794 } |
|
2795 if (query) { |
|
2796 aMedia->AppendQuery(query); |
|
2797 } |
|
2798 if (hitStop) { |
|
2799 break; |
|
2800 } |
|
2801 } |
|
2802 return true; |
|
2803 } |
|
2804 |
|
2805 bool |
|
2806 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery) |
|
2807 { |
|
2808 if (!ExpectSymbol('(', true)) { |
|
2809 REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart); |
|
2810 return false; |
|
2811 } |
|
2812 if (! GetToken(true)) { |
|
2813 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF); |
|
2814 return false; |
|
2815 } |
|
2816 if (eCSSToken_Ident != mToken.mType) { |
|
2817 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName); |
|
2818 UngetToken(); |
|
2819 SkipUntil(')'); |
|
2820 return false; |
|
2821 } |
|
2822 |
|
2823 nsMediaExpression *expr = aQuery->NewExpression(); |
|
2824 |
|
2825 // case insensitive from CSS - must be lower cased |
|
2826 nsContentUtils::ASCIIToLower(mToken.mIdent); |
|
2827 const char16_t *featureString; |
|
2828 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) { |
|
2829 expr->mRange = nsMediaExpression::eMin; |
|
2830 featureString = mToken.mIdent.get() + 4; |
|
2831 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) { |
|
2832 expr->mRange = nsMediaExpression::eMax; |
|
2833 featureString = mToken.mIdent.get() + 4; |
|
2834 } else { |
|
2835 expr->mRange = nsMediaExpression::eEqual; |
|
2836 featureString = mToken.mIdent.get(); |
|
2837 } |
|
2838 |
|
2839 nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString); |
|
2840 if (!mediaFeatureAtom) { |
|
2841 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?"); |
|
2842 } |
|
2843 const nsMediaFeature *feature = nsMediaFeatures::features; |
|
2844 for (; feature->mName; ++feature) { |
|
2845 if (*(feature->mName) == mediaFeatureAtom) { |
|
2846 break; |
|
2847 } |
|
2848 } |
|
2849 if (!feature->mName || |
|
2850 (expr->mRange != nsMediaExpression::eEqual && |
|
2851 feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) { |
|
2852 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName); |
|
2853 SkipUntil(')'); |
|
2854 return false; |
|
2855 } |
|
2856 expr->mFeature = feature; |
|
2857 |
|
2858 if (!GetToken(true) || mToken.IsSymbol(')')) { |
|
2859 // Query expressions for any feature can be given without a value. |
|
2860 // However, min/max prefixes are not allowed. |
|
2861 if (expr->mRange != nsMediaExpression::eEqual) { |
|
2862 REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue); |
|
2863 return false; |
|
2864 } |
|
2865 expr->mValue.Reset(); |
|
2866 return true; |
|
2867 } |
|
2868 |
|
2869 if (!mToken.IsSymbol(':')) { |
|
2870 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd); |
|
2871 UngetToken(); |
|
2872 SkipUntil(')'); |
|
2873 return false; |
|
2874 } |
|
2875 |
|
2876 bool rv = false; |
|
2877 switch (feature->mValueType) { |
|
2878 case nsMediaFeature::eLength: |
|
2879 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_LENGTH, nullptr); |
|
2880 break; |
|
2881 case nsMediaFeature::eInteger: |
|
2882 case nsMediaFeature::eBoolInteger: |
|
2883 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_INTEGER, nullptr); |
|
2884 // Enforce extra restrictions for eBoolInteger |
|
2885 if (rv && |
|
2886 feature->mValueType == nsMediaFeature::eBoolInteger && |
|
2887 expr->mValue.GetIntValue() > 1) |
|
2888 rv = false; |
|
2889 break; |
|
2890 case nsMediaFeature::eFloat: |
|
2891 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_NUMBER, nullptr); |
|
2892 break; |
|
2893 case nsMediaFeature::eIntRatio: |
|
2894 { |
|
2895 // Two integers separated by '/', with optional whitespace on |
|
2896 // either side of the '/'. |
|
2897 nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2); |
|
2898 expr->mValue.SetArrayValue(a, eCSSUnit_Array); |
|
2899 // We don't bother with ParseNonNegativeVariant since we have to |
|
2900 // check for != 0 as well; no need to worry about the UngetToken |
|
2901 // since we're throwing out up to the next ')' anyway. |
|
2902 rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nullptr) && |
|
2903 a->Item(0).GetIntValue() > 0 && |
|
2904 ExpectSymbol('/', true) && |
|
2905 ParseVariant(a->Item(1), VARIANT_INTEGER, nullptr) && |
|
2906 a->Item(1).GetIntValue() > 0; |
|
2907 } |
|
2908 break; |
|
2909 case nsMediaFeature::eResolution: |
|
2910 rv = GetToken(true); |
|
2911 if (!rv) |
|
2912 break; |
|
2913 rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f; |
|
2914 if (!rv) { |
|
2915 UngetToken(); |
|
2916 break; |
|
2917 } |
|
2918 // No worries about whether unitless zero is allowed, since the |
|
2919 // value must be positive (and we checked that above). |
|
2920 NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied"); |
|
2921 if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) { |
|
2922 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch); |
|
2923 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) { |
|
2924 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel); |
|
2925 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) { |
|
2926 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter); |
|
2927 } else { |
|
2928 rv = false; |
|
2929 } |
|
2930 break; |
|
2931 case nsMediaFeature::eEnumerated: |
|
2932 rv = ParseVariant(expr->mValue, VARIANT_KEYWORD, |
|
2933 feature->mData.mKeywordTable); |
|
2934 break; |
|
2935 case nsMediaFeature::eIdent: |
|
2936 rv = ParseVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr); |
|
2937 break; |
|
2938 } |
|
2939 if (!rv || !ExpectSymbol(')', true)) { |
|
2940 REPORT_UNEXPECTED(PEMQExpectedFeatureValue); |
|
2941 SkipUntil(')'); |
|
2942 return false; |
|
2943 } |
|
2944 |
|
2945 return true; |
|
2946 } |
|
2947 |
|
2948 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]" |
|
2949 bool |
|
2950 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData) |
|
2951 { |
|
2952 nsRefPtr<nsMediaList> media = new nsMediaList(); |
|
2953 |
|
2954 nsAutoString url; |
|
2955 if (!ParseURLOrString(url)) { |
|
2956 REPORT_UNEXPECTED_TOKEN(PEImportNotURI); |
|
2957 return false; |
|
2958 } |
|
2959 |
|
2960 if (!ExpectSymbol(';', true)) { |
|
2961 if (!GatherMedia(media, true) || |
|
2962 !ExpectSymbol(';', true)) { |
|
2963 REPORT_UNEXPECTED_TOKEN(PEImportUnexpected); |
|
2964 // don't advance section, simply ignore invalid @import |
|
2965 return false; |
|
2966 } |
|
2967 |
|
2968 // Safe to assert this, since we ensured that there is something |
|
2969 // other than the ';' coming after the @import's url() token. |
|
2970 NS_ASSERTION(media->Length() != 0, "media list must be nonempty"); |
|
2971 } |
|
2972 |
|
2973 ProcessImport(url, media, aAppendFunc, aData); |
|
2974 return true; |
|
2975 } |
|
2976 |
|
2977 |
|
2978 void |
|
2979 CSSParserImpl::ProcessImport(const nsString& aURLSpec, |
|
2980 nsMediaList* aMedia, |
|
2981 RuleAppendFunc aAppendFunc, |
|
2982 void* aData) |
|
2983 { |
|
2984 nsRefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec); |
|
2985 (*aAppendFunc)(rule, aData); |
|
2986 |
|
2987 // Diagnose bad URIs even if we don't have a child loader. |
|
2988 nsCOMPtr<nsIURI> url; |
|
2989 // Charset will be deduced from mBaseURI, which is more or less correct. |
|
2990 nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI); |
|
2991 |
|
2992 if (NS_FAILED(rv)) { |
|
2993 if (rv == NS_ERROR_MALFORMED_URI) { |
|
2994 // import url is bad |
|
2995 REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec); |
|
2996 OUTPUT_ERROR(); |
|
2997 } |
|
2998 return; |
|
2999 } |
|
3000 |
|
3001 if (mChildLoader) { |
|
3002 mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule); |
|
3003 } |
|
3004 } |
|
3005 |
|
3006 // Parse the {} part of an @media or @-moz-document rule. |
|
3007 bool |
|
3008 CSSParserImpl::ParseGroupRule(css::GroupRule* aRule, |
|
3009 RuleAppendFunc aAppendFunc, |
|
3010 void* aData) |
|
3011 { |
|
3012 // XXXbz this could use better error reporting throughout the method |
|
3013 if (!ExpectSymbol('{', true)) { |
|
3014 return false; |
|
3015 } |
|
3016 |
|
3017 // push rule on stack, loop over children |
|
3018 PushGroup(aRule); |
|
3019 nsCSSSection holdSection = mSection; |
|
3020 mSection = eCSSSection_General; |
|
3021 |
|
3022 for (;;) { |
|
3023 // Get next non-whitespace token |
|
3024 if (! GetToken(true)) { |
|
3025 REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2); |
|
3026 break; |
|
3027 } |
|
3028 if (mToken.IsSymbol('}')) { // done! |
|
3029 UngetToken(); |
|
3030 break; |
|
3031 } |
|
3032 if (eCSSToken_AtKeyword == mToken.mType) { |
|
3033 // Parse for nested rules |
|
3034 ParseAtRule(aAppendFunc, aData, true); |
|
3035 continue; |
|
3036 } |
|
3037 UngetToken(); |
|
3038 ParseRuleSet(AppendRuleToSheet, this, true); |
|
3039 } |
|
3040 PopGroup(); |
|
3041 |
|
3042 if (!ExpectSymbol('}', true)) { |
|
3043 mSection = holdSection; |
|
3044 return false; |
|
3045 } |
|
3046 (*aAppendFunc)(aRule, aData); |
|
3047 return true; |
|
3048 } |
|
3049 |
|
3050 // Parse a CSS2 media rule: "@media medium [, medium] { ... }" |
|
3051 bool |
|
3052 CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData) |
|
3053 { |
|
3054 nsRefPtr<nsMediaList> media = new nsMediaList(); |
|
3055 |
|
3056 if (GatherMedia(media, true)) { |
|
3057 // XXXbz this could use better error reporting throughout the method |
|
3058 nsRefPtr<css::MediaRule> rule = new css::MediaRule(); |
|
3059 // Append first, so when we do SetMedia() the rule |
|
3060 // knows what its stylesheet is. |
|
3061 if (ParseGroupRule(rule, aAppendFunc, aData)) { |
|
3062 rule->SetMedia(media); |
|
3063 return true; |
|
3064 } |
|
3065 } |
|
3066 |
|
3067 return false; |
|
3068 } |
|
3069 |
|
3070 // Parse a @-moz-document rule. This is like an @media rule, but instead |
|
3071 // of a medium it has a nonempty list of items where each item is either |
|
3072 // url(), url-prefix(), or domain(). |
|
3073 bool |
|
3074 CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData) |
|
3075 { |
|
3076 css::DocumentRule::URL *urls = nullptr; |
|
3077 css::DocumentRule::URL **next = &urls; |
|
3078 do { |
|
3079 if (!GetToken(true)) { |
|
3080 REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF); |
|
3081 delete urls; |
|
3082 return false; |
|
3083 } |
|
3084 |
|
3085 if (!(eCSSToken_URL == mToken.mType || |
|
3086 (eCSSToken_Function == mToken.mType && |
|
3087 (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") || |
|
3088 mToken.mIdent.LowerCaseEqualsLiteral("domain") || |
|
3089 mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) { |
|
3090 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc2); |
|
3091 UngetToken(); |
|
3092 delete urls; |
|
3093 return false; |
|
3094 } |
|
3095 css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL; |
|
3096 next = &cur->next; |
|
3097 if (mToken.mType == eCSSToken_URL) { |
|
3098 cur->func = css::DocumentRule::eURL; |
|
3099 CopyUTF16toUTF8(mToken.mIdent, cur->url); |
|
3100 } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) { |
|
3101 // regexp() is different from url-prefix() and domain() (but |
|
3102 // probably the way they *should* have been* in that it requires a |
|
3103 // string argument, and doesn't try to behave like url(). |
|
3104 cur->func = css::DocumentRule::eRegExp; |
|
3105 GetToken(true); |
|
3106 // copy before we know it's valid (but before ExpectSymbol changes |
|
3107 // mToken.mIdent) |
|
3108 CopyUTF16toUTF8(mToken.mIdent, cur->url); |
|
3109 if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) { |
|
3110 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString); |
|
3111 SkipUntil(')'); |
|
3112 delete urls; |
|
3113 return false; |
|
3114 } |
|
3115 } else { |
|
3116 if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) { |
|
3117 cur->func = css::DocumentRule::eURLPrefix; |
|
3118 } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) { |
|
3119 cur->func = css::DocumentRule::eDomain; |
|
3120 } |
|
3121 |
|
3122 NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point"); |
|
3123 if (!mScanner->NextURL(mToken) || mToken.mType != eCSSToken_URL) { |
|
3124 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI); |
|
3125 SkipUntil(')'); |
|
3126 delete urls; |
|
3127 return false; |
|
3128 } |
|
3129 |
|
3130 // We could try to make the URL (as long as it's not domain()) |
|
3131 // canonical and absolute with NS_NewURI and GetSpec, but I'm |
|
3132 // inclined to think we shouldn't. |
|
3133 CopyUTF16toUTF8(mToken.mIdent, cur->url); |
|
3134 } |
|
3135 } while (ExpectSymbol(',', true)); |
|
3136 |
|
3137 nsRefPtr<css::DocumentRule> rule = new css::DocumentRule(); |
|
3138 rule->SetURLs(urls); |
|
3139 |
|
3140 return ParseGroupRule(rule, aAppendFunc, aData); |
|
3141 } |
|
3142 |
|
3143 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;" |
|
3144 bool |
|
3145 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData) |
|
3146 { |
|
3147 if (!GetToken(true)) { |
|
3148 REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF); |
|
3149 return false; |
|
3150 } |
|
3151 |
|
3152 nsAutoString prefix; |
|
3153 nsAutoString url; |
|
3154 |
|
3155 if (eCSSToken_Ident == mToken.mType) { |
|
3156 prefix = mToken.mIdent; |
|
3157 // user-specified identifiers are case-sensitive (bug 416106) |
|
3158 } else { |
|
3159 UngetToken(); |
|
3160 } |
|
3161 |
|
3162 if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) { |
|
3163 if (mHavePushBack) { |
|
3164 REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected); |
|
3165 } else { |
|
3166 REPORT_UNEXPECTED_EOF(PEAtNSURIEOF); |
|
3167 } |
|
3168 return false; |
|
3169 } |
|
3170 |
|
3171 ProcessNameSpace(prefix, url, aAppendFunc, aData); |
|
3172 return true; |
|
3173 } |
|
3174 |
|
3175 void |
|
3176 CSSParserImpl::ProcessNameSpace(const nsString& aPrefix, |
|
3177 const nsString& aURLSpec, |
|
3178 RuleAppendFunc aAppendFunc, |
|
3179 void* aData) |
|
3180 { |
|
3181 nsCOMPtr<nsIAtom> prefix; |
|
3182 |
|
3183 if (!aPrefix.IsEmpty()) { |
|
3184 prefix = do_GetAtom(aPrefix); |
|
3185 if (!prefix) { |
|
3186 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?"); |
|
3187 } |
|
3188 } |
|
3189 |
|
3190 nsRefPtr<css::NameSpaceRule> rule = new css::NameSpaceRule(prefix, aURLSpec); |
|
3191 (*aAppendFunc)(rule, aData); |
|
3192 |
|
3193 // If this was the first namespace rule encountered, it will trigger |
|
3194 // creation of a namespace map. |
|
3195 if (!mNameSpaceMap) { |
|
3196 mNameSpaceMap = mSheet->GetNameSpaceMap(); |
|
3197 } |
|
3198 } |
|
3199 |
|
3200 // font-face-rule: '@font-face' '{' font-description '}' |
|
3201 // font-description: font-descriptor+ |
|
3202 bool |
|
3203 CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData) |
|
3204 { |
|
3205 if (!ExpectSymbol('{', true)) { |
|
3206 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart); |
|
3207 return false; |
|
3208 } |
|
3209 |
|
3210 nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule()); |
|
3211 |
|
3212 for (;;) { |
|
3213 if (!GetToken(true)) { |
|
3214 REPORT_UNEXPECTED_EOF(PEFontFaceEOF); |
|
3215 break; |
|
3216 } |
|
3217 if (mToken.IsSymbol('}')) { // done! |
|
3218 UngetToken(); |
|
3219 break; |
|
3220 } |
|
3221 |
|
3222 // ignore extra semicolons |
|
3223 if (mToken.IsSymbol(';')) |
|
3224 continue; |
|
3225 |
|
3226 if (!ParseFontDescriptor(rule)) { |
|
3227 REPORT_UNEXPECTED(PEDeclSkipped); |
|
3228 OUTPUT_ERROR(); |
|
3229 if (!SkipDeclaration(true)) |
|
3230 break; |
|
3231 } |
|
3232 } |
|
3233 if (!ExpectSymbol('}', true)) { |
|
3234 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd); |
|
3235 return false; |
|
3236 } |
|
3237 (*aAppendFunc)(rule, aData); |
|
3238 return true; |
|
3239 } |
|
3240 |
|
3241 // font-descriptor: font-family-desc |
|
3242 // | font-style-desc |
|
3243 // | font-weight-desc |
|
3244 // | font-stretch-desc |
|
3245 // | font-src-desc |
|
3246 // | unicode-range-desc |
|
3247 // |
|
3248 // All font-*-desc productions follow the pattern |
|
3249 // IDENT ':' value ';' |
|
3250 // |
|
3251 // On entry to this function, mToken is the IDENT. |
|
3252 |
|
3253 bool |
|
3254 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule) |
|
3255 { |
|
3256 if (eCSSToken_Ident != mToken.mType) { |
|
3257 REPORT_UNEXPECTED_TOKEN(PEFontDescExpected); |
|
3258 return false; |
|
3259 } |
|
3260 |
|
3261 nsString descName = mToken.mIdent; |
|
3262 if (!ExpectSymbol(':', true)) { |
|
3263 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon); |
|
3264 OUTPUT_ERROR(); |
|
3265 return false; |
|
3266 } |
|
3267 |
|
3268 nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName); |
|
3269 nsCSSValue value; |
|
3270 |
|
3271 if (descID == eCSSFontDesc_UNKNOWN) { |
|
3272 if (NonMozillaVendorIdentifier(descName)) { |
|
3273 // silently skip other vendors' extensions |
|
3274 SkipDeclaration(true); |
|
3275 return true; |
|
3276 } else { |
|
3277 REPORT_UNEXPECTED_P(PEUnknownFontDesc, descName); |
|
3278 return false; |
|
3279 } |
|
3280 } |
|
3281 |
|
3282 if (!ParseFontDescriptorValue(descID, value)) { |
|
3283 REPORT_UNEXPECTED_P(PEValueParsingError, descName); |
|
3284 return false; |
|
3285 } |
|
3286 |
|
3287 if (!ExpectEndProperty()) |
|
3288 return false; |
|
3289 |
|
3290 aRule->SetDesc(descID, value); |
|
3291 return true; |
|
3292 } |
|
3293 |
|
3294 // @font-feature-values <font-family># { |
|
3295 // @<feature-type> { |
|
3296 // <feature-ident> : <feature-index>+; |
|
3297 // <feature-ident> : <feature-index>+; |
|
3298 // ... |
|
3299 // } |
|
3300 // ... |
|
3301 // } |
|
3302 |
|
3303 bool |
|
3304 CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc, |
|
3305 void* aData) |
|
3306 { |
|
3307 nsRefPtr<nsCSSFontFeatureValuesRule> |
|
3308 valuesRule(new nsCSSFontFeatureValuesRule()); |
|
3309 |
|
3310 // parse family list |
|
3311 nsCSSValue familyValue; |
|
3312 |
|
3313 if (!ParseFamily(familyValue) || |
|
3314 familyValue.GetUnit() != eCSSUnit_Families) |
|
3315 { |
|
3316 REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily); |
|
3317 return false; |
|
3318 } |
|
3319 |
|
3320 // add family to rule |
|
3321 nsAutoString familyList; |
|
3322 bool hasGeneric; |
|
3323 familyValue.GetStringValue(familyList); |
|
3324 valuesRule->SetFamilyList(familyList, hasGeneric); |
|
3325 |
|
3326 // family list has generic ==> parse error |
|
3327 if (hasGeneric) { |
|
3328 REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList); |
|
3329 return false; |
|
3330 } |
|
3331 |
|
3332 // open brace |
|
3333 if (!ExpectSymbol('{', true)) { |
|
3334 REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart); |
|
3335 return false; |
|
3336 } |
|
3337 |
|
3338 // list of sets of feature values, each set bound to a specific |
|
3339 // feature-type (e.g. swash, annotation) |
|
3340 for (;;) { |
|
3341 if (!GetToken(true)) { |
|
3342 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF); |
|
3343 break; |
|
3344 } |
|
3345 if (mToken.IsSymbol('}')) { // done! |
|
3346 UngetToken(); |
|
3347 break; |
|
3348 } |
|
3349 |
|
3350 if (!ParseFontFeatureValueSet(valuesRule)) { |
|
3351 if (!SkipAtRule(false)) { |
|
3352 break; |
|
3353 } |
|
3354 } |
|
3355 } |
|
3356 if (!ExpectSymbol('}', true)) { |
|
3357 REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd); |
|
3358 SkipUntil('}'); |
|
3359 return false; |
|
3360 } |
|
3361 |
|
3362 (*aAppendFunc)(valuesRule, aData); |
|
3363 return true; |
|
3364 } |
|
3365 |
|
3366 #define NUMVALUES_NO_LIMIT 0xFFFF |
|
3367 |
|
3368 // parse a single value set containing name-value pairs for a single feature type |
|
3369 // @<feature-type> { [ <feature-ident> : <feature-index>+ ; ]* } |
|
3370 // Ex: @swash { flowing: 1; delicate: 2; } |
|
3371 bool |
|
3372 CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule |
|
3373 *aFeatureValuesRule) |
|
3374 { |
|
3375 // -- @keyword (e.g. swash, styleset) |
|
3376 if (eCSSToken_AtKeyword != mToken.mType) { |
|
3377 REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt); |
|
3378 OUTPUT_ERROR(); |
|
3379 UngetToken(); |
|
3380 return false; |
|
3381 } |
|
3382 |
|
3383 // which font-specific variant of font-variant-alternates |
|
3384 int32_t whichVariant; |
|
3385 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); |
|
3386 if (keyword == eCSSKeyword_UNKNOWN || |
|
3387 !nsCSSProps::FindKeyword(keyword, |
|
3388 nsCSSProps::kFontVariantAlternatesFuncsKTable, |
|
3389 whichVariant)) |
|
3390 { |
|
3391 if (!NonMozillaVendorIdentifier(mToken.mIdent)) { |
|
3392 REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue); |
|
3393 OUTPUT_ERROR(); |
|
3394 } |
|
3395 UngetToken(); |
|
3396 return false; |
|
3397 } |
|
3398 |
|
3399 nsAutoString featureType(mToken.mIdent); |
|
3400 |
|
3401 // open brace |
|
3402 if (!ExpectSymbol('{', true)) { |
|
3403 REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart); |
|
3404 return false; |
|
3405 } |
|
3406 |
|
3407 // styleset and character-variant can be multi-valued, otherwise single value |
|
3408 int32_t limitNumValues; |
|
3409 |
|
3410 switch (keyword) { |
|
3411 case eCSSKeyword_styleset: |
|
3412 limitNumValues = NUMVALUES_NO_LIMIT; |
|
3413 break; |
|
3414 case eCSSKeyword_character_variant: |
|
3415 limitNumValues = 2; |
|
3416 break; |
|
3417 default: |
|
3418 limitNumValues = 1; |
|
3419 break; |
|
3420 } |
|
3421 |
|
3422 // -- ident integer+ [, ident integer+] |
|
3423 nsAutoTArray<gfxFontFeatureValueSet::ValueList, 5> values; |
|
3424 |
|
3425 // list of font-feature-values-declaration's |
|
3426 for (;;) { |
|
3427 nsAutoString valueId; |
|
3428 |
|
3429 if (!GetToken(true)) { |
|
3430 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF); |
|
3431 break; |
|
3432 } |
|
3433 |
|
3434 // ignore extra semicolons |
|
3435 if (mToken.IsSymbol(';')) { |
|
3436 continue; |
|
3437 } |
|
3438 |
|
3439 // close brace ==> done |
|
3440 if (mToken.IsSymbol('}')) { |
|
3441 break; |
|
3442 } |
|
3443 |
|
3444 // ident |
|
3445 if (eCSSToken_Ident != mToken.mType) { |
|
3446 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent); |
|
3447 if (!SkipDeclaration(true)) { |
|
3448 break; |
|
3449 } |
|
3450 continue; |
|
3451 } |
|
3452 |
|
3453 valueId.Assign(mToken.mIdent); |
|
3454 |
|
3455 // colon |
|
3456 if (!ExpectSymbol(':', true)) { |
|
3457 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon); |
|
3458 OUTPUT_ERROR(); |
|
3459 if (!SkipDeclaration(true)) { |
|
3460 break; |
|
3461 } |
|
3462 continue; |
|
3463 } |
|
3464 |
|
3465 // value list |
|
3466 nsAutoTArray<uint32_t,4> featureSelectors; |
|
3467 |
|
3468 nsCSSValue intValue; |
|
3469 while (ParseNonNegativeVariant(intValue, VARIANT_INTEGER, nullptr)) { |
|
3470 featureSelectors.AppendElement(uint32_t(intValue.GetIntValue())); |
|
3471 } |
|
3472 |
|
3473 int32_t numValues = featureSelectors.Length(); |
|
3474 |
|
3475 if (numValues == 0) { |
|
3476 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue); |
|
3477 OUTPUT_ERROR(); |
|
3478 if (!SkipDeclaration(true)) { |
|
3479 break; |
|
3480 } |
|
3481 continue; |
|
3482 } |
|
3483 |
|
3484 if (numValues > limitNumValues) { |
|
3485 REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType); |
|
3486 OUTPUT_ERROR(); |
|
3487 if (!SkipDeclaration(true)) { |
|
3488 break; |
|
3489 } |
|
3490 continue; |
|
3491 } |
|
3492 |
|
3493 if (!GetToken(true)) { |
|
3494 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF); |
|
3495 gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors); |
|
3496 values.AppendElement(v); |
|
3497 break; |
|
3498 } |
|
3499 |
|
3500 // ';' or '}' to end definition |
|
3501 if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) { |
|
3502 REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing); |
|
3503 OUTPUT_ERROR(); |
|
3504 if (!SkipDeclaration(true)) { |
|
3505 break; |
|
3506 } |
|
3507 continue; |
|
3508 } |
|
3509 |
|
3510 gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors); |
|
3511 values.AppendElement(v); |
|
3512 |
|
3513 if (mToken.IsSymbol('}')) { |
|
3514 break; |
|
3515 } |
|
3516 } |
|
3517 |
|
3518 aFeatureValuesRule->AddValueList(whichVariant, values); |
|
3519 return true; |
|
3520 } |
|
3521 |
|
3522 bool |
|
3523 CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData) |
|
3524 { |
|
3525 if (!GetToken(true)) { |
|
3526 REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF); |
|
3527 return false; |
|
3528 } |
|
3529 |
|
3530 if (mToken.mType != eCSSToken_Ident) { |
|
3531 REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName); |
|
3532 UngetToken(); |
|
3533 return false; |
|
3534 } |
|
3535 nsString name(mToken.mIdent); |
|
3536 |
|
3537 if (!ExpectSymbol('{', true)) { |
|
3538 REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace); |
|
3539 return false; |
|
3540 } |
|
3541 |
|
3542 nsRefPtr<nsCSSKeyframesRule> rule = new nsCSSKeyframesRule(name); |
|
3543 |
|
3544 while (!ExpectSymbol('}', true)) { |
|
3545 nsRefPtr<nsCSSKeyframeRule> kid = ParseKeyframeRule(); |
|
3546 if (kid) { |
|
3547 rule->AppendStyleRule(kid); |
|
3548 } else { |
|
3549 OUTPUT_ERROR(); |
|
3550 SkipRuleSet(true); |
|
3551 } |
|
3552 } |
|
3553 |
|
3554 (*aAppendFunc)(rule, aData); |
|
3555 return true; |
|
3556 } |
|
3557 |
|
3558 bool |
|
3559 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData) |
|
3560 { |
|
3561 // TODO: There can be page selectors after @page such as ":first", ":left". |
|
3562 uint32_t parseFlags = eParseDeclaration_InBraces | |
|
3563 eParseDeclaration_AllowImportant; |
|
3564 |
|
3565 // Forbid viewport units in @page rules. See bug 811391. |
|
3566 NS_ABORT_IF_FALSE(mViewportUnitsEnabled, |
|
3567 "Viewport units should be enabled outside of @page rules."); |
|
3568 mViewportUnitsEnabled = false; |
|
3569 nsAutoPtr<css::Declaration> declaration( |
|
3570 ParseDeclarationBlock(parseFlags, |
|
3571 eCSSContext_Page)); |
|
3572 mViewportUnitsEnabled = true; |
|
3573 |
|
3574 if (!declaration) { |
|
3575 return false; |
|
3576 } |
|
3577 |
|
3578 // Takes ownership of declaration. |
|
3579 nsRefPtr<nsCSSPageRule> rule = new nsCSSPageRule(declaration); |
|
3580 |
|
3581 (*aAppendFunc)(rule, aData); |
|
3582 return true; |
|
3583 } |
|
3584 |
|
3585 already_AddRefed<nsCSSKeyframeRule> |
|
3586 CSSParserImpl::ParseKeyframeRule() |
|
3587 { |
|
3588 InfallibleTArray<float> selectorList; |
|
3589 if (!ParseKeyframeSelectorList(selectorList)) { |
|
3590 REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored); |
|
3591 return nullptr; |
|
3592 } |
|
3593 |
|
3594 // Ignore !important in keyframe rules |
|
3595 uint32_t parseFlags = eParseDeclaration_InBraces; |
|
3596 nsAutoPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags)); |
|
3597 if (!declaration) { |
|
3598 return nullptr; |
|
3599 } |
|
3600 |
|
3601 // Takes ownership of declaration, and steals contents of selectorList. |
|
3602 nsRefPtr<nsCSSKeyframeRule> rule = |
|
3603 new nsCSSKeyframeRule(selectorList, declaration); |
|
3604 |
|
3605 return rule.forget(); |
|
3606 } |
|
3607 |
|
3608 bool |
|
3609 CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList) |
|
3610 { |
|
3611 for (;;) { |
|
3612 if (!GetToken(true)) { |
|
3613 // The first time through the loop, this means we got an empty |
|
3614 // list. Otherwise, it means we have a trailing comma. |
|
3615 return false; |
|
3616 } |
|
3617 float value; |
|
3618 switch (mToken.mType) { |
|
3619 case eCSSToken_Percentage: |
|
3620 value = mToken.mNumber; |
|
3621 break; |
|
3622 case eCSSToken_Ident: |
|
3623 if (mToken.mIdent.LowerCaseEqualsLiteral("from")) { |
|
3624 value = 0.0f; |
|
3625 break; |
|
3626 } |
|
3627 if (mToken.mIdent.LowerCaseEqualsLiteral("to")) { |
|
3628 value = 1.0f; |
|
3629 break; |
|
3630 } |
|
3631 // fall through |
|
3632 default: |
|
3633 UngetToken(); |
|
3634 // The first time through the loop, this means we got an empty |
|
3635 // list. Otherwise, it means we have a trailing comma. |
|
3636 return false; |
|
3637 } |
|
3638 aSelectorList.AppendElement(value); |
|
3639 if (!ExpectSymbol(',', true)) { |
|
3640 return true; |
|
3641 } |
|
3642 } |
|
3643 } |
|
3644 |
|
3645 // supports_rule |
|
3646 // : "@supports" supports_condition group_rule_body |
|
3647 // ; |
|
3648 bool |
|
3649 CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData) |
|
3650 { |
|
3651 bool conditionMet = false; |
|
3652 nsString condition; |
|
3653 |
|
3654 mScanner->StartRecording(); |
|
3655 bool parsed = ParseSupportsCondition(conditionMet); |
|
3656 |
|
3657 if (!parsed) { |
|
3658 mScanner->StopRecording(); |
|
3659 return false; |
|
3660 } |
|
3661 |
|
3662 if (!ExpectSymbol('{', true)) { |
|
3663 REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart); |
|
3664 mScanner->StopRecording(); |
|
3665 return false; |
|
3666 } |
|
3667 |
|
3668 UngetToken(); |
|
3669 mScanner->StopRecording(condition); |
|
3670 |
|
3671 // Remove the "{" that would follow the condition. |
|
3672 if (condition.Length() != 0) { |
|
3673 condition.Truncate(condition.Length() - 1); |
|
3674 } |
|
3675 |
|
3676 // Remove spaces from the start and end of the recorded supports condition. |
|
3677 condition.Trim(" ", true, true, false); |
|
3678 |
|
3679 // Record whether we are in a failing @supports, so that property parse |
|
3680 // errors don't get reported. |
|
3681 nsAutoFailingSupportsRule failing(this, conditionMet); |
|
3682 |
|
3683 nsRefPtr<css::GroupRule> rule = new CSSSupportsRule(conditionMet, condition); |
|
3684 return ParseGroupRule(rule, aAppendFunc, aProcessData); |
|
3685 } |
|
3686 |
|
3687 // supports_condition |
|
3688 // : supports_condition_in_parens supports_condition_terms |
|
3689 // | supports_condition_negation |
|
3690 // ; |
|
3691 bool |
|
3692 CSSParserImpl::ParseSupportsCondition(bool& aConditionMet) |
|
3693 { |
|
3694 mInSupportsCondition = true; |
|
3695 |
|
3696 if (!GetToken(true)) { |
|
3697 REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2); |
|
3698 return false; |
|
3699 } |
|
3700 |
|
3701 UngetToken(); |
|
3702 |
|
3703 mScanner->ClearSeenBadToken(); |
|
3704 |
|
3705 if (mToken.IsSymbol('(') || |
|
3706 mToken.mType == eCSSToken_Function || |
|
3707 mToken.mType == eCSSToken_URL || |
|
3708 mToken.mType == eCSSToken_Bad_URL) { |
|
3709 bool result = ParseSupportsConditionInParens(aConditionMet) && |
|
3710 ParseSupportsConditionTerms(aConditionMet) && |
|
3711 !mScanner->SeenBadToken(); |
|
3712 mInSupportsCondition = false; |
|
3713 return result; |
|
3714 } |
|
3715 |
|
3716 if (mToken.mType == eCSSToken_Ident && |
|
3717 mToken.mIdent.LowerCaseEqualsLiteral("not")) { |
|
3718 bool result = ParseSupportsConditionNegation(aConditionMet) && |
|
3719 !mScanner->SeenBadToken(); |
|
3720 mInSupportsCondition = false; |
|
3721 return result; |
|
3722 } |
|
3723 |
|
3724 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart); |
|
3725 mInSupportsCondition = false; |
|
3726 return false; |
|
3727 } |
|
3728 |
|
3729 // supports_condition_negation |
|
3730 // : 'not' S+ supports_condition_in_parens |
|
3731 // ; |
|
3732 bool |
|
3733 CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet) |
|
3734 { |
|
3735 if (!GetToken(true)) { |
|
3736 REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF); |
|
3737 return false; |
|
3738 } |
|
3739 |
|
3740 if (mToken.mType != eCSSToken_Ident || |
|
3741 !mToken.mIdent.LowerCaseEqualsLiteral("not")) { |
|
3742 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot); |
|
3743 return false; |
|
3744 } |
|
3745 |
|
3746 if (!RequireWhitespace()) { |
|
3747 REPORT_UNEXPECTED(PESupportsWhitespaceRequired); |
|
3748 return false; |
|
3749 } |
|
3750 |
|
3751 if (ParseSupportsConditionInParens(aConditionMet)) { |
|
3752 aConditionMet = !aConditionMet; |
|
3753 return true; |
|
3754 } |
|
3755 |
|
3756 return false; |
|
3757 } |
|
3758 |
|
3759 // supports_condition_in_parens |
|
3760 // : '(' S* supports_condition_in_parens_inside_parens ')' S* |
|
3761 // | general_enclosed |
|
3762 // ; |
|
3763 bool |
|
3764 CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet) |
|
3765 { |
|
3766 if (!GetToken(true)) { |
|
3767 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF); |
|
3768 return false; |
|
3769 } |
|
3770 |
|
3771 if (mToken.mType == eCSSToken_URL) { |
|
3772 aConditionMet = false; |
|
3773 return true; |
|
3774 } |
|
3775 |
|
3776 if (mToken.mType == eCSSToken_Function || |
|
3777 mToken.mType == eCSSToken_Bad_URL) { |
|
3778 if (!SkipUntil(')')) { |
|
3779 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF); |
|
3780 return false; |
|
3781 } |
|
3782 aConditionMet = false; |
|
3783 return true; |
|
3784 } |
|
3785 |
|
3786 if (!mToken.IsSymbol('(')) { |
|
3787 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction); |
|
3788 UngetToken(); |
|
3789 return false; |
|
3790 } |
|
3791 |
|
3792 if (!ParseSupportsConditionInParensInsideParens(aConditionMet)) { |
|
3793 if (!SkipUntil(')')) { |
|
3794 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF); |
|
3795 return false; |
|
3796 } |
|
3797 aConditionMet = false; |
|
3798 return true; |
|
3799 } |
|
3800 |
|
3801 if (!(ExpectSymbol(')', true))) { |
|
3802 SkipUntil(')'); |
|
3803 aConditionMet = false; |
|
3804 return true; |
|
3805 } |
|
3806 |
|
3807 return true; |
|
3808 } |
|
3809 |
|
3810 // supports_condition_in_parens_inside_parens |
|
3811 // : core_declaration |
|
3812 // | supports_condition_negation |
|
3813 // | supports_condition_in_parens supports_condition_terms |
|
3814 // ; |
|
3815 bool |
|
3816 CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet) |
|
3817 { |
|
3818 if (!GetToken(true)) { |
|
3819 return false; |
|
3820 } |
|
3821 |
|
3822 if (mToken.mType == eCSSToken_Ident) { |
|
3823 if (!mToken.mIdent.LowerCaseEqualsLiteral("not")) { |
|
3824 nsAutoString propertyName = mToken.mIdent; |
|
3825 if (!ExpectSymbol(':', true)) { |
|
3826 return false; |
|
3827 } |
|
3828 |
|
3829 nsCSSProperty propID = LookupEnabledProperty(propertyName); |
|
3830 if (propID == eCSSProperty_UNKNOWN) { |
|
3831 if (ExpectSymbol(')', true)) { |
|
3832 UngetToken(); |
|
3833 return false; |
|
3834 } |
|
3835 aConditionMet = false; |
|
3836 SkipUntil(')'); |
|
3837 UngetToken(); |
|
3838 } else if (propID == eCSSPropertyExtra_variable) { |
|
3839 if (ExpectSymbol(')', false)) { |
|
3840 UngetToken(); |
|
3841 return false; |
|
3842 } |
|
3843 CSSVariableDeclarations::Type variableType; |
|
3844 nsString variableValue; |
|
3845 aConditionMet = |
|
3846 ParseVariableDeclaration(&variableType, variableValue) && |
|
3847 ParsePriority() != ePriority_Error; |
|
3848 if (!aConditionMet) { |
|
3849 SkipUntil(')'); |
|
3850 UngetToken(); |
|
3851 } |
|
3852 } else { |
|
3853 if (ExpectSymbol(')', true)) { |
|
3854 UngetToken(); |
|
3855 return false; |
|
3856 } |
|
3857 aConditionMet = ParseProperty(propID) && |
|
3858 ParsePriority() != ePriority_Error; |
|
3859 if (!aConditionMet) { |
|
3860 SkipUntil(')'); |
|
3861 UngetToken(); |
|
3862 } |
|
3863 mTempData.ClearProperty(propID); |
|
3864 mTempData.AssertInitialState(); |
|
3865 } |
|
3866 return true; |
|
3867 } |
|
3868 |
|
3869 UngetToken(); |
|
3870 return ParseSupportsConditionNegation(aConditionMet); |
|
3871 } |
|
3872 |
|
3873 UngetToken(); |
|
3874 return ParseSupportsConditionInParens(aConditionMet) && |
|
3875 ParseSupportsConditionTerms(aConditionMet); |
|
3876 } |
|
3877 |
|
3878 // supports_condition_terms |
|
3879 // : S+ 'and' supports_condition_terms_after_operator('and') |
|
3880 // | S+ 'or' supports_condition_terms_after_operator('or') |
|
3881 // | |
|
3882 // ; |
|
3883 bool |
|
3884 CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet) |
|
3885 { |
|
3886 if (!RequireWhitespace() || !GetToken(false)) { |
|
3887 return true; |
|
3888 } |
|
3889 |
|
3890 if (mToken.mType != eCSSToken_Ident) { |
|
3891 UngetToken(); |
|
3892 return true; |
|
3893 } |
|
3894 |
|
3895 if (mToken.mIdent.LowerCaseEqualsLiteral("and")) { |
|
3896 return ParseSupportsConditionTermsAfterOperator(aConditionMet, eAnd); |
|
3897 } |
|
3898 |
|
3899 if (mToken.mIdent.LowerCaseEqualsLiteral("or")) { |
|
3900 return ParseSupportsConditionTermsAfterOperator(aConditionMet, eOr); |
|
3901 } |
|
3902 |
|
3903 UngetToken(); |
|
3904 return true; |
|
3905 } |
|
3906 |
|
3907 // supports_condition_terms_after_operator(operator) |
|
3908 // : S+ supports_condition_in_parens ( <operator> supports_condition_in_parens )* |
|
3909 // ; |
|
3910 bool |
|
3911 CSSParserImpl::ParseSupportsConditionTermsAfterOperator( |
|
3912 bool& aConditionMet, |
|
3913 CSSParserImpl::SupportsConditionTermOperator aOperator) |
|
3914 { |
|
3915 if (!RequireWhitespace()) { |
|
3916 REPORT_UNEXPECTED(PESupportsWhitespaceRequired); |
|
3917 return false; |
|
3918 } |
|
3919 |
|
3920 const char* token = aOperator == eAnd ? "and" : "or"; |
|
3921 for (;;) { |
|
3922 bool termConditionMet = false; |
|
3923 if (!ParseSupportsConditionInParens(termConditionMet)) { |
|
3924 return false; |
|
3925 } |
|
3926 aConditionMet = aOperator == eAnd ? aConditionMet && termConditionMet : |
|
3927 aConditionMet || termConditionMet; |
|
3928 |
|
3929 if (!GetToken(true)) { |
|
3930 return true; |
|
3931 } |
|
3932 |
|
3933 if (mToken.mType != eCSSToken_Ident || |
|
3934 !mToken.mIdent.LowerCaseEqualsASCII(token)) { |
|
3935 UngetToken(); |
|
3936 return true; |
|
3937 } |
|
3938 } |
|
3939 } |
|
3940 |
|
3941 bool |
|
3942 CSSParserImpl::SkipUntil(char16_t aStopSymbol) |
|
3943 { |
|
3944 nsCSSToken* tk = &mToken; |
|
3945 nsAutoTArray<char16_t, 16> stack; |
|
3946 stack.AppendElement(aStopSymbol); |
|
3947 for (;;) { |
|
3948 if (!GetToken(true)) { |
|
3949 return false; |
|
3950 } |
|
3951 if (eCSSToken_Symbol == tk->mType) { |
|
3952 char16_t symbol = tk->mSymbol; |
|
3953 uint32_t stackTopIndex = stack.Length() - 1; |
|
3954 if (symbol == stack.ElementAt(stackTopIndex)) { |
|
3955 stack.RemoveElementAt(stackTopIndex); |
|
3956 if (stackTopIndex == 0) { |
|
3957 return true; |
|
3958 } |
|
3959 |
|
3960 // Just handle out-of-memory by parsing incorrectly. It's |
|
3961 // highly unlikely we're dealing with a legitimate style sheet |
|
3962 // anyway. |
|
3963 } else if ('{' == symbol) { |
|
3964 stack.AppendElement('}'); |
|
3965 } else if ('[' == symbol) { |
|
3966 stack.AppendElement(']'); |
|
3967 } else if ('(' == symbol) { |
|
3968 stack.AppendElement(')'); |
|
3969 } |
|
3970 } else if (eCSSToken_Function == tk->mType || |
|
3971 eCSSToken_Bad_URL == tk->mType) { |
|
3972 stack.AppendElement(')'); |
|
3973 } |
|
3974 } |
|
3975 } |
|
3976 |
|
3977 bool |
|
3978 CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol) |
|
3979 { |
|
3980 nsCSSToken* tk = &mToken; |
|
3981 nsAutoTArray<char16_t, 16> stack; |
|
3982 stack.AppendElement(aStopSymbol); |
|
3983 for (;;) { |
|
3984 if (!GetToken(true)) { |
|
3985 return true; |
|
3986 } |
|
3987 if (eCSSToken_Symbol == tk->mType) { |
|
3988 char16_t symbol = tk->mSymbol; |
|
3989 uint32_t stackTopIndex = stack.Length() - 1; |
|
3990 if (symbol == stack.ElementAt(stackTopIndex)) { |
|
3991 stack.RemoveElementAt(stackTopIndex); |
|
3992 if (stackTopIndex == 0) { |
|
3993 return true; |
|
3994 } |
|
3995 |
|
3996 // Just handle out-of-memory by parsing incorrectly. It's |
|
3997 // highly unlikely we're dealing with a legitimate style sheet |
|
3998 // anyway. |
|
3999 } else if ('{' == symbol) { |
|
4000 stack.AppendElement('}'); |
|
4001 } else if ('[' == symbol) { |
|
4002 stack.AppendElement(']'); |
|
4003 } else if ('(' == symbol) { |
|
4004 stack.AppendElement(')'); |
|
4005 } else if (')' == symbol || |
|
4006 ']' == symbol || |
|
4007 '}' == symbol) { |
|
4008 UngetToken(); |
|
4009 return false; |
|
4010 } |
|
4011 } else if (eCSSToken_Function == tk->mType || |
|
4012 eCSSToken_Bad_URL == tk->mType) { |
|
4013 stack.AppendElement(')'); |
|
4014 } |
|
4015 } |
|
4016 } |
|
4017 |
|
4018 void |
|
4019 CSSParserImpl::SkipUntilOneOf(const char16_t* aStopSymbolChars) |
|
4020 { |
|
4021 nsCSSToken* tk = &mToken; |
|
4022 nsDependentString stopSymbolChars(aStopSymbolChars); |
|
4023 for (;;) { |
|
4024 if (!GetToken(true)) { |
|
4025 break; |
|
4026 } |
|
4027 if (eCSSToken_Symbol == tk->mType) { |
|
4028 char16_t symbol = tk->mSymbol; |
|
4029 if (stopSymbolChars.FindChar(symbol) != -1) { |
|
4030 break; |
|
4031 } else if ('{' == symbol) { |
|
4032 SkipUntil('}'); |
|
4033 } else if ('[' == symbol) { |
|
4034 SkipUntil(']'); |
|
4035 } else if ('(' == symbol) { |
|
4036 SkipUntil(')'); |
|
4037 } |
|
4038 } else if (eCSSToken_Function == tk->mType || |
|
4039 eCSSToken_Bad_URL == tk->mType) { |
|
4040 SkipUntil(')'); |
|
4041 } |
|
4042 } |
|
4043 } |
|
4044 |
|
4045 void |
|
4046 CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars) |
|
4047 { |
|
4048 uint32_t i = aStopSymbolChars.Length(); |
|
4049 while (i--) { |
|
4050 SkipUntil(aStopSymbolChars[i]); |
|
4051 } |
|
4052 } |
|
4053 |
|
4054 bool |
|
4055 CSSParserImpl::SkipDeclaration(bool aCheckForBraces) |
|
4056 { |
|
4057 nsCSSToken* tk = &mToken; |
|
4058 for (;;) { |
|
4059 if (!GetToken(true)) { |
|
4060 if (aCheckForBraces) { |
|
4061 REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF); |
|
4062 } |
|
4063 return false; |
|
4064 } |
|
4065 if (eCSSToken_Symbol == tk->mType) { |
|
4066 char16_t symbol = tk->mSymbol; |
|
4067 if (';' == symbol) { |
|
4068 break; |
|
4069 } |
|
4070 if (aCheckForBraces) { |
|
4071 if ('}' == symbol) { |
|
4072 UngetToken(); |
|
4073 break; |
|
4074 } |
|
4075 } |
|
4076 if ('{' == symbol) { |
|
4077 SkipUntil('}'); |
|
4078 } else if ('(' == symbol) { |
|
4079 SkipUntil(')'); |
|
4080 } else if ('[' == symbol) { |
|
4081 SkipUntil(']'); |
|
4082 } |
|
4083 } else if (eCSSToken_Function == tk->mType || |
|
4084 eCSSToken_Bad_URL == tk->mType) { |
|
4085 SkipUntil(')'); |
|
4086 } |
|
4087 } |
|
4088 return true; |
|
4089 } |
|
4090 |
|
4091 void |
|
4092 CSSParserImpl::SkipRuleSet(bool aInsideBraces) |
|
4093 { |
|
4094 nsCSSToken* tk = &mToken; |
|
4095 for (;;) { |
|
4096 if (!GetToken(true)) { |
|
4097 REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF); |
|
4098 break; |
|
4099 } |
|
4100 if (eCSSToken_Symbol == tk->mType) { |
|
4101 char16_t symbol = tk->mSymbol; |
|
4102 if ('}' == symbol && aInsideBraces) { |
|
4103 // leave block closer for higher-level grammar to consume |
|
4104 UngetToken(); |
|
4105 break; |
|
4106 } else if ('{' == symbol) { |
|
4107 SkipUntil('}'); |
|
4108 break; |
|
4109 } else if ('(' == symbol) { |
|
4110 SkipUntil(')'); |
|
4111 } else if ('[' == symbol) { |
|
4112 SkipUntil(']'); |
|
4113 } |
|
4114 } else if (eCSSToken_Function == tk->mType || |
|
4115 eCSSToken_Bad_URL == tk->mType) { |
|
4116 SkipUntil(')'); |
|
4117 } |
|
4118 } |
|
4119 } |
|
4120 |
|
4121 void |
|
4122 CSSParserImpl::PushGroup(css::GroupRule* aRule) |
|
4123 { |
|
4124 mGroupStack.AppendElement(aRule); |
|
4125 } |
|
4126 |
|
4127 void |
|
4128 CSSParserImpl::PopGroup() |
|
4129 { |
|
4130 uint32_t count = mGroupStack.Length(); |
|
4131 if (0 < count) { |
|
4132 mGroupStack.RemoveElementAt(count - 1); |
|
4133 } |
|
4134 } |
|
4135 |
|
4136 void |
|
4137 CSSParserImpl::AppendRule(css::Rule* aRule) |
|
4138 { |
|
4139 uint32_t count = mGroupStack.Length(); |
|
4140 if (0 < count) { |
|
4141 mGroupStack[count - 1]->AppendStyleRule(aRule); |
|
4142 } |
|
4143 else { |
|
4144 mSheet->AppendStyleRule(aRule); |
|
4145 } |
|
4146 } |
|
4147 |
|
4148 bool |
|
4149 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData, |
|
4150 bool aInsideBraces) |
|
4151 { |
|
4152 // First get the list of selectors for the rule |
|
4153 nsCSSSelectorList* slist = nullptr; |
|
4154 uint32_t linenum, colnum; |
|
4155 if (!GetNextTokenLocation(true, &linenum, &colnum) || |
|
4156 !ParseSelectorList(slist, char16_t('{'))) { |
|
4157 REPORT_UNEXPECTED(PEBadSelectorRSIgnored); |
|
4158 OUTPUT_ERROR(); |
|
4159 SkipRuleSet(aInsideBraces); |
|
4160 return false; |
|
4161 } |
|
4162 NS_ASSERTION(nullptr != slist, "null selector list"); |
|
4163 CLEAR_ERROR(); |
|
4164 |
|
4165 // Next parse the declaration block |
|
4166 uint32_t parseFlags = eParseDeclaration_InBraces | |
|
4167 eParseDeclaration_AllowImportant; |
|
4168 css::Declaration* declaration = ParseDeclarationBlock(parseFlags); |
|
4169 if (nullptr == declaration) { |
|
4170 delete slist; |
|
4171 return false; |
|
4172 } |
|
4173 |
|
4174 #if 0 |
|
4175 slist->Dump(); |
|
4176 fputs("{\n", stdout); |
|
4177 declaration->List(); |
|
4178 fputs("}\n", stdout); |
|
4179 #endif |
|
4180 |
|
4181 // Translate the selector list and declaration block into style data |
|
4182 |
|
4183 nsRefPtr<css::StyleRule> rule = new css::StyleRule(slist, declaration); |
|
4184 rule->SetLineNumberAndColumnNumber(linenum, colnum); |
|
4185 (*aAppendFunc)(rule, aData); |
|
4186 |
|
4187 return true; |
|
4188 } |
|
4189 |
|
4190 bool |
|
4191 CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead, |
|
4192 char16_t aStopChar) |
|
4193 { |
|
4194 nsCSSSelectorList* list = nullptr; |
|
4195 if (! ParseSelectorGroup(list)) { |
|
4196 // must have at least one selector group |
|
4197 aListHead = nullptr; |
|
4198 return false; |
|
4199 } |
|
4200 NS_ASSERTION(nullptr != list, "no selector list"); |
|
4201 aListHead = list; |
|
4202 |
|
4203 // After that there must either be a "," or a "{" (the latter if |
|
4204 // StopChar is nonzero) |
|
4205 nsCSSToken* tk = &mToken; |
|
4206 for (;;) { |
|
4207 if (! GetToken(true)) { |
|
4208 if (aStopChar == char16_t(0)) { |
|
4209 return true; |
|
4210 } |
|
4211 |
|
4212 REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF); |
|
4213 break; |
|
4214 } |
|
4215 |
|
4216 if (eCSSToken_Symbol == tk->mType) { |
|
4217 if (',' == tk->mSymbol) { |
|
4218 nsCSSSelectorList* newList = nullptr; |
|
4219 // Another selector group must follow |
|
4220 if (! ParseSelectorGroup(newList)) { |
|
4221 break; |
|
4222 } |
|
4223 // add new list to the end of the selector list |
|
4224 list->mNext = newList; |
|
4225 list = newList; |
|
4226 continue; |
|
4227 } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) { |
|
4228 UngetToken(); |
|
4229 return true; |
|
4230 } |
|
4231 } |
|
4232 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra); |
|
4233 UngetToken(); |
|
4234 break; |
|
4235 } |
|
4236 |
|
4237 delete aListHead; |
|
4238 aListHead = nullptr; |
|
4239 return false; |
|
4240 } |
|
4241 |
|
4242 static bool IsUniversalSelector(const nsCSSSelector& aSelector) |
|
4243 { |
|
4244 return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) && |
|
4245 (aSelector.mLowercaseTag == nullptr) && |
|
4246 (aSelector.mIDList == nullptr) && |
|
4247 (aSelector.mClassList == nullptr) && |
|
4248 (aSelector.mAttrList == nullptr) && |
|
4249 (aSelector.mNegations == nullptr) && |
|
4250 (aSelector.mPseudoClassList == nullptr)); |
|
4251 } |
|
4252 |
|
4253 bool |
|
4254 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList) |
|
4255 { |
|
4256 char16_t combinator = 0; |
|
4257 nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList()); |
|
4258 |
|
4259 for (;;) { |
|
4260 if (!ParseSelector(list, combinator)) { |
|
4261 return false; |
|
4262 } |
|
4263 |
|
4264 // Look for a combinator. |
|
4265 if (!GetToken(false)) { |
|
4266 break; // EOF ok here |
|
4267 } |
|
4268 |
|
4269 combinator = char16_t(0); |
|
4270 if (mToken.mType == eCSSToken_Whitespace) { |
|
4271 if (!GetToken(true)) { |
|
4272 break; // EOF ok here |
|
4273 } |
|
4274 combinator = char16_t(' '); |
|
4275 } |
|
4276 |
|
4277 if (mToken.mType != eCSSToken_Symbol) { |
|
4278 UngetToken(); // not a combinator |
|
4279 } else { |
|
4280 char16_t symbol = mToken.mSymbol; |
|
4281 if (symbol == '+' || symbol == '>' || symbol == '~') { |
|
4282 combinator = mToken.mSymbol; |
|
4283 } else { |
|
4284 UngetToken(); // not a combinator |
|
4285 if (symbol == ',' || symbol == '{' || symbol == ')') { |
|
4286 break; // end of selector group |
|
4287 } |
|
4288 } |
|
4289 } |
|
4290 |
|
4291 if (!combinator) { |
|
4292 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra); |
|
4293 return false; |
|
4294 } |
|
4295 } |
|
4296 |
|
4297 aList = list.forget(); |
|
4298 return true; |
|
4299 } |
|
4300 |
|
4301 #define SEL_MASK_NSPACE 0x01 |
|
4302 #define SEL_MASK_ELEM 0x02 |
|
4303 #define SEL_MASK_ID 0x04 |
|
4304 #define SEL_MASK_CLASS 0x08 |
|
4305 #define SEL_MASK_ATTRIB 0x10 |
|
4306 #define SEL_MASK_PCLASS 0x20 |
|
4307 #define SEL_MASK_PELEM 0x40 |
|
4308 |
|
4309 // |
|
4310 // Parses an ID selector #name |
|
4311 // |
|
4312 CSSParserImpl::nsSelectorParsingStatus |
|
4313 CSSParserImpl::ParseIDSelector(int32_t& aDataMask, |
|
4314 nsCSSSelector& aSelector) |
|
4315 { |
|
4316 NS_ASSERTION(!mToken.mIdent.IsEmpty(), |
|
4317 "Empty mIdent in eCSSToken_ID token?"); |
|
4318 aDataMask |= SEL_MASK_ID; |
|
4319 aSelector.AddID(mToken.mIdent); |
|
4320 return eSelectorParsingStatus_Continue; |
|
4321 } |
|
4322 |
|
4323 // |
|
4324 // Parses a class selector .name |
|
4325 // |
|
4326 CSSParserImpl::nsSelectorParsingStatus |
|
4327 CSSParserImpl::ParseClassSelector(int32_t& aDataMask, |
|
4328 nsCSSSelector& aSelector) |
|
4329 { |
|
4330 if (! GetToken(false)) { // get ident |
|
4331 REPORT_UNEXPECTED_EOF(PEClassSelEOF); |
|
4332 return eSelectorParsingStatus_Error; |
|
4333 } |
|
4334 if (eCSSToken_Ident != mToken.mType) { // malformed selector |
|
4335 REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent); |
|
4336 UngetToken(); |
|
4337 return eSelectorParsingStatus_Error; |
|
4338 } |
|
4339 aDataMask |= SEL_MASK_CLASS; |
|
4340 |
|
4341 aSelector.AddClass(mToken.mIdent); |
|
4342 |
|
4343 return eSelectorParsingStatus_Continue; |
|
4344 } |
|
4345 |
|
4346 // |
|
4347 // Parse a type element selector or a universal selector |
|
4348 // namespace|type or namespace|* or *|* or * |
|
4349 // |
|
4350 CSSParserImpl::nsSelectorParsingStatus |
|
4351 CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask, |
|
4352 nsCSSSelector& aSelector, |
|
4353 bool aIsNegated) |
|
4354 { |
|
4355 nsAutoString buffer; |
|
4356 if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace |
|
4357 if (ExpectSymbol('|', false)) { // was namespace |
|
4358 aDataMask |= SEL_MASK_NSPACE; |
|
4359 aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard |
|
4360 |
|
4361 if (! GetToken(false)) { |
|
4362 REPORT_UNEXPECTED_EOF(PETypeSelEOF); |
|
4363 return eSelectorParsingStatus_Error; |
|
4364 } |
|
4365 if (eCSSToken_Ident == mToken.mType) { // element name |
|
4366 aDataMask |= SEL_MASK_ELEM; |
|
4367 |
|
4368 aSelector.SetTag(mToken.mIdent); |
|
4369 } |
|
4370 else if (mToken.IsSymbol('*')) { // universal selector |
|
4371 aDataMask |= SEL_MASK_ELEM; |
|
4372 // don't set tag |
|
4373 } |
|
4374 else { |
|
4375 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType); |
|
4376 UngetToken(); |
|
4377 return eSelectorParsingStatus_Error; |
|
4378 } |
|
4379 } |
|
4380 else { // was universal element selector |
|
4381 SetDefaultNamespaceOnSelector(aSelector); |
|
4382 aDataMask |= SEL_MASK_ELEM; |
|
4383 // don't set any tag in the selector |
|
4384 } |
|
4385 if (! GetToken(false)) { // premature eof is ok (here!) |
|
4386 return eSelectorParsingStatus_Done; |
|
4387 } |
|
4388 } |
|
4389 else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name |
|
4390 buffer = mToken.mIdent; // hang on to ident |
|
4391 |
|
4392 if (ExpectSymbol('|', false)) { // was namespace |
|
4393 aDataMask |= SEL_MASK_NSPACE; |
|
4394 int32_t nameSpaceID = GetNamespaceIdForPrefix(buffer); |
|
4395 if (nameSpaceID == kNameSpaceID_Unknown) { |
|
4396 return eSelectorParsingStatus_Error; |
|
4397 } |
|
4398 aSelector.SetNameSpace(nameSpaceID); |
|
4399 |
|
4400 if (! GetToken(false)) { |
|
4401 REPORT_UNEXPECTED_EOF(PETypeSelEOF); |
|
4402 return eSelectorParsingStatus_Error; |
|
4403 } |
|
4404 if (eCSSToken_Ident == mToken.mType) { // element name |
|
4405 aDataMask |= SEL_MASK_ELEM; |
|
4406 aSelector.SetTag(mToken.mIdent); |
|
4407 } |
|
4408 else if (mToken.IsSymbol('*')) { // universal selector |
|
4409 aDataMask |= SEL_MASK_ELEM; |
|
4410 // don't set tag |
|
4411 } |
|
4412 else { |
|
4413 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType); |
|
4414 UngetToken(); |
|
4415 return eSelectorParsingStatus_Error; |
|
4416 } |
|
4417 } |
|
4418 else { // was element name |
|
4419 SetDefaultNamespaceOnSelector(aSelector); |
|
4420 aSelector.SetTag(buffer); |
|
4421 |
|
4422 aDataMask |= SEL_MASK_ELEM; |
|
4423 } |
|
4424 if (! GetToken(false)) { // premature eof is ok (here!) |
|
4425 return eSelectorParsingStatus_Done; |
|
4426 } |
|
4427 } |
|
4428 else if (mToken.IsSymbol('|')) { // No namespace |
|
4429 aDataMask |= SEL_MASK_NSPACE; |
|
4430 aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace |
|
4431 |
|
4432 // get mandatory tag |
|
4433 if (! GetToken(false)) { |
|
4434 REPORT_UNEXPECTED_EOF(PETypeSelEOF); |
|
4435 return eSelectorParsingStatus_Error; |
|
4436 } |
|
4437 if (eCSSToken_Ident == mToken.mType) { // element name |
|
4438 aDataMask |= SEL_MASK_ELEM; |
|
4439 aSelector.SetTag(mToken.mIdent); |
|
4440 } |
|
4441 else if (mToken.IsSymbol('*')) { // universal selector |
|
4442 aDataMask |= SEL_MASK_ELEM; |
|
4443 // don't set tag |
|
4444 } |
|
4445 else { |
|
4446 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType); |
|
4447 UngetToken(); |
|
4448 return eSelectorParsingStatus_Error; |
|
4449 } |
|
4450 if (! GetToken(false)) { // premature eof is ok (here!) |
|
4451 return eSelectorParsingStatus_Done; |
|
4452 } |
|
4453 } |
|
4454 else { |
|
4455 SetDefaultNamespaceOnSelector(aSelector); |
|
4456 } |
|
4457 |
|
4458 if (aIsNegated) { |
|
4459 // restore last token read in case of a negated type selector |
|
4460 UngetToken(); |
|
4461 } |
|
4462 return eSelectorParsingStatus_Continue; |
|
4463 } |
|
4464 |
|
4465 // |
|
4466 // Parse attribute selectors [attr], [attr=value], [attr|=value], |
|
4467 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value] |
|
4468 // |
|
4469 CSSParserImpl::nsSelectorParsingStatus |
|
4470 CSSParserImpl::ParseAttributeSelector(int32_t& aDataMask, |
|
4471 nsCSSSelector& aSelector) |
|
4472 { |
|
4473 if (! GetToken(true)) { // premature EOF |
|
4474 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); |
|
4475 return eSelectorParsingStatus_Error; |
|
4476 } |
|
4477 |
|
4478 int32_t nameSpaceID = kNameSpaceID_None; |
|
4479 nsAutoString attr; |
|
4480 if (mToken.IsSymbol('*')) { // wildcard namespace |
|
4481 nameSpaceID = kNameSpaceID_Unknown; |
|
4482 if (ExpectSymbol('|', false)) { |
|
4483 if (! GetToken(false)) { // premature EOF |
|
4484 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); |
|
4485 return eSelectorParsingStatus_Error; |
|
4486 } |
|
4487 if (eCSSToken_Ident == mToken.mType) { // attr name |
|
4488 attr = mToken.mIdent; |
|
4489 } |
|
4490 else { |
|
4491 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); |
|
4492 UngetToken(); |
|
4493 return eSelectorParsingStatus_Error; |
|
4494 } |
|
4495 } |
|
4496 else { |
|
4497 REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar); |
|
4498 return eSelectorParsingStatus_Error; |
|
4499 } |
|
4500 } |
|
4501 else if (mToken.IsSymbol('|')) { // NO namespace |
|
4502 if (! GetToken(false)) { // premature EOF |
|
4503 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); |
|
4504 return eSelectorParsingStatus_Error; |
|
4505 } |
|
4506 if (eCSSToken_Ident == mToken.mType) { // attr name |
|
4507 attr = mToken.mIdent; |
|
4508 } |
|
4509 else { |
|
4510 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); |
|
4511 UngetToken(); |
|
4512 return eSelectorParsingStatus_Error; |
|
4513 } |
|
4514 } |
|
4515 else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace |
|
4516 attr = mToken.mIdent; // hang on to it |
|
4517 if (ExpectSymbol('|', false)) { // was a namespace |
|
4518 nameSpaceID = GetNamespaceIdForPrefix(attr); |
|
4519 if (nameSpaceID == kNameSpaceID_Unknown) { |
|
4520 return eSelectorParsingStatus_Error; |
|
4521 } |
|
4522 if (! GetToken(false)) { // premature EOF |
|
4523 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); |
|
4524 return eSelectorParsingStatus_Error; |
|
4525 } |
|
4526 if (eCSSToken_Ident == mToken.mType) { // attr name |
|
4527 attr = mToken.mIdent; |
|
4528 } |
|
4529 else { |
|
4530 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); |
|
4531 UngetToken(); |
|
4532 return eSelectorParsingStatus_Error; |
|
4533 } |
|
4534 } |
|
4535 } |
|
4536 else { // malformed |
|
4537 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected); |
|
4538 UngetToken(); |
|
4539 return eSelectorParsingStatus_Error; |
|
4540 } |
|
4541 |
|
4542 if (! GetToken(true)) { // premature EOF |
|
4543 REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF); |
|
4544 return eSelectorParsingStatus_Error; |
|
4545 } |
|
4546 if ((eCSSToken_Symbol == mToken.mType) || |
|
4547 (eCSSToken_Includes == mToken.mType) || |
|
4548 (eCSSToken_Dashmatch == mToken.mType) || |
|
4549 (eCSSToken_Beginsmatch == mToken.mType) || |
|
4550 (eCSSToken_Endsmatch == mToken.mType) || |
|
4551 (eCSSToken_Containsmatch == mToken.mType)) { |
|
4552 uint8_t func; |
|
4553 if (eCSSToken_Includes == mToken.mType) { |
|
4554 func = NS_ATTR_FUNC_INCLUDES; |
|
4555 } |
|
4556 else if (eCSSToken_Dashmatch == mToken.mType) { |
|
4557 func = NS_ATTR_FUNC_DASHMATCH; |
|
4558 } |
|
4559 else if (eCSSToken_Beginsmatch == mToken.mType) { |
|
4560 func = NS_ATTR_FUNC_BEGINSMATCH; |
|
4561 } |
|
4562 else if (eCSSToken_Endsmatch == mToken.mType) { |
|
4563 func = NS_ATTR_FUNC_ENDSMATCH; |
|
4564 } |
|
4565 else if (eCSSToken_Containsmatch == mToken.mType) { |
|
4566 func = NS_ATTR_FUNC_CONTAINSMATCH; |
|
4567 } |
|
4568 else if (']' == mToken.mSymbol) { |
|
4569 aDataMask |= SEL_MASK_ATTRIB; |
|
4570 aSelector.AddAttribute(nameSpaceID, attr); |
|
4571 func = NS_ATTR_FUNC_SET; |
|
4572 } |
|
4573 else if ('=' == mToken.mSymbol) { |
|
4574 func = NS_ATTR_FUNC_EQUALS; |
|
4575 } |
|
4576 else { |
|
4577 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected); |
|
4578 UngetToken(); // bad function |
|
4579 return eSelectorParsingStatus_Error; |
|
4580 } |
|
4581 if (NS_ATTR_FUNC_SET != func) { // get value |
|
4582 if (! GetToken(true)) { // premature EOF |
|
4583 REPORT_UNEXPECTED_EOF(PEAttSelValueEOF); |
|
4584 return eSelectorParsingStatus_Error; |
|
4585 } |
|
4586 if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) { |
|
4587 nsAutoString value(mToken.mIdent); |
|
4588 if (! GetToken(true)) { // premature EOF |
|
4589 REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF); |
|
4590 return eSelectorParsingStatus_Error; |
|
4591 } |
|
4592 if (mToken.IsSymbol(']')) { |
|
4593 bool isCaseSensitive = true; |
|
4594 |
|
4595 // For cases when this style sheet is applied to an HTML |
|
4596 // element in an HTML document, and the attribute selector is |
|
4597 // for a non-namespaced attribute, then check to see if it's |
|
4598 // one of the known attributes whose VALUE is |
|
4599 // case-insensitive. |
|
4600 if (nameSpaceID == kNameSpaceID_None) { |
|
4601 static const char* caseInsensitiveHTMLAttribute[] = { |
|
4602 // list based on http://www.w3.org/TR/html4/ |
|
4603 "lang", |
|
4604 "dir", |
|
4605 "http-equiv", |
|
4606 "text", |
|
4607 "link", |
|
4608 "vlink", |
|
4609 "alink", |
|
4610 "compact", |
|
4611 "align", |
|
4612 "frame", |
|
4613 "rules", |
|
4614 "valign", |
|
4615 "scope", |
|
4616 "axis", |
|
4617 "nowrap", |
|
4618 "hreflang", |
|
4619 "rel", |
|
4620 "rev", |
|
4621 "charset", |
|
4622 "codetype", |
|
4623 "declare", |
|
4624 "valuetype", |
|
4625 "shape", |
|
4626 "nohref", |
|
4627 "media", |
|
4628 "bgcolor", |
|
4629 "clear", |
|
4630 "color", |
|
4631 "face", |
|
4632 "noshade", |
|
4633 "noresize", |
|
4634 "scrolling", |
|
4635 "target", |
|
4636 "method", |
|
4637 "enctype", |
|
4638 "accept-charset", |
|
4639 "accept", |
|
4640 "checked", |
|
4641 "multiple", |
|
4642 "selected", |
|
4643 "disabled", |
|
4644 "readonly", |
|
4645 "language", |
|
4646 "defer", |
|
4647 "type", |
|
4648 // additional attributes not in HTML4 |
|
4649 "direction", // marquee |
|
4650 nullptr |
|
4651 }; |
|
4652 short i = 0; |
|
4653 const char* htmlAttr; |
|
4654 while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) { |
|
4655 if (attr.LowerCaseEqualsASCII(htmlAttr)) { |
|
4656 isCaseSensitive = false; |
|
4657 break; |
|
4658 } |
|
4659 } |
|
4660 } |
|
4661 aDataMask |= SEL_MASK_ATTRIB; |
|
4662 aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive); |
|
4663 } |
|
4664 else { |
|
4665 REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose); |
|
4666 UngetToken(); |
|
4667 return eSelectorParsingStatus_Error; |
|
4668 } |
|
4669 } |
|
4670 else { |
|
4671 REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue); |
|
4672 UngetToken(); |
|
4673 return eSelectorParsingStatus_Error; |
|
4674 } |
|
4675 } |
|
4676 } |
|
4677 else { |
|
4678 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected); |
|
4679 UngetToken(); // bad dog, no biscut! |
|
4680 return eSelectorParsingStatus_Error; |
|
4681 } |
|
4682 return eSelectorParsingStatus_Continue; |
|
4683 } |
|
4684 |
|
4685 // |
|
4686 // Parse pseudo-classes and pseudo-elements |
|
4687 // |
|
4688 CSSParserImpl::nsSelectorParsingStatus |
|
4689 CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, |
|
4690 nsCSSSelector& aSelector, |
|
4691 bool aIsNegated, |
|
4692 nsIAtom** aPseudoElement, |
|
4693 nsAtomList** aPseudoElementArgs, |
|
4694 nsCSSPseudoElements::Type* aPseudoElementType) |
|
4695 { |
|
4696 NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs), |
|
4697 "expected location to store pseudo element"); |
|
4698 NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs), |
|
4699 "negated selectors shouldn't have a place to store " |
|
4700 "pseudo elements"); |
|
4701 if (! GetToken(false)) { // premature eof |
|
4702 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF); |
|
4703 return eSelectorParsingStatus_Error; |
|
4704 } |
|
4705 |
|
4706 // First, find out whether we are parsing a CSS3 pseudo-element |
|
4707 bool parsingPseudoElement = false; |
|
4708 if (mToken.IsSymbol(':')) { |
|
4709 parsingPseudoElement = true; |
|
4710 if (! GetToken(false)) { // premature eof |
|
4711 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF); |
|
4712 return eSelectorParsingStatus_Error; |
|
4713 } |
|
4714 } |
|
4715 |
|
4716 // Do some sanity-checking on the token |
|
4717 if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) { |
|
4718 // malformed selector |
|
4719 REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName); |
|
4720 UngetToken(); |
|
4721 return eSelectorParsingStatus_Error; |
|
4722 } |
|
4723 |
|
4724 // OK, now we know we have an mIdent. Atomize it. All the atoms, for |
|
4725 // pseudo-classes as well as pseudo-elements, start with a single ':'. |
|
4726 nsAutoString buffer; |
|
4727 buffer.Append(char16_t(':')); |
|
4728 buffer.Append(mToken.mIdent); |
|
4729 nsContentUtils::ASCIIToLower(buffer); |
|
4730 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer); |
|
4731 if (!pseudo) { |
|
4732 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?"); |
|
4733 } |
|
4734 |
|
4735 // stash away some info about this pseudo so we only have to get it once. |
|
4736 bool isTreePseudo = false; |
|
4737 nsCSSPseudoElements::Type pseudoElementType = |
|
4738 nsCSSPseudoElements::GetPseudoType(pseudo); |
|
4739 nsCSSPseudoClasses::Type pseudoClassType = |
|
4740 nsCSSPseudoClasses::GetPseudoType(pseudo); |
|
4741 bool pseudoClassIsUserAction = |
|
4742 nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType); |
|
4743 |
|
4744 if (!mUnsafeRulesEnabled && |
|
4745 pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount && |
|
4746 nsCSSPseudoElements::PseudoElementIsChromeOnly(pseudoElementType)) { |
|
4747 // This pseudo-element is not exposed to content. |
|
4748 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown); |
|
4749 UngetToken(); |
|
4750 return eSelectorParsingStatus_Error; |
|
4751 } |
|
4752 |
|
4753 // We currently allow :-moz-placeholder and ::-moz-placeholder. We have to |
|
4754 // be a bit stricter regarding the pseudo-element parsing rules. |
|
4755 if (pseudoElementType == nsCSSPseudoElements::ePseudo_mozPlaceholder && |
|
4756 pseudoClassType == nsCSSPseudoClasses::ePseudoClass_mozPlaceholder) { |
|
4757 if (parsingPseudoElement) { |
|
4758 pseudoClassType = nsCSSPseudoClasses::ePseudoClass_NotPseudoClass; |
|
4759 } else { |
|
4760 pseudoElementType = nsCSSPseudoElements::ePseudo_NotPseudoElement; |
|
4761 } |
|
4762 } |
|
4763 |
|
4764 #ifdef MOZ_XUL |
|
4765 isTreePseudo = (pseudoElementType == nsCSSPseudoElements::ePseudo_XULTree); |
|
4766 // If a tree pseudo-element is using the function syntax, it will |
|
4767 // get isTree set here and will pass the check below that only |
|
4768 // allows functions if they are in our list of things allowed to be |
|
4769 // functions. If it is _not_ using the function syntax, isTree will |
|
4770 // be false, and it will still pass that check. So the tree |
|
4771 // pseudo-elements are allowed to be either functions or not, as |
|
4772 // desired. |
|
4773 bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo; |
|
4774 #endif |
|
4775 bool isPseudoElement = |
|
4776 (pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount); |
|
4777 // anonymous boxes are only allowed if they're the tree boxes or we have |
|
4778 // enabled unsafe rules |
|
4779 bool isAnonBox = isTreePseudo || |
|
4780 (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox && |
|
4781 mUnsafeRulesEnabled); |
|
4782 bool isPseudoClass = |
|
4783 (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass); |
|
4784 |
|
4785 NS_ASSERTION(!isPseudoClass || |
|
4786 pseudoElementType == nsCSSPseudoElements::ePseudo_NotPseudoElement, |
|
4787 "Why is this atom both a pseudo-class and a pseudo-element?"); |
|
4788 NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1, |
|
4789 "Shouldn't be more than one of these"); |
|
4790 |
|
4791 if (!isPseudoClass && !isPseudoElement && !isAnonBox) { |
|
4792 // Not a pseudo-class, not a pseudo-element.... forget it |
|
4793 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown); |
|
4794 UngetToken(); |
|
4795 return eSelectorParsingStatus_Error; |
|
4796 } |
|
4797 |
|
4798 // If it's a function token, it better be on our "ok" list, and if the name |
|
4799 // is that of a function pseudo it better be a function token |
|
4800 if ((eCSSToken_Function == mToken.mType) != |
|
4801 ( |
|
4802 #ifdef MOZ_XUL |
|
4803 isTree || |
|
4804 #endif |
|
4805 nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType || |
|
4806 nsCSSPseudoClasses::HasStringArg(pseudoClassType) || |
|
4807 nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) || |
|
4808 nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) { |
|
4809 // There are no other function pseudos |
|
4810 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc); |
|
4811 UngetToken(); |
|
4812 return eSelectorParsingStatus_Error; |
|
4813 } |
|
4814 |
|
4815 // If it starts with "::", it better be a pseudo-element |
|
4816 if (parsingPseudoElement && |
|
4817 !isPseudoElement && |
|
4818 !isAnonBox) { |
|
4819 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE); |
|
4820 UngetToken(); |
|
4821 return eSelectorParsingStatus_Error; |
|
4822 } |
|
4823 |
|
4824 if (!parsingPseudoElement && |
|
4825 nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType) { |
|
4826 if (aIsNegated) { // :not() can't be itself negated |
|
4827 REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot); |
|
4828 UngetToken(); |
|
4829 return eSelectorParsingStatus_Error; |
|
4830 } |
|
4831 // CSS 3 Negation pseudo-class takes one simple selector as argument |
|
4832 nsSelectorParsingStatus parsingStatus = |
|
4833 ParseNegatedSimpleSelector(aDataMask, aSelector); |
|
4834 if (eSelectorParsingStatus_Continue != parsingStatus) { |
|
4835 return parsingStatus; |
|
4836 } |
|
4837 } |
|
4838 else if (!parsingPseudoElement && isPseudoClass) { |
|
4839 if (aSelector.IsPseudoElement()) { |
|
4840 nsCSSPseudoElements::Type type = aSelector.PseudoType(); |
|
4841 if (!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) { |
|
4842 // We only allow user action pseudo-classes on certain pseudo-elements. |
|
4843 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC); |
|
4844 UngetToken(); |
|
4845 return eSelectorParsingStatus_Error; |
|
4846 } |
|
4847 if (!pseudoClassIsUserAction) { |
|
4848 // CSS 4 Selectors says that pseudo-elements can only be followed by |
|
4849 // a user action pseudo-class. |
|
4850 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction); |
|
4851 UngetToken(); |
|
4852 return eSelectorParsingStatus_Error; |
|
4853 } |
|
4854 } |
|
4855 aDataMask |= SEL_MASK_PCLASS; |
|
4856 if (eCSSToken_Function == mToken.mType) { |
|
4857 nsSelectorParsingStatus parsingStatus; |
|
4858 if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) { |
|
4859 parsingStatus = |
|
4860 ParsePseudoClassWithIdentArg(aSelector, pseudoClassType); |
|
4861 } |
|
4862 else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) { |
|
4863 parsingStatus = |
|
4864 ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType); |
|
4865 } |
|
4866 else { |
|
4867 NS_ABORT_IF_FALSE(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType), |
|
4868 "unexpected pseudo with function token"); |
|
4869 parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector, |
|
4870 pseudoClassType); |
|
4871 } |
|
4872 if (eSelectorParsingStatus_Continue != parsingStatus) { |
|
4873 if (eSelectorParsingStatus_Error == parsingStatus) { |
|
4874 SkipUntil(')'); |
|
4875 } |
|
4876 return parsingStatus; |
|
4877 } |
|
4878 } |
|
4879 else { |
|
4880 aSelector.AddPseudoClass(pseudoClassType); |
|
4881 } |
|
4882 } |
|
4883 else if (isPseudoElement || isAnonBox) { |
|
4884 // Pseudo-element. Make some more sanity checks. |
|
4885 |
|
4886 if (aIsNegated) { // pseudo-elements can't be negated |
|
4887 REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot); |
|
4888 UngetToken(); |
|
4889 return eSelectorParsingStatus_Error; |
|
4890 } |
|
4891 // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed |
|
4892 // to have a single ':' on them. Others (CSS3+ pseudo-elements and |
|
4893 // various -moz-* pseudo-elements) must have |parsingPseudoElement| |
|
4894 // set. |
|
4895 if (!parsingPseudoElement && |
|
4896 !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo) |
|
4897 #ifdef MOZ_XUL |
|
4898 && !isTreePseudo |
|
4899 #endif |
|
4900 ) { |
|
4901 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly); |
|
4902 UngetToken(); |
|
4903 return eSelectorParsingStatus_Error; |
|
4904 } |
|
4905 |
|
4906 if (0 == (aDataMask & SEL_MASK_PELEM)) { |
|
4907 aDataMask |= SEL_MASK_PELEM; |
|
4908 NS_ADDREF(*aPseudoElement = pseudo); |
|
4909 *aPseudoElementType = pseudoElementType; |
|
4910 |
|
4911 #ifdef MOZ_XUL |
|
4912 if (isTree) { |
|
4913 // We have encountered a pseudoelement of the form |
|
4914 // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each |
|
4915 // item in the list to the pseudoclass list. They will be pulled |
|
4916 // from the list later along with the pseudo-element. |
|
4917 if (!ParseTreePseudoElement(aPseudoElementArgs)) { |
|
4918 return eSelectorParsingStatus_Error; |
|
4919 } |
|
4920 } |
|
4921 #endif |
|
4922 |
|
4923 // Pseudo-elements can only be followed by user action pseudo-classes |
|
4924 // or be the end of the selector. So the next non-whitespace token must |
|
4925 // be ':', '{' or ',' or EOF. |
|
4926 if (!GetToken(true)) { // premature eof is ok (here!) |
|
4927 return eSelectorParsingStatus_Done; |
|
4928 } |
|
4929 if (parsingPseudoElement && mToken.IsSymbol(':')) { |
|
4930 UngetToken(); |
|
4931 return eSelectorParsingStatus_Continue; |
|
4932 } |
|
4933 if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) { |
|
4934 UngetToken(); |
|
4935 return eSelectorParsingStatus_Done; |
|
4936 } |
|
4937 REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC); |
|
4938 UngetToken(); |
|
4939 return eSelectorParsingStatus_Error; |
|
4940 } |
|
4941 else { // multiple pseudo elements, not legal |
|
4942 REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE); |
|
4943 UngetToken(); |
|
4944 return eSelectorParsingStatus_Error; |
|
4945 } |
|
4946 } |
|
4947 #ifdef DEBUG |
|
4948 else { |
|
4949 // We should never end up here. Indeed, if we ended up here, we know (from |
|
4950 // the current if/else cascade) that !isPseudoElement and !isAnonBox. But |
|
4951 // then due to our earlier check we know that isPseudoClass. Since we |
|
4952 // didn't fall into the isPseudoClass case in this cascade, we must have |
|
4953 // parsingPseudoElement. But we've already checked the |
|
4954 // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if |
|
4955 // it's happened. |
|
4956 NS_NOTREACHED("How did this happen?"); |
|
4957 } |
|
4958 #endif |
|
4959 return eSelectorParsingStatus_Continue; |
|
4960 } |
|
4961 |
|
4962 // |
|
4963 // Parse the argument of a negation pseudo-class :not() |
|
4964 // |
|
4965 CSSParserImpl::nsSelectorParsingStatus |
|
4966 CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask, |
|
4967 nsCSSSelector& aSelector) |
|
4968 { |
|
4969 if (! GetToken(true)) { // premature eof |
|
4970 REPORT_UNEXPECTED_EOF(PENegationEOF); |
|
4971 return eSelectorParsingStatus_Error; |
|
4972 } |
|
4973 |
|
4974 if (mToken.IsSymbol(')')) { |
|
4975 REPORT_UNEXPECTED_TOKEN(PENegationBadArg); |
|
4976 return eSelectorParsingStatus_Error; |
|
4977 } |
|
4978 |
|
4979 // Create a new nsCSSSelector and add it to the end of |
|
4980 // aSelector.mNegations. |
|
4981 // Given the current parsing rules, every selector in mNegations |
|
4982 // contains only one simple selector (css3 definition) within it. |
|
4983 // This could easily change in future versions of CSS, and the only |
|
4984 // thing we need to change to support that is this parsing code and the |
|
4985 // serialization code for nsCSSSelector. |
|
4986 nsCSSSelector *newSel = new nsCSSSelector(); |
|
4987 nsCSSSelector* negations = &aSelector; |
|
4988 while (negations->mNegations) { |
|
4989 negations = negations->mNegations; |
|
4990 } |
|
4991 negations->mNegations = newSel; |
|
4992 |
|
4993 nsSelectorParsingStatus parsingStatus; |
|
4994 if (eCSSToken_ID == mToken.mType) { // #id |
|
4995 parsingStatus = ParseIDSelector(aDataMask, *newSel); |
|
4996 } |
|
4997 else if (mToken.IsSymbol('.')) { // .class |
|
4998 parsingStatus = ParseClassSelector(aDataMask, *newSel); |
|
4999 } |
|
5000 else if (mToken.IsSymbol(':')) { // :pseudo |
|
5001 parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true, |
|
5002 nullptr, nullptr, nullptr); |
|
5003 } |
|
5004 else if (mToken.IsSymbol('[')) { // [attribute |
|
5005 parsingStatus = ParseAttributeSelector(aDataMask, *newSel); |
|
5006 if (eSelectorParsingStatus_Error == parsingStatus) { |
|
5007 // Skip forward to the matching ']' |
|
5008 SkipUntil(']'); |
|
5009 } |
|
5010 } |
|
5011 else { |
|
5012 // then it should be a type element or universal selector |
|
5013 parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true); |
|
5014 } |
|
5015 if (eSelectorParsingStatus_Error == parsingStatus) { |
|
5016 REPORT_UNEXPECTED_TOKEN(PENegationBadInner); |
|
5017 SkipUntil(')'); |
|
5018 return parsingStatus; |
|
5019 } |
|
5020 // close the parenthesis |
|
5021 if (!ExpectSymbol(')', true)) { |
|
5022 REPORT_UNEXPECTED_TOKEN(PENegationNoClose); |
|
5023 SkipUntil(')'); |
|
5024 return eSelectorParsingStatus_Error; |
|
5025 } |
|
5026 |
|
5027 NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown || |
|
5028 (!newSel->mIDList && !newSel->mClassList && |
|
5029 !newSel->mPseudoClassList && !newSel->mAttrList), |
|
5030 "Need to fix the serialization code to deal with this"); |
|
5031 |
|
5032 return eSelectorParsingStatus_Continue; |
|
5033 } |
|
5034 |
|
5035 // |
|
5036 // Parse the argument of a pseudo-class that has an ident arg |
|
5037 // |
|
5038 CSSParserImpl::nsSelectorParsingStatus |
|
5039 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector, |
|
5040 nsCSSPseudoClasses::Type aType) |
|
5041 { |
|
5042 if (! GetToken(true)) { // premature eof |
|
5043 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); |
|
5044 return eSelectorParsingStatus_Error; |
|
5045 } |
|
5046 // We expect an identifier with a language abbreviation |
|
5047 if (eCSSToken_Ident != mToken.mType) { |
|
5048 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent); |
|
5049 UngetToken(); |
|
5050 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5051 } |
|
5052 |
|
5053 // -moz-locale-dir and -moz-dir can only have values of 'ltr' or 'rtl'. |
|
5054 if (aType == nsCSSPseudoClasses::ePseudoClass_mozLocaleDir || |
|
5055 aType == nsCSSPseudoClasses::ePseudoClass_dir) { |
|
5056 nsContentUtils::ASCIIToLower(mToken.mIdent); // case insensitive |
|
5057 if (!mToken.mIdent.EqualsLiteral("ltr") && |
|
5058 !mToken.mIdent.EqualsLiteral("rtl")) { |
|
5059 REPORT_UNEXPECTED_TOKEN(PEBadDirValue); |
|
5060 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5061 } |
|
5062 } |
|
5063 |
|
5064 // Add the pseudo with the language parameter |
|
5065 aSelector.AddPseudoClass(aType, mToken.mIdent.get()); |
|
5066 |
|
5067 // close the parenthesis |
|
5068 if (!ExpectSymbol(')', true)) { |
|
5069 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose); |
|
5070 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5071 } |
|
5072 |
|
5073 return eSelectorParsingStatus_Continue; |
|
5074 } |
|
5075 |
|
5076 CSSParserImpl::nsSelectorParsingStatus |
|
5077 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector, |
|
5078 nsCSSPseudoClasses::Type aType) |
|
5079 { |
|
5080 int32_t numbers[2] = { 0, 0 }; |
|
5081 int32_t sign[2] = { 1, 1 }; |
|
5082 bool hasSign[2] = { false, false }; |
|
5083 bool lookForB = true; |
|
5084 |
|
5085 // Follow the whitespace rules as proposed in |
|
5086 // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html |
|
5087 |
|
5088 if (! GetToken(true)) { |
|
5089 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); |
|
5090 return eSelectorParsingStatus_Error; |
|
5091 } |
|
5092 |
|
5093 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) { |
|
5094 hasSign[0] = true; |
|
5095 if (mToken.IsSymbol('-')) { |
|
5096 sign[0] = -1; |
|
5097 } |
|
5098 if (! GetToken(false)) { |
|
5099 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); |
|
5100 return eSelectorParsingStatus_Error; |
|
5101 } |
|
5102 } |
|
5103 |
|
5104 if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) { |
|
5105 // The CSS tokenization doesn't handle :nth-child() containing - well: |
|
5106 // 2n-1 is a dimension |
|
5107 // n-1 is an identifier |
|
5108 // The easiest way to deal with that is to push everything from the |
|
5109 // minus on back onto the scanner's pushback buffer. |
|
5110 uint32_t truncAt = 0; |
|
5111 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) { |
|
5112 truncAt = 1; |
|
5113 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-")) && !hasSign[0]) { |
|
5114 truncAt = 2; |
|
5115 } |
|
5116 if (truncAt != 0) { |
|
5117 mScanner->Backup(mToken.mIdent.Length() - truncAt); |
|
5118 mToken.mIdent.Truncate(truncAt); |
|
5119 } |
|
5120 } |
|
5121 |
|
5122 if (eCSSToken_Ident == mToken.mType) { |
|
5123 if (mToken.mIdent.LowerCaseEqualsLiteral("odd") && !hasSign[0]) { |
|
5124 numbers[0] = 2; |
|
5125 numbers[1] = 1; |
|
5126 lookForB = false; |
|
5127 } |
|
5128 else if (mToken.mIdent.LowerCaseEqualsLiteral("even") && !hasSign[0]) { |
|
5129 numbers[0] = 2; |
|
5130 numbers[1] = 0; |
|
5131 lookForB = false; |
|
5132 } |
|
5133 else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) { |
|
5134 numbers[0] = sign[0]; |
|
5135 } |
|
5136 else if (mToken.mIdent.LowerCaseEqualsLiteral("-n") && !hasSign[0]) { |
|
5137 numbers[0] = -1; |
|
5138 } |
|
5139 else { |
|
5140 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); |
|
5141 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5142 } |
|
5143 } |
|
5144 else if (eCSSToken_Number == mToken.mType) { |
|
5145 if (!mToken.mIntegerValid) { |
|
5146 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); |
|
5147 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5148 } |
|
5149 // for +-an case |
|
5150 if (mToken.mHasSign && hasSign[0]) { |
|
5151 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); |
|
5152 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5153 } |
|
5154 int32_t intValue = mToken.mInteger * sign[0]; |
|
5155 // for -a/**/n case |
|
5156 if (! GetToken(false)) { |
|
5157 numbers[1] = intValue; |
|
5158 lookForB = false; |
|
5159 } |
|
5160 else { |
|
5161 if (eCSSToken_Ident == mToken.mType && mToken.mIdent.LowerCaseEqualsLiteral("n")) { |
|
5162 numbers[0] = intValue; |
|
5163 } |
|
5164 else if (eCSSToken_Ident == mToken.mType && StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) { |
|
5165 numbers[0] = intValue; |
|
5166 mScanner->Backup(mToken.mIdent.Length() - 1); |
|
5167 } |
|
5168 else { |
|
5169 UngetToken(); |
|
5170 numbers[1] = intValue; |
|
5171 lookForB = false; |
|
5172 } |
|
5173 } |
|
5174 } |
|
5175 else if (eCSSToken_Dimension == mToken.mType) { |
|
5176 if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) { |
|
5177 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); |
|
5178 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5179 } |
|
5180 // for +-an case |
|
5181 if ( mToken.mHasSign && hasSign[0] ) { |
|
5182 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); |
|
5183 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5184 } |
|
5185 numbers[0] = mToken.mInteger * sign[0]; |
|
5186 } |
|
5187 // XXX If it's a ')', is that valid? (as 0n+0) |
|
5188 else { |
|
5189 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); |
|
5190 UngetToken(); |
|
5191 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5192 } |
|
5193 |
|
5194 if (! GetToken(true)) { |
|
5195 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); |
|
5196 return eSelectorParsingStatus_Error; |
|
5197 } |
|
5198 if (lookForB && !mToken.IsSymbol(')')) { |
|
5199 // The '+' or '-' sign can optionally be separated by whitespace. |
|
5200 // If it is separated by whitespace from what follows it, it appears |
|
5201 // as a separate token rather than part of the number token. |
|
5202 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) { |
|
5203 hasSign[1] = true; |
|
5204 if (mToken.IsSymbol('-')) { |
|
5205 sign[1] = -1; |
|
5206 } |
|
5207 if (! GetToken(true)) { |
|
5208 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); |
|
5209 return eSelectorParsingStatus_Error; |
|
5210 } |
|
5211 } |
|
5212 if (eCSSToken_Number != mToken.mType || |
|
5213 !mToken.mIntegerValid || mToken.mHasSign == hasSign[1]) { |
|
5214 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); |
|
5215 UngetToken(); |
|
5216 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5217 } |
|
5218 numbers[1] = mToken.mInteger * sign[1]; |
|
5219 if (! GetToken(true)) { |
|
5220 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); |
|
5221 return eSelectorParsingStatus_Error; |
|
5222 } |
|
5223 } |
|
5224 if (!mToken.IsSymbol(')')) { |
|
5225 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose); |
|
5226 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5227 } |
|
5228 aSelector.AddPseudoClass(aType, numbers); |
|
5229 return eSelectorParsingStatus_Continue; |
|
5230 } |
|
5231 |
|
5232 // |
|
5233 // Parse the argument of a pseudo-class that has a selector list argument. |
|
5234 // Such selector lists cannot contain combinators, but can contain |
|
5235 // anything that goes between a pair of combinators. |
|
5236 // |
|
5237 CSSParserImpl::nsSelectorParsingStatus |
|
5238 CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, |
|
5239 nsCSSPseudoClasses::Type aType) |
|
5240 { |
|
5241 nsAutoPtr<nsCSSSelectorList> slist; |
|
5242 if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) { |
|
5243 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5244 } |
|
5245 |
|
5246 // Check that none of the selectors in the list have combinators or |
|
5247 // pseudo-elements. |
|
5248 for (nsCSSSelectorList *l = slist; l; l = l->mNext) { |
|
5249 nsCSSSelector *s = l->mSelectors; |
|
5250 if (s->mNext || s->IsPseudoElement()) { |
|
5251 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5252 } |
|
5253 } |
|
5254 |
|
5255 // Add the pseudo with the selector list parameter |
|
5256 aSelector.AddPseudoClass(aType, slist.forget()); |
|
5257 |
|
5258 // close the parenthesis |
|
5259 if (!ExpectSymbol(')', true)) { |
|
5260 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose); |
|
5261 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') |
|
5262 } |
|
5263 |
|
5264 return eSelectorParsingStatus_Continue; |
|
5265 } |
|
5266 |
|
5267 |
|
5268 /** |
|
5269 * This is the format for selectors: |
|
5270 * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]* |
|
5271 */ |
|
5272 bool |
|
5273 CSSParserImpl::ParseSelector(nsCSSSelectorList* aList, |
|
5274 char16_t aPrevCombinator) |
|
5275 { |
|
5276 if (! GetToken(true)) { |
|
5277 REPORT_UNEXPECTED_EOF(PESelectorEOF); |
|
5278 return false; |
|
5279 } |
|
5280 |
|
5281 nsCSSSelector* selector = aList->AddSelector(aPrevCombinator); |
|
5282 nsCOMPtr<nsIAtom> pseudoElement; |
|
5283 nsAutoPtr<nsAtomList> pseudoElementArgs; |
|
5284 nsCSSPseudoElements::Type pseudoElementType = |
|
5285 nsCSSPseudoElements::ePseudo_NotPseudoElement; |
|
5286 |
|
5287 int32_t dataMask = 0; |
|
5288 nsSelectorParsingStatus parsingStatus = |
|
5289 ParseTypeOrUniversalSelector(dataMask, *selector, false); |
|
5290 |
|
5291 while (parsingStatus == eSelectorParsingStatus_Continue) { |
|
5292 if (eCSSToken_ID == mToken.mType) { // #id |
|
5293 parsingStatus = ParseIDSelector(dataMask, *selector); |
|
5294 } |
|
5295 else if (mToken.IsSymbol('.')) { // .class |
|
5296 parsingStatus = ParseClassSelector(dataMask, *selector); |
|
5297 } |
|
5298 else if (mToken.IsSymbol(':')) { // :pseudo |
|
5299 parsingStatus = ParsePseudoSelector(dataMask, *selector, false, |
|
5300 getter_AddRefs(pseudoElement), |
|
5301 getter_Transfers(pseudoElementArgs), |
|
5302 &pseudoElementType); |
|
5303 if (pseudoElement && |
|
5304 pseudoElementType != nsCSSPseudoElements::ePseudo_AnonBox) { |
|
5305 // Pseudo-elements other than anonymous boxes are represented with |
|
5306 // a special ':' combinator. |
|
5307 |
|
5308 aList->mWeight += selector->CalcWeight(); |
|
5309 |
|
5310 selector = aList->AddSelector(':'); |
|
5311 |
|
5312 selector->mLowercaseTag.swap(pseudoElement); |
|
5313 selector->mClassList = pseudoElementArgs.forget(); |
|
5314 selector->SetPseudoType(pseudoElementType); |
|
5315 } |
|
5316 } |
|
5317 else if (mToken.IsSymbol('[')) { // [attribute |
|
5318 parsingStatus = ParseAttributeSelector(dataMask, *selector); |
|
5319 if (eSelectorParsingStatus_Error == parsingStatus) { |
|
5320 SkipUntil(']'); |
|
5321 } |
|
5322 } |
|
5323 else { // not a selector token, we're done |
|
5324 parsingStatus = eSelectorParsingStatus_Done; |
|
5325 UngetToken(); |
|
5326 break; |
|
5327 } |
|
5328 |
|
5329 if (parsingStatus != eSelectorParsingStatus_Continue) { |
|
5330 break; |
|
5331 } |
|
5332 |
|
5333 if (! GetToken(false)) { // premature eof is ok (here!) |
|
5334 parsingStatus = eSelectorParsingStatus_Done; |
|
5335 break; |
|
5336 } |
|
5337 } |
|
5338 |
|
5339 if (parsingStatus == eSelectorParsingStatus_Error) { |
|
5340 return false; |
|
5341 } |
|
5342 |
|
5343 if (!dataMask) { |
|
5344 if (selector->mNext) { |
|
5345 REPORT_UNEXPECTED(PESelectorGroupExtraCombinator); |
|
5346 } else { |
|
5347 REPORT_UNEXPECTED(PESelectorGroupNoSelector); |
|
5348 } |
|
5349 return false; |
|
5350 } |
|
5351 |
|
5352 if (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox) { |
|
5353 // We got an anonymous box pseudo-element; it must be the only |
|
5354 // thing in this selector group. |
|
5355 if (selector->mNext || !IsUniversalSelector(*selector)) { |
|
5356 REPORT_UNEXPECTED(PEAnonBoxNotAlone); |
|
5357 return false; |
|
5358 } |
|
5359 |
|
5360 // Rewrite the current selector as this pseudo-element. |
|
5361 // It does not contribute to selector weight. |
|
5362 selector->mLowercaseTag.swap(pseudoElement); |
|
5363 selector->mClassList = pseudoElementArgs.forget(); |
|
5364 selector->SetPseudoType(pseudoElementType); |
|
5365 return true; |
|
5366 } |
|
5367 |
|
5368 aList->mWeight += selector->CalcWeight(); |
|
5369 |
|
5370 return true; |
|
5371 } |
|
5372 |
|
5373 css::Declaration* |
|
5374 CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext) |
|
5375 { |
|
5376 bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0; |
|
5377 |
|
5378 if (checkForBraces) { |
|
5379 if (!ExpectSymbol('{', true)) { |
|
5380 REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart); |
|
5381 OUTPUT_ERROR(); |
|
5382 return nullptr; |
|
5383 } |
|
5384 } |
|
5385 css::Declaration* declaration = new css::Declaration(); |
|
5386 mData.AssertInitialState(); |
|
5387 if (declaration) { |
|
5388 for (;;) { |
|
5389 bool changed; |
|
5390 if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) { |
|
5391 if (!SkipDeclaration(checkForBraces)) { |
|
5392 break; |
|
5393 } |
|
5394 if (checkForBraces) { |
|
5395 if (ExpectSymbol('}', true)) { |
|
5396 break; |
|
5397 } |
|
5398 } |
|
5399 // Since the skipped declaration didn't end the block we parse |
|
5400 // the next declaration. |
|
5401 } |
|
5402 } |
|
5403 declaration->CompressFrom(&mData); |
|
5404 } |
|
5405 return declaration; |
|
5406 } |
|
5407 |
|
5408 bool |
|
5409 CSSParserImpl::ParseColor(nsCSSValue& aValue) |
|
5410 { |
|
5411 if (!GetToken(true)) { |
|
5412 REPORT_UNEXPECTED_EOF(PEColorEOF); |
|
5413 return false; |
|
5414 } |
|
5415 |
|
5416 nsCSSToken* tk = &mToken; |
|
5417 nscolor rgba; |
|
5418 switch (tk->mType) { |
|
5419 case eCSSToken_ID: |
|
5420 case eCSSToken_Hash: |
|
5421 // #xxyyzz |
|
5422 if (NS_HexToRGB(tk->mIdent, &rgba)) { |
|
5423 MOZ_ASSERT(tk->mIdent.Length() == 3 || tk->mIdent.Length() == 6, |
|
5424 "unexpected hex color length"); |
|
5425 nsCSSUnit unit = tk->mIdent.Length() == 3 ? |
|
5426 eCSSUnit_ShortHexColor : |
|
5427 eCSSUnit_HexColor; |
|
5428 aValue.SetIntegerColorValue(rgba, unit); |
|
5429 return true; |
|
5430 } |
|
5431 break; |
|
5432 |
|
5433 case eCSSToken_Ident: |
|
5434 if (NS_ColorNameToRGB(tk->mIdent, &rgba)) { |
|
5435 aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident); |
|
5436 return true; |
|
5437 } |
|
5438 else { |
|
5439 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent); |
|
5440 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword |
|
5441 int32_t value; |
|
5442 if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) { |
|
5443 aValue.SetIntValue(value, eCSSUnit_EnumColor); |
|
5444 return true; |
|
5445 } |
|
5446 } |
|
5447 } |
|
5448 break; |
|
5449 case eCSSToken_Function: |
|
5450 if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) { |
|
5451 // rgb ( component , component , component ) |
|
5452 if (GetToken(true)) { |
|
5453 UngetToken(); |
|
5454 } |
|
5455 if (mToken.mType == eCSSToken_Number) { |
|
5456 uint8_t r, g, b; |
|
5457 if (ParseNumberColorComponent(r, ',') && |
|
5458 ParseNumberColorComponent(g, ',') && |
|
5459 ParseNumberColorComponent(b, ')')) { |
|
5460 aValue.SetIntegerColorValue(NS_RGB(r, g, b), eCSSUnit_RGBColor); |
|
5461 return true; |
|
5462 } |
|
5463 } else { |
|
5464 float r, g, b; |
|
5465 if (ParsePercentageColorComponent(r, ',') && |
|
5466 ParsePercentageColorComponent(g, ',') && |
|
5467 ParsePercentageColorComponent(b, ')')) { |
|
5468 aValue.SetFloatColorValue(r, g, b, 1.0f, |
|
5469 eCSSUnit_PercentageRGBColor); |
|
5470 return true; |
|
5471 } |
|
5472 } |
|
5473 SkipUntil(')'); |
|
5474 return false; |
|
5475 } |
|
5476 else if (mToken.mIdent.LowerCaseEqualsLiteral("rgba")) { |
|
5477 // rgba ( component , component , component , opacity ) |
|
5478 if (GetToken(true)) { |
|
5479 UngetToken(); |
|
5480 } |
|
5481 if (mToken.mType == eCSSToken_Number) { |
|
5482 uint8_t r, g, b, a; |
|
5483 if (ParseNumberColorComponent(r, ',') && |
|
5484 ParseNumberColorComponent(g, ',') && |
|
5485 ParseNumberColorComponent(b, ',') && |
|
5486 ParseColorOpacity(a)) { |
|
5487 aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a), |
|
5488 eCSSUnit_RGBAColor); |
|
5489 return true; |
|
5490 } |
|
5491 } else { |
|
5492 float r, g, b, a; |
|
5493 if (ParsePercentageColorComponent(r, ',') && |
|
5494 ParsePercentageColorComponent(g, ',') && |
|
5495 ParsePercentageColorComponent(b, ',') && |
|
5496 ParseColorOpacity(a)) { |
|
5497 aValue.SetFloatColorValue(r, g, b, a, eCSSUnit_PercentageRGBAColor); |
|
5498 return true; |
|
5499 } |
|
5500 } |
|
5501 SkipUntil(')'); |
|
5502 return false; |
|
5503 } |
|
5504 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) { |
|
5505 // hsl ( hue , saturation , lightness ) |
|
5506 // "hue" is a number, "saturation" and "lightness" are percentages. |
|
5507 float h, s, l; |
|
5508 if (ParseHSLColor(h, s, l, ')')) { |
|
5509 aValue.SetFloatColorValue(h, s, l, 1.0f, eCSSUnit_HSLColor); |
|
5510 return true; |
|
5511 } |
|
5512 SkipUntil(')'); |
|
5513 return false; |
|
5514 } |
|
5515 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsla")) { |
|
5516 // hsla ( hue , saturation , lightness , opacity ) |
|
5517 // "hue" is a number, "saturation" and "lightness" are percentages, |
|
5518 // "opacity" is a number. |
|
5519 float h, s, l, a; |
|
5520 if (ParseHSLColor(h, s, l, ',') && |
|
5521 ParseColorOpacity(a)) { |
|
5522 aValue.SetFloatColorValue(h, s, l, a, eCSSUnit_HSLAColor); |
|
5523 return true; |
|
5524 } |
|
5525 SkipUntil(')'); |
|
5526 return false; |
|
5527 } |
|
5528 break; |
|
5529 default: |
|
5530 break; |
|
5531 } |
|
5532 |
|
5533 // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804) |
|
5534 if (mHashlessColorQuirk) { |
|
5535 // - If the string starts with 'a-f', the nsCSSScanner builds the |
|
5536 // token as a eCSSToken_Ident and we can parse the string as a |
|
5537 // 'xxyyzz' RGB color. |
|
5538 // - If it only contains '0-9' digits, the token is a |
|
5539 // eCSSToken_Number and it must be converted back to a 6 |
|
5540 // characters string to be parsed as a RGB color. |
|
5541 // - If it starts with '0-9' and contains any 'a-f', the token is a |
|
5542 // eCSSToken_Dimension, the mNumber part must be converted back to |
|
5543 // a string and the mIdent part must be appended to that string so |
|
5544 // that the resulting string has 6 characters. |
|
5545 // Note: This is a hack for Nav compatibility. Do not attempt to |
|
5546 // simplify it by hacking into the ncCSSScanner. This would be very |
|
5547 // bad. |
|
5548 nsAutoString str; |
|
5549 char buffer[20]; |
|
5550 switch (tk->mType) { |
|
5551 case eCSSToken_Ident: |
|
5552 str.Assign(tk->mIdent); |
|
5553 break; |
|
5554 |
|
5555 case eCSSToken_Number: |
|
5556 if (tk->mIntegerValid) { |
|
5557 PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger); |
|
5558 str.AssignWithConversion(buffer); |
|
5559 } |
|
5560 break; |
|
5561 |
|
5562 case eCSSToken_Dimension: |
|
5563 if (tk->mIdent.Length() <= 6) { |
|
5564 PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber); |
|
5565 nsAutoString temp; |
|
5566 temp.AssignWithConversion(buffer); |
|
5567 temp.Right(str, 6 - tk->mIdent.Length()); |
|
5568 str.Append(tk->mIdent); |
|
5569 } |
|
5570 break; |
|
5571 default: |
|
5572 // There is a whole bunch of cases that are |
|
5573 // not handled by this switch. Ignore them. |
|
5574 break; |
|
5575 } |
|
5576 if (NS_HexToRGB(str, &rgba)) { |
|
5577 aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor); |
|
5578 return true; |
|
5579 } |
|
5580 } |
|
5581 |
|
5582 // It's not a color |
|
5583 REPORT_UNEXPECTED_TOKEN(PEColorNotColor); |
|
5584 UngetToken(); |
|
5585 return false; |
|
5586 } |
|
5587 |
|
5588 bool |
|
5589 CSSParserImpl::ParseNumberColorComponent(uint8_t& aComponent, char aStop) |
|
5590 { |
|
5591 if (!GetToken(true)) { |
|
5592 REPORT_UNEXPECTED_EOF(PEColorComponentEOF); |
|
5593 return false; |
|
5594 } |
|
5595 |
|
5596 if (mToken.mType != eCSSToken_Number || !mToken.mIntegerValid) { |
|
5597 REPORT_UNEXPECTED_TOKEN(PEExpectedInt); |
|
5598 UngetToken(); |
|
5599 return false; |
|
5600 } |
|
5601 |
|
5602 float value = mToken.mNumber; |
|
5603 if (value < 0.0f) value = 0.0f; |
|
5604 if (value > 255.0f) value = 255.0f; |
|
5605 |
|
5606 if (ExpectSymbol(aStop, true)) { |
|
5607 aComponent = NSToIntRound(value); |
|
5608 return true; |
|
5609 } |
|
5610 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop); |
|
5611 return false; |
|
5612 } |
|
5613 |
|
5614 bool |
|
5615 CSSParserImpl::ParsePercentageColorComponent(float& aComponent, char aStop) |
|
5616 { |
|
5617 if (!GetToken(true)) { |
|
5618 REPORT_UNEXPECTED_EOF(PEColorComponentEOF); |
|
5619 return false; |
|
5620 } |
|
5621 |
|
5622 if (mToken.mType != eCSSToken_Percentage) { |
|
5623 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent); |
|
5624 UngetToken(); |
|
5625 return false; |
|
5626 } |
|
5627 |
|
5628 float value = mToken.mNumber; |
|
5629 if (value < 0.0f) value = 0.0f; |
|
5630 if (value > 1.0f) value = 1.0f; |
|
5631 |
|
5632 if (ExpectSymbol(aStop, true)) { |
|
5633 aComponent = value; |
|
5634 return true; |
|
5635 } |
|
5636 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop); |
|
5637 return false; |
|
5638 } |
|
5639 |
|
5640 |
|
5641 bool |
|
5642 CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness, |
|
5643 char aStop) |
|
5644 { |
|
5645 float h, s, l; |
|
5646 |
|
5647 // Get the hue |
|
5648 if (!GetToken(true)) { |
|
5649 REPORT_UNEXPECTED_EOF(PEColorHueEOF); |
|
5650 return false; |
|
5651 } |
|
5652 if (mToken.mType != eCSSToken_Number) { |
|
5653 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber); |
|
5654 UngetToken(); |
|
5655 return false; |
|
5656 } |
|
5657 h = mToken.mNumber; |
|
5658 h /= 360.0f; |
|
5659 // hue values are wraparound |
|
5660 h = h - floor(h); |
|
5661 |
|
5662 if (!ExpectSymbol(',', true)) { |
|
5663 REPORT_UNEXPECTED_TOKEN(PEExpectedComma); |
|
5664 return false; |
|
5665 } |
|
5666 |
|
5667 // Get the saturation |
|
5668 if (!GetToken(true)) { |
|
5669 REPORT_UNEXPECTED_EOF(PEColorSaturationEOF); |
|
5670 return false; |
|
5671 } |
|
5672 if (mToken.mType != eCSSToken_Percentage) { |
|
5673 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent); |
|
5674 UngetToken(); |
|
5675 return false; |
|
5676 } |
|
5677 s = mToken.mNumber; |
|
5678 if (s < 0.0f) s = 0.0f; |
|
5679 if (s > 1.0f) s = 1.0f; |
|
5680 |
|
5681 if (!ExpectSymbol(',', true)) { |
|
5682 REPORT_UNEXPECTED_TOKEN(PEExpectedComma); |
|
5683 return false; |
|
5684 } |
|
5685 |
|
5686 // Get the lightness |
|
5687 if (!GetToken(true)) { |
|
5688 REPORT_UNEXPECTED_EOF(PEColorLightnessEOF); |
|
5689 return false; |
|
5690 } |
|
5691 if (mToken.mType != eCSSToken_Percentage) { |
|
5692 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent); |
|
5693 UngetToken(); |
|
5694 return false; |
|
5695 } |
|
5696 l = mToken.mNumber; |
|
5697 if (l < 0.0f) l = 0.0f; |
|
5698 if (l > 1.0f) l = 1.0f; |
|
5699 |
|
5700 if (ExpectSymbol(aStop, true)) { |
|
5701 aHue = h; |
|
5702 aSaturation = s; |
|
5703 aLightness = l; |
|
5704 return true; |
|
5705 } |
|
5706 |
|
5707 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop); |
|
5708 return false; |
|
5709 } |
|
5710 |
|
5711 |
|
5712 bool |
|
5713 CSSParserImpl::ParseColorOpacity(uint8_t& aOpacity) |
|
5714 { |
|
5715 float floatOpacity; |
|
5716 if (!ParseColorOpacity(floatOpacity)) { |
|
5717 return false; |
|
5718 } |
|
5719 |
|
5720 uint8_t value = nsStyleUtil::FloatToColorComponent(floatOpacity); |
|
5721 // Need to compare to something slightly larger |
|
5722 // than 0.5 due to floating point inaccuracies. |
|
5723 NS_ASSERTION(fabs(255.0f*mToken.mNumber - value) <= 0.51f, |
|
5724 "FloatToColorComponent did something weird"); |
|
5725 |
|
5726 aOpacity = value; |
|
5727 return true; |
|
5728 } |
|
5729 |
|
5730 bool |
|
5731 CSSParserImpl::ParseColorOpacity(float& aOpacity) |
|
5732 { |
|
5733 if (!GetToken(true)) { |
|
5734 REPORT_UNEXPECTED_EOF(PEColorOpacityEOF); |
|
5735 return false; |
|
5736 } |
|
5737 |
|
5738 if (mToken.mType != eCSSToken_Number) { |
|
5739 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber); |
|
5740 UngetToken(); |
|
5741 return false; |
|
5742 } |
|
5743 |
|
5744 if (!ExpectSymbol(')', true)) { |
|
5745 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen); |
|
5746 return false; |
|
5747 } |
|
5748 |
|
5749 if (mToken.mNumber < 0.0f) { |
|
5750 mToken.mNumber = 0.0f; |
|
5751 } else if (mToken.mNumber > 1.0f) { |
|
5752 mToken.mNumber = 1.0f; |
|
5753 } |
|
5754 |
|
5755 aOpacity = mToken.mNumber; |
|
5756 return true; |
|
5757 } |
|
5758 |
|
5759 #ifdef MOZ_XUL |
|
5760 bool |
|
5761 CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs) |
|
5762 { |
|
5763 // The argument to a tree pseudo-element is a sequence of identifiers |
|
5764 // that are either space- or comma-separated. (Was the intent to |
|
5765 // allow only comma-separated? That's not what was done.) |
|
5766 nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass |
|
5767 |
|
5768 while (!ExpectSymbol(')', true)) { |
|
5769 if (!GetToken(true)) { |
|
5770 return false; |
|
5771 } |
|
5772 if (eCSSToken_Ident == mToken.mType) { |
|
5773 fakeSelector.AddClass(mToken.mIdent); |
|
5774 } |
|
5775 else if (!mToken.IsSymbol(',')) { |
|
5776 UngetToken(); |
|
5777 SkipUntil(')'); |
|
5778 return false; |
|
5779 } |
|
5780 } |
|
5781 *aPseudoElementArgs = fakeSelector.mClassList; |
|
5782 fakeSelector.mClassList = nullptr; |
|
5783 return true; |
|
5784 } |
|
5785 #endif |
|
5786 |
|
5787 //---------------------------------------------------------------------- |
|
5788 |
|
5789 bool |
|
5790 CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration, |
|
5791 uint32_t aFlags, |
|
5792 bool aMustCallValueAppended, |
|
5793 bool* aChanged, |
|
5794 nsCSSContextType aContext) |
|
5795 { |
|
5796 NS_PRECONDITION(aContext == eCSSContext_General || |
|
5797 aContext == eCSSContext_Page, |
|
5798 "Must be page or general context"); |
|
5799 |
|
5800 bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0; |
|
5801 |
|
5802 mTempData.AssertInitialState(); |
|
5803 |
|
5804 // Get property name |
|
5805 nsCSSToken* tk = &mToken; |
|
5806 nsAutoString propertyName; |
|
5807 for (;;) { |
|
5808 if (!GetToken(true)) { |
|
5809 if (checkForBraces) { |
|
5810 REPORT_UNEXPECTED_EOF(PEDeclEndEOF); |
|
5811 } |
|
5812 return false; |
|
5813 } |
|
5814 if (eCSSToken_Ident == tk->mType) { |
|
5815 propertyName = tk->mIdent; |
|
5816 // grab the ident before the ExpectSymbol trashes the token |
|
5817 if (!ExpectSymbol(':', true)) { |
|
5818 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon); |
|
5819 REPORT_UNEXPECTED(PEDeclDropped); |
|
5820 OUTPUT_ERROR(); |
|
5821 return false; |
|
5822 } |
|
5823 break; |
|
5824 } |
|
5825 if (tk->IsSymbol(';')) { |
|
5826 // dangling semicolons are skipped |
|
5827 continue; |
|
5828 } |
|
5829 |
|
5830 if (!tk->IsSymbol('}')) { |
|
5831 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected); |
|
5832 REPORT_UNEXPECTED(PEDeclSkipped); |
|
5833 OUTPUT_ERROR(); |
|
5834 |
|
5835 if (eCSSToken_AtKeyword == tk->mType) { |
|
5836 SkipAtRule(checkForBraces); |
|
5837 return true; // Not a declaration, but don't skip until ';' |
|
5838 } |
|
5839 } |
|
5840 // Not a declaration... |
|
5841 UngetToken(); |
|
5842 return false; |
|
5843 } |
|
5844 |
|
5845 // Don't report property parse errors if we're inside a failing @supports |
|
5846 // rule. |
|
5847 nsAutoSuppressErrors suppressErrors(this, mInFailingSupportsRule); |
|
5848 |
|
5849 // Information about a parsed non-custom property. |
|
5850 nsCSSProperty propID; |
|
5851 |
|
5852 // Information about a parsed custom property. |
|
5853 CSSVariableDeclarations::Type variableType; |
|
5854 nsString variableValue; |
|
5855 |
|
5856 // Check if the property name is a custom property. |
|
5857 bool customProperty = nsLayoutUtils::CSSVariablesEnabled() && |
|
5858 nsCSSProps::IsCustomPropertyName(propertyName) && |
|
5859 aContext == eCSSContext_General; |
|
5860 |
|
5861 if (customProperty) { |
|
5862 if (!ParseVariableDeclaration(&variableType, variableValue)) { |
|
5863 REPORT_UNEXPECTED_P(PEValueParsingError, propertyName); |
|
5864 REPORT_UNEXPECTED(PEDeclDropped); |
|
5865 OUTPUT_ERROR(); |
|
5866 return false; |
|
5867 } |
|
5868 } else { |
|
5869 // Map property name to its ID. |
|
5870 propID = LookupEnabledProperty(propertyName); |
|
5871 if (eCSSProperty_UNKNOWN == propID || |
|
5872 (aContext == eCSSContext_Page && |
|
5873 !nsCSSProps::PropHasFlags(propID, |
|
5874 CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property |
|
5875 if (!NonMozillaVendorIdentifier(propertyName)) { |
|
5876 REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName); |
|
5877 REPORT_UNEXPECTED(PEDeclDropped); |
|
5878 OUTPUT_ERROR(); |
|
5879 } |
|
5880 return false; |
|
5881 } |
|
5882 // Then parse the property. |
|
5883 if (!ParseProperty(propID)) { |
|
5884 // XXX Much better to put stuff in the value parsers instead... |
|
5885 REPORT_UNEXPECTED_P(PEValueParsingError, propertyName); |
|
5886 REPORT_UNEXPECTED(PEDeclDropped); |
|
5887 OUTPUT_ERROR(); |
|
5888 mTempData.ClearProperty(propID); |
|
5889 mTempData.AssertInitialState(); |
|
5890 return false; |
|
5891 } |
|
5892 } |
|
5893 |
|
5894 CLEAR_ERROR(); |
|
5895 |
|
5896 // Look for "!important". |
|
5897 PriorityParsingStatus status; |
|
5898 if ((aFlags & eParseDeclaration_AllowImportant) != 0) { |
|
5899 status = ParsePriority(); |
|
5900 } else { |
|
5901 status = ePriority_None; |
|
5902 } |
|
5903 |
|
5904 // Look for a semicolon or close brace. |
|
5905 if (status != ePriority_Error) { |
|
5906 if (!GetToken(true)) { |
|
5907 // EOF is always ok |
|
5908 } else if (mToken.IsSymbol(';')) { |
|
5909 // semicolon is always ok |
|
5910 } else if (mToken.IsSymbol('}')) { |
|
5911 // brace is ok if checkForBraces, but don't eat it |
|
5912 UngetToken(); |
|
5913 if (!checkForBraces) { |
|
5914 status = ePriority_Error; |
|
5915 } |
|
5916 } else { |
|
5917 UngetToken(); |
|
5918 status = ePriority_Error; |
|
5919 } |
|
5920 } |
|
5921 |
|
5922 if (status == ePriority_Error) { |
|
5923 if (checkForBraces) { |
|
5924 REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2); |
|
5925 } else { |
|
5926 REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd); |
|
5927 } |
|
5928 REPORT_UNEXPECTED(PEDeclDropped); |
|
5929 OUTPUT_ERROR(); |
|
5930 if (!customProperty) { |
|
5931 mTempData.ClearProperty(propID); |
|
5932 } |
|
5933 mTempData.AssertInitialState(); |
|
5934 return false; |
|
5935 } |
|
5936 |
|
5937 if (customProperty) { |
|
5938 MOZ_ASSERT(Substring(propertyName, 0, |
|
5939 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--")); |
|
5940 // remove '--' |
|
5941 nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH); |
|
5942 aDeclaration->AddVariableDeclaration(varName, variableType, variableValue, |
|
5943 status == ePriority_Important, false); |
|
5944 } else { |
|
5945 *aChanged |= mData.TransferFromBlock(mTempData, propID, |
|
5946 status == ePriority_Important, |
|
5947 false, aMustCallValueAppended, |
|
5948 aDeclaration); |
|
5949 } |
|
5950 |
|
5951 return true; |
|
5952 } |
|
5953 |
|
5954 static const nsCSSProperty kBorderTopIDs[] = { |
|
5955 eCSSProperty_border_top_width, |
|
5956 eCSSProperty_border_top_style, |
|
5957 eCSSProperty_border_top_color |
|
5958 }; |
|
5959 static const nsCSSProperty kBorderRightIDs[] = { |
|
5960 eCSSProperty_border_right_width_value, |
|
5961 eCSSProperty_border_right_style_value, |
|
5962 eCSSProperty_border_right_color_value, |
|
5963 eCSSProperty_border_right_width, |
|
5964 eCSSProperty_border_right_style, |
|
5965 eCSSProperty_border_right_color |
|
5966 }; |
|
5967 static const nsCSSProperty kBorderBottomIDs[] = { |
|
5968 eCSSProperty_border_bottom_width, |
|
5969 eCSSProperty_border_bottom_style, |
|
5970 eCSSProperty_border_bottom_color |
|
5971 }; |
|
5972 static const nsCSSProperty kBorderLeftIDs[] = { |
|
5973 eCSSProperty_border_left_width_value, |
|
5974 eCSSProperty_border_left_style_value, |
|
5975 eCSSProperty_border_left_color_value, |
|
5976 eCSSProperty_border_left_width, |
|
5977 eCSSProperty_border_left_style, |
|
5978 eCSSProperty_border_left_color |
|
5979 }; |
|
5980 static const nsCSSProperty kBorderStartIDs[] = { |
|
5981 eCSSProperty_border_start_width_value, |
|
5982 eCSSProperty_border_start_style_value, |
|
5983 eCSSProperty_border_start_color_value, |
|
5984 eCSSProperty_border_start_width, |
|
5985 eCSSProperty_border_start_style, |
|
5986 eCSSProperty_border_start_color |
|
5987 }; |
|
5988 static const nsCSSProperty kBorderEndIDs[] = { |
|
5989 eCSSProperty_border_end_width_value, |
|
5990 eCSSProperty_border_end_style_value, |
|
5991 eCSSProperty_border_end_color_value, |
|
5992 eCSSProperty_border_end_width, |
|
5993 eCSSProperty_border_end_style, |
|
5994 eCSSProperty_border_end_color |
|
5995 }; |
|
5996 static const nsCSSProperty kColumnRuleIDs[] = { |
|
5997 eCSSProperty__moz_column_rule_width, |
|
5998 eCSSProperty__moz_column_rule_style, |
|
5999 eCSSProperty__moz_column_rule_color |
|
6000 }; |
|
6001 |
|
6002 bool |
|
6003 CSSParserImpl::ParseEnum(nsCSSValue& aValue, |
|
6004 const KTableValue aKeywordTable[]) |
|
6005 { |
|
6006 nsSubstring* ident = NextIdent(); |
|
6007 if (nullptr == ident) { |
|
6008 return false; |
|
6009 } |
|
6010 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident); |
|
6011 if (eCSSKeyword_UNKNOWN < keyword) { |
|
6012 int32_t value; |
|
6013 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) { |
|
6014 aValue.SetIntValue(value, eCSSUnit_Enumerated); |
|
6015 return true; |
|
6016 } |
|
6017 } |
|
6018 |
|
6019 // Put the unknown identifier back and return |
|
6020 UngetToken(); |
|
6021 return false; |
|
6022 } |
|
6023 |
|
6024 |
|
6025 struct UnitInfo { |
|
6026 char name[6]; // needs to be long enough for the longest unit, with |
|
6027 // terminating null. |
|
6028 uint32_t length; |
|
6029 nsCSSUnit unit; |
|
6030 int32_t type; |
|
6031 }; |
|
6032 |
|
6033 #define STR_WITH_LEN(_str) \ |
|
6034 _str, sizeof(_str) - 1 |
|
6035 |
|
6036 const UnitInfo UnitData[] = { |
|
6037 { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH }, |
|
6038 { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH }, |
|
6039 { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH }, |
|
6040 { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH }, |
|
6041 { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH }, |
|
6042 { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH }, |
|
6043 { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH }, |
|
6044 { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH }, |
|
6045 { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH }, |
|
6046 { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH }, |
|
6047 { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH }, |
|
6048 { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH }, |
|
6049 { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH }, |
|
6050 { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH }, |
|
6051 { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH }, |
|
6052 { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE }, |
|
6053 { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE }, |
|
6054 { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE }, |
|
6055 { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE }, |
|
6056 { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY }, |
|
6057 { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY }, |
|
6058 { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME }, |
|
6059 { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME } |
|
6060 }; |
|
6061 |
|
6062 #undef STR_WITH_LEN |
|
6063 |
|
6064 bool |
|
6065 CSSParserImpl::TranslateDimension(nsCSSValue& aValue, |
|
6066 int32_t aVariantMask, |
|
6067 float aNumber, |
|
6068 const nsString& aUnit) |
|
6069 { |
|
6070 nsCSSUnit units; |
|
6071 int32_t type = 0; |
|
6072 if (!aUnit.IsEmpty()) { |
|
6073 uint32_t i; |
|
6074 for (i = 0; i < ArrayLength(UnitData); ++i) { |
|
6075 if (aUnit.LowerCaseEqualsASCII(UnitData[i].name, |
|
6076 UnitData[i].length)) { |
|
6077 units = UnitData[i].unit; |
|
6078 type = UnitData[i].type; |
|
6079 break; |
|
6080 } |
|
6081 } |
|
6082 |
|
6083 if (i == ArrayLength(UnitData)) { |
|
6084 // Unknown unit |
|
6085 return false; |
|
6086 } |
|
6087 |
|
6088 if (!mViewportUnitsEnabled && |
|
6089 (eCSSUnit_ViewportWidth == units || |
|
6090 eCSSUnit_ViewportHeight == units || |
|
6091 eCSSUnit_ViewportMin == units || |
|
6092 eCSSUnit_ViewportMax == units)) { |
|
6093 // Viewport units aren't allowed right now, probably because we're |
|
6094 // inside an @page declaration. Fail. |
|
6095 return false; |
|
6096 } |
|
6097 } else { |
|
6098 // Must be a zero number... |
|
6099 NS_ASSERTION(0 == aNumber, "numbers without units must be 0"); |
|
6100 if ((VARIANT_LENGTH & aVariantMask) != 0) { |
|
6101 units = eCSSUnit_Pixel; |
|
6102 type = VARIANT_LENGTH; |
|
6103 } |
|
6104 else if ((VARIANT_ANGLE & aVariantMask) != 0) { |
|
6105 NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE, |
|
6106 "must have allowed zero angle"); |
|
6107 units = eCSSUnit_Degree; |
|
6108 type = VARIANT_ANGLE; |
|
6109 } |
|
6110 else { |
|
6111 NS_ERROR("Variant mask does not include dimension; why were we called?"); |
|
6112 return false; |
|
6113 } |
|
6114 } |
|
6115 if ((type & aVariantMask) != 0) { |
|
6116 aValue.SetFloatValue(aNumber, units); |
|
6117 return true; |
|
6118 } |
|
6119 return false; |
|
6120 } |
|
6121 |
|
6122 // Note that this does include VARIANT_CALC, which is numeric. This is |
|
6123 // because calc() parsing, as proposed, drops range restrictions inside |
|
6124 // the calc() expression and clamps the result of the calculation to the |
|
6125 // range. |
|
6126 #define VARIANT_ALL_NONNUMERIC \ |
|
6127 VARIANT_KEYWORD | \ |
|
6128 VARIANT_COLOR | \ |
|
6129 VARIANT_URL | \ |
|
6130 VARIANT_STRING | \ |
|
6131 VARIANT_COUNTER | \ |
|
6132 VARIANT_ATTR | \ |
|
6133 VARIANT_IDENTIFIER | \ |
|
6134 VARIANT_IDENTIFIER_NO_INHERIT | \ |
|
6135 VARIANT_AUTO | \ |
|
6136 VARIANT_INHERIT | \ |
|
6137 VARIANT_NONE | \ |
|
6138 VARIANT_NORMAL | \ |
|
6139 VARIANT_SYSFONT | \ |
|
6140 VARIANT_GRADIENT | \ |
|
6141 VARIANT_TIMING_FUNCTION | \ |
|
6142 VARIANT_ALL | \ |
|
6143 VARIANT_CALC | \ |
|
6144 VARIANT_OPENTYPE_SVG_KEYWORD |
|
6145 |
|
6146 // Note that callers passing VARIANT_CALC in aVariantMask will get |
|
6147 // full-range parsing inside the calc() expression, and the code that |
|
6148 // computes the calc will be required to clamp the resulting value to an |
|
6149 // appropriate range. |
|
6150 bool |
|
6151 CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue, |
|
6152 int32_t aVariantMask, |
|
6153 const KTableValue aKeywordTable[]) |
|
6154 { |
|
6155 // The variant mask must only contain non-numeric variants or the ones |
|
6156 // that we specifically handle. |
|
6157 NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC | |
|
6158 VARIANT_NUMBER | |
|
6159 VARIANT_LENGTH | |
|
6160 VARIANT_PERCENT | |
|
6161 VARIANT_INTEGER)) == 0, |
|
6162 "need to update code below to handle additional variants"); |
|
6163 |
|
6164 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) { |
|
6165 if (eCSSUnit_Number == aValue.GetUnit() || |
|
6166 aValue.IsLengthUnit()){ |
|
6167 if (aValue.GetFloatValue() < 0) { |
|
6168 UngetToken(); |
|
6169 return false; |
|
6170 } |
|
6171 } |
|
6172 else if (aValue.GetUnit() == eCSSUnit_Percent) { |
|
6173 if (aValue.GetPercentValue() < 0) { |
|
6174 UngetToken(); |
|
6175 return false; |
|
6176 } |
|
6177 } else if (aValue.GetUnit() == eCSSUnit_Integer) { |
|
6178 if (aValue.GetIntValue() < 0) { |
|
6179 UngetToken(); |
|
6180 return false; |
|
6181 } |
|
6182 } |
|
6183 return true; |
|
6184 } |
|
6185 return false; |
|
6186 } |
|
6187 |
|
6188 // Note that callers passing VARIANT_CALC in aVariantMask will get |
|
6189 // full-range parsing inside the calc() expression, and the code that |
|
6190 // computes the calc will be required to clamp the resulting value to an |
|
6191 // appropriate range. |
|
6192 bool |
|
6193 CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue, |
|
6194 int32_t aVariantMask, |
|
6195 const KTableValue aKeywordTable[]) |
|
6196 { |
|
6197 // The variant mask must only contain non-numeric variants or the ones |
|
6198 // that we specifically handle. |
|
6199 NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC | |
|
6200 VARIANT_NUMBER | |
|
6201 VARIANT_INTEGER)) == 0, |
|
6202 "need to update code below to handle additional variants"); |
|
6203 |
|
6204 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) { |
|
6205 if (aValue.GetUnit() == eCSSUnit_Integer) { |
|
6206 if (aValue.GetIntValue() < 1) { |
|
6207 UngetToken(); |
|
6208 return false; |
|
6209 } |
|
6210 } else if (eCSSUnit_Number == aValue.GetUnit()) { |
|
6211 if (aValue.GetFloatValue() < 1.0f) { |
|
6212 UngetToken(); |
|
6213 return false; |
|
6214 } |
|
6215 } |
|
6216 return true; |
|
6217 } |
|
6218 return false; |
|
6219 } |
|
6220 |
|
6221 // Assigns to aValue iff it returns true. |
|
6222 bool |
|
6223 CSSParserImpl::ParseVariant(nsCSSValue& aValue, |
|
6224 int32_t aVariantMask, |
|
6225 const KTableValue aKeywordTable[]) |
|
6226 { |
|
6227 NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) || |
|
6228 !(aVariantMask & VARIANT_NUMBER), |
|
6229 "can't distinguish colors from numbers"); |
|
6230 NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) || |
|
6231 !(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)), |
|
6232 "can't distinguish colors from lengths"); |
|
6233 NS_ASSERTION(!(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)) || |
|
6234 !(aVariantMask & VARIANT_NUMBER), |
|
6235 "can't distinguish lengths from numbers"); |
|
6236 NS_ABORT_IF_FALSE(!(aVariantMask & VARIANT_IDENTIFIER) || |
|
6237 !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT), |
|
6238 "must not set both VARIANT_IDENTIFIER and " |
|
6239 "VARIANT_IDENTIFIER_NO_INHERIT"); |
|
6240 |
|
6241 if (!GetToken(true)) { |
|
6242 return false; |
|
6243 } |
|
6244 nsCSSToken* tk = &mToken; |
|
6245 if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) && |
|
6246 (eCSSToken_Ident == tk->mType)) { |
|
6247 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent); |
|
6248 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword |
|
6249 if ((aVariantMask & VARIANT_AUTO) != 0) { |
|
6250 if (eCSSKeyword_auto == keyword) { |
|
6251 aValue.SetAutoValue(); |
|
6252 return true; |
|
6253 } |
|
6254 } |
|
6255 if ((aVariantMask & VARIANT_INHERIT) != 0) { |
|
6256 // XXX Should we check IsParsingCompoundProperty, or do all |
|
6257 // callers handle it? (Not all callers set it, though, since |
|
6258 // they want the quirks that are disabled by setting it.) |
|
6259 |
|
6260 // IMPORTANT: If new keywords are added here, |
|
6261 // they probably need to be added in ParseCustomIdent as well. |
|
6262 if (eCSSKeyword_inherit == keyword) { |
|
6263 aValue.SetInheritValue(); |
|
6264 return true; |
|
6265 } |
|
6266 else if (eCSSKeyword_initial == keyword) { |
|
6267 aValue.SetInitialValue(); |
|
6268 return true; |
|
6269 } |
|
6270 else if (eCSSKeyword_unset == keyword && |
|
6271 nsLayoutUtils::UnsetValueEnabled()) { |
|
6272 aValue.SetUnsetValue(); |
|
6273 return true; |
|
6274 } |
|
6275 } |
|
6276 if ((aVariantMask & VARIANT_NONE) != 0) { |
|
6277 if (eCSSKeyword_none == keyword) { |
|
6278 aValue.SetNoneValue(); |
|
6279 return true; |
|
6280 } |
|
6281 } |
|
6282 if ((aVariantMask & VARIANT_ALL) != 0) { |
|
6283 if (eCSSKeyword_all == keyword) { |
|
6284 aValue.SetAllValue(); |
|
6285 return true; |
|
6286 } |
|
6287 } |
|
6288 if ((aVariantMask & VARIANT_NORMAL) != 0) { |
|
6289 if (eCSSKeyword_normal == keyword) { |
|
6290 aValue.SetNormalValue(); |
|
6291 return true; |
|
6292 } |
|
6293 } |
|
6294 if ((aVariantMask & VARIANT_SYSFONT) != 0) { |
|
6295 if (eCSSKeyword__moz_use_system_font == keyword && |
|
6296 !IsParsingCompoundProperty()) { |
|
6297 aValue.SetSystemFontValue(); |
|
6298 return true; |
|
6299 } |
|
6300 } |
|
6301 if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) { |
|
6302 static bool sOpentypeSVGEnabled; |
|
6303 static bool sOpentypeSVGEnabledCached = false; |
|
6304 if (!sOpentypeSVGEnabledCached) { |
|
6305 sOpentypeSVGEnabledCached = true; |
|
6306 Preferences::AddBoolVarCache(&sOpentypeSVGEnabled, |
|
6307 "gfx.font_rendering.opentype_svg.enabled"); |
|
6308 } |
|
6309 if (sOpentypeSVGEnabled) { |
|
6310 aVariantMask |= VARIANT_KEYWORD; |
|
6311 } |
|
6312 } |
|
6313 if ((aVariantMask & VARIANT_KEYWORD) != 0) { |
|
6314 int32_t value; |
|
6315 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) { |
|
6316 aValue.SetIntValue(value, eCSSUnit_Enumerated); |
|
6317 return true; |
|
6318 } |
|
6319 } |
|
6320 } |
|
6321 } |
|
6322 // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or |
|
6323 // VARIANT_ZERO_ANGLE. |
|
6324 if (((aVariantMask & VARIANT_NUMBER) != 0) && |
|
6325 (eCSSToken_Number == tk->mType)) { |
|
6326 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number); |
|
6327 return true; |
|
6328 } |
|
6329 if (((aVariantMask & VARIANT_INTEGER) != 0) && |
|
6330 (eCSSToken_Number == tk->mType) && tk->mIntegerValid) { |
|
6331 aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer); |
|
6332 return true; |
|
6333 } |
|
6334 if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | |
|
6335 VARIANT_FREQUENCY | VARIANT_TIME)) != 0 && |
|
6336 eCSSToken_Dimension == tk->mType) || |
|
6337 ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 && |
|
6338 eCSSToken_Number == tk->mType && |
|
6339 tk->mNumber == 0.0f)) { |
|
6340 if (((aVariantMask & VARIANT_POSITIVE_DIMENSION) != 0 && |
|
6341 tk->mNumber <= 0.0) || |
|
6342 ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 && |
|
6343 tk->mNumber < 0.0)) { |
|
6344 UngetToken(); |
|
6345 return false; |
|
6346 } |
|
6347 if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) { |
|
6348 return true; |
|
6349 } |
|
6350 // Put the token back; we didn't parse it, so we shouldn't consume it |
|
6351 UngetToken(); |
|
6352 return false; |
|
6353 } |
|
6354 if (((aVariantMask & VARIANT_PERCENT) != 0) && |
|
6355 (eCSSToken_Percentage == tk->mType)) { |
|
6356 aValue.SetPercentValue(tk->mNumber); |
|
6357 return true; |
|
6358 } |
|
6359 if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px |
|
6360 if (((aVariantMask & VARIANT_LENGTH) != 0) && |
|
6361 (eCSSToken_Number == tk->mType)) { |
|
6362 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel); |
|
6363 return true; |
|
6364 } |
|
6365 } |
|
6366 |
|
6367 if (IsSVGMode() && !IsParsingCompoundProperty()) { |
|
6368 // STANDARD: SVG Spec states that lengths and coordinates can be unitless |
|
6369 // in which case they default to user-units (1 px = 1 user unit) |
|
6370 if (((aVariantMask & VARIANT_LENGTH) != 0) && |
|
6371 (eCSSToken_Number == tk->mType)) { |
|
6372 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel); |
|
6373 return true; |
|
6374 } |
|
6375 } |
|
6376 |
|
6377 if (((aVariantMask & VARIANT_URL) != 0) && |
|
6378 eCSSToken_URL == tk->mType) { |
|
6379 SetValueToURL(aValue, tk->mIdent); |
|
6380 return true; |
|
6381 } |
|
6382 if ((aVariantMask & VARIANT_GRADIENT) != 0 && |
|
6383 eCSSToken_Function == tk->mType) { |
|
6384 // a generated gradient |
|
6385 nsDependentString tmp(tk->mIdent, 0); |
|
6386 bool isLegacy = false; |
|
6387 if (StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) { |
|
6388 tmp.Rebind(tmp, 5); |
|
6389 isLegacy = true; |
|
6390 } |
|
6391 bool isRepeating = false; |
|
6392 if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) { |
|
6393 tmp.Rebind(tmp, 10); |
|
6394 isRepeating = true; |
|
6395 } |
|
6396 |
|
6397 if (tmp.LowerCaseEqualsLiteral("linear-gradient")) { |
|
6398 return ParseLinearGradient(aValue, isRepeating, isLegacy); |
|
6399 } |
|
6400 if (tmp.LowerCaseEqualsLiteral("radial-gradient")) { |
|
6401 return ParseRadialGradient(aValue, isRepeating, isLegacy); |
|
6402 } |
|
6403 } |
|
6404 if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 && |
|
6405 eCSSToken_Function == tk->mType && |
|
6406 tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) { |
|
6407 return ParseImageRect(aValue); |
|
6408 } |
|
6409 if ((aVariantMask & VARIANT_ELEMENT) != 0 && |
|
6410 eCSSToken_Function == tk->mType && |
|
6411 tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) { |
|
6412 return ParseElement(aValue); |
|
6413 } |
|
6414 if ((aVariantMask & VARIANT_COLOR) != 0) { |
|
6415 if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix |
|
6416 (eCSSToken_ID == tk->mType) || |
|
6417 (eCSSToken_Hash == tk->mType) || |
|
6418 (eCSSToken_Ident == tk->mType) || |
|
6419 ((eCSSToken_Function == tk->mType) && |
|
6420 (tk->mIdent.LowerCaseEqualsLiteral("rgb") || |
|
6421 tk->mIdent.LowerCaseEqualsLiteral("hsl") || |
|
6422 tk->mIdent.LowerCaseEqualsLiteral("rgba") || |
|
6423 tk->mIdent.LowerCaseEqualsLiteral("hsla")))) |
|
6424 { |
|
6425 // Put token back so that parse color can get it |
|
6426 UngetToken(); |
|
6427 if (ParseColor(aValue)) { |
|
6428 return true; |
|
6429 } |
|
6430 return false; |
|
6431 } |
|
6432 } |
|
6433 if (((aVariantMask & VARIANT_STRING) != 0) && |
|
6434 (eCSSToken_String == tk->mType)) { |
|
6435 nsAutoString buffer; |
|
6436 buffer.Append(tk->mIdent); |
|
6437 aValue.SetStringValue(buffer, eCSSUnit_String); |
|
6438 return true; |
|
6439 } |
|
6440 if (((aVariantMask & |
|
6441 (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) && |
|
6442 (eCSSToken_Ident == tk->mType) && |
|
6443 ((aVariantMask & VARIANT_IDENTIFIER) != 0 || |
|
6444 !(tk->mIdent.LowerCaseEqualsLiteral("inherit") || |
|
6445 tk->mIdent.LowerCaseEqualsLiteral("initial") || |
|
6446 (tk->mIdent.LowerCaseEqualsLiteral("unset") && |
|
6447 nsLayoutUtils::UnsetValueEnabled())))) { |
|
6448 aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident); |
|
6449 return true; |
|
6450 } |
|
6451 if (((aVariantMask & VARIANT_COUNTER) != 0) && |
|
6452 (eCSSToken_Function == tk->mType) && |
|
6453 (tk->mIdent.LowerCaseEqualsLiteral("counter") || |
|
6454 tk->mIdent.LowerCaseEqualsLiteral("counters"))) { |
|
6455 return ParseCounter(aValue); |
|
6456 } |
|
6457 if (((aVariantMask & VARIANT_ATTR) != 0) && |
|
6458 (eCSSToken_Function == tk->mType) && |
|
6459 tk->mIdent.LowerCaseEqualsLiteral("attr")) { |
|
6460 if (!ParseAttr(aValue)) { |
|
6461 SkipUntil(')'); |
|
6462 return false; |
|
6463 } |
|
6464 return true; |
|
6465 } |
|
6466 if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) && |
|
6467 (eCSSToken_Function == tk->mType)) { |
|
6468 if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) { |
|
6469 if (!ParseTransitionTimingFunctionValues(aValue)) { |
|
6470 SkipUntil(')'); |
|
6471 return false; |
|
6472 } |
|
6473 return true; |
|
6474 } |
|
6475 if (tk->mIdent.LowerCaseEqualsLiteral("steps")) { |
|
6476 if (!ParseTransitionStepTimingFunctionValues(aValue)) { |
|
6477 SkipUntil(')'); |
|
6478 return false; |
|
6479 } |
|
6480 return true; |
|
6481 } |
|
6482 } |
|
6483 if ((aVariantMask & VARIANT_CALC) && |
|
6484 (eCSSToken_Function == tk->mType) && |
|
6485 (tk->mIdent.LowerCaseEqualsLiteral("calc") || |
|
6486 tk->mIdent.LowerCaseEqualsLiteral("-moz-calc"))) { |
|
6487 // calc() currently allows only lengths and percents inside it. |
|
6488 return ParseCalc(aValue, aVariantMask & VARIANT_LP); |
|
6489 } |
|
6490 |
|
6491 UngetToken(); |
|
6492 return false; |
|
6493 } |
|
6494 |
|
6495 bool |
|
6496 CSSParserImpl::ParseCustomIdent(nsCSSValue& aValue, |
|
6497 const nsAutoString& aIdentValue, |
|
6498 const nsCSSKeyword aExcludedKeywords[], |
|
6499 const nsCSSProps::KTableValue aPropertyKTable[]) |
|
6500 { |
|
6501 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aIdentValue); |
|
6502 if (keyword == eCSSKeyword_UNKNOWN) { |
|
6503 // Fast path for identifiers that are not known CSS keywords: |
|
6504 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident); |
|
6505 return true; |
|
6506 } |
|
6507 if (keyword == eCSSKeyword_inherit || |
|
6508 keyword == eCSSKeyword_initial || |
|
6509 keyword == eCSSKeyword_unset || |
|
6510 keyword == eCSSKeyword_default || |
|
6511 (aPropertyKTable && |
|
6512 nsCSSProps::FindIndexOfKeyword(keyword, aPropertyKTable) >= 0)) { |
|
6513 return false; |
|
6514 } |
|
6515 if (aExcludedKeywords) { |
|
6516 for (uint32_t i = 0;; i++) { |
|
6517 nsCSSKeyword excludedKeyword = aExcludedKeywords[i]; |
|
6518 if (excludedKeyword == eCSSKeyword_UNKNOWN) { |
|
6519 break; |
|
6520 } |
|
6521 if (excludedKeyword == keyword) { |
|
6522 return false; |
|
6523 } |
|
6524 } |
|
6525 } |
|
6526 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident); |
|
6527 return true; |
|
6528 } |
|
6529 |
|
6530 bool |
|
6531 CSSParserImpl::ParseCounter(nsCSSValue& aValue) |
|
6532 { |
|
6533 nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ? |
|
6534 eCSSUnit_Counter : eCSSUnit_Counters); |
|
6535 |
|
6536 // A non-iterative for loop to break out when an error occurs. |
|
6537 for (;;) { |
|
6538 if (!GetToken(true)) { |
|
6539 break; |
|
6540 } |
|
6541 if (eCSSToken_Ident != mToken.mType) { |
|
6542 UngetToken(); |
|
6543 break; |
|
6544 } |
|
6545 |
|
6546 nsRefPtr<nsCSSValue::Array> val = |
|
6547 nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3); |
|
6548 |
|
6549 val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident); |
|
6550 |
|
6551 if (eCSSUnit_Counters == unit) { |
|
6552 // must have a comma and then a separator string |
|
6553 if (!ExpectSymbol(',', true) || !GetToken(true)) { |
|
6554 break; |
|
6555 } |
|
6556 if (eCSSToken_String != mToken.mType) { |
|
6557 UngetToken(); |
|
6558 break; |
|
6559 } |
|
6560 val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String); |
|
6561 } |
|
6562 |
|
6563 // get optional type |
|
6564 int32_t type = NS_STYLE_LIST_STYLE_DECIMAL; |
|
6565 if (ExpectSymbol(',', true)) { |
|
6566 if (!GetToken(true)) { |
|
6567 break; |
|
6568 } |
|
6569 nsCSSKeyword keyword; |
|
6570 if (eCSSToken_Ident != mToken.mType || |
|
6571 (keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) == |
|
6572 eCSSKeyword_UNKNOWN || |
|
6573 !nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable, |
|
6574 type)) { |
|
6575 UngetToken(); |
|
6576 break; |
|
6577 } |
|
6578 } |
|
6579 |
|
6580 int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1; |
|
6581 val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated); |
|
6582 |
|
6583 if (!ExpectSymbol(')', true)) { |
|
6584 break; |
|
6585 } |
|
6586 |
|
6587 aValue.SetArrayValue(val, unit); |
|
6588 return true; |
|
6589 } |
|
6590 |
|
6591 SkipUntil(')'); |
|
6592 return false; |
|
6593 } |
|
6594 |
|
6595 bool |
|
6596 CSSParserImpl::ParseAttr(nsCSSValue& aValue) |
|
6597 { |
|
6598 if (!GetToken(true)) { |
|
6599 return false; |
|
6600 } |
|
6601 |
|
6602 nsAutoString attr; |
|
6603 if (eCSSToken_Ident == mToken.mType) { // attr name or namespace |
|
6604 nsAutoString holdIdent(mToken.mIdent); |
|
6605 if (ExpectSymbol('|', false)) { // namespace |
|
6606 int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent); |
|
6607 if (nameSpaceID == kNameSpaceID_Unknown) { |
|
6608 return false; |
|
6609 } |
|
6610 attr.AppendInt(nameSpaceID, 10); |
|
6611 attr.Append(char16_t('|')); |
|
6612 if (! GetToken(false)) { |
|
6613 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); |
|
6614 return false; |
|
6615 } |
|
6616 if (eCSSToken_Ident == mToken.mType) { |
|
6617 attr.Append(mToken.mIdent); |
|
6618 } |
|
6619 else { |
|
6620 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); |
|
6621 UngetToken(); |
|
6622 return false; |
|
6623 } |
|
6624 } |
|
6625 else { // no namespace |
|
6626 attr = holdIdent; |
|
6627 } |
|
6628 } |
|
6629 else if (mToken.IsSymbol('*')) { // namespace wildcard |
|
6630 // Wildcard namespace makes no sense here and is not allowed |
|
6631 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); |
|
6632 UngetToken(); |
|
6633 return false; |
|
6634 } |
|
6635 else if (mToken.IsSymbol('|')) { // explicit NO namespace |
|
6636 if (! GetToken(false)) { |
|
6637 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); |
|
6638 return false; |
|
6639 } |
|
6640 if (eCSSToken_Ident == mToken.mType) { |
|
6641 attr.Append(mToken.mIdent); |
|
6642 } |
|
6643 else { |
|
6644 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); |
|
6645 UngetToken(); |
|
6646 return false; |
|
6647 } |
|
6648 } |
|
6649 else { |
|
6650 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected); |
|
6651 UngetToken(); |
|
6652 return false; |
|
6653 } |
|
6654 if (!ExpectSymbol(')', true)) { |
|
6655 return false; |
|
6656 } |
|
6657 aValue.SetStringValue(attr, eCSSUnit_Attr); |
|
6658 return true; |
|
6659 } |
|
6660 |
|
6661 bool |
|
6662 CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL) |
|
6663 { |
|
6664 if (!mSheetPrincipal) { |
|
6665 NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an " |
|
6666 "origin principal"); |
|
6667 return false; |
|
6668 } |
|
6669 |
|
6670 nsRefPtr<nsStringBuffer> buffer(nsCSSValue::BufferFromString(aURL)); |
|
6671 |
|
6672 // Note: urlVal retains its own reference to |buffer|. |
|
6673 mozilla::css::URLValue *urlVal = |
|
6674 new mozilla::css::URLValue(buffer, mBaseURI, mSheetURI, mSheetPrincipal); |
|
6675 aValue.SetURLValue(urlVal); |
|
6676 return true; |
|
6677 } |
|
6678 |
|
6679 /** |
|
6680 * Parse the image-orientation property, which has the grammar: |
|
6681 * <angle> flip? | flip | from-image |
|
6682 */ |
|
6683 bool |
|
6684 CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue) |
|
6685 { |
|
6686 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) { |
|
6687 // 'inherit', 'initial' and 'unset' must be alone |
|
6688 return true; |
|
6689 } |
|
6690 |
|
6691 // Check for an angle with optional 'flip'. |
|
6692 nsCSSValue angle; |
|
6693 if (ParseVariant(angle, VARIANT_ANGLE, nullptr)) { |
|
6694 nsCSSValue flip; |
|
6695 |
|
6696 if (ParseVariant(flip, VARIANT_KEYWORD, nsCSSProps::kImageOrientationFlipKTable)) { |
|
6697 nsRefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(2); |
|
6698 array->Item(0) = angle; |
|
6699 array->Item(1) = flip; |
|
6700 aValue.SetArrayValue(array, eCSSUnit_Array); |
|
6701 } else { |
|
6702 aValue = angle; |
|
6703 } |
|
6704 |
|
6705 return true; |
|
6706 } |
|
6707 |
|
6708 // The remaining possibilities (bare 'flip' and 'from-image') are both |
|
6709 // keywords, so we can handle them at the same time. |
|
6710 nsCSSValue keyword; |
|
6711 if (ParseVariant(keyword, VARIANT_KEYWORD, nsCSSProps::kImageOrientationKTable)) { |
|
6712 aValue = keyword; |
|
6713 return true; |
|
6714 } |
|
6715 |
|
6716 // All possibilities failed. |
|
6717 return false; |
|
6718 } |
|
6719 |
|
6720 /** |
|
6721 * Parse the arguments of -moz-image-rect() function. |
|
6722 * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>) |
|
6723 */ |
|
6724 bool |
|
6725 CSSParserImpl::ParseImageRect(nsCSSValue& aImage) |
|
6726 { |
|
6727 // A non-iterative for loop to break out when an error occurs. |
|
6728 for (;;) { |
|
6729 nsCSSValue newFunction; |
|
6730 static const uint32_t kNumArgs = 5; |
|
6731 nsCSSValue::Array* func = |
|
6732 newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs); |
|
6733 |
|
6734 // func->Item(0) is reserved for the function name. |
|
6735 nsCSSValue& url = func->Item(1); |
|
6736 nsCSSValue& top = func->Item(2); |
|
6737 nsCSSValue& right = func->Item(3); |
|
6738 nsCSSValue& bottom = func->Item(4); |
|
6739 nsCSSValue& left = func->Item(5); |
|
6740 |
|
6741 nsAutoString urlString; |
|
6742 if (!ParseURLOrString(urlString) || |
|
6743 !SetValueToURL(url, urlString) || |
|
6744 !ExpectSymbol(',', true)) { |
|
6745 break; |
|
6746 } |
|
6747 |
|
6748 static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT; |
|
6749 if (!ParseNonNegativeVariant(top, VARIANT_SIDE, nullptr) || |
|
6750 !ExpectSymbol(',', true) || |
|
6751 !ParseNonNegativeVariant(right, VARIANT_SIDE, nullptr) || |
|
6752 !ExpectSymbol(',', true) || |
|
6753 !ParseNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) || |
|
6754 !ExpectSymbol(',', true) || |
|
6755 !ParseNonNegativeVariant(left, VARIANT_SIDE, nullptr) || |
|
6756 !ExpectSymbol(')', true)) |
|
6757 break; |
|
6758 |
|
6759 aImage = newFunction; |
|
6760 return true; |
|
6761 } |
|
6762 |
|
6763 SkipUntil(')'); |
|
6764 return false; |
|
6765 } |
|
6766 |
|
6767 // <element>: -moz-element(# <element_id> ) |
|
6768 bool |
|
6769 CSSParserImpl::ParseElement(nsCSSValue& aValue) |
|
6770 { |
|
6771 // A non-iterative for loop to break out when an error occurs. |
|
6772 for (;;) { |
|
6773 if (!GetToken(true)) |
|
6774 break; |
|
6775 |
|
6776 if (mToken.mType == eCSSToken_ID) { |
|
6777 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element); |
|
6778 } else { |
|
6779 UngetToken(); |
|
6780 break; |
|
6781 } |
|
6782 |
|
6783 if (!ExpectSymbol(')', true)) |
|
6784 break; |
|
6785 |
|
6786 return true; |
|
6787 } |
|
6788 |
|
6789 // If we detect a syntax error, we must match the opening parenthesis of the |
|
6790 // function with the closing parenthesis and skip all the tokens in between. |
|
6791 SkipUntil(')'); |
|
6792 return false; |
|
6793 } |
|
6794 |
|
6795 // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] |
|
6796 bool |
|
6797 CSSParserImpl::ParseFlex() |
|
6798 { |
|
6799 // First check for inherit / initial / unset |
|
6800 nsCSSValue tmpVal; |
|
6801 if (ParseVariant(tmpVal, VARIANT_INHERIT, nullptr)) { |
|
6802 AppendValue(eCSSProperty_flex_grow, tmpVal); |
|
6803 AppendValue(eCSSProperty_flex_shrink, tmpVal); |
|
6804 AppendValue(eCSSProperty_flex_basis, tmpVal); |
|
6805 return true; |
|
6806 } |
|
6807 |
|
6808 // Next, check for 'none' == '0 0 auto' |
|
6809 if (ParseVariant(tmpVal, VARIANT_NONE, nullptr)) { |
|
6810 AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number)); |
|
6811 AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number)); |
|
6812 AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto)); |
|
6813 return true; |
|
6814 } |
|
6815 |
|
6816 // OK, try parsing our value as individual per-subproperty components: |
|
6817 // [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] |
|
6818 |
|
6819 // Each subproperty has a default value that it takes when it's omitted in a |
|
6820 // "flex" shorthand value. These default values are *only* for the shorthand |
|
6821 // syntax -- they're distinct from the subproperties' own initial values. We |
|
6822 // start with each subproperty at its default, as if we had "flex: 1 1 0%". |
|
6823 nsCSSValue flexGrow(1.0f, eCSSUnit_Number); |
|
6824 nsCSSValue flexShrink(1.0f, eCSSUnit_Number); |
|
6825 nsCSSValue flexBasis(0.0f, eCSSUnit_Percent); |
|
6826 |
|
6827 // OVERVIEW OF PARSING STRATEGY: |
|
6828 // ============================= |
|
6829 // a) Parse the first component as either flex-basis or flex-grow. |
|
6830 // b) If it wasn't flex-grow, parse the _next_ component as flex-grow. |
|
6831 // c) Now we've just parsed flex-grow -- so try parsing the next thing as |
|
6832 // flex-shrink. |
|
6833 // d) Finally: If we didn't get flex-basis at the beginning, try to parse |
|
6834 // it now, at the end. |
|
6835 // |
|
6836 // More details in each section below. |
|
6837 |
|
6838 uint32_t flexBasisVariantMask = |
|
6839 (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT)); |
|
6840 |
|
6841 // (a) Parse first component. It can be either be a 'flex-basis' value or a |
|
6842 // 'flex-grow' value, so we use the flex-basis-specific variant mask, along |
|
6843 // with VARIANT_NUMBER to accept 'flex-grow' values. |
|
6844 // |
|
6845 // NOTE: if we encounter unitless 0 here, we *must* interpret it as a |
|
6846 // 'flex-grow' value (a number), *not* as a 'flex-basis' value (a length). |
|
6847 // Conveniently, that's the behavior this combined variant-mask gives us -- |
|
6848 // it'll treat unitless 0 as a number. The flexbox spec requires this: |
|
6849 // "a unitless zero that is not already preceded by two flex factors must be |
|
6850 // interpreted as a flex factor. |
|
6851 if (!ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER, |
|
6852 nsCSSProps::kWidthKTable)) { |
|
6853 // First component was not a valid flex-basis or flex-grow value. Fail. |
|
6854 return false; |
|
6855 } |
|
6856 |
|
6857 // Record what we just parsed as either flex-basis or flex-grow: |
|
6858 bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number); |
|
6859 (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal; |
|
6860 |
|
6861 // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow. |
|
6862 bool doneParsing = false; |
|
6863 if (wasFirstComponentFlexBasis) { |
|
6864 if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) { |
|
6865 flexGrow = tmpVal; |
|
6866 } else { |
|
6867 // Failed to parse anything after our flex-basis -- that's fine. We can |
|
6868 // skip the remaining parsing. |
|
6869 doneParsing = true; |
|
6870 } |
|
6871 } |
|
6872 |
|
6873 if (!doneParsing) { |
|
6874 // (c) OK -- the last thing we parsed was flex-grow, so look for a |
|
6875 // flex-shrink in the next position. |
|
6876 if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) { |
|
6877 flexShrink = tmpVal; |
|
6878 } |
|
6879 |
|
6880 // d) Finally: If we didn't get flex-basis at the beginning, try to parse |
|
6881 // it now, at the end. |
|
6882 // |
|
6883 // NOTE: If we encounter unitless 0 in this final position, we'll parse it |
|
6884 // as a 'flex-basis' value. That's OK, because we know it must have |
|
6885 // been "preceded by 2 flex factors" (justification below), which gets us |
|
6886 // out of the spec's requirement of otherwise having to treat unitless 0 |
|
6887 // as a flex factor. |
|
6888 // |
|
6889 // JUSTIFICATION: How do we know that a unitless 0 here must have been |
|
6890 // preceded by 2 flex factors? Well, suppose we had a unitless 0 that |
|
6891 // was preceded by only 1 flex factor. Then, we would have already |
|
6892 // accepted this unitless 0 as the 'flex-shrink' value, up above (since |
|
6893 // it's a valid flex-shrink value), and we'd have moved on to the next |
|
6894 // token (if any). And of course, if we instead had a unitless 0 preceded |
|
6895 // by *no* flex factors (if it were the first token), we would've already |
|
6896 // parsed it in our very first call to ParseNonNegativeVariant(). So, any |
|
6897 // unitless 0 encountered here *must* have been preceded by 2 flex factors. |
|
6898 if (!wasFirstComponentFlexBasis && |
|
6899 ParseNonNegativeVariant(tmpVal, flexBasisVariantMask, |
|
6900 nsCSSProps::kWidthKTable)) { |
|
6901 flexBasis = tmpVal; |
|
6902 } |
|
6903 } |
|
6904 |
|
6905 AppendValue(eCSSProperty_flex_grow, flexGrow); |
|
6906 AppendValue(eCSSProperty_flex_shrink, flexShrink); |
|
6907 AppendValue(eCSSProperty_flex_basis, flexBasis); |
|
6908 |
|
6909 return true; |
|
6910 } |
|
6911 |
|
6912 // flex-flow: <flex-direction> || <flex-wrap> |
|
6913 bool |
|
6914 CSSParserImpl::ParseFlexFlow() |
|
6915 { |
|
6916 static const nsCSSProperty kFlexFlowSubprops[] = { |
|
6917 eCSSProperty_flex_direction, |
|
6918 eCSSProperty_flex_wrap |
|
6919 }; |
|
6920 const size_t numProps = MOZ_ARRAY_LENGTH(kFlexFlowSubprops); |
|
6921 nsCSSValue values[numProps]; |
|
6922 |
|
6923 int32_t found = ParseChoice(values, kFlexFlowSubprops, numProps); |
|
6924 |
|
6925 // Bail if we didn't successfully parse anything |
|
6926 if (found < 1) { |
|
6927 return false; |
|
6928 } |
|
6929 |
|
6930 // If either property didn't get an explicit value, use its initial value. |
|
6931 if ((found & 1) == 0) { |
|
6932 values[0].SetIntValue(NS_STYLE_FLEX_DIRECTION_ROW, eCSSUnit_Enumerated); |
|
6933 } |
|
6934 if ((found & 2) == 0) { |
|
6935 values[1].SetIntValue(NS_STYLE_FLEX_WRAP_NOWRAP, eCSSUnit_Enumerated); |
|
6936 } |
|
6937 |
|
6938 // Store these values and declare success! |
|
6939 for (size_t i = 0; i < numProps; i++) { |
|
6940 AppendValue(kFlexFlowSubprops[i], values[i]); |
|
6941 } |
|
6942 return true; |
|
6943 } |
|
6944 |
|
6945 bool |
|
6946 CSSParserImpl::ParseGridAutoFlow() |
|
6947 { |
|
6948 nsCSSValue value; |
|
6949 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
6950 AppendValue(eCSSProperty_grid_auto_flow, value); |
|
6951 return true; |
|
6952 } |
|
6953 |
|
6954 static const int32_t mask[] = { |
|
6955 NS_STYLE_GRID_AUTO_FLOW_COLUMN | NS_STYLE_GRID_AUTO_FLOW_ROW, |
|
6956 MASK_END_VALUE |
|
6957 }; |
|
6958 if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) { |
|
6959 return false; |
|
6960 } |
|
6961 int32_t bitField = value.GetIntValue(); |
|
6962 |
|
6963 // Requires one of these |
|
6964 if (!(bitField & NS_STYLE_GRID_AUTO_FLOW_NONE || |
|
6965 bitField & NS_STYLE_GRID_AUTO_FLOW_COLUMN || |
|
6966 bitField & NS_STYLE_GRID_AUTO_FLOW_ROW)) { |
|
6967 return false; |
|
6968 } |
|
6969 |
|
6970 // 'none' is only valid if it occurs alone: |
|
6971 if (bitField & NS_STYLE_GRID_AUTO_FLOW_NONE && |
|
6972 bitField != NS_STYLE_GRID_AUTO_FLOW_NONE) { |
|
6973 return false; |
|
6974 } |
|
6975 |
|
6976 AppendValue(eCSSProperty_grid_auto_flow, value); |
|
6977 return true; |
|
6978 } |
|
6979 |
|
6980 CSSParseResult |
|
6981 CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue) |
|
6982 { |
|
6983 if (!ExpectSymbol('(', true)) { |
|
6984 return CSSParseResult::NotFound; |
|
6985 } |
|
6986 if (!GetToken(true) || mToken.IsSymbol(')')) { |
|
6987 return CSSParseResult::Ok; |
|
6988 } |
|
6989 // 'return' so far leaves aValue untouched, to represent an empty list. |
|
6990 |
|
6991 nsCSSValueList* item; |
|
6992 if (aValue.GetUnit() == eCSSUnit_List) { |
|
6993 // Find the end of an existing list. |
|
6994 // The grid-template shorthand uses this, at most once for a given list. |
|
6995 |
|
6996 // NOTE: we could avoid this traversal by somehow keeping around |
|
6997 // a pointer to the last item from the previous call. |
|
6998 // It's not yet clear if this is worth the additional code complexity. |
|
6999 item = aValue.GetListValue(); |
|
7000 while (item->mNext) { |
|
7001 item = item->mNext; |
|
7002 } |
|
7003 item->mNext = new nsCSSValueList; |
|
7004 item = item->mNext; |
|
7005 } else { |
|
7006 MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "Unexpected unit"); |
|
7007 item = aValue.SetListValue(); |
|
7008 } |
|
7009 for (;;) { |
|
7010 if (!(eCSSToken_Ident == mToken.mType && |
|
7011 ParseCustomIdent(item->mValue, mToken.mIdent))) { |
|
7012 UngetToken(); |
|
7013 SkipUntil(')'); |
|
7014 return CSSParseResult::Error; |
|
7015 } |
|
7016 if (!GetToken(true) || mToken.IsSymbol(')')) { |
|
7017 return CSSParseResult::Ok; |
|
7018 } |
|
7019 item->mNext = new nsCSSValueList; |
|
7020 item = item->mNext; |
|
7021 } |
|
7022 } |
|
7023 |
|
7024 // Assuming the 'repeat(' function token has already been consumed, |
|
7025 // parse the rest of repeat(<positive-integer>, <line-names>+) |
|
7026 // Append to the linked list whose end is given by |aTailPtr|, |
|
7027 // and updated |aTailPtr| to point to the new end of the list. |
|
7028 bool |
|
7029 CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr) |
|
7030 { |
|
7031 if (!(GetToken(true) && |
|
7032 mToken.mType == eCSSToken_Number && |
|
7033 mToken.mIntegerValid && |
|
7034 mToken.mInteger > 0)) { |
|
7035 SkipUntil(')'); |
|
7036 return false; |
|
7037 } |
|
7038 int32_t repetitions = std::min(mToken.mInteger, |
|
7039 GRID_TEMPLATE_MAX_REPETITIONS); |
|
7040 if (!ExpectSymbol(',', true)) { |
|
7041 SkipUntil(')'); |
|
7042 return false; |
|
7043 } |
|
7044 |
|
7045 // Parse at least one <line-names> |
|
7046 nsCSSValueList* tail = *aTailPtr; |
|
7047 do { |
|
7048 tail->mNext = new nsCSSValueList; |
|
7049 tail = tail->mNext; |
|
7050 if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) { |
|
7051 SkipUntil(')'); |
|
7052 return false; |
|
7053 } |
|
7054 } while (!ExpectSymbol(')', true)); |
|
7055 nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext; |
|
7056 nsCSSValueList* lastRepeatedItem = tail; |
|
7057 |
|
7058 // Our repeated items are already in the target list once, |
|
7059 // so they need to be repeated |repetitions - 1| more times. |
|
7060 MOZ_ASSERT(repetitions > 0, "Expected positive repetitions"); |
|
7061 while (--repetitions) { |
|
7062 nsCSSValueList* repeatedItem = firstRepeatedItem; |
|
7063 for (;;) { |
|
7064 tail->mNext = new nsCSSValueList; |
|
7065 tail = tail->mNext; |
|
7066 tail->mValue = repeatedItem->mValue; |
|
7067 if (repeatedItem == lastRepeatedItem) { |
|
7068 break; |
|
7069 } |
|
7070 repeatedItem = repeatedItem->mNext; |
|
7071 } |
|
7072 } |
|
7073 *aTailPtr = tail; |
|
7074 return true; |
|
7075 } |
|
7076 |
|
7077 // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>? |
|
7078 bool |
|
7079 CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue) |
|
7080 { |
|
7081 nsCSSValueList* item = aValue.SetListValue(); |
|
7082 // This marker distinguishes the value from a <track-list>. |
|
7083 item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID, |
|
7084 eCSSUnit_Enumerated); |
|
7085 for (;;) { |
|
7086 // First try to parse repeat(<positive-integer>, <line-names>+) |
|
7087 if (!GetToken(true)) { |
|
7088 return true; |
|
7089 } |
|
7090 if (mToken.mType == eCSSToken_Function && |
|
7091 mToken.mIdent.LowerCaseEqualsLiteral("repeat")) { |
|
7092 if (!ParseGridLineNameListRepeat(&item)) { |
|
7093 return false; |
|
7094 } |
|
7095 } else { |
|
7096 UngetToken(); |
|
7097 |
|
7098 // This was not a repeat() function. Try to parse <line-names>. |
|
7099 nsCSSValue lineNames; |
|
7100 CSSParseResult result = ParseGridLineNames(lineNames); |
|
7101 if (result == CSSParseResult::NotFound) { |
|
7102 return true; |
|
7103 } |
|
7104 if (result == CSSParseResult::Error) { |
|
7105 return false; |
|
7106 } |
|
7107 item->mNext = new nsCSSValueList; |
|
7108 item = item->mNext; |
|
7109 item->mValue = lineNames; |
|
7110 } |
|
7111 } |
|
7112 } |
|
7113 |
|
7114 // Parse a <track-breadth> |
|
7115 bool |
|
7116 CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue) |
|
7117 { |
|
7118 if (ParseNonNegativeVariant(aValue, |
|
7119 VARIANT_LPCALC | VARIANT_KEYWORD, |
|
7120 nsCSSProps::kGridTrackBreadthKTable)) { |
|
7121 return true; |
|
7122 } |
|
7123 |
|
7124 // Attempt to parse <flex> (a dimension with the "fr" unit) |
|
7125 if (!GetToken(true)) { |
|
7126 return false; |
|
7127 } |
|
7128 if (!(eCSSToken_Dimension == mToken.mType && |
|
7129 mToken.mIdent.LowerCaseEqualsLiteral("fr") && |
|
7130 mToken.mNumber >= 0)) { |
|
7131 UngetToken(); |
|
7132 return false; |
|
7133 } |
|
7134 aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction); |
|
7135 return true; |
|
7136 } |
|
7137 |
|
7138 // Parse a <track-size> |
|
7139 CSSParseResult |
|
7140 CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue) |
|
7141 { |
|
7142 // Attempt to parse 'auto' or a single <track-breadth> |
|
7143 if (ParseGridTrackBreadth(aValue) || |
|
7144 ParseVariant(aValue, VARIANT_AUTO, nullptr)) { |
|
7145 return CSSParseResult::Ok; |
|
7146 } |
|
7147 |
|
7148 // Attempt to parse a minmax() function |
|
7149 if (!GetToken(true)) { |
|
7150 return CSSParseResult::NotFound; |
|
7151 } |
|
7152 if (!(eCSSToken_Function == mToken.mType && |
|
7153 mToken.mIdent.LowerCaseEqualsLiteral("minmax"))) { |
|
7154 UngetToken(); |
|
7155 return CSSParseResult::NotFound; |
|
7156 } |
|
7157 nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2); |
|
7158 if (ParseGridTrackBreadth(func->Item(1)) && |
|
7159 ExpectSymbol(',', true) && |
|
7160 ParseGridTrackBreadth(func->Item(2)) && |
|
7161 ExpectSymbol(')', true)) { |
|
7162 return CSSParseResult::Ok; |
|
7163 } |
|
7164 SkipUntil(')'); |
|
7165 return CSSParseResult::Error; |
|
7166 } |
|
7167 |
|
7168 bool |
|
7169 CSSParserImpl::ParseGridAutoColumnsRows(nsCSSProperty aPropID) |
|
7170 { |
|
7171 nsCSSValue value; |
|
7172 if (ParseVariant(value, VARIANT_INHERIT, nullptr) || |
|
7173 ParseGridTrackSize(value) == CSSParseResult::Ok) { |
|
7174 AppendValue(aPropID, value); |
|
7175 return true; |
|
7176 } |
|
7177 return false; |
|
7178 } |
|
7179 |
|
7180 bool |
|
7181 CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue, |
|
7182 const nsCSSValue& aFirstLineNames) |
|
7183 { |
|
7184 nsCSSValueList* firstLineNamesItem = aValue.SetListValue(); |
|
7185 firstLineNamesItem->mValue = aFirstLineNames; |
|
7186 |
|
7187 // This function is trying to parse <track-list>, which is |
|
7188 // [ <line-names>? [ <track-size> | <repeat()> ] ]+ <line-names>? |
|
7189 // and we're already past the first "<line-names>?". |
|
7190 // |
|
7191 // Each iteration of the following loop attempts to parse either a |
|
7192 // repeat() or a <track-size> expression, and then an (optional) |
|
7193 // <line-names> expression. |
|
7194 // |
|
7195 // The only successful exit point from this loop is the ::NotFound |
|
7196 // case after ParseGridTrackSize(); i.e. we'll greedily parse |
|
7197 // repeat()/<track-size> until we can't find one. |
|
7198 nsCSSValueList* item = firstLineNamesItem; |
|
7199 for (;;) { |
|
7200 // First try to parse repeat() |
|
7201 if (!GetToken(true)) { |
|
7202 break; |
|
7203 } |
|
7204 if (mToken.mType == eCSSToken_Function && |
|
7205 mToken.mIdent.LowerCaseEqualsLiteral("repeat")) { |
|
7206 if (!ParseGridTrackListRepeat(&item)) { |
|
7207 return false; |
|
7208 } |
|
7209 } else { |
|
7210 UngetToken(); |
|
7211 |
|
7212 // This was not a repeat() function. Try to parse <track-size>. |
|
7213 nsCSSValue trackSize; |
|
7214 CSSParseResult result = ParseGridTrackSize(trackSize); |
|
7215 if (result == CSSParseResult::Error) { |
|
7216 return false; |
|
7217 } |
|
7218 if (result == CSSParseResult::NotFound) { |
|
7219 // What we've parsed so far is a valid <track-list> |
|
7220 // (modulo the "at least one <track-size>" check below.) |
|
7221 // Stop here. |
|
7222 break; |
|
7223 } |
|
7224 item->mNext = new nsCSSValueList; |
|
7225 item = item->mNext; |
|
7226 item->mValue = trackSize; |
|
7227 |
|
7228 item->mNext = new nsCSSValueList; |
|
7229 item = item->mNext; |
|
7230 } |
|
7231 if (ParseGridLineNames(item->mValue) == CSSParseResult::Error) { |
|
7232 return false; |
|
7233 } |
|
7234 } |
|
7235 |
|
7236 // Require at least one <track-size>. |
|
7237 if (item == firstLineNamesItem) { |
|
7238 return false; |
|
7239 } |
|
7240 |
|
7241 MOZ_ASSERT(aValue.GetListValue() && |
|
7242 aValue.GetListValue()->mNext && |
|
7243 aValue.GetListValue()->mNext->mNext, |
|
7244 "<track-list> should have a minimum length of 3"); |
|
7245 return true; |
|
7246 } |
|
7247 |
|
7248 // Takes ownership of |aSecond| |
|
7249 static void |
|
7250 ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond) |
|
7251 { |
|
7252 if (aSecond.GetUnit() == eCSSUnit_Null) { |
|
7253 // Nothing to do. |
|
7254 return; |
|
7255 } |
|
7256 if (aFirst.GetUnit() == eCSSUnit_Null) { |
|
7257 // Empty or omitted <line-names>. Replace it. |
|
7258 aFirst = aSecond; |
|
7259 return; |
|
7260 } |
|
7261 |
|
7262 // Join the two <line-names> lists. |
|
7263 nsCSSValueList* source = aSecond.GetListValue(); |
|
7264 nsCSSValueList* target = aFirst.GetListValue(); |
|
7265 // Find the end: |
|
7266 while (target->mNext) { |
|
7267 target = target->mNext; |
|
7268 } |
|
7269 // Copy the first name. We can't take ownership of it |
|
7270 // as it'll be destroyed when |aSecond| goes out of scope. |
|
7271 target->mNext = new nsCSSValueList; |
|
7272 target = target->mNext; |
|
7273 target->mValue = source->mValue; |
|
7274 // Move the rest of the linked list. |
|
7275 target->mNext = source->mNext; |
|
7276 source->mNext = nullptr; |
|
7277 } |
|
7278 |
|
7279 // Assuming the 'repeat(' function token has already been consumed, |
|
7280 // parse the rest of |
|
7281 // repeat( <positive-integer> , |
|
7282 // [ <line-names>? <track-size> ]+ <line-names>? ) |
|
7283 // Append to the linked list whose end is given by |aTailPtr|, |
|
7284 // and updated |aTailPtr| to point to the new end of the list. |
|
7285 bool |
|
7286 CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr) |
|
7287 { |
|
7288 if (!(GetToken(true) && |
|
7289 mToken.mType == eCSSToken_Number && |
|
7290 mToken.mIntegerValid && |
|
7291 mToken.mInteger > 0)) { |
|
7292 SkipUntil(')'); |
|
7293 return false; |
|
7294 } |
|
7295 int32_t repetitions = std::min(mToken.mInteger, |
|
7296 GRID_TEMPLATE_MAX_REPETITIONS); |
|
7297 if (!ExpectSymbol(',', true)) { |
|
7298 SkipUntil(')'); |
|
7299 return false; |
|
7300 } |
|
7301 |
|
7302 // Parse [ <line-names>? <track-size> ]+ <line-names>? |
|
7303 // but keep the first and last <line-names> separate |
|
7304 // because they'll need to be joined. |
|
7305 // http://dev.w3.org/csswg/css-grid/#repeat-notation |
|
7306 nsCSSValue firstLineNames; |
|
7307 nsCSSValue trackSize; |
|
7308 nsCSSValue lastLineNames; |
|
7309 // Optional |
|
7310 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) { |
|
7311 SkipUntil(')'); |
|
7312 return false; |
|
7313 } |
|
7314 // Required |
|
7315 if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) { |
|
7316 SkipUntil(')'); |
|
7317 return false; |
|
7318 } |
|
7319 // Use nsAutoPtr to free the list in case of early return. |
|
7320 nsAutoPtr<nsCSSValueList> firstTrackSizeItemAuto(new nsCSSValueList); |
|
7321 firstTrackSizeItemAuto->mValue = trackSize; |
|
7322 |
|
7323 nsCSSValueList* item = firstTrackSizeItemAuto; |
|
7324 for (;;) { |
|
7325 // Optional |
|
7326 if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) { |
|
7327 SkipUntil(')'); |
|
7328 return false; |
|
7329 } |
|
7330 |
|
7331 if (ExpectSymbol(')', true)) { |
|
7332 break; |
|
7333 } |
|
7334 |
|
7335 // Required |
|
7336 if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) { |
|
7337 SkipUntil(')'); |
|
7338 return false; |
|
7339 } |
|
7340 |
|
7341 item->mNext = new nsCSSValueList; |
|
7342 item = item->mNext; |
|
7343 item->mValue = lastLineNames; |
|
7344 // Do not append to this list at the next iteration. |
|
7345 lastLineNames.Reset(); |
|
7346 |
|
7347 item->mNext = new nsCSSValueList; |
|
7348 item = item->mNext; |
|
7349 item->mValue = trackSize; |
|
7350 } |
|
7351 nsCSSValueList* lastTrackSizeItem = item; |
|
7352 |
|
7353 // [ <line-names>? <track-size> ]+ <line-names>? is now parsed into: |
|
7354 // * firstLineNames: the first <line-names> |
|
7355 // * a linked list of odd length >= 1, from firstTrackSizeItem |
|
7356 // (the first <track-size>) to lastTrackSizeItem (the last), |
|
7357 // with the <line-names> sublists in between |
|
7358 // * lastLineNames: the last <line-names> |
|
7359 |
|
7360 |
|
7361 // Join the last and first <line-names> (in that order.) |
|
7362 // For example, repeat(3, (a) 100px (b) 200px (c)) results in |
|
7363 // (a) 100px (b) 200px (c a) 100px (b) 200px (c a) 100px (b) 200px (c) |
|
7364 // This is (c a). |
|
7365 // Make deep copies: the originals will be moved. |
|
7366 nsCSSValue joinerLineNames; |
|
7367 { |
|
7368 nsCSSValueList* target = nullptr; |
|
7369 if (lastLineNames.GetUnit() != eCSSUnit_Null) { |
|
7370 target = joinerLineNames.SetListValue(); |
|
7371 nsCSSValueList* source = lastLineNames.GetListValue(); |
|
7372 for (;;) { |
|
7373 target->mValue = source->mValue; |
|
7374 source = source->mNext; |
|
7375 if (!source) { |
|
7376 break; |
|
7377 } |
|
7378 target->mNext = new nsCSSValueList; |
|
7379 target = target->mNext; |
|
7380 } |
|
7381 } |
|
7382 |
|
7383 if (firstLineNames.GetUnit() != eCSSUnit_Null) { |
|
7384 if (target) { |
|
7385 target->mNext = new nsCSSValueList; |
|
7386 target = target->mNext; |
|
7387 } else { |
|
7388 target = joinerLineNames.SetListValue(); |
|
7389 } |
|
7390 nsCSSValueList* source = firstLineNames.GetListValue(); |
|
7391 for (;;) { |
|
7392 target->mValue = source->mValue; |
|
7393 source = source->mNext; |
|
7394 if (!source) { |
|
7395 break; |
|
7396 } |
|
7397 target->mNext = new nsCSSValueList; |
|
7398 target = target->mNext; |
|
7399 } |
|
7400 } |
|
7401 } |
|
7402 |
|
7403 // Join our first <line-names> with the one before repeat(). |
|
7404 // (a) repeat(1, (b) 20px) expands to (a b) 20px |
|
7405 nsCSSValueList* previousItemBeforeRepeat = *aTailPtr; |
|
7406 ConcatLineNames(previousItemBeforeRepeat->mValue, firstLineNames); |
|
7407 |
|
7408 // Move our linked list |
|
7409 // (first to last <track-size>, with the <line-names> sublists in between). |
|
7410 // This is the first repetition. |
|
7411 NS_ASSERTION(previousItemBeforeRepeat->mNext == nullptr, |
|
7412 "Expected the end of a linked list"); |
|
7413 previousItemBeforeRepeat->mNext = firstTrackSizeItemAuto.forget(); |
|
7414 nsCSSValueList* firstTrackSizeItem = previousItemBeforeRepeat->mNext; |
|
7415 nsCSSValueList* tail = lastTrackSizeItem; |
|
7416 |
|
7417 // Repeat |repetitions - 1| more times: |
|
7418 // * the joiner <line-names> |
|
7419 // * the linked list |
|
7420 // (first to last <track-size>, with the <line-names> sublists in between) |
|
7421 MOZ_ASSERT(repetitions > 0, "Expected positive repetitions"); |
|
7422 while (--repetitions) { |
|
7423 tail->mNext = new nsCSSValueList; |
|
7424 tail = tail->mNext; |
|
7425 tail->mValue = joinerLineNames; |
|
7426 |
|
7427 nsCSSValueList* repeatedItem = firstTrackSizeItem; |
|
7428 for (;;) { |
|
7429 tail->mNext = new nsCSSValueList; |
|
7430 tail = tail->mNext; |
|
7431 tail->mValue = repeatedItem->mValue; |
|
7432 if (repeatedItem == lastTrackSizeItem) { |
|
7433 break; |
|
7434 } |
|
7435 repeatedItem = repeatedItem->mNext; |
|
7436 } |
|
7437 } |
|
7438 |
|
7439 // Finally, move our last <line-names>. |
|
7440 // Any <line-names> immediately after repeat() will append to it. |
|
7441 tail->mNext = new nsCSSValueList; |
|
7442 tail = tail->mNext; |
|
7443 tail->mValue = lastLineNames; |
|
7444 |
|
7445 *aTailPtr = tail; |
|
7446 return true; |
|
7447 } |
|
7448 |
|
7449 bool |
|
7450 CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSProperty aPropID) |
|
7451 { |
|
7452 nsCSSValue value; |
|
7453 if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { |
|
7454 AppendValue(aPropID, value); |
|
7455 return true; |
|
7456 } |
|
7457 |
|
7458 nsSubstring* ident = NextIdent(); |
|
7459 if (ident) { |
|
7460 if (ident->LowerCaseEqualsLiteral("subgrid")) { |
|
7461 if (!ParseOptionalLineNameListAfterSubgrid(value)) { |
|
7462 return false; |
|
7463 } |
|
7464 AppendValue(aPropID, value); |
|
7465 return true; |
|
7466 } |
|
7467 UngetToken(); |
|
7468 } |
|
7469 |
|
7470 nsCSSValue firstLineNames; |
|
7471 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error || |
|
7472 !ParseGridTrackListWithFirstLineNames(value, firstLineNames)) { |
|
7473 return false; |
|
7474 } |
|
7475 AppendValue(aPropID, value); |
|
7476 return true; |
|
7477 } |
|
7478 |
|
7479 bool |
|
7480 CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput, |
|
7481 css::GridTemplateAreasValue* aAreas, |
|
7482 nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices) |
|
7483 { |
|
7484 aAreas->mTemplates.AppendElement(mToken.mIdent); |
|
7485 |
|
7486 nsCSSGridTemplateAreaScanner scanner(aInput); |
|
7487 nsCSSGridTemplateAreaToken token; |
|
7488 css::GridNamedArea* currentArea = nullptr; |
|
7489 uint32_t row = aAreas->NRows(); |
|
7490 uint32_t column; |
|
7491 for (column = 1; scanner.Next(token); column++) { |
|
7492 if (token.isTrash) { |
|
7493 return false; |
|
7494 } |
|
7495 if (currentArea) { |
|
7496 if (token.mName == currentArea->mName) { |
|
7497 if (currentArea->mRowStart == row) { |
|
7498 // Next column in the first row of this named area. |
|
7499 currentArea->mColumnEnd++; |
|
7500 } |
|
7501 continue; |
|
7502 } |
|
7503 // We're exiting |currentArea|, so currentArea is ending at |column|. |
|
7504 // Make sure that this is consistent with currentArea on previous rows: |
|
7505 if (currentArea->mColumnEnd != column) { |
|
7506 NS_ASSERTION(currentArea->mRowStart != row, |
|
7507 "Inconsistent column end for the first row of a named area."); |
|
7508 // Not a rectangle |
|
7509 return false; |
|
7510 } |
|
7511 currentArea = nullptr; |
|
7512 } |
|
7513 if (!token.mName.IsEmpty()) { |
|
7514 // Named cell that doesn't have a cell with the same name on its left. |
|
7515 |
|
7516 // Check if this is the continuation of an existing named area: |
|
7517 uint32_t index; |
|
7518 if (aAreaIndices.Get(token.mName, &index)) { |
|
7519 MOZ_ASSERT(index < aAreas->mNamedAreas.Length(), |
|
7520 "Invalid aAreaIndices hash table"); |
|
7521 currentArea = &aAreas->mNamedAreas[index]; |
|
7522 if (currentArea->mColumnStart != column || |
|
7523 currentArea->mRowEnd != row) { |
|
7524 // Existing named area, but not forming a rectangle |
|
7525 return false; |
|
7526 } |
|
7527 // Next row of an existing named area |
|
7528 currentArea->mRowEnd++; |
|
7529 } else { |
|
7530 // New named area |
|
7531 aAreaIndices.Put(token.mName, aAreas->mNamedAreas.Length()); |
|
7532 currentArea = aAreas->mNamedAreas.AppendElement(); |
|
7533 currentArea->mName = token.mName; |
|
7534 // For column or row N (starting at 1), |
|
7535 // the start line is N, the end line is N + 1 |
|
7536 currentArea->mColumnStart = column; |
|
7537 currentArea->mColumnEnd = column + 1; |
|
7538 currentArea->mRowStart = row; |
|
7539 currentArea->mRowEnd = row + 1; |
|
7540 } |
|
7541 } |
|
7542 } |
|
7543 if (currentArea && currentArea->mColumnEnd != column) { |
|
7544 NS_ASSERTION(currentArea->mRowStart != row, |
|
7545 "Inconsistent column end for the first row of a named area."); |
|
7546 // Not a rectangle |
|
7547 return false; |
|
7548 } |
|
7549 |
|
7550 // On the first row, set the number of columns |
|
7551 // that grid-template-areas contributes to the explicit grid. |
|
7552 // On other rows, check that the number of columns is consistent |
|
7553 // between rows. |
|
7554 if (row == 1) { |
|
7555 aAreas->mNColumns = column; |
|
7556 } else if (aAreas->mNColumns != column) { |
|
7557 return false; |
|
7558 } |
|
7559 return true; |
|
7560 } |
|
7561 |
|
7562 bool |
|
7563 CSSParserImpl::ParseGridTemplateAreas() |
|
7564 { |
|
7565 nsCSSValue value; |
|
7566 if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { |
|
7567 AppendValue(eCSSProperty_grid_template_areas, value); |
|
7568 return true; |
|
7569 } |
|
7570 |
|
7571 nsRefPtr<css::GridTemplateAreasValue> areas = |
|
7572 new css::GridTemplateAreasValue(); |
|
7573 nsDataHashtable<nsStringHashKey, uint32_t> areaIndices; |
|
7574 for (;;) { |
|
7575 if (!GetToken(true)) { |
|
7576 break; |
|
7577 } |
|
7578 if (eCSSToken_String != mToken.mType) { |
|
7579 UngetToken(); |
|
7580 break; |
|
7581 } |
|
7582 if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) { |
|
7583 return false; |
|
7584 } |
|
7585 } |
|
7586 |
|
7587 if (areas->NRows() == 0) { |
|
7588 return false; |
|
7589 } |
|
7590 |
|
7591 AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas)); |
|
7592 return true; |
|
7593 } |
|
7594 |
|
7595 bool |
|
7596 CSSParserImpl::ParseGridTemplate() |
|
7597 { |
|
7598 // none | |
|
7599 // subgrid | |
|
7600 // <'grid-template-columns'> / <'grid-template-rows'> | |
|
7601 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+ |
|
7602 nsCSSValue value; |
|
7603 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
7604 AppendValue(eCSSProperty_grid_template_areas, value); |
|
7605 AppendValue(eCSSProperty_grid_template_columns, value); |
|
7606 AppendValue(eCSSProperty_grid_template_rows, value); |
|
7607 return true; |
|
7608 } |
|
7609 |
|
7610 // TODO (bug 983175): add parsing for 'subgrid' by itself |
|
7611 |
|
7612 // 'none' can appear either by itself, |
|
7613 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'> |
|
7614 if (ParseVariant(value, VARIANT_NONE, nullptr)) { |
|
7615 AppendValue(eCSSProperty_grid_template_columns, value); |
|
7616 if (ExpectSymbol('/', true)) { |
|
7617 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false); |
|
7618 } |
|
7619 AppendValue(eCSSProperty_grid_template_areas, value); |
|
7620 AppendValue(eCSSProperty_grid_template_rows, value); |
|
7621 return true; |
|
7622 } |
|
7623 |
|
7624 // 'subgrid' can appear either by itself, |
|
7625 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'> |
|
7626 nsSubstring* ident = NextIdent(); |
|
7627 if (ident) { |
|
7628 if (ident->LowerCaseEqualsLiteral("subgrid")) { |
|
7629 if (!ParseOptionalLineNameListAfterSubgrid(value)) { |
|
7630 return false; |
|
7631 } |
|
7632 AppendValue(eCSSProperty_grid_template_columns, value); |
|
7633 if (ExpectSymbol('/', true)) { |
|
7634 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false); |
|
7635 } |
|
7636 if (value.GetListValue()->mNext) { |
|
7637 // Non-empty <line-name-list> after 'subgrid'. |
|
7638 // This is only valid as part of <'grid-template-columns'>, |
|
7639 // which must be followed by a slash. |
|
7640 return false; |
|
7641 } |
|
7642 // 'subgrid' by itself sets both grid-template-columns |
|
7643 // and grid-template-rows. |
|
7644 AppendValue(eCSSProperty_grid_template_rows, value); |
|
7645 value.SetNoneValue(); |
|
7646 AppendValue(eCSSProperty_grid_template_areas, value); |
|
7647 return true; |
|
7648 } |
|
7649 UngetToken(); |
|
7650 } |
|
7651 |
|
7652 // [ <line-names>? ] here is ambiguous: |
|
7653 // it can be either the start of a <track-list>, |
|
7654 // or the start of [ <line-names>? <string> <track-size>? <line-names>? ]+ |
|
7655 nsCSSValue firstLineNames; |
|
7656 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error || |
|
7657 !GetToken(true)) { |
|
7658 return false; |
|
7659 } |
|
7660 if (mToken.mType == eCSSToken_String) { |
|
7661 // [ <track-list> / ]? was omitted |
|
7662 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+ |
|
7663 value.SetNoneValue(); |
|
7664 AppendValue(eCSSProperty_grid_template_columns, value); |
|
7665 return ParseGridTemplateAfterString(firstLineNames); |
|
7666 } |
|
7667 UngetToken(); |
|
7668 |
|
7669 if (!(ParseGridTrackListWithFirstLineNames(value, firstLineNames) && |
|
7670 ExpectSymbol('/', true))) { |
|
7671 return false; |
|
7672 } |
|
7673 AppendValue(eCSSProperty_grid_template_columns, value); |
|
7674 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ true); |
|
7675 } |
|
7676 |
|
7677 // Helper for parsing the 'grid-template' shorthand |
|
7678 // |
|
7679 // NOTE: This parses the portion after the slash, for *one* of the |
|
7680 // following types of expressions: |
|
7681 // - <'grid-template-columns'> / <'grid-template-rows'> |
|
7682 // - <track-list> / [ <line-names>? <string> <track-size>? <line-names>? ]+ |
|
7683 // |
|
7684 // We don't know which type of expression we've got until we've parsed the |
|
7685 // second half, since the pre-slash part is ambiguous. The various return |
|
7686 // clauses below are labeled with the type of expression they're completing. |
|
7687 bool |
|
7688 CSSParserImpl::ParseGridTemplateAfterSlash(bool aColumnsIsTrackList) |
|
7689 { |
|
7690 nsCSSValue rowsValue; |
|
7691 if (ParseVariant(rowsValue, VARIANT_NONE, nullptr)) { |
|
7692 // <'grid-template-columns'> / <'grid-template-rows'> |
|
7693 AppendValue(eCSSProperty_grid_template_rows, rowsValue); |
|
7694 nsCSSValue areasValue(eCSSUnit_None); // implied |
|
7695 AppendValue(eCSSProperty_grid_template_areas, areasValue); |
|
7696 return true; |
|
7697 } |
|
7698 |
|
7699 nsSubstring* ident = NextIdent(); |
|
7700 if (ident) { |
|
7701 if (ident->LowerCaseEqualsLiteral("subgrid")) { |
|
7702 if (!ParseOptionalLineNameListAfterSubgrid(rowsValue)) { |
|
7703 return false; |
|
7704 } |
|
7705 // <'grid-template-columns'> / <'grid-template-rows'> |
|
7706 AppendValue(eCSSProperty_grid_template_rows, rowsValue); |
|
7707 nsCSSValue areasValue(eCSSUnit_None); // implied |
|
7708 AppendValue(eCSSProperty_grid_template_areas, areasValue); |
|
7709 return true; |
|
7710 } |
|
7711 UngetToken(); |
|
7712 } |
|
7713 |
|
7714 nsCSSValue firstLineNames; |
|
7715 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error || |
|
7716 !GetToken(true)) { |
|
7717 return false; |
|
7718 } |
|
7719 if (aColumnsIsTrackList && mToken.mType == eCSSToken_String) { |
|
7720 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+ |
|
7721 return ParseGridTemplateAfterString(firstLineNames); |
|
7722 } |
|
7723 UngetToken(); |
|
7724 |
|
7725 if (!ParseGridTrackListWithFirstLineNames(rowsValue, firstLineNames)) { |
|
7726 return false; |
|
7727 } |
|
7728 |
|
7729 // <'grid-template-columns'> / <'grid-template-rows'> |
|
7730 AppendValue(eCSSProperty_grid_template_rows, rowsValue); |
|
7731 nsCSSValue areasValue(eCSSUnit_None); // implied |
|
7732 AppendValue(eCSSProperty_grid_template_areas, areasValue); |
|
7733 return true; |
|
7734 } |
|
7735 |
|
7736 // Helper for parsing the 'grid-template' shorthand: |
|
7737 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+ |
|
7738 // with a <line-names>? already consumed, stored in |aFirstLineNames|, |
|
7739 // and the current token a <string> |
|
7740 bool |
|
7741 CSSParserImpl::ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames) |
|
7742 { |
|
7743 MOZ_ASSERT(mToken.mType == eCSSToken_String, |
|
7744 "ParseGridTemplateAfterString called with a non-string token"); |
|
7745 |
|
7746 nsCSSValue rowsValue; |
|
7747 nsRefPtr<css::GridTemplateAreasValue> areas = |
|
7748 new css::GridTemplateAreasValue(); |
|
7749 nsDataHashtable<nsStringHashKey, uint32_t> areaIndices; |
|
7750 nsCSSValueList* rowsItem = rowsValue.SetListValue(); |
|
7751 rowsItem->mValue = aFirstLineNames; |
|
7752 |
|
7753 for (;;) { |
|
7754 if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) { |
|
7755 return false; |
|
7756 } |
|
7757 |
|
7758 rowsItem->mNext = new nsCSSValueList; |
|
7759 rowsItem = rowsItem->mNext; |
|
7760 CSSParseResult result = ParseGridTrackSize(rowsItem->mValue); |
|
7761 if (result == CSSParseResult::Error) { |
|
7762 return false; |
|
7763 } |
|
7764 if (result == CSSParseResult::NotFound) { |
|
7765 rowsItem->mValue.SetAutoValue(); |
|
7766 } |
|
7767 |
|
7768 rowsItem->mNext = new nsCSSValueList; |
|
7769 rowsItem = rowsItem->mNext; |
|
7770 result = ParseGridLineNames(rowsItem->mValue); |
|
7771 if (result == CSSParseResult::Error) { |
|
7772 return false; |
|
7773 } |
|
7774 if (result == CSSParseResult::Ok) { |
|
7775 // Append to the same list as the previous call to ParseGridLineNames. |
|
7776 result = ParseGridLineNames(rowsItem->mValue); |
|
7777 if (result == CSSParseResult::Error) { |
|
7778 return false; |
|
7779 } |
|
7780 if (result == CSSParseResult::Ok) { |
|
7781 // Parsed <line-name> twice. |
|
7782 // The property value can not end here, we expect a string next. |
|
7783 if (!GetToken(true)) { |
|
7784 return false; |
|
7785 } |
|
7786 if (eCSSToken_String != mToken.mType) { |
|
7787 UngetToken(); |
|
7788 return false; |
|
7789 } |
|
7790 continue; |
|
7791 } |
|
7792 } |
|
7793 |
|
7794 // Did not find a <line-names>. |
|
7795 // Next, we expect either a string or the end of the property value. |
|
7796 if (!GetToken(true)) { |
|
7797 break; |
|
7798 } |
|
7799 if (eCSSToken_String != mToken.mType) { |
|
7800 UngetToken(); |
|
7801 break; |
|
7802 } |
|
7803 } |
|
7804 |
|
7805 AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas)); |
|
7806 AppendValue(eCSSProperty_grid_template_rows, rowsValue); |
|
7807 return true; |
|
7808 } |
|
7809 |
|
7810 // <'grid-template'> | |
|
7811 // [ <'grid-auto-flow'> [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]? ] |
|
7812 bool |
|
7813 CSSParserImpl::ParseGrid() |
|
7814 { |
|
7815 nsCSSValue value; |
|
7816 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
7817 for (const nsCSSProperty* subprops = |
|
7818 nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid); |
|
7819 *subprops != eCSSProperty_UNKNOWN; ++subprops) { |
|
7820 AppendValue(*subprops, value); |
|
7821 } |
|
7822 return true; |
|
7823 } |
|
7824 |
|
7825 // 'none' at the beginning could be a <'grid-auto-flow'> |
|
7826 // (which also covers 'none' by itself) |
|
7827 // or a <'grid-template-columns'> (as part of <'grid-template'>) |
|
7828 if (ParseVariant(value, VARIANT_NONE, nullptr)) { |
|
7829 if (ExpectSymbol('/', true)) { |
|
7830 AppendValue(eCSSProperty_grid_template_columns, value); |
|
7831 |
|
7832 // Set grid-auto-* subproperties to their initial values. |
|
7833 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated); |
|
7834 AppendValue(eCSSProperty_grid_auto_flow, value); |
|
7835 value.SetAutoValue(); |
|
7836 AppendValue(eCSSProperty_grid_auto_columns, value); |
|
7837 AppendValue(eCSSProperty_grid_auto_rows, value); |
|
7838 |
|
7839 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false); |
|
7840 } |
|
7841 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated); |
|
7842 AppendValue(eCSSProperty_grid_auto_flow, value); |
|
7843 return ParseGridShorthandAutoProps(); |
|
7844 } |
|
7845 |
|
7846 // An empty value is always invalid. |
|
7847 if (!GetToken(true)) { |
|
7848 return false; |
|
7849 } |
|
7850 |
|
7851 // If the value starts with a 'dense', 'column' or 'row' keyword, |
|
7852 // it can only start with a <'grid-auto-flow'> |
|
7853 if (mToken.mType == eCSSToken_Ident) { |
|
7854 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); |
|
7855 if (keyword == eCSSKeyword_dense || |
|
7856 keyword == eCSSKeyword_column || |
|
7857 keyword == eCSSKeyword_row) { |
|
7858 UngetToken(); |
|
7859 return ParseGridAutoFlow() && ParseGridShorthandAutoProps(); |
|
7860 } |
|
7861 } |
|
7862 UngetToken(); |
|
7863 |
|
7864 // Set other subproperties to their initial values |
|
7865 // and parse <'grid-template'>. |
|
7866 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated); |
|
7867 AppendValue(eCSSProperty_grid_auto_flow, value); |
|
7868 value.SetAutoValue(); |
|
7869 AppendValue(eCSSProperty_grid_auto_columns, value); |
|
7870 AppendValue(eCSSProperty_grid_auto_rows, value); |
|
7871 return ParseGridTemplate(); |
|
7872 } |
|
7873 |
|
7874 // Parse [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]? |
|
7875 // for the 'grid' shorthand. |
|
7876 // Assumes that <'grid-auto-flow'> was already parsed by the caller. |
|
7877 bool |
|
7878 CSSParserImpl::ParseGridShorthandAutoProps() |
|
7879 { |
|
7880 nsCSSValue autoColumnsValue; |
|
7881 nsCSSValue autoRowsValue; |
|
7882 CSSParseResult result = ParseGridTrackSize(autoColumnsValue); |
|
7883 if (result == CSSParseResult::Error) { |
|
7884 return false; |
|
7885 } |
|
7886 if (result == CSSParseResult::NotFound) { |
|
7887 autoColumnsValue.SetAutoValue(); |
|
7888 autoRowsValue.SetAutoValue(); |
|
7889 } else { |
|
7890 if (!ExpectSymbol('/', true)) { |
|
7891 autoRowsValue.SetAutoValue(); |
|
7892 } else if (ParseGridTrackSize(autoRowsValue) != CSSParseResult::Ok) { |
|
7893 return false; |
|
7894 } |
|
7895 } |
|
7896 AppendValue(eCSSProperty_grid_auto_columns, autoColumnsValue); |
|
7897 AppendValue(eCSSProperty_grid_auto_rows, autoRowsValue); |
|
7898 nsCSSValue templateValue(eCSSUnit_None); // Initial values |
|
7899 AppendValue(eCSSProperty_grid_template_areas, templateValue); |
|
7900 AppendValue(eCSSProperty_grid_template_columns, templateValue); |
|
7901 AppendValue(eCSSProperty_grid_template_rows, templateValue); |
|
7902 return true; |
|
7903 } |
|
7904 |
|
7905 // Parse a <grid-line>. |
|
7906 // If successful, set aValue to eCSSUnit_Auto, |
|
7907 // or a eCSSUnit_List containing, in that order: |
|
7908 // |
|
7909 // * An optional eCSSUnit_Enumerated marking a "span" keyword. |
|
7910 // * An optional eCSSUnit_Integer |
|
7911 // * An optional eCSSUnit_Ident |
|
7912 // |
|
7913 // At least one of eCSSUnit_Integer or eCSSUnit_Ident is present. |
|
7914 bool |
|
7915 CSSParserImpl::ParseGridLine(nsCSSValue& aValue) |
|
7916 { |
|
7917 // <grid-line> = |
|
7918 // auto | |
|
7919 // <custom-ident> | |
|
7920 // [ <integer> && <custom-ident>? ] | |
|
7921 // [ span && [ <integer> || <custom-ident> ] ] |
|
7922 // |
|
7923 // Syntactically, this simplifies to: |
|
7924 // |
|
7925 // <grid-line> = |
|
7926 // auto | |
|
7927 // [ span? && [ <integer> || <custom-ident> ] ] |
|
7928 |
|
7929 if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) { |
|
7930 return true; |
|
7931 } |
|
7932 |
|
7933 static const nsCSSKeyword kGridLineKeywords[] = { |
|
7934 eCSSKeyword_span, |
|
7935 eCSSKeyword_UNKNOWN // End-of-array marker |
|
7936 }; |
|
7937 bool hasSpan = false; |
|
7938 bool hasInteger = false; |
|
7939 bool hasIdent = false; |
|
7940 int32_t integer; |
|
7941 nsCSSValue ident; |
|
7942 |
|
7943 if (!GetToken(true)) { |
|
7944 return false; |
|
7945 } |
|
7946 if (mToken.mType == eCSSToken_Ident && |
|
7947 mToken.mIdent.LowerCaseEqualsLiteral("span")) { |
|
7948 hasSpan = true; |
|
7949 if (!GetToken(true)) { |
|
7950 return false; |
|
7951 } |
|
7952 } |
|
7953 |
|
7954 do { |
|
7955 if (!hasIdent && |
|
7956 mToken.mType == eCSSToken_Ident && |
|
7957 ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) { |
|
7958 hasIdent = true; |
|
7959 } else if (!hasInteger && |
|
7960 mToken.mType == eCSSToken_Number && |
|
7961 mToken.mIntegerValid && |
|
7962 mToken.mInteger != 0) { |
|
7963 hasInteger = true; |
|
7964 integer = mToken.mInteger; |
|
7965 } else { |
|
7966 UngetToken(); |
|
7967 break; |
|
7968 } |
|
7969 } while (!(hasInteger && hasIdent) && GetToken(true)); |
|
7970 |
|
7971 // Require at least one of <integer> or <custom-ident> |
|
7972 if (!(hasInteger || hasIdent)) { |
|
7973 return false; |
|
7974 } |
|
7975 |
|
7976 if (!hasSpan && GetToken(true)) { |
|
7977 if (mToken.mType == eCSSToken_Ident && |
|
7978 mToken.mIdent.LowerCaseEqualsLiteral("span")) { |
|
7979 hasSpan = true; |
|
7980 } else { |
|
7981 UngetToken(); |
|
7982 } |
|
7983 } |
|
7984 |
|
7985 nsCSSValueList* item = aValue.SetListValue(); |
|
7986 if (hasSpan) { |
|
7987 // Given "span", a negative <integer> is invalid. |
|
7988 if (hasInteger && integer < 0) { |
|
7989 return false; |
|
7990 } |
|
7991 // '1' here is a dummy value. |
|
7992 // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword. |
|
7993 item->mValue.SetIntValue(1, eCSSUnit_Enumerated); |
|
7994 item->mNext = new nsCSSValueList; |
|
7995 item = item->mNext; |
|
7996 } |
|
7997 if (hasInteger) { |
|
7998 item->mValue.SetIntValue(integer, eCSSUnit_Integer); |
|
7999 if (hasIdent) { |
|
8000 item->mNext = new nsCSSValueList; |
|
8001 item = item->mNext; |
|
8002 } |
|
8003 } |
|
8004 if (hasIdent) { |
|
8005 item->mValue = ident; |
|
8006 } |
|
8007 return true; |
|
8008 } |
|
8009 |
|
8010 bool |
|
8011 CSSParserImpl::ParseGridAutoPosition() |
|
8012 { |
|
8013 nsCSSValue value; |
|
8014 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
8015 AppendValue(eCSSProperty_grid_auto_position, value); |
|
8016 return true; |
|
8017 } |
|
8018 nsCSSValue columnStartValue; |
|
8019 nsCSSValue rowStartValue; |
|
8020 if (ParseGridLine(columnStartValue) && |
|
8021 ExpectSymbol('/', true) && |
|
8022 ParseGridLine(rowStartValue)) { |
|
8023 value.SetPairValue(columnStartValue, rowStartValue); |
|
8024 AppendValue(eCSSProperty_grid_auto_position, value); |
|
8025 return true; |
|
8026 } |
|
8027 return false; |
|
8028 } |
|
8029 |
|
8030 bool |
|
8031 CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSProperty aPropID) |
|
8032 { |
|
8033 nsCSSValue value; |
|
8034 if (ParseVariant(value, VARIANT_INHERIT, nullptr) || |
|
8035 ParseGridLine(value)) { |
|
8036 AppendValue(aPropID, value); |
|
8037 return true; |
|
8038 } |
|
8039 return false; |
|
8040 } |
|
8041 |
|
8042 // If |aFallback| is a List containing a single Ident, set |aValue| to that. |
|
8043 // Otherwise, set |aValue| to Auto. |
|
8044 // Used with |aFallback| from ParseGridLine() |
|
8045 static void |
|
8046 HandleGridLineFallback(const nsCSSValue& aFallback, nsCSSValue& aValue) |
|
8047 { |
|
8048 if (aFallback.GetUnit() == eCSSUnit_List && |
|
8049 aFallback.GetListValue()->mValue.GetUnit() == eCSSUnit_Ident && |
|
8050 !aFallback.GetListValue()->mNext) { |
|
8051 aValue = aFallback; |
|
8052 } else { |
|
8053 aValue.SetAutoValue(); |
|
8054 } |
|
8055 } |
|
8056 |
|
8057 bool |
|
8058 CSSParserImpl::ParseGridColumnRow(nsCSSProperty aStartPropID, |
|
8059 nsCSSProperty aEndPropID) |
|
8060 { |
|
8061 nsCSSValue value; |
|
8062 nsCSSValue secondValue; |
|
8063 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
8064 AppendValue(aStartPropID, value); |
|
8065 AppendValue(aEndPropID, value); |
|
8066 return true; |
|
8067 } |
|
8068 |
|
8069 if (!ParseGridLine(value)) { |
|
8070 return false; |
|
8071 } |
|
8072 if (GetToken(true)) { |
|
8073 if (mToken.IsSymbol('/')) { |
|
8074 if (ParseGridLine(secondValue)) { |
|
8075 AppendValue(aStartPropID, value); |
|
8076 AppendValue(aEndPropID, secondValue); |
|
8077 return true; |
|
8078 } else { |
|
8079 return false; |
|
8080 } |
|
8081 } |
|
8082 UngetToken(); |
|
8083 } |
|
8084 |
|
8085 // A single <custom-ident> is repeated to both properties, |
|
8086 // anything else sets the grid-{column,row}-end property to 'auto'. |
|
8087 HandleGridLineFallback(value, secondValue); |
|
8088 |
|
8089 AppendValue(aStartPropID, value); |
|
8090 AppendValue(aEndPropID, secondValue); |
|
8091 return true; |
|
8092 } |
|
8093 |
|
8094 bool |
|
8095 CSSParserImpl::ParseGridArea() |
|
8096 { |
|
8097 nsCSSValue values[4]; |
|
8098 if (ParseVariant(values[0], VARIANT_INHERIT, nullptr)) { |
|
8099 AppendValue(eCSSProperty_grid_row_start, values[0]); |
|
8100 AppendValue(eCSSProperty_grid_column_start, values[0]); |
|
8101 AppendValue(eCSSProperty_grid_row_end, values[0]); |
|
8102 AppendValue(eCSSProperty_grid_column_end, values[0]); |
|
8103 return true; |
|
8104 } |
|
8105 |
|
8106 int32_t i = 0; |
|
8107 for (;;) { |
|
8108 if (!ParseGridLine(values[i])) { |
|
8109 return false; |
|
8110 } |
|
8111 if (++i == 4 || !GetToken(true)) { |
|
8112 break; |
|
8113 } |
|
8114 if (!mToken.IsSymbol('/')) { |
|
8115 UngetToken(); |
|
8116 break; |
|
8117 } |
|
8118 } |
|
8119 |
|
8120 MOZ_ASSERT(i >= 1, "should have parsed at least one grid-line (or returned)"); |
|
8121 if (i < 2) { |
|
8122 HandleGridLineFallback(values[0], values[1]); |
|
8123 } |
|
8124 if (i < 3) { |
|
8125 HandleGridLineFallback(values[0], values[2]); |
|
8126 } |
|
8127 if (i < 4) { |
|
8128 HandleGridLineFallback(values[1], values[3]); |
|
8129 } |
|
8130 |
|
8131 AppendValue(eCSSProperty_grid_row_start, values[0]); |
|
8132 AppendValue(eCSSProperty_grid_column_start, values[1]); |
|
8133 AppendValue(eCSSProperty_grid_row_end, values[2]); |
|
8134 AppendValue(eCSSProperty_grid_column_end, values[3]); |
|
8135 return true; |
|
8136 } |
|
8137 |
|
8138 // <color-stop> : <color> [ <percentage> | <length> ]? |
|
8139 bool |
|
8140 CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient) |
|
8141 { |
|
8142 nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement(); |
|
8143 if (!ParseVariant(stop->mColor, VARIANT_COLOR, nullptr)) { |
|
8144 return false; |
|
8145 } |
|
8146 |
|
8147 // Stop positions do not have to fall between the starting-point and |
|
8148 // ending-point, so we don't use ParseNonNegativeVariant. |
|
8149 if (!ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr)) { |
|
8150 stop->mLocation.SetNoneValue(); |
|
8151 } |
|
8152 return true; |
|
8153 } |
|
8154 |
|
8155 // <gradient> |
|
8156 // : linear-gradient( <linear-gradient-line>? <color-stops> ')' |
|
8157 // | radial-gradient( <radial-gradient-line>? <color-stops> ')' |
|
8158 // |
|
8159 // <linear-gradient-line> : [ to [left | right] || [top | bottom] ] , |
|
8160 // | <legacy-gradient-line> |
|
8161 // <radial-gradient-line> : [ <shape> || <size> ] [ at <position> ]? , |
|
8162 // | [ at <position> ] , |
|
8163 // | <legacy-gradient-line>? <legacy-shape-size>? |
|
8164 // <shape> : circle | ellipse |
|
8165 // <size> : closest-side | closest-corner | farthest-side | farthest-corner |
|
8166 // | <length> | [<length> | <percentage>]{2} |
|
8167 // |
|
8168 // <legacy-gradient-line> : [ <position> || <angle>] , |
|
8169 // |
|
8170 // <legacy-shape-size> : [ <shape> || <legacy-size> ] , |
|
8171 // <legacy-size> : closest-side | closest-corner | farthest-side |
|
8172 // | farthest-corner | contain | cover |
|
8173 // |
|
8174 // <color-stops> : <color-stop> , <color-stop> [, <color-stop>]* |
|
8175 bool |
|
8176 CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating, |
|
8177 bool aIsLegacy) |
|
8178 { |
|
8179 nsRefPtr<nsCSSValueGradient> cssGradient |
|
8180 = new nsCSSValueGradient(false, aIsRepeating); |
|
8181 |
|
8182 if (!GetToken(true)) { |
|
8183 return false; |
|
8184 } |
|
8185 |
|
8186 if (mToken.mType == eCSSToken_Ident && |
|
8187 mToken.mIdent.LowerCaseEqualsLiteral("to")) { |
|
8188 |
|
8189 // "to" syntax doesn't allow explicit "center" |
|
8190 if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) { |
|
8191 SkipUntil(')'); |
|
8192 return false; |
|
8193 } |
|
8194 |
|
8195 // [ to [left | right] || [top | bottom] ] , |
|
8196 const nsCSSValue& xValue = cssGradient->mBgPos.mXValue; |
|
8197 const nsCSSValue& yValue = cssGradient->mBgPos.mYValue; |
|
8198 if (xValue.GetUnit() != eCSSUnit_Enumerated || |
|
8199 !(xValue.GetIntValue() & (NS_STYLE_BG_POSITION_LEFT | |
|
8200 NS_STYLE_BG_POSITION_CENTER | |
|
8201 NS_STYLE_BG_POSITION_RIGHT)) || |
|
8202 yValue.GetUnit() != eCSSUnit_Enumerated || |
|
8203 !(yValue.GetIntValue() & (NS_STYLE_BG_POSITION_TOP | |
|
8204 NS_STYLE_BG_POSITION_CENTER | |
|
8205 NS_STYLE_BG_POSITION_BOTTOM))) { |
|
8206 SkipUntil(')'); |
|
8207 return false; |
|
8208 } |
|
8209 |
|
8210 if (!ExpectSymbol(',', true)) { |
|
8211 SkipUntil(')'); |
|
8212 return false; |
|
8213 } |
|
8214 |
|
8215 return ParseGradientColorStops(cssGradient, aValue); |
|
8216 } |
|
8217 |
|
8218 if (!aIsLegacy) { |
|
8219 UngetToken(); |
|
8220 |
|
8221 // <angle> , |
|
8222 if (ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) && |
|
8223 !ExpectSymbol(',', true)) { |
|
8224 SkipUntil(')'); |
|
8225 return false; |
|
8226 } |
|
8227 |
|
8228 return ParseGradientColorStops(cssGradient, aValue); |
|
8229 } |
|
8230 |
|
8231 nsCSSTokenType ty = mToken.mType; |
|
8232 nsString id = mToken.mIdent; |
|
8233 UngetToken(); |
|
8234 |
|
8235 // <legacy-gradient-line> |
|
8236 bool haveGradientLine = IsLegacyGradientLine(ty, id); |
|
8237 if (haveGradientLine) { |
|
8238 cssGradient->mIsLegacySyntax = true; |
|
8239 bool haveAngle = |
|
8240 ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr); |
|
8241 |
|
8242 // if we got an angle, we might now have a comma, ending the gradient-line |
|
8243 if (!haveAngle || !ExpectSymbol(',', true)) { |
|
8244 if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) { |
|
8245 SkipUntil(')'); |
|
8246 return false; |
|
8247 } |
|
8248 |
|
8249 if (!ExpectSymbol(',', true) && |
|
8250 // if we didn't already get an angle, we might have one now, |
|
8251 // otherwise it's an error |
|
8252 (haveAngle || |
|
8253 !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) || |
|
8254 // now we better have a comma |
|
8255 !ExpectSymbol(',', true))) { |
|
8256 SkipUntil(')'); |
|
8257 return false; |
|
8258 } |
|
8259 } |
|
8260 } |
|
8261 |
|
8262 return ParseGradientColorStops(cssGradient, aValue); |
|
8263 } |
|
8264 |
|
8265 bool |
|
8266 CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating, |
|
8267 bool aIsLegacy) |
|
8268 { |
|
8269 nsRefPtr<nsCSSValueGradient> cssGradient |
|
8270 = new nsCSSValueGradient(true, aIsRepeating); |
|
8271 |
|
8272 // [ <shape> || <size> ] |
|
8273 bool haveShape = |
|
8274 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, |
|
8275 nsCSSProps::kRadialGradientShapeKTable); |
|
8276 |
|
8277 bool haveSize = ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD, |
|
8278 aIsLegacy ? |
|
8279 nsCSSProps::kRadialGradientLegacySizeKTable : |
|
8280 nsCSSProps::kRadialGradientSizeKTable); |
|
8281 if (haveSize) { |
|
8282 if (!haveShape) { |
|
8283 // <size> <shape> |
|
8284 haveShape = ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, |
|
8285 nsCSSProps::kRadialGradientShapeKTable); |
|
8286 } |
|
8287 } else if (!aIsLegacy) { |
|
8288 // Save RadialShape before parsing RadiusX because RadialShape and |
|
8289 // RadiusX share the storage. |
|
8290 int32_t shape = |
|
8291 cssGradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated ? |
|
8292 cssGradient->GetRadialShape().GetIntValue() : -1; |
|
8293 // <length> | [<length> | <percentage>]{2} |
|
8294 cssGradient->mIsExplicitSize = true; |
|
8295 haveSize = |
|
8296 ParseNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP, nullptr); |
|
8297 if (!haveSize) { |
|
8298 // It was not an explicit size after all. |
|
8299 // Note that ParseNonNegativeVariant may have put something |
|
8300 // invalid into our storage, but only in the case where it was |
|
8301 // rejected only for being negative. Since this means the token |
|
8302 // was a length or a percentage, we know it's not valid syntax |
|
8303 // (which must be a comma, the 'at' keyword, or a color), so we |
|
8304 // know this value will be dropped. This means it doesn't matter |
|
8305 // that we have something invalid in our storage. |
|
8306 cssGradient->mIsExplicitSize = false; |
|
8307 } else { |
|
8308 // vertical extent is optional |
|
8309 bool haveYSize = |
|
8310 ParseNonNegativeVariant(cssGradient->GetRadiusY(), VARIANT_LP, nullptr); |
|
8311 if (!haveShape) { |
|
8312 nsCSSValue shapeValue; |
|
8313 haveShape = ParseVariant(shapeValue, VARIANT_KEYWORD, |
|
8314 nsCSSProps::kRadialGradientShapeKTable); |
|
8315 if (haveShape) { |
|
8316 shape = shapeValue.GetIntValue(); |
|
8317 } |
|
8318 } |
|
8319 if (haveYSize |
|
8320 ? shape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR |
|
8321 : cssGradient->GetRadiusX().GetUnit() == eCSSUnit_Percent || |
|
8322 shape == NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL) { |
|
8323 SkipUntil(')'); |
|
8324 return false; |
|
8325 } |
|
8326 } |
|
8327 } |
|
8328 |
|
8329 if ((haveShape || haveSize) && ExpectSymbol(',', true)) { |
|
8330 // [ <shape> || <size> ] , |
|
8331 return ParseGradientColorStops(cssGradient, aValue); |
|
8332 } |
|
8333 |
|
8334 if (!GetToken(true)) { |
|
8335 return false; |
|
8336 } |
|
8337 |
|
8338 if (!aIsLegacy) { |
|
8339 if (mToken.mType == eCSSToken_Ident && |
|
8340 mToken.mIdent.LowerCaseEqualsLiteral("at")) { |
|
8341 // [ <shape> || <size> ]? at <position> , |
|
8342 if (!ParseBoxPositionValues(cssGradient->mBgPos, false) || |
|
8343 !ExpectSymbol(',', true)) { |
|
8344 SkipUntil(')'); |
|
8345 return false; |
|
8346 } |
|
8347 |
|
8348 return ParseGradientColorStops(cssGradient, aValue); |
|
8349 } |
|
8350 |
|
8351 // <color-stops> only |
|
8352 UngetToken(); |
|
8353 return ParseGradientColorStops(cssGradient, aValue); |
|
8354 } |
|
8355 MOZ_ASSERT(!cssGradient->mIsExplicitSize); |
|
8356 |
|
8357 nsCSSTokenType ty = mToken.mType; |
|
8358 nsString id = mToken.mIdent; |
|
8359 UngetToken(); |
|
8360 |
|
8361 // <legacy-gradient-line> |
|
8362 bool haveGradientLine = false; |
|
8363 // if we already encountered a shape or size, |
|
8364 // we can not have a gradient-line in legacy syntax |
|
8365 if (!haveShape && !haveSize) { |
|
8366 haveGradientLine = IsLegacyGradientLine(ty, id); |
|
8367 } |
|
8368 if (haveGradientLine) { |
|
8369 bool haveAngle = |
|
8370 ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr); |
|
8371 |
|
8372 // if we got an angle, we might now have a comma, ending the gradient-line |
|
8373 if (!haveAngle || !ExpectSymbol(',', true)) { |
|
8374 if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) { |
|
8375 SkipUntil(')'); |
|
8376 return false; |
|
8377 } |
|
8378 |
|
8379 if (!ExpectSymbol(',', true) && |
|
8380 // if we didn't already get an angle, we might have one now, |
|
8381 // otherwise it's an error |
|
8382 (haveAngle || |
|
8383 !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) || |
|
8384 // now we better have a comma |
|
8385 !ExpectSymbol(',', true))) { |
|
8386 SkipUntil(')'); |
|
8387 return false; |
|
8388 } |
|
8389 } |
|
8390 |
|
8391 if (cssGradient->mAngle.GetUnit() != eCSSUnit_None) { |
|
8392 cssGradient->mIsLegacySyntax = true; |
|
8393 } |
|
8394 } |
|
8395 |
|
8396 // radial gradients might have a shape and size here for legacy syntax |
|
8397 if (!haveShape && !haveSize) { |
|
8398 haveShape = |
|
8399 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, |
|
8400 nsCSSProps::kRadialGradientShapeKTable); |
|
8401 haveSize = |
|
8402 ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD, |
|
8403 nsCSSProps::kRadialGradientLegacySizeKTable); |
|
8404 |
|
8405 // could be in either order |
|
8406 if (!haveShape) { |
|
8407 haveShape = |
|
8408 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, |
|
8409 nsCSSProps::kRadialGradientShapeKTable); |
|
8410 } |
|
8411 } |
|
8412 |
|
8413 if ((haveShape || haveSize) && !ExpectSymbol(',', true)) { |
|
8414 SkipUntil(')'); |
|
8415 return false; |
|
8416 } |
|
8417 |
|
8418 return ParseGradientColorStops(cssGradient, aValue); |
|
8419 } |
|
8420 |
|
8421 bool |
|
8422 CSSParserImpl::IsLegacyGradientLine(const nsCSSTokenType& aType, |
|
8423 const nsString& aId) |
|
8424 { |
|
8425 // N.B. ParseBoxPositionValues is not guaranteed to put back |
|
8426 // everything it scanned if it fails, so we must only call it |
|
8427 // if there is no alternative to consuming a <box-position>. |
|
8428 // ParseVariant, as used here, will either succeed and consume |
|
8429 // a single token, or fail and consume none, so we can be more |
|
8430 // cavalier about calling it. |
|
8431 |
|
8432 bool haveGradientLine = false; |
|
8433 switch (aType) { |
|
8434 case eCSSToken_Percentage: |
|
8435 case eCSSToken_Number: |
|
8436 case eCSSToken_Dimension: |
|
8437 haveGradientLine = true; |
|
8438 break; |
|
8439 |
|
8440 case eCSSToken_Function: |
|
8441 if (aId.LowerCaseEqualsLiteral("calc") || |
|
8442 aId.LowerCaseEqualsLiteral("-moz-calc")) { |
|
8443 haveGradientLine = true; |
|
8444 break; |
|
8445 } |
|
8446 // fall through |
|
8447 case eCSSToken_ID: |
|
8448 case eCSSToken_Hash: |
|
8449 // this is a color |
|
8450 break; |
|
8451 |
|
8452 case eCSSToken_Ident: { |
|
8453 // This is only a gradient line if it's a box position keyword. |
|
8454 nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(aId); |
|
8455 int32_t junk; |
|
8456 if (kw != eCSSKeyword_UNKNOWN && |
|
8457 nsCSSProps::FindKeyword(kw, nsCSSProps::kBackgroundPositionKTable, |
|
8458 junk)) { |
|
8459 haveGradientLine = true; |
|
8460 } |
|
8461 break; |
|
8462 } |
|
8463 |
|
8464 default: |
|
8465 // error |
|
8466 break; |
|
8467 } |
|
8468 |
|
8469 return haveGradientLine; |
|
8470 } |
|
8471 |
|
8472 bool |
|
8473 CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient, |
|
8474 nsCSSValue& aValue) |
|
8475 { |
|
8476 // At least two color stops are required |
|
8477 if (!ParseColorStop(aGradient) || |
|
8478 !ExpectSymbol(',', true) || |
|
8479 !ParseColorStop(aGradient)) { |
|
8480 SkipUntil(')'); |
|
8481 return false; |
|
8482 } |
|
8483 |
|
8484 // Additional color stops |
|
8485 while (ExpectSymbol(',', true)) { |
|
8486 if (!ParseColorStop(aGradient)) { |
|
8487 SkipUntil(')'); |
|
8488 return false; |
|
8489 } |
|
8490 } |
|
8491 |
|
8492 if (!ExpectSymbol(')', true)) { |
|
8493 SkipUntil(')'); |
|
8494 return false; |
|
8495 } |
|
8496 |
|
8497 aValue.SetGradientValue(aGradient); |
|
8498 return true; |
|
8499 } |
|
8500 |
|
8501 int32_t |
|
8502 CSSParserImpl::ParseChoice(nsCSSValue aValues[], |
|
8503 const nsCSSProperty aPropIDs[], int32_t aNumIDs) |
|
8504 { |
|
8505 int32_t found = 0; |
|
8506 nsAutoParseCompoundProperty compound(this); |
|
8507 |
|
8508 int32_t loop; |
|
8509 for (loop = 0; loop < aNumIDs; loop++) { |
|
8510 // Try each property parser in order |
|
8511 int32_t hadFound = found; |
|
8512 int32_t index; |
|
8513 for (index = 0; index < aNumIDs; index++) { |
|
8514 int32_t bit = 1 << index; |
|
8515 if ((found & bit) == 0) { |
|
8516 if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) { |
|
8517 found |= bit; |
|
8518 // It's more efficient to break since it will reset |hadFound| |
|
8519 // to |found|. Furthermore, ParseListStyle depends on our going |
|
8520 // through the properties in order for each value.. |
|
8521 break; |
|
8522 } |
|
8523 } |
|
8524 } |
|
8525 if (found == hadFound) { // found nothing new |
|
8526 break; |
|
8527 } |
|
8528 } |
|
8529 if (0 < found) { |
|
8530 if (1 == found) { // only first property |
|
8531 if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit |
|
8532 for (loop = 1; loop < aNumIDs; loop++) { |
|
8533 aValues[loop].SetInheritValue(); |
|
8534 } |
|
8535 found = ((1 << aNumIDs) - 1); |
|
8536 } |
|
8537 else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial |
|
8538 for (loop = 1; loop < aNumIDs; loop++) { |
|
8539 aValues[loop].SetInitialValue(); |
|
8540 } |
|
8541 found = ((1 << aNumIDs) - 1); |
|
8542 } |
|
8543 else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset |
|
8544 for (loop = 1; loop < aNumIDs; loop++) { |
|
8545 aValues[loop].SetUnsetValue(); |
|
8546 } |
|
8547 found = ((1 << aNumIDs) - 1); |
|
8548 } |
|
8549 } |
|
8550 else { // more than one value, verify no inherits, initials or unsets |
|
8551 for (loop = 0; loop < aNumIDs; loop++) { |
|
8552 if (eCSSUnit_Inherit == aValues[loop].GetUnit()) { |
|
8553 found = -1; |
|
8554 break; |
|
8555 } |
|
8556 else if (eCSSUnit_Initial == aValues[loop].GetUnit()) { |
|
8557 found = -1; |
|
8558 break; |
|
8559 } |
|
8560 else if (eCSSUnit_Unset == aValues[loop].GetUnit()) { |
|
8561 found = -1; |
|
8562 break; |
|
8563 } |
|
8564 } |
|
8565 } |
|
8566 } |
|
8567 return found; |
|
8568 } |
|
8569 |
|
8570 void |
|
8571 CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue) |
|
8572 { |
|
8573 mTempData.AddLonghandProperty(aPropID, aValue); |
|
8574 } |
|
8575 |
|
8576 /** |
|
8577 * Parse a "box" property. Box properties have 1 to 4 values. When less |
|
8578 * than 4 values are provided a standard mapping is used to replicate |
|
8579 * existing values. |
|
8580 */ |
|
8581 bool |
|
8582 CSSParserImpl::ParseBoxProperties(const nsCSSProperty aPropIDs[]) |
|
8583 { |
|
8584 // Get up to four values for the property |
|
8585 int32_t count = 0; |
|
8586 nsCSSRect result; |
|
8587 NS_FOR_CSS_SIDES (index) { |
|
8588 if (! ParseSingleValueProperty(result.*(nsCSSRect::sides[index]), |
|
8589 aPropIDs[index])) { |
|
8590 break; |
|
8591 } |
|
8592 count++; |
|
8593 } |
|
8594 if (count == 0) { |
|
8595 return false; |
|
8596 } |
|
8597 |
|
8598 if (1 < count) { // verify no more than single inherit, initial or unset |
|
8599 NS_FOR_CSS_SIDES (index) { |
|
8600 nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit(); |
|
8601 if (eCSSUnit_Inherit == unit || |
|
8602 eCSSUnit_Initial == unit || |
|
8603 eCSSUnit_Unset == unit) { |
|
8604 return false; |
|
8605 } |
|
8606 } |
|
8607 } |
|
8608 |
|
8609 // Provide missing values by replicating some of the values found |
|
8610 switch (count) { |
|
8611 case 1: // Make right == top |
|
8612 result.mRight = result.mTop; |
|
8613 case 2: // Make bottom == top |
|
8614 result.mBottom = result.mTop; |
|
8615 case 3: // Make left == right |
|
8616 result.mLeft = result.mRight; |
|
8617 } |
|
8618 |
|
8619 NS_FOR_CSS_SIDES (index) { |
|
8620 AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index])); |
|
8621 } |
|
8622 return true; |
|
8623 } |
|
8624 |
|
8625 // Similar to ParseBoxProperties, except there is only one property |
|
8626 // with the result as its value, not four. Requires values be nonnegative. |
|
8627 bool |
|
8628 CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask, |
|
8629 /** outparam */ nsCSSValue& aValue) |
|
8630 { |
|
8631 nsCSSRect& result = aValue.SetRectValue(); |
|
8632 |
|
8633 int32_t count = 0; |
|
8634 NS_FOR_CSS_SIDES (index) { |
|
8635 if (!ParseNonNegativeVariant(result.*(nsCSSRect::sides[index]), |
|
8636 aVariantMask, nullptr)) { |
|
8637 break; |
|
8638 } |
|
8639 count++; |
|
8640 } |
|
8641 |
|
8642 if (count == 0) { |
|
8643 return false; |
|
8644 } |
|
8645 |
|
8646 // Provide missing values by replicating some of the values found |
|
8647 switch (count) { |
|
8648 case 1: // Make right == top |
|
8649 result.mRight = result.mTop; |
|
8650 case 2: // Make bottom == top |
|
8651 result.mBottom = result.mTop; |
|
8652 case 3: // Make left == right |
|
8653 result.mLeft = result.mRight; |
|
8654 } |
|
8655 |
|
8656 return true; |
|
8657 } |
|
8658 |
|
8659 bool |
|
8660 CSSParserImpl::ParseDirectionalBoxProperty(nsCSSProperty aProperty, |
|
8661 int32_t aSourceType) |
|
8662 { |
|
8663 const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty); |
|
8664 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN, |
|
8665 "not box property with physical vs. logical cascading"); |
|
8666 nsCSSValue value; |
|
8667 if (!ParseSingleValueProperty(value, subprops[0])) { |
|
8668 return false; |
|
8669 } |
|
8670 |
|
8671 AppendValue(subprops[0], value); |
|
8672 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated); |
|
8673 AppendValue(subprops[1], typeVal); |
|
8674 AppendValue(subprops[2], typeVal); |
|
8675 return true; |
|
8676 } |
|
8677 |
|
8678 bool |
|
8679 CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID) |
|
8680 { |
|
8681 nsCSSValue dimenX, dimenY; |
|
8682 // required first value |
|
8683 if (! ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr)) |
|
8684 return false; |
|
8685 |
|
8686 // optional second value (forbidden if first value is inherit/initial/unset) |
|
8687 if (dimenX.GetUnit() != eCSSUnit_Inherit && |
|
8688 dimenX.GetUnit() != eCSSUnit_Initial && |
|
8689 dimenX.GetUnit() != eCSSUnit_Unset) { |
|
8690 ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr); |
|
8691 } |
|
8692 |
|
8693 if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) { |
|
8694 AppendValue(aPropID, dimenX); |
|
8695 } else { |
|
8696 nsCSSValue value; |
|
8697 value.SetPairValue(dimenX, dimenY); |
|
8698 AppendValue(aPropID, value); |
|
8699 } |
|
8700 return true; |
|
8701 } |
|
8702 |
|
8703 bool |
|
8704 CSSParserImpl::ParseBoxCornerRadii(const nsCSSProperty aPropIDs[]) |
|
8705 { |
|
8706 // Rectangles are used as scratch storage. |
|
8707 // top => top-left, right => top-right, |
|
8708 // bottom => bottom-right, left => bottom-left. |
|
8709 nsCSSRect dimenX, dimenY; |
|
8710 int32_t countX = 0, countY = 0; |
|
8711 |
|
8712 NS_FOR_CSS_SIDES (side) { |
|
8713 if (! ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side], |
|
8714 (side > 0 ? 0 : VARIANT_INHERIT) | |
|
8715 VARIANT_LP | VARIANT_CALC, |
|
8716 nullptr)) |
|
8717 break; |
|
8718 countX++; |
|
8719 } |
|
8720 if (countX == 0) |
|
8721 return false; |
|
8722 |
|
8723 if (ExpectSymbol('/', true)) { |
|
8724 NS_FOR_CSS_SIDES (side) { |
|
8725 if (! ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side], |
|
8726 VARIANT_LP | VARIANT_CALC, nullptr)) |
|
8727 break; |
|
8728 countY++; |
|
8729 } |
|
8730 if (countY == 0) |
|
8731 return false; |
|
8732 } |
|
8733 |
|
8734 // if 'initial', 'inherit' or 'unset' was used, it must be the only value |
|
8735 if (countX > 1 || countY > 0) { |
|
8736 nsCSSUnit unit = dimenX.mTop.GetUnit(); |
|
8737 if (eCSSUnit_Inherit == unit || |
|
8738 eCSSUnit_Initial == unit || |
|
8739 eCSSUnit_Unset == unit) |
|
8740 return false; |
|
8741 } |
|
8742 |
|
8743 // if we have no Y-values, use the X-values |
|
8744 if (countY == 0) { |
|
8745 dimenY = dimenX; |
|
8746 countY = countX; |
|
8747 } |
|
8748 |
|
8749 // Provide missing values by replicating some of the values found |
|
8750 switch (countX) { |
|
8751 case 1: dimenX.mRight = dimenX.mTop; // top-right same as top-left, and |
|
8752 case 2: dimenX.mBottom = dimenX.mTop; // bottom-right same as top-left, and |
|
8753 case 3: dimenX.mLeft = dimenX.mRight; // bottom-left same as top-right |
|
8754 } |
|
8755 |
|
8756 switch (countY) { |
|
8757 case 1: dimenY.mRight = dimenY.mTop; // top-right same as top-left, and |
|
8758 case 2: dimenY.mBottom = dimenY.mTop; // bottom-right same as top-left, and |
|
8759 case 3: dimenY.mLeft = dimenY.mRight; // bottom-left same as top-right |
|
8760 } |
|
8761 |
|
8762 NS_FOR_CSS_SIDES(side) { |
|
8763 nsCSSValue& x = dimenX.*nsCSSRect::sides[side]; |
|
8764 nsCSSValue& y = dimenY.*nsCSSRect::sides[side]; |
|
8765 |
|
8766 if (x == y) { |
|
8767 AppendValue(aPropIDs[side], x); |
|
8768 } else { |
|
8769 nsCSSValue pair; |
|
8770 pair.SetPairValue(x, y); |
|
8771 AppendValue(aPropIDs[side], pair); |
|
8772 } |
|
8773 } |
|
8774 return true; |
|
8775 } |
|
8776 |
|
8777 // These must be in CSS order (top,right,bottom,left) for indexing to work |
|
8778 static const nsCSSProperty kBorderStyleIDs[] = { |
|
8779 eCSSProperty_border_top_style, |
|
8780 eCSSProperty_border_right_style_value, |
|
8781 eCSSProperty_border_bottom_style, |
|
8782 eCSSProperty_border_left_style_value |
|
8783 }; |
|
8784 static const nsCSSProperty kBorderWidthIDs[] = { |
|
8785 eCSSProperty_border_top_width, |
|
8786 eCSSProperty_border_right_width_value, |
|
8787 eCSSProperty_border_bottom_width, |
|
8788 eCSSProperty_border_left_width_value |
|
8789 }; |
|
8790 static const nsCSSProperty kBorderColorIDs[] = { |
|
8791 eCSSProperty_border_top_color, |
|
8792 eCSSProperty_border_right_color_value, |
|
8793 eCSSProperty_border_bottom_color, |
|
8794 eCSSProperty_border_left_color_value |
|
8795 }; |
|
8796 static const nsCSSProperty kBorderRadiusIDs[] = { |
|
8797 eCSSProperty_border_top_left_radius, |
|
8798 eCSSProperty_border_top_right_radius, |
|
8799 eCSSProperty_border_bottom_right_radius, |
|
8800 eCSSProperty_border_bottom_left_radius |
|
8801 }; |
|
8802 static const nsCSSProperty kOutlineRadiusIDs[] = { |
|
8803 eCSSProperty__moz_outline_radius_topLeft, |
|
8804 eCSSProperty__moz_outline_radius_topRight, |
|
8805 eCSSProperty__moz_outline_radius_bottomRight, |
|
8806 eCSSProperty__moz_outline_radius_bottomLeft |
|
8807 }; |
|
8808 |
|
8809 void |
|
8810 CSSParserImpl::SaveInputState(CSSParserInputState& aState) |
|
8811 { |
|
8812 aState.mToken = mToken; |
|
8813 aState.mHavePushBack = mHavePushBack; |
|
8814 mScanner->SavePosition(aState.mPosition); |
|
8815 } |
|
8816 |
|
8817 void |
|
8818 CSSParserImpl::RestoreSavedInputState(const CSSParserInputState& aState) |
|
8819 { |
|
8820 mToken = aState.mToken; |
|
8821 mHavePushBack = aState.mHavePushBack; |
|
8822 mScanner->RestoreSavedPosition(aState.mPosition); |
|
8823 } |
|
8824 |
|
8825 bool |
|
8826 CSSParserImpl::ParseProperty(nsCSSProperty aPropID) |
|
8827 { |
|
8828 // Can't use AutoRestore<bool> because it's a bitfield. |
|
8829 NS_ABORT_IF_FALSE(!mHashlessColorQuirk, |
|
8830 "hashless color quirk should not be set"); |
|
8831 NS_ABORT_IF_FALSE(!mUnitlessLengthQuirk, |
|
8832 "unitless length quirk should not be set"); |
|
8833 |
|
8834 if (mNavQuirkMode) { |
|
8835 mHashlessColorQuirk = |
|
8836 nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_HASHLESS_COLOR_QUIRK); |
|
8837 mUnitlessLengthQuirk = |
|
8838 nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_UNITLESS_LENGTH_QUIRK); |
|
8839 } |
|
8840 |
|
8841 // Save the current input state so that we can restore it later if we |
|
8842 // have to re-parse the property value as a variable-reference-containing |
|
8843 // token stream. |
|
8844 CSSParserInputState stateBeforeProperty; |
|
8845 SaveInputState(stateBeforeProperty); |
|
8846 mScanner->ClearSeenVariableReference(); |
|
8847 |
|
8848 NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range"); |
|
8849 bool allowVariables = true; |
|
8850 bool result; |
|
8851 switch (nsCSSProps::PropertyParseType(aPropID)) { |
|
8852 case CSS_PROPERTY_PARSE_INACCESSIBLE: { |
|
8853 // The user can't use these |
|
8854 REPORT_UNEXPECTED(PEInaccessibleProperty2); |
|
8855 allowVariables = false; |
|
8856 result = false; |
|
8857 break; |
|
8858 } |
|
8859 case CSS_PROPERTY_PARSE_FUNCTION: { |
|
8860 result = ParsePropertyByFunction(aPropID); |
|
8861 break; |
|
8862 } |
|
8863 case CSS_PROPERTY_PARSE_VALUE: { |
|
8864 result = false; |
|
8865 nsCSSValue value; |
|
8866 if (ParseSingleValueProperty(value, aPropID)) { |
|
8867 AppendValue(aPropID, value); |
|
8868 result = true; |
|
8869 } |
|
8870 // XXX Report errors? |
|
8871 break; |
|
8872 } |
|
8873 case CSS_PROPERTY_PARSE_VALUE_LIST: { |
|
8874 result = ParseValueList(aPropID); |
|
8875 break; |
|
8876 } |
|
8877 default: { |
|
8878 result = false; |
|
8879 allowVariables = false; |
|
8880 NS_ABORT_IF_FALSE(false, |
|
8881 "Property's flags field in nsCSSPropList.h is missing " |
|
8882 "one of the CSS_PROPERTY_PARSE_* constants"); |
|
8883 break; |
|
8884 } |
|
8885 } |
|
8886 |
|
8887 if (result) { |
|
8888 // We need to call ExpectEndProperty() to decide whether to reparse |
|
8889 // with variables. This is needed because the property parsing may |
|
8890 // have stopped upon finding a variable (e.g., 'margin: 1px var(a)') |
|
8891 // in a way that future variable substitutions will be valid, or |
|
8892 // because it parsed everything that's possible but we still want to |
|
8893 // act as though the property contains variables even though we know |
|
8894 // the substitution will never work (e.g., for 'margin: 1px 2px 3px |
|
8895 // 4px 5px var(a)'). |
|
8896 // |
|
8897 // It would be nice to find a better solution here |
|
8898 // (and for the SkipUntilOneOf below), though, that doesn't depend |
|
8899 // on using what we don't accept for doing parsing correctly. |
|
8900 if (!ExpectEndProperty()) { |
|
8901 result = false; |
|
8902 } |
|
8903 } |
|
8904 |
|
8905 bool seenVariable = mScanner->SeenVariableReference() || |
|
8906 (stateBeforeProperty.mHavePushBack && |
|
8907 stateBeforeProperty.mToken.mType == eCSSToken_Function && |
|
8908 stateBeforeProperty.mToken.mIdent.LowerCaseEqualsLiteral("var")); |
|
8909 bool parseAsTokenStream; |
|
8910 |
|
8911 if (!result && allowVariables) { |
|
8912 parseAsTokenStream = true; |
|
8913 if (!seenVariable) { |
|
8914 // We might have stopped parsing the property before its end and before |
|
8915 // finding a variable reference. Keep checking until the end of the |
|
8916 // property. |
|
8917 CSSParserInputState stateAtError; |
|
8918 SaveInputState(stateAtError); |
|
8919 |
|
8920 const char16_t stopChars[] = { ';', '!', '}', ')', 0 }; |
|
8921 SkipUntilOneOf(stopChars); |
|
8922 UngetToken(); |
|
8923 parseAsTokenStream = mScanner->SeenVariableReference(); |
|
8924 |
|
8925 if (!parseAsTokenStream) { |
|
8926 // If we parsed to the end of the propery and didn't find any variable |
|
8927 // references, then the real position we want to report the error at |
|
8928 // is |stateAtError|. |
|
8929 RestoreSavedInputState(stateAtError); |
|
8930 } |
|
8931 } |
|
8932 } else { |
|
8933 parseAsTokenStream = false; |
|
8934 } |
|
8935 |
|
8936 if (parseAsTokenStream) { |
|
8937 // Go back to the start of the property value and parse it to make sure |
|
8938 // its variable references are syntactically valid and is otherwise |
|
8939 // balanced. |
|
8940 RestoreSavedInputState(stateBeforeProperty); |
|
8941 |
|
8942 if (!mInSupportsCondition) { |
|
8943 mScanner->StartRecording(); |
|
8944 } |
|
8945 |
|
8946 CSSVariableDeclarations::Type type; |
|
8947 bool dropBackslash; |
|
8948 nsString impliedCharacters; |
|
8949 nsCSSValue value; |
|
8950 if (ParseValueWithVariables(&type, &dropBackslash, impliedCharacters, |
|
8951 nullptr, nullptr)) { |
|
8952 MOZ_ASSERT(type == CSSVariableDeclarations::eTokenStream, |
|
8953 "a non-custom property reparsed since it contained variable " |
|
8954 "references should not have been 'initial' or 'inherit'"); |
|
8955 |
|
8956 nsString propertyValue; |
|
8957 |
|
8958 if (!mInSupportsCondition) { |
|
8959 // If we are in an @supports condition, we don't need to store the |
|
8960 // actual token stream on the nsCSSValue. |
|
8961 mScanner->StopRecording(propertyValue); |
|
8962 if (dropBackslash) { |
|
8963 MOZ_ASSERT(!propertyValue.IsEmpty() && |
|
8964 propertyValue[propertyValue.Length() - 1] == '\\'); |
|
8965 propertyValue.Truncate(propertyValue.Length() - 1); |
|
8966 } |
|
8967 propertyValue.Append(impliedCharacters); |
|
8968 } |
|
8969 |
|
8970 if (mHavePushBack) { |
|
8971 // If we came to the end of a property value that had a variable |
|
8972 // reference and a token was pushed back, then it would have been |
|
8973 // ended by '!', ')', ';', ']' or '}'. We should remove it from the |
|
8974 // recorded property value. |
|
8975 MOZ_ASSERT(mToken.IsSymbol('!') || |
|
8976 mToken.IsSymbol(')') || |
|
8977 mToken.IsSymbol(';') || |
|
8978 mToken.IsSymbol(']') || |
|
8979 mToken.IsSymbol('}')); |
|
8980 if (!mInSupportsCondition) { |
|
8981 MOZ_ASSERT(!propertyValue.IsEmpty()); |
|
8982 MOZ_ASSERT(propertyValue[propertyValue.Length() - 1] == |
|
8983 mToken.mSymbol); |
|
8984 propertyValue.Truncate(propertyValue.Length() - 1); |
|
8985 } |
|
8986 } |
|
8987 |
|
8988 if (!mInSupportsCondition) { |
|
8989 if (nsCSSProps::IsShorthand(aPropID)) { |
|
8990 // If this is a shorthand property, we store the token stream on each |
|
8991 // of its corresponding longhand properties. |
|
8992 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) { |
|
8993 nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream; |
|
8994 tokenStream->mPropertyID = *p; |
|
8995 tokenStream->mShorthandPropertyID = aPropID; |
|
8996 tokenStream->mTokenStream = propertyValue; |
|
8997 tokenStream->mBaseURI = mBaseURI; |
|
8998 tokenStream->mSheetURI = mSheetURI; |
|
8999 tokenStream->mSheetPrincipal = mSheetPrincipal; |
|
9000 tokenStream->mSheet = mSheet; |
|
9001 tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber(); |
|
9002 tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset(); |
|
9003 value.SetTokenStreamValue(tokenStream); |
|
9004 AppendValue(*p, value); |
|
9005 } |
|
9006 } else { |
|
9007 nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream; |
|
9008 tokenStream->mPropertyID = aPropID; |
|
9009 tokenStream->mTokenStream = propertyValue; |
|
9010 tokenStream->mBaseURI = mBaseURI; |
|
9011 tokenStream->mSheetURI = mSheetURI; |
|
9012 tokenStream->mSheetPrincipal = mSheetPrincipal; |
|
9013 tokenStream->mSheet = mSheet; |
|
9014 tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber(); |
|
9015 tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset(); |
|
9016 value.SetTokenStreamValue(tokenStream); |
|
9017 AppendValue(aPropID, value); |
|
9018 } |
|
9019 } |
|
9020 result = true; |
|
9021 } else { |
|
9022 if (!mInSupportsCondition) { |
|
9023 mScanner->StopRecording(); |
|
9024 } |
|
9025 } |
|
9026 } |
|
9027 |
|
9028 if (mNavQuirkMode) { |
|
9029 mHashlessColorQuirk = false; |
|
9030 mUnitlessLengthQuirk = false; |
|
9031 } |
|
9032 |
|
9033 return result; |
|
9034 } |
|
9035 |
|
9036 bool |
|
9037 CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID) |
|
9038 { |
|
9039 switch (aPropID) { // handle shorthand or multiple properties |
|
9040 case eCSSProperty_background: |
|
9041 return ParseBackground(); |
|
9042 case eCSSProperty_background_repeat: |
|
9043 return ParseBackgroundRepeat(); |
|
9044 case eCSSProperty_background_position: |
|
9045 return ParseBackgroundPosition(); |
|
9046 case eCSSProperty_background_size: |
|
9047 return ParseBackgroundSize(); |
|
9048 case eCSSProperty_border: |
|
9049 return ParseBorderSide(kBorderTopIDs, true); |
|
9050 case eCSSProperty_border_color: |
|
9051 return ParseBorderColor(); |
|
9052 case eCSSProperty_border_spacing: |
|
9053 return ParseBorderSpacing(); |
|
9054 case eCSSProperty_border_style: |
|
9055 return ParseBorderStyle(); |
|
9056 case eCSSProperty_border_bottom: |
|
9057 return ParseBorderSide(kBorderBottomIDs, false); |
|
9058 case eCSSProperty_border_end: |
|
9059 return ParseDirectionalBorderSide(kBorderEndIDs, |
|
9060 NS_BOXPROP_SOURCE_LOGICAL); |
|
9061 case eCSSProperty_border_left: |
|
9062 return ParseDirectionalBorderSide(kBorderLeftIDs, |
|
9063 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9064 case eCSSProperty_border_right: |
|
9065 return ParseDirectionalBorderSide(kBorderRightIDs, |
|
9066 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9067 case eCSSProperty_border_start: |
|
9068 return ParseDirectionalBorderSide(kBorderStartIDs, |
|
9069 NS_BOXPROP_SOURCE_LOGICAL); |
|
9070 case eCSSProperty_border_top: |
|
9071 return ParseBorderSide(kBorderTopIDs, false); |
|
9072 case eCSSProperty_border_bottom_colors: |
|
9073 case eCSSProperty_border_left_colors: |
|
9074 case eCSSProperty_border_right_colors: |
|
9075 case eCSSProperty_border_top_colors: |
|
9076 return ParseBorderColors(aPropID); |
|
9077 case eCSSProperty_border_image_slice: |
|
9078 return ParseBorderImageSlice(true, nullptr); |
|
9079 case eCSSProperty_border_image_width: |
|
9080 return ParseBorderImageWidth(true); |
|
9081 case eCSSProperty_border_image_outset: |
|
9082 return ParseBorderImageOutset(true); |
|
9083 case eCSSProperty_border_image_repeat: |
|
9084 return ParseBorderImageRepeat(true); |
|
9085 case eCSSProperty_border_image: |
|
9086 return ParseBorderImage(); |
|
9087 case eCSSProperty_border_width: |
|
9088 return ParseBorderWidth(); |
|
9089 case eCSSProperty_border_end_color: |
|
9090 return ParseDirectionalBoxProperty(eCSSProperty_border_end_color, |
|
9091 NS_BOXPROP_SOURCE_LOGICAL); |
|
9092 case eCSSProperty_border_left_color: |
|
9093 return ParseDirectionalBoxProperty(eCSSProperty_border_left_color, |
|
9094 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9095 case eCSSProperty_border_right_color: |
|
9096 return ParseDirectionalBoxProperty(eCSSProperty_border_right_color, |
|
9097 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9098 case eCSSProperty_border_start_color: |
|
9099 return ParseDirectionalBoxProperty(eCSSProperty_border_start_color, |
|
9100 NS_BOXPROP_SOURCE_LOGICAL); |
|
9101 case eCSSProperty_border_end_width: |
|
9102 return ParseDirectionalBoxProperty(eCSSProperty_border_end_width, |
|
9103 NS_BOXPROP_SOURCE_LOGICAL); |
|
9104 case eCSSProperty_border_left_width: |
|
9105 return ParseDirectionalBoxProperty(eCSSProperty_border_left_width, |
|
9106 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9107 case eCSSProperty_border_right_width: |
|
9108 return ParseDirectionalBoxProperty(eCSSProperty_border_right_width, |
|
9109 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9110 case eCSSProperty_border_start_width: |
|
9111 return ParseDirectionalBoxProperty(eCSSProperty_border_start_width, |
|
9112 NS_BOXPROP_SOURCE_LOGICAL); |
|
9113 case eCSSProperty_border_end_style: |
|
9114 return ParseDirectionalBoxProperty(eCSSProperty_border_end_style, |
|
9115 NS_BOXPROP_SOURCE_LOGICAL); |
|
9116 case eCSSProperty_border_left_style: |
|
9117 return ParseDirectionalBoxProperty(eCSSProperty_border_left_style, |
|
9118 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9119 case eCSSProperty_border_right_style: |
|
9120 return ParseDirectionalBoxProperty(eCSSProperty_border_right_style, |
|
9121 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9122 case eCSSProperty_border_start_style: |
|
9123 return ParseDirectionalBoxProperty(eCSSProperty_border_start_style, |
|
9124 NS_BOXPROP_SOURCE_LOGICAL); |
|
9125 case eCSSProperty_border_radius: |
|
9126 return ParseBoxCornerRadii(kBorderRadiusIDs); |
|
9127 case eCSSProperty__moz_outline_radius: |
|
9128 return ParseBoxCornerRadii(kOutlineRadiusIDs); |
|
9129 |
|
9130 case eCSSProperty_border_top_left_radius: |
|
9131 case eCSSProperty_border_top_right_radius: |
|
9132 case eCSSProperty_border_bottom_right_radius: |
|
9133 case eCSSProperty_border_bottom_left_radius: |
|
9134 case eCSSProperty__moz_outline_radius_topLeft: |
|
9135 case eCSSProperty__moz_outline_radius_topRight: |
|
9136 case eCSSProperty__moz_outline_radius_bottomRight: |
|
9137 case eCSSProperty__moz_outline_radius_bottomLeft: |
|
9138 return ParseBoxCornerRadius(aPropID); |
|
9139 |
|
9140 case eCSSProperty_box_shadow: |
|
9141 case eCSSProperty_text_shadow: |
|
9142 return ParseShadowList(aPropID); |
|
9143 |
|
9144 case eCSSProperty_clip: |
|
9145 return ParseRect(eCSSProperty_clip); |
|
9146 case eCSSProperty__moz_columns: |
|
9147 return ParseColumns(); |
|
9148 case eCSSProperty__moz_column_rule: |
|
9149 return ParseBorderSide(kColumnRuleIDs, false); |
|
9150 case eCSSProperty_content: |
|
9151 return ParseContent(); |
|
9152 case eCSSProperty_counter_increment: |
|
9153 case eCSSProperty_counter_reset: |
|
9154 return ParseCounterData(aPropID); |
|
9155 case eCSSProperty_cursor: |
|
9156 return ParseCursor(); |
|
9157 case eCSSProperty_filter: |
|
9158 return ParseFilter(); |
|
9159 case eCSSProperty_flex: |
|
9160 return ParseFlex(); |
|
9161 case eCSSProperty_flex_flow: |
|
9162 return ParseFlexFlow(); |
|
9163 case eCSSProperty_font: |
|
9164 return ParseFont(); |
|
9165 case eCSSProperty_grid_auto_flow: |
|
9166 return ParseGridAutoFlow(); |
|
9167 case eCSSProperty_grid_auto_columns: |
|
9168 case eCSSProperty_grid_auto_rows: |
|
9169 return ParseGridAutoColumnsRows(aPropID); |
|
9170 case eCSSProperty_grid_template_areas: |
|
9171 return ParseGridTemplateAreas(); |
|
9172 case eCSSProperty_grid_template_columns: |
|
9173 case eCSSProperty_grid_template_rows: |
|
9174 return ParseGridTemplateColumnsRows(aPropID); |
|
9175 case eCSSProperty_grid_template: |
|
9176 return ParseGridTemplate(); |
|
9177 case eCSSProperty_grid: |
|
9178 return ParseGrid(); |
|
9179 case eCSSProperty_grid_auto_position: |
|
9180 return ParseGridAutoPosition(); |
|
9181 case eCSSProperty_grid_column_start: |
|
9182 case eCSSProperty_grid_column_end: |
|
9183 case eCSSProperty_grid_row_start: |
|
9184 case eCSSProperty_grid_row_end: |
|
9185 return ParseGridColumnRowStartEnd(aPropID); |
|
9186 case eCSSProperty_grid_column: |
|
9187 return ParseGridColumnRow(eCSSProperty_grid_column_start, |
|
9188 eCSSProperty_grid_column_end); |
|
9189 case eCSSProperty_grid_row: |
|
9190 return ParseGridColumnRow(eCSSProperty_grid_row_start, |
|
9191 eCSSProperty_grid_row_end); |
|
9192 case eCSSProperty_grid_area: |
|
9193 return ParseGridArea(); |
|
9194 case eCSSProperty_image_region: |
|
9195 return ParseRect(eCSSProperty_image_region); |
|
9196 case eCSSProperty_list_style: |
|
9197 return ParseListStyle(); |
|
9198 case eCSSProperty_margin: |
|
9199 return ParseMargin(); |
|
9200 case eCSSProperty_margin_end: |
|
9201 return ParseDirectionalBoxProperty(eCSSProperty_margin_end, |
|
9202 NS_BOXPROP_SOURCE_LOGICAL); |
|
9203 case eCSSProperty_margin_left: |
|
9204 return ParseDirectionalBoxProperty(eCSSProperty_margin_left, |
|
9205 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9206 case eCSSProperty_margin_right: |
|
9207 return ParseDirectionalBoxProperty(eCSSProperty_margin_right, |
|
9208 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9209 case eCSSProperty_margin_start: |
|
9210 return ParseDirectionalBoxProperty(eCSSProperty_margin_start, |
|
9211 NS_BOXPROP_SOURCE_LOGICAL); |
|
9212 case eCSSProperty_outline: |
|
9213 return ParseOutline(); |
|
9214 case eCSSProperty_overflow: |
|
9215 return ParseOverflow(); |
|
9216 case eCSSProperty_padding: |
|
9217 return ParsePadding(); |
|
9218 case eCSSProperty_padding_end: |
|
9219 return ParseDirectionalBoxProperty(eCSSProperty_padding_end, |
|
9220 NS_BOXPROP_SOURCE_LOGICAL); |
|
9221 case eCSSProperty_padding_left: |
|
9222 return ParseDirectionalBoxProperty(eCSSProperty_padding_left, |
|
9223 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9224 case eCSSProperty_padding_right: |
|
9225 return ParseDirectionalBoxProperty(eCSSProperty_padding_right, |
|
9226 NS_BOXPROP_SOURCE_PHYSICAL); |
|
9227 case eCSSProperty_padding_start: |
|
9228 return ParseDirectionalBoxProperty(eCSSProperty_padding_start, |
|
9229 NS_BOXPROP_SOURCE_LOGICAL); |
|
9230 case eCSSProperty_quotes: |
|
9231 return ParseQuotes(); |
|
9232 case eCSSProperty_size: |
|
9233 return ParseSize(); |
|
9234 case eCSSProperty_text_decoration: |
|
9235 return ParseTextDecoration(); |
|
9236 case eCSSProperty_will_change: |
|
9237 return ParseWillChange(); |
|
9238 case eCSSProperty_transform: |
|
9239 return ParseTransform(false); |
|
9240 case eCSSProperty__moz_transform: |
|
9241 return ParseTransform(true); |
|
9242 case eCSSProperty_transform_origin: |
|
9243 return ParseTransformOrigin(false); |
|
9244 case eCSSProperty_perspective_origin: |
|
9245 return ParseTransformOrigin(true); |
|
9246 case eCSSProperty_transition: |
|
9247 return ParseTransition(); |
|
9248 case eCSSProperty_animation: |
|
9249 return ParseAnimation(); |
|
9250 case eCSSProperty_transition_property: |
|
9251 return ParseTransitionProperty(); |
|
9252 case eCSSProperty_fill: |
|
9253 case eCSSProperty_stroke: |
|
9254 return ParsePaint(aPropID); |
|
9255 case eCSSProperty_stroke_dasharray: |
|
9256 return ParseDasharray(); |
|
9257 case eCSSProperty_marker: |
|
9258 return ParseMarker(); |
|
9259 case eCSSProperty_paint_order: |
|
9260 return ParsePaintOrder(); |
|
9261 case eCSSProperty_all: |
|
9262 return ParseAll(); |
|
9263 default: |
|
9264 NS_ABORT_IF_FALSE(false, "should not be called"); |
|
9265 return false; |
|
9266 } |
|
9267 } |
|
9268 |
|
9269 // Bits used in determining which background position info we have |
|
9270 #define BG_CENTER NS_STYLE_BG_POSITION_CENTER |
|
9271 #define BG_TOP NS_STYLE_BG_POSITION_TOP |
|
9272 #define BG_BOTTOM NS_STYLE_BG_POSITION_BOTTOM |
|
9273 #define BG_LEFT NS_STYLE_BG_POSITION_LEFT |
|
9274 #define BG_RIGHT NS_STYLE_BG_POSITION_RIGHT |
|
9275 #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM) |
|
9276 #define BG_TB (BG_TOP | BG_BOTTOM) |
|
9277 #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT) |
|
9278 #define BG_LR (BG_LEFT | BG_RIGHT) |
|
9279 |
|
9280 bool |
|
9281 CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue, |
|
9282 nsCSSProperty aPropID) |
|
9283 { |
|
9284 if (aPropID == eCSSPropertyExtra_x_none_value) { |
|
9285 return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nullptr); |
|
9286 } |
|
9287 |
|
9288 if (aPropID == eCSSPropertyExtra_x_auto_value) { |
|
9289 return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nullptr); |
|
9290 } |
|
9291 |
|
9292 if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) { |
|
9293 NS_ABORT_IF_FALSE(false, "not a single value property"); |
|
9294 return false; |
|
9295 } |
|
9296 |
|
9297 if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) { |
|
9298 switch (aPropID) { |
|
9299 case eCSSProperty_font_family: |
|
9300 return ParseFamily(aValue); |
|
9301 case eCSSProperty_font_synthesis: |
|
9302 return ParseFontSynthesis(aValue); |
|
9303 case eCSSProperty_font_variant_alternates: |
|
9304 return ParseFontVariantAlternates(aValue); |
|
9305 case eCSSProperty_font_variant_east_asian: |
|
9306 return ParseFontVariantEastAsian(aValue); |
|
9307 case eCSSProperty_font_variant_ligatures: |
|
9308 return ParseFontVariantLigatures(aValue); |
|
9309 case eCSSProperty_font_variant_numeric: |
|
9310 return ParseFontVariantNumeric(aValue); |
|
9311 case eCSSProperty_font_feature_settings: |
|
9312 return ParseFontFeatureSettings(aValue); |
|
9313 case eCSSProperty_font_weight: |
|
9314 return ParseFontWeight(aValue); |
|
9315 case eCSSProperty_image_orientation: |
|
9316 return ParseImageOrientation(aValue); |
|
9317 case eCSSProperty_marks: |
|
9318 return ParseMarks(aValue); |
|
9319 case eCSSProperty_text_align: |
|
9320 return ParseTextAlign(aValue); |
|
9321 case eCSSProperty_text_align_last: |
|
9322 return ParseTextAlignLast(aValue); |
|
9323 case eCSSProperty_text_decoration_line: |
|
9324 return ParseTextDecorationLine(aValue); |
|
9325 case eCSSProperty_text_combine_upright: |
|
9326 return ParseTextCombineUpright(aValue); |
|
9327 case eCSSProperty_text_overflow: |
|
9328 return ParseTextOverflow(aValue); |
|
9329 case eCSSProperty_touch_action: |
|
9330 return ParseTouchAction(aValue); |
|
9331 default: |
|
9332 NS_ABORT_IF_FALSE(false, "should not reach here"); |
|
9333 return false; |
|
9334 } |
|
9335 } |
|
9336 |
|
9337 uint32_t variant = nsCSSProps::ParserVariant(aPropID); |
|
9338 if (variant == 0) { |
|
9339 NS_ABORT_IF_FALSE(false, "not a single value property"); |
|
9340 return false; |
|
9341 } |
|
9342 |
|
9343 // We only allow 'script-level' when unsafe rules are enabled, because |
|
9344 // otherwise it could interfere with rulenode optimizations if used in |
|
9345 // a non-MathML-enabled document. We also only allow math-display when |
|
9346 // unsafe rules are enabled. |
|
9347 if (!mUnsafeRulesEnabled && |
|
9348 (aPropID == eCSSProperty_script_level || |
|
9349 aPropID == eCSSProperty_math_display)) |
|
9350 return false; |
|
9351 |
|
9352 const KTableValue *kwtable = nsCSSProps::kKeywordTableTable[aPropID]; |
|
9353 switch (nsCSSProps::ValueRestrictions(aPropID)) { |
|
9354 default: |
|
9355 NS_ABORT_IF_FALSE(false, "should not be reached"); |
|
9356 case 0: |
|
9357 return ParseVariant(aValue, variant, kwtable); |
|
9358 case CSS_PROPERTY_VALUE_NONNEGATIVE: |
|
9359 return ParseNonNegativeVariant(aValue, variant, kwtable); |
|
9360 case CSS_PROPERTY_VALUE_AT_LEAST_ONE: |
|
9361 return ParseOneOrLargerVariant(aValue, variant, kwtable); |
|
9362 } |
|
9363 } |
|
9364 |
|
9365 // nsFont::EnumerateFamilies callback for ParseFontDescriptorValue |
|
9366 struct MOZ_STACK_CLASS ExtractFirstFamilyData { |
|
9367 nsAutoString mFamilyName; |
|
9368 bool mGood; |
|
9369 ExtractFirstFamilyData() : mFamilyName(), mGood(false) {} |
|
9370 }; |
|
9371 |
|
9372 static bool |
|
9373 ExtractFirstFamily(const nsString& aFamily, |
|
9374 bool aGeneric, |
|
9375 void* aData) |
|
9376 { |
|
9377 ExtractFirstFamilyData* realData = (ExtractFirstFamilyData*) aData; |
|
9378 if (aGeneric || realData->mFamilyName.Length() > 0) { |
|
9379 realData->mGood = false; |
|
9380 return false; |
|
9381 } |
|
9382 realData->mFamilyName.Assign(aFamily); |
|
9383 realData->mGood = true; |
|
9384 return true; |
|
9385 } |
|
9386 |
|
9387 // font-descriptor: descriptor ':' value ';' |
|
9388 // caller has advanced mToken to point at the descriptor |
|
9389 bool |
|
9390 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID, |
|
9391 nsCSSValue& aValue) |
|
9392 { |
|
9393 switch (aDescID) { |
|
9394 // These four are similar to the properties of the same name, |
|
9395 // possibly with more restrictions on the values they can take. |
|
9396 case eCSSFontDesc_Family: { |
|
9397 if (!ParseFamily(aValue) || |
|
9398 aValue.GetUnit() != eCSSUnit_Families) |
|
9399 return false; |
|
9400 |
|
9401 // the style parameters to the nsFont constructor are ignored, |
|
9402 // because it's only being used to call EnumerateFamilies |
|
9403 nsAutoString valueStr; |
|
9404 aValue.GetStringValue(valueStr); |
|
9405 nsFont font(valueStr, 0, 0, 0, 0, 0, 0); |
|
9406 ExtractFirstFamilyData dat; |
|
9407 |
|
9408 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat); |
|
9409 if (!dat.mGood) |
|
9410 return false; |
|
9411 |
|
9412 aValue.SetStringValue(dat.mFamilyName, eCSSUnit_String); |
|
9413 return true; |
|
9414 } |
|
9415 |
|
9416 case eCSSFontDesc_Style: |
|
9417 // property is VARIANT_HMK|VARIANT_SYSFONT |
|
9418 return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL, |
|
9419 nsCSSProps::kFontStyleKTable); |
|
9420 |
|
9421 case eCSSFontDesc_Weight: |
|
9422 return (ParseFontWeight(aValue) && |
|
9423 aValue.GetUnit() != eCSSUnit_Inherit && |
|
9424 aValue.GetUnit() != eCSSUnit_Initial && |
|
9425 aValue.GetUnit() != eCSSUnit_Unset && |
|
9426 (aValue.GetUnit() != eCSSUnit_Enumerated || |
|
9427 (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER && |
|
9428 aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER))); |
|
9429 |
|
9430 case eCSSFontDesc_Stretch: |
|
9431 // property is VARIANT_HK|VARIANT_SYSFONT |
|
9432 return ParseVariant(aValue, VARIANT_KEYWORD, |
|
9433 nsCSSProps::kFontStretchKTable); |
|
9434 |
|
9435 // These two are unique to @font-face and have their own special grammar. |
|
9436 case eCSSFontDesc_Src: |
|
9437 return ParseFontSrc(aValue); |
|
9438 |
|
9439 case eCSSFontDesc_UnicodeRange: |
|
9440 return ParseFontRanges(aValue); |
|
9441 |
|
9442 case eCSSFontDesc_FontFeatureSettings: |
|
9443 return ParseFontFeatureSettings(aValue); |
|
9444 |
|
9445 case eCSSFontDesc_FontLanguageOverride: |
|
9446 return ParseVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, nullptr); |
|
9447 |
|
9448 case eCSSFontDesc_UNKNOWN: |
|
9449 case eCSSFontDesc_COUNT: |
|
9450 NS_NOTREACHED("bad nsCSSFontDesc code"); |
|
9451 } |
|
9452 // explicitly do NOT have a default case to let the compiler |
|
9453 // help find missing descriptors |
|
9454 return false; |
|
9455 } |
|
9456 |
|
9457 void |
|
9458 CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties) |
|
9459 { |
|
9460 nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated); |
|
9461 for (const nsCSSProperty *prop = aSourceProperties; |
|
9462 *prop != eCSSProperty_UNKNOWN; ++prop) { |
|
9463 AppendValue(*prop, physical); |
|
9464 } |
|
9465 } |
|
9466 |
|
9467 static nsCSSValue |
|
9468 BoxPositionMaskToCSSValue(int32_t aMask, bool isX) |
|
9469 { |
|
9470 int32_t val = NS_STYLE_BG_POSITION_CENTER; |
|
9471 if (isX) { |
|
9472 if (aMask & BG_LEFT) { |
|
9473 val = NS_STYLE_BG_POSITION_LEFT; |
|
9474 } |
|
9475 else if (aMask & BG_RIGHT) { |
|
9476 val = NS_STYLE_BG_POSITION_RIGHT; |
|
9477 } |
|
9478 } |
|
9479 else { |
|
9480 if (aMask & BG_TOP) { |
|
9481 val = NS_STYLE_BG_POSITION_TOP; |
|
9482 } |
|
9483 else if (aMask & BG_BOTTOM) { |
|
9484 val = NS_STYLE_BG_POSITION_BOTTOM; |
|
9485 } |
|
9486 } |
|
9487 |
|
9488 return nsCSSValue(val, eCSSUnit_Enumerated); |
|
9489 } |
|
9490 |
|
9491 bool |
|
9492 CSSParserImpl::ParseBackground() |
|
9493 { |
|
9494 nsAutoParseCompoundProperty compound(this); |
|
9495 |
|
9496 // background-color can only be set once, so it's not a list. |
|
9497 nsCSSValue color; |
|
9498 |
|
9499 // Check first for inherit/initial/unset. |
|
9500 if (ParseVariant(color, VARIANT_INHERIT, nullptr)) { |
|
9501 // must be alone |
|
9502 for (const nsCSSProperty* subprops = |
|
9503 nsCSSProps::SubpropertyEntryFor(eCSSProperty_background); |
|
9504 *subprops != eCSSProperty_UNKNOWN; ++subprops) { |
|
9505 AppendValue(*subprops, color); |
|
9506 } |
|
9507 return true; |
|
9508 } |
|
9509 |
|
9510 nsCSSValue image, repeat, attachment, clip, origin, position, size; |
|
9511 BackgroundParseState state(color, image.SetListValue(), |
|
9512 repeat.SetPairListValue(), |
|
9513 attachment.SetListValue(), clip.SetListValue(), |
|
9514 origin.SetListValue(), position.SetListValue(), |
|
9515 size.SetPairListValue()); |
|
9516 |
|
9517 for (;;) { |
|
9518 if (!ParseBackgroundItem(state)) { |
|
9519 return false; |
|
9520 } |
|
9521 // If we saw a color, this must be the last item. |
|
9522 if (color.GetUnit() != eCSSUnit_Null) { |
|
9523 break; |
|
9524 } |
|
9525 // If there's a comma, expect another item. |
|
9526 if (!ExpectSymbol(',', true)) { |
|
9527 break; |
|
9528 } |
|
9529 // Chain another entry on all the lists. |
|
9530 state.mImage->mNext = new nsCSSValueList; |
|
9531 state.mImage = state.mImage->mNext; |
|
9532 state.mRepeat->mNext = new nsCSSValuePairList; |
|
9533 state.mRepeat = state.mRepeat->mNext; |
|
9534 state.mAttachment->mNext = new nsCSSValueList; |
|
9535 state.mAttachment = state.mAttachment->mNext; |
|
9536 state.mClip->mNext = new nsCSSValueList; |
|
9537 state.mClip = state.mClip->mNext; |
|
9538 state.mOrigin->mNext = new nsCSSValueList; |
|
9539 state.mOrigin = state.mOrigin->mNext; |
|
9540 state.mPosition->mNext = new nsCSSValueList; |
|
9541 state.mPosition = state.mPosition->mNext; |
|
9542 state.mSize->mNext = new nsCSSValuePairList; |
|
9543 state.mSize = state.mSize->mNext; |
|
9544 } |
|
9545 |
|
9546 // If we get to this point without seeing a color, provide a default. |
|
9547 if (color.GetUnit() == eCSSUnit_Null) { |
|
9548 color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor); |
|
9549 } |
|
9550 |
|
9551 AppendValue(eCSSProperty_background_image, image); |
|
9552 AppendValue(eCSSProperty_background_repeat, repeat); |
|
9553 AppendValue(eCSSProperty_background_attachment, attachment); |
|
9554 AppendValue(eCSSProperty_background_clip, clip); |
|
9555 AppendValue(eCSSProperty_background_origin, origin); |
|
9556 AppendValue(eCSSProperty_background_position, position); |
|
9557 AppendValue(eCSSProperty_background_size, size); |
|
9558 AppendValue(eCSSProperty_background_color, color); |
|
9559 return true; |
|
9560 } |
|
9561 |
|
9562 // Parse one item of the background shorthand property. |
|
9563 bool |
|
9564 CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState) |
|
9565 |
|
9566 { |
|
9567 // Fill in the values that the shorthand will set if we don't find |
|
9568 // other values. |
|
9569 aState.mImage->mValue.SetNoneValue(); |
|
9570 aState.mRepeat->mXValue.SetIntValue(NS_STYLE_BG_REPEAT_REPEAT, |
|
9571 eCSSUnit_Enumerated); |
|
9572 aState.mRepeat->mYValue.Reset(); |
|
9573 aState.mAttachment->mValue.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL, |
|
9574 eCSSUnit_Enumerated); |
|
9575 aState.mClip->mValue.SetIntValue(NS_STYLE_BG_CLIP_BORDER, |
|
9576 eCSSUnit_Enumerated); |
|
9577 aState.mOrigin->mValue.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING, |
|
9578 eCSSUnit_Enumerated); |
|
9579 nsRefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4); |
|
9580 aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array); |
|
9581 positionArr->Item(1).SetPercentValue(0.0f); |
|
9582 positionArr->Item(3).SetPercentValue(0.0f); |
|
9583 aState.mSize->mXValue.SetAutoValue(); |
|
9584 aState.mSize->mYValue.SetAutoValue(); |
|
9585 |
|
9586 bool haveColor = false, |
|
9587 haveImage = false, |
|
9588 haveRepeat = false, |
|
9589 haveAttach = false, |
|
9590 havePositionAndSize = false, |
|
9591 haveOrigin = false, |
|
9592 haveSomething = false; |
|
9593 |
|
9594 while (GetToken(true)) { |
|
9595 nsCSSTokenType tt = mToken.mType; |
|
9596 UngetToken(); // ...but we'll still cheat and use mToken |
|
9597 if (tt == eCSSToken_Symbol) { |
|
9598 // ExpectEndProperty only looks for symbols, and nothing else will |
|
9599 // show up as one. |
|
9600 break; |
|
9601 } |
|
9602 |
|
9603 if (tt == eCSSToken_Ident) { |
|
9604 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); |
|
9605 int32_t dummy; |
|
9606 if (keyword == eCSSKeyword_inherit || |
|
9607 keyword == eCSSKeyword_initial || |
|
9608 keyword == eCSSKeyword_unset) { |
|
9609 return false; |
|
9610 } else if (keyword == eCSSKeyword_none) { |
|
9611 if (haveImage) |
|
9612 return false; |
|
9613 haveImage = true; |
|
9614 if (!ParseSingleValueProperty(aState.mImage->mValue, |
|
9615 eCSSProperty_background_image)) { |
|
9616 NS_NOTREACHED("should be able to parse"); |
|
9617 return false; |
|
9618 } |
|
9619 } else if (nsCSSProps::FindKeyword(keyword, |
|
9620 nsCSSProps::kBackgroundAttachmentKTable, dummy)) { |
|
9621 if (haveAttach) |
|
9622 return false; |
|
9623 haveAttach = true; |
|
9624 if (!ParseSingleValueProperty(aState.mAttachment->mValue, |
|
9625 eCSSProperty_background_attachment)) { |
|
9626 NS_NOTREACHED("should be able to parse"); |
|
9627 return false; |
|
9628 } |
|
9629 } else if (nsCSSProps::FindKeyword(keyword, |
|
9630 nsCSSProps::kBackgroundRepeatKTable, dummy)) { |
|
9631 if (haveRepeat) |
|
9632 return false; |
|
9633 haveRepeat = true; |
|
9634 nsCSSValuePair scratch; |
|
9635 if (!ParseBackgroundRepeatValues(scratch)) { |
|
9636 NS_NOTREACHED("should be able to parse"); |
|
9637 return false; |
|
9638 } |
|
9639 aState.mRepeat->mXValue = scratch.mXValue; |
|
9640 aState.mRepeat->mYValue = scratch.mYValue; |
|
9641 } else if (nsCSSProps::FindKeyword(keyword, |
|
9642 nsCSSProps::kBackgroundPositionKTable, dummy)) { |
|
9643 if (havePositionAndSize) |
|
9644 return false; |
|
9645 havePositionAndSize = true; |
|
9646 if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) { |
|
9647 return false; |
|
9648 } |
|
9649 if (ExpectSymbol('/', true)) { |
|
9650 nsCSSValuePair scratch; |
|
9651 if (!ParseBackgroundSizeValues(scratch)) { |
|
9652 return false; |
|
9653 } |
|
9654 aState.mSize->mXValue = scratch.mXValue; |
|
9655 aState.mSize->mYValue = scratch.mYValue; |
|
9656 } |
|
9657 } else if (nsCSSProps::FindKeyword(keyword, |
|
9658 nsCSSProps::kBackgroundOriginKTable, dummy)) { |
|
9659 if (haveOrigin) |
|
9660 return false; |
|
9661 haveOrigin = true; |
|
9662 if (!ParseSingleValueProperty(aState.mOrigin->mValue, |
|
9663 eCSSProperty_background_origin)) { |
|
9664 NS_NOTREACHED("should be able to parse"); |
|
9665 return false; |
|
9666 } |
|
9667 |
|
9668 // The spec allows a second box value (for background-clip), |
|
9669 // immediately following the first one (for background-origin). |
|
9670 |
|
9671 // 'background-clip' and 'background-origin' use the same keyword table |
|
9672 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[ |
|
9673 eCSSProperty_background_origin] == |
|
9674 nsCSSProps::kBackgroundOriginKTable); |
|
9675 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[ |
|
9676 eCSSProperty_background_clip] == |
|
9677 nsCSSProps::kBackgroundOriginKTable); |
|
9678 static_assert(NS_STYLE_BG_CLIP_BORDER == |
|
9679 NS_STYLE_BG_ORIGIN_BORDER && |
|
9680 NS_STYLE_BG_CLIP_PADDING == |
|
9681 NS_STYLE_BG_ORIGIN_PADDING && |
|
9682 NS_STYLE_BG_CLIP_CONTENT == |
|
9683 NS_STYLE_BG_ORIGIN_CONTENT, |
|
9684 "bg-clip and bg-origin style constants must agree"); |
|
9685 |
|
9686 if (!ParseSingleValueProperty(aState.mClip->mValue, |
|
9687 eCSSProperty_background_clip)) { |
|
9688 // When exactly one <box> value is set, it is used for both |
|
9689 // 'background-origin' and 'background-clip'. |
|
9690 // See assertions above showing these values are compatible. |
|
9691 aState.mClip->mValue = aState.mOrigin->mValue; |
|
9692 } |
|
9693 } else { |
|
9694 if (haveColor) |
|
9695 return false; |
|
9696 haveColor = true; |
|
9697 if (!ParseSingleValueProperty(aState.mColor, |
|
9698 eCSSProperty_background_color)) { |
|
9699 return false; |
|
9700 } |
|
9701 } |
|
9702 } else if (tt == eCSSToken_URL || |
|
9703 (tt == eCSSToken_Function && |
|
9704 (mToken.mIdent.LowerCaseEqualsLiteral("linear-gradient") || |
|
9705 mToken.mIdent.LowerCaseEqualsLiteral("radial-gradient") || |
|
9706 mToken.mIdent.LowerCaseEqualsLiteral("repeating-linear-gradient") || |
|
9707 mToken.mIdent.LowerCaseEqualsLiteral("repeating-radial-gradient") || |
|
9708 mToken.mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient") || |
|
9709 mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient") || |
|
9710 mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") || |
|
9711 mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") || |
|
9712 mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect") || |
|
9713 mToken.mIdent.LowerCaseEqualsLiteral("-moz-element")))) { |
|
9714 if (haveImage) |
|
9715 return false; |
|
9716 haveImage = true; |
|
9717 if (!ParseSingleValueProperty(aState.mImage->mValue, |
|
9718 eCSSProperty_background_image)) { |
|
9719 return false; |
|
9720 } |
|
9721 } else if (tt == eCSSToken_Dimension || |
|
9722 tt == eCSSToken_Number || |
|
9723 tt == eCSSToken_Percentage || |
|
9724 (tt == eCSSToken_Function && |
|
9725 (mToken.mIdent.LowerCaseEqualsLiteral("calc") || |
|
9726 mToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")))) { |
|
9727 if (havePositionAndSize) |
|
9728 return false; |
|
9729 havePositionAndSize = true; |
|
9730 if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) { |
|
9731 return false; |
|
9732 } |
|
9733 if (ExpectSymbol('/', true)) { |
|
9734 nsCSSValuePair scratch; |
|
9735 if (!ParseBackgroundSizeValues(scratch)) { |
|
9736 return false; |
|
9737 } |
|
9738 aState.mSize->mXValue = scratch.mXValue; |
|
9739 aState.mSize->mYValue = scratch.mYValue; |
|
9740 } |
|
9741 } else { |
|
9742 if (haveColor) |
|
9743 return false; |
|
9744 haveColor = true; |
|
9745 // Note: This parses 'inherit', 'initial' and 'unset', but |
|
9746 // we've already checked for them, so it's ok. |
|
9747 if (!ParseSingleValueProperty(aState.mColor, |
|
9748 eCSSProperty_background_color)) { |
|
9749 return false; |
|
9750 } |
|
9751 } |
|
9752 haveSomething = true; |
|
9753 } |
|
9754 |
|
9755 return haveSomething; |
|
9756 } |
|
9757 |
|
9758 // This function is very similar to ParseBackgroundPosition and |
|
9759 // ParseBackgroundSize. |
|
9760 bool |
|
9761 CSSParserImpl::ParseValueList(nsCSSProperty aPropID) |
|
9762 { |
|
9763 // aPropID is a single value prop-id |
|
9764 nsCSSValue value; |
|
9765 // 'initial', 'inherit' and 'unset' stand alone, no list permitted. |
|
9766 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
9767 nsCSSValueList* item = value.SetListValue(); |
|
9768 for (;;) { |
|
9769 if (!ParseSingleValueProperty(item->mValue, aPropID)) { |
|
9770 return false; |
|
9771 } |
|
9772 if (!ExpectSymbol(',', true)) { |
|
9773 break; |
|
9774 } |
|
9775 item->mNext = new nsCSSValueList; |
|
9776 item = item->mNext; |
|
9777 } |
|
9778 } |
|
9779 AppendValue(aPropID, value); |
|
9780 return true; |
|
9781 } |
|
9782 |
|
9783 bool |
|
9784 CSSParserImpl::ParseBackgroundRepeat() |
|
9785 { |
|
9786 nsCSSValue value; |
|
9787 // 'initial', 'inherit' and 'unset' stand alone, no list permitted. |
|
9788 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
9789 nsCSSValuePair valuePair; |
|
9790 if (!ParseBackgroundRepeatValues(valuePair)) { |
|
9791 return false; |
|
9792 } |
|
9793 nsCSSValuePairList* item = value.SetPairListValue(); |
|
9794 for (;;) { |
|
9795 item->mXValue = valuePair.mXValue; |
|
9796 item->mYValue = valuePair.mYValue; |
|
9797 if (!ExpectSymbol(',', true)) { |
|
9798 break; |
|
9799 } |
|
9800 if (!ParseBackgroundRepeatValues(valuePair)) { |
|
9801 return false; |
|
9802 } |
|
9803 item->mNext = new nsCSSValuePairList; |
|
9804 item = item->mNext; |
|
9805 } |
|
9806 } |
|
9807 |
|
9808 AppendValue(eCSSProperty_background_repeat, value); |
|
9809 return true; |
|
9810 } |
|
9811 |
|
9812 bool |
|
9813 CSSParserImpl::ParseBackgroundRepeatValues(nsCSSValuePair& aValue) |
|
9814 { |
|
9815 nsCSSValue& xValue = aValue.mXValue; |
|
9816 nsCSSValue& yValue = aValue.mYValue; |
|
9817 |
|
9818 if (ParseEnum(xValue, nsCSSProps::kBackgroundRepeatKTable)) { |
|
9819 int32_t value = xValue.GetIntValue(); |
|
9820 // For single values set yValue as eCSSUnit_Null. |
|
9821 if (value == NS_STYLE_BG_REPEAT_REPEAT_X || |
|
9822 value == NS_STYLE_BG_REPEAT_REPEAT_Y || |
|
9823 !ParseEnum(yValue, nsCSSProps::kBackgroundRepeatPartKTable)) { |
|
9824 // the caller will fail cases like "repeat-x no-repeat" |
|
9825 // by expecting a list separator or an end property. |
|
9826 yValue.Reset(); |
|
9827 } |
|
9828 return true; |
|
9829 } |
|
9830 |
|
9831 return false; |
|
9832 } |
|
9833 |
|
9834 // This function is very similar to ParseBackgroundList and ParseBackgroundSize. |
|
9835 bool |
|
9836 CSSParserImpl::ParseBackgroundPosition() |
|
9837 { |
|
9838 nsCSSValue value; |
|
9839 // 'initial', 'inherit' and 'unset' stand alone, no list permitted. |
|
9840 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
9841 nsCSSValue itemValue; |
|
9842 if (!ParseBackgroundPositionValues(itemValue, false)) { |
|
9843 return false; |
|
9844 } |
|
9845 nsCSSValueList* item = value.SetListValue(); |
|
9846 for (;;) { |
|
9847 item->mValue = itemValue; |
|
9848 if (!ExpectSymbol(',', true)) { |
|
9849 break; |
|
9850 } |
|
9851 if (!ParseBackgroundPositionValues(itemValue, false)) { |
|
9852 return false; |
|
9853 } |
|
9854 item->mNext = new nsCSSValueList; |
|
9855 item = item->mNext; |
|
9856 } |
|
9857 } |
|
9858 AppendValue(eCSSProperty_background_position, value); |
|
9859 return true; |
|
9860 } |
|
9861 |
|
9862 /** |
|
9863 * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used |
|
9864 * for parsing the CSS 2.1 background-position syntax (which has at |
|
9865 * most two values). (Compare to the css3-background syntax which |
|
9866 * takes up to four values.) Some current CSS specifications that |
|
9867 * use background-position-like syntax still use this old syntax. |
|
9868 ** |
|
9869 * Parses two values that correspond to positions in a box. These can be |
|
9870 * values corresponding to percentages of the box, raw offsets, or keywords |
|
9871 * like "top," "left center," etc. |
|
9872 * |
|
9873 * @param aOut The nsCSSValuePair in which to place the result. |
|
9874 * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are |
|
9875 * legal values |
|
9876 * @param aAllowExplicitCenter If true, 'center' is a legal value |
|
9877 * @return Whether or not the operation succeeded. |
|
9878 */ |
|
9879 bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut, |
|
9880 bool aAcceptsInherit, |
|
9881 bool aAllowExplicitCenter) |
|
9882 { |
|
9883 // First try a percentage or a length value |
|
9884 nsCSSValue &xValue = aOut.mXValue, |
|
9885 &yValue = aOut.mYValue; |
|
9886 int32_t variantMask = |
|
9887 (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC; |
|
9888 if (ParseVariant(xValue, variantMask, nullptr)) { |
|
9889 if (eCSSUnit_Inherit == xValue.GetUnit() || |
|
9890 eCSSUnit_Initial == xValue.GetUnit() || |
|
9891 eCSSUnit_Unset == xValue.GetUnit()) { // both are inherit, initial or unset |
|
9892 yValue = xValue; |
|
9893 return true; |
|
9894 } |
|
9895 // We have one percentage/length/calc. Get the optional second |
|
9896 // percentage/length/calc/keyword. |
|
9897 if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) { |
|
9898 // We have two numbers |
|
9899 return true; |
|
9900 } |
|
9901 |
|
9902 if (ParseEnum(yValue, nsCSSProps::kBackgroundPositionKTable)) { |
|
9903 int32_t yVal = yValue.GetIntValue(); |
|
9904 if (!(yVal & BG_CTB)) { |
|
9905 // The second keyword can only be 'center', 'top', or 'bottom' |
|
9906 return false; |
|
9907 } |
|
9908 yValue = BoxPositionMaskToCSSValue(yVal, false); |
|
9909 return true; |
|
9910 } |
|
9911 |
|
9912 // If only one percentage or length value is given, it sets the |
|
9913 // horizontal position only, and the vertical position will be 50%. |
|
9914 yValue.SetPercentValue(0.5f); |
|
9915 return true; |
|
9916 } |
|
9917 |
|
9918 // Now try keywords. We do this manually to allow for the first |
|
9919 // appearance of "center" to apply to the either the x or y |
|
9920 // position (it's ambiguous so we have to disambiguate). Each |
|
9921 // allowed keyword value is assigned it's own bit. We don't allow |
|
9922 // any duplicate keywords other than center. We try to get two |
|
9923 // keywords but it's okay if there is only one. |
|
9924 int32_t mask = 0; |
|
9925 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) { |
|
9926 int32_t bit = xValue.GetIntValue(); |
|
9927 mask |= bit; |
|
9928 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) { |
|
9929 bit = xValue.GetIntValue(); |
|
9930 if (mask & (bit & ~BG_CENTER)) { |
|
9931 // Only the 'center' keyword can be duplicated. |
|
9932 return false; |
|
9933 } |
|
9934 mask |= bit; |
|
9935 } |
|
9936 else { |
|
9937 // Only one keyword. See if we have a length, percentage, or calc. |
|
9938 if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) { |
|
9939 if (!(mask & BG_CLR)) { |
|
9940 // The first keyword can only be 'center', 'left', or 'right' |
|
9941 return false; |
|
9942 } |
|
9943 |
|
9944 xValue = BoxPositionMaskToCSSValue(mask, true); |
|
9945 return true; |
|
9946 } |
|
9947 } |
|
9948 } |
|
9949 |
|
9950 // Check for bad input. Bad input consists of no matching keywords, |
|
9951 // or pairs of x keywords or pairs of y keywords. |
|
9952 if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) || |
|
9953 (mask == (BG_LEFT | BG_RIGHT)) || |
|
9954 (!aAllowExplicitCenter && (mask & BG_CENTER))) { |
|
9955 return false; |
|
9956 } |
|
9957 |
|
9958 // Create style values |
|
9959 xValue = BoxPositionMaskToCSSValue(mask, true); |
|
9960 yValue = BoxPositionMaskToCSSValue(mask, false); |
|
9961 return true; |
|
9962 } |
|
9963 |
|
9964 bool CSSParserImpl::ParseBackgroundPositionValues(nsCSSValue& aOut, |
|
9965 bool aAcceptsInherit) |
|
9966 { |
|
9967 // css3-background allows positions to be defined as offsets |
|
9968 // from an edge. There can be 2 keywords and 2 offsets given. These |
|
9969 // four 'values' are stored in an array in the following order: |
|
9970 // [keyword offset keyword offset]. If a keyword or offset isn't |
|
9971 // parsed the value of the corresponding array element is set |
|
9972 // to eCSSUnit_Null by a call to nsCSSValue::Reset(). |
|
9973 if (aAcceptsInherit && ParseVariant(aOut, VARIANT_INHERIT, nullptr)) { |
|
9974 return true; |
|
9975 } |
|
9976 |
|
9977 nsRefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(4); |
|
9978 aOut.SetArrayValue(value, eCSSUnit_Array); |
|
9979 |
|
9980 // The following clarifies organisation of the array. |
|
9981 nsCSSValue &xEdge = value->Item(0), |
|
9982 &xOffset = value->Item(1), |
|
9983 &yEdge = value->Item(2), |
|
9984 &yOffset = value->Item(3); |
|
9985 |
|
9986 // Parse all the values into the array. |
|
9987 uint32_t valueCount = 0; |
|
9988 for (int32_t i = 0; i < 4; i++) { |
|
9989 if (!ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD, |
|
9990 nsCSSProps::kBackgroundPositionKTable)) { |
|
9991 break; |
|
9992 } |
|
9993 ++valueCount; |
|
9994 } |
|
9995 |
|
9996 switch (valueCount) { |
|
9997 case 4: |
|
9998 // "If three or four values are given, then each <percentage> or <length> |
|
9999 // represents an offset and must be preceded by a keyword, which specifies |
|
10000 // from which edge the offset is given." |
|
10001 if (eCSSUnit_Enumerated != xEdge.GetUnit() || |
|
10002 BG_CENTER == xEdge.GetIntValue() || |
|
10003 eCSSUnit_Enumerated == xOffset.GetUnit() || |
|
10004 eCSSUnit_Enumerated != yEdge.GetUnit() || |
|
10005 BG_CENTER == yEdge.GetIntValue() || |
|
10006 eCSSUnit_Enumerated == yOffset.GetUnit()) { |
|
10007 return false; |
|
10008 } |
|
10009 break; |
|
10010 case 3: |
|
10011 // "If three or four values are given, then each <percentage> or<length> |
|
10012 // represents an offset and must be preceded by a keyword, which specifies |
|
10013 // from which edge the offset is given." ... "If three values are given, |
|
10014 // the missing offset is assumed to be zero." |
|
10015 if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) { |
|
10016 // keyword offset keyword |
|
10017 // Second value is non-keyword, thus first value must be a non-center |
|
10018 // keyword. |
|
10019 if (eCSSUnit_Enumerated != value->Item(0).GetUnit() || |
|
10020 BG_CENTER == value->Item(0).GetIntValue()) { |
|
10021 return false; |
|
10022 } |
|
10023 |
|
10024 // Remaining value must be a keyword. |
|
10025 if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) { |
|
10026 return false; |
|
10027 } |
|
10028 |
|
10029 yOffset.Reset(); // Everything else is in the correct position. |
|
10030 } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) { |
|
10031 // keyword keyword offset |
|
10032 // Third value is non-keyword, thus second value must be non-center |
|
10033 // keyword. |
|
10034 if (BG_CENTER == value->Item(1).GetIntValue()) { |
|
10035 return false; |
|
10036 } |
|
10037 |
|
10038 // Remaining value must be a keyword. |
|
10039 if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) { |
|
10040 return false; |
|
10041 } |
|
10042 |
|
10043 // Move the values to the correct position in the array. |
|
10044 value->Item(3) = value->Item(2); // yOffset |
|
10045 value->Item(2) = value->Item(1); // yEdge |
|
10046 value->Item(1).Reset(); // xOffset |
|
10047 } else { |
|
10048 return false; |
|
10049 } |
|
10050 break; |
|
10051 case 2: |
|
10052 // "If two values are given and at least one value is not a keyword, then |
|
10053 // the first value represents the horizontal position (or offset) and the |
|
10054 // second represents the vertical position (or offset)" |
|
10055 if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) { |
|
10056 if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) { |
|
10057 // keyword keyword |
|
10058 value->Item(2) = value->Item(1); // move yEdge to correct position |
|
10059 xOffset.Reset(); |
|
10060 yOffset.Reset(); |
|
10061 } else { |
|
10062 // keyword offset |
|
10063 // First value must represent horizontal position. |
|
10064 if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) { |
|
10065 return false; |
|
10066 } |
|
10067 value->Item(3) = value->Item(1); // move yOffset to correct position |
|
10068 xOffset.Reset(); |
|
10069 yEdge.Reset(); |
|
10070 } |
|
10071 } else { |
|
10072 if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) { |
|
10073 // offset keyword |
|
10074 // Second value must represent vertical position. |
|
10075 if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) { |
|
10076 return false; |
|
10077 } |
|
10078 value->Item(2) = value->Item(1); // move yEdge to correct position |
|
10079 value->Item(1) = value->Item(0); // move xOffset to correct position |
|
10080 xEdge.Reset(); |
|
10081 yOffset.Reset(); |
|
10082 } else { |
|
10083 // offset offset |
|
10084 value->Item(3) = value->Item(1); // move yOffset to correct position |
|
10085 value->Item(1) = value->Item(0); // move xOffset to correct position |
|
10086 xEdge.Reset(); |
|
10087 yEdge.Reset(); |
|
10088 } |
|
10089 } |
|
10090 break; |
|
10091 case 1: |
|
10092 // "If only one value is specified, the second value is assumed to be |
|
10093 // center." |
|
10094 if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) { |
|
10095 xOffset.Reset(); |
|
10096 } else { |
|
10097 value->Item(1) = value->Item(0); // move xOffset to correct position |
|
10098 xEdge.Reset(); |
|
10099 } |
|
10100 yEdge.SetIntValue(NS_STYLE_BG_POSITION_CENTER, eCSSUnit_Enumerated); |
|
10101 yOffset.Reset(); |
|
10102 break; |
|
10103 default: |
|
10104 return false; |
|
10105 } |
|
10106 |
|
10107 // For compatibility with CSS2.1 code the edges can be unspecified. |
|
10108 // Unspecified edges are recorded as nullptr. |
|
10109 NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() || |
|
10110 eCSSUnit_Null == xEdge.GetUnit()) && |
|
10111 (eCSSUnit_Enumerated == yEdge.GetUnit() || |
|
10112 eCSSUnit_Null == yEdge.GetUnit()) && |
|
10113 eCSSUnit_Enumerated != xOffset.GetUnit() && |
|
10114 eCSSUnit_Enumerated != yOffset.GetUnit(), |
|
10115 "Unexpected units"); |
|
10116 |
|
10117 // Keywords in first and second pairs can not both be vertical or |
|
10118 // horizontal keywords. (eg. left right, bottom top). Additionally, |
|
10119 // non-center keyword can not be duplicated (eg. left left). |
|
10120 int32_t xEdgeEnum = |
|
10121 xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0; |
|
10122 int32_t yEdgeEnum = |
|
10123 yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0; |
|
10124 if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) || |
|
10125 (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) || |
|
10126 (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) { |
|
10127 return false; |
|
10128 } |
|
10129 |
|
10130 // The values could be in an order that is different than expected. |
|
10131 // eg. x contains vertical information, y contains horizontal information. |
|
10132 // Swap if incorrect order. |
|
10133 if (xEdgeEnum & (BG_TOP | BG_BOTTOM) || |
|
10134 yEdgeEnum & (BG_LEFT | BG_RIGHT)) { |
|
10135 nsCSSValue swapEdge = xEdge; |
|
10136 nsCSSValue swapOffset = xOffset; |
|
10137 xEdge = yEdge; |
|
10138 xOffset = yOffset; |
|
10139 yEdge = swapEdge; |
|
10140 yOffset = swapOffset; |
|
10141 } |
|
10142 |
|
10143 return true; |
|
10144 } |
|
10145 |
|
10146 // This function is very similar to ParseBackgroundList and |
|
10147 // ParseBackgroundPosition. |
|
10148 bool |
|
10149 CSSParserImpl::ParseBackgroundSize() |
|
10150 { |
|
10151 nsCSSValue value; |
|
10152 // 'initial', 'inherit' and 'unset' stand alone, no list permitted. |
|
10153 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
10154 nsCSSValuePair valuePair; |
|
10155 if (!ParseBackgroundSizeValues(valuePair)) { |
|
10156 return false; |
|
10157 } |
|
10158 nsCSSValuePairList* item = value.SetPairListValue(); |
|
10159 for (;;) { |
|
10160 item->mXValue = valuePair.mXValue; |
|
10161 item->mYValue = valuePair.mYValue; |
|
10162 if (!ExpectSymbol(',', true)) { |
|
10163 break; |
|
10164 } |
|
10165 if (!ParseBackgroundSizeValues(valuePair)) { |
|
10166 return false; |
|
10167 } |
|
10168 item->mNext = new nsCSSValuePairList; |
|
10169 item = item->mNext; |
|
10170 } |
|
10171 } |
|
10172 AppendValue(eCSSProperty_background_size, value); |
|
10173 return true; |
|
10174 } |
|
10175 |
|
10176 /** |
|
10177 * Parses two values that correspond to lengths for the background-size |
|
10178 * property. These can be one or two lengths (or the 'auto' keyword) or |
|
10179 * percentages corresponding to the element's dimensions or the single keywords |
|
10180 * 'contain' or 'cover'. 'initial', 'inherit' and 'unset' must be handled by |
|
10181 * the caller if desired. |
|
10182 * |
|
10183 * @param aOut The nsCSSValuePair in which to place the result. |
|
10184 * @return Whether or not the operation succeeded. |
|
10185 */ |
|
10186 #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC) |
|
10187 bool CSSParserImpl::ParseBackgroundSizeValues(nsCSSValuePair &aOut) |
|
10188 { |
|
10189 // First try a percentage or a length value |
|
10190 nsCSSValue &xValue = aOut.mXValue, |
|
10191 &yValue = aOut.mYValue; |
|
10192 if (ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr)) { |
|
10193 // We have one percentage/length/calc/auto. Get the optional second |
|
10194 // percentage/length/calc/keyword. |
|
10195 if (ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr)) { |
|
10196 // We have a second percentage/length/calc/auto. |
|
10197 return true; |
|
10198 } |
|
10199 |
|
10200 // If only one percentage or length value is given, it sets the |
|
10201 // horizontal size only, and the vertical size will be as if by 'auto'. |
|
10202 yValue.SetAutoValue(); |
|
10203 return true; |
|
10204 } |
|
10205 |
|
10206 // Now address 'contain' and 'cover'. |
|
10207 if (!ParseEnum(xValue, nsCSSProps::kBackgroundSizeKTable)) |
|
10208 return false; |
|
10209 yValue.Reset(); |
|
10210 return true; |
|
10211 } |
|
10212 #undef BG_SIZE_VARIANT |
|
10213 |
|
10214 bool |
|
10215 CSSParserImpl::ParseBorderColor() |
|
10216 { |
|
10217 static const nsCSSProperty kBorderColorSources[] = { |
|
10218 eCSSProperty_border_left_color_ltr_source, |
|
10219 eCSSProperty_border_left_color_rtl_source, |
|
10220 eCSSProperty_border_right_color_ltr_source, |
|
10221 eCSSProperty_border_right_color_rtl_source, |
|
10222 eCSSProperty_UNKNOWN |
|
10223 }; |
|
10224 |
|
10225 // do this now, in case 4 values weren't specified |
|
10226 InitBoxPropsAsPhysical(kBorderColorSources); |
|
10227 return ParseBoxProperties(kBorderColorIDs); |
|
10228 } |
|
10229 |
|
10230 void |
|
10231 CSSParserImpl::SetBorderImageInitialValues() |
|
10232 { |
|
10233 // border-image-source: none |
|
10234 nsCSSValue source; |
|
10235 source.SetNoneValue(); |
|
10236 AppendValue(eCSSProperty_border_image_source, source); |
|
10237 |
|
10238 // border-image-slice: 100% |
|
10239 nsCSSValue sliceBoxValue; |
|
10240 nsCSSRect& sliceBox = sliceBoxValue.SetRectValue(); |
|
10241 sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent)); |
|
10242 nsCSSValue slice; |
|
10243 nsCSSValueList* sliceList = slice.SetListValue(); |
|
10244 sliceList->mValue = sliceBoxValue; |
|
10245 AppendValue(eCSSProperty_border_image_slice, slice); |
|
10246 |
|
10247 // border-image-width: 1 |
|
10248 nsCSSValue width; |
|
10249 nsCSSRect& widthBox = width.SetRectValue(); |
|
10250 widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number)); |
|
10251 AppendValue(eCSSProperty_border_image_width, width); |
|
10252 |
|
10253 // border-image-outset: 0 |
|
10254 nsCSSValue outset; |
|
10255 nsCSSRect& outsetBox = outset.SetRectValue(); |
|
10256 outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number)); |
|
10257 AppendValue(eCSSProperty_border_image_outset, outset); |
|
10258 |
|
10259 // border-image-repeat: repeat |
|
10260 nsCSSValue repeat; |
|
10261 nsCSSValuePair repeatPair; |
|
10262 repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, |
|
10263 eCSSUnit_Enumerated)); |
|
10264 repeat.SetPairValue(&repeatPair); |
|
10265 AppendValue(eCSSProperty_border_image_repeat, repeat); |
|
10266 } |
|
10267 |
|
10268 bool |
|
10269 CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit, |
|
10270 bool* aConsumedTokens) |
|
10271 { |
|
10272 // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill? |
|
10273 nsCSSValue value; |
|
10274 |
|
10275 if (aConsumedTokens) { |
|
10276 *aConsumedTokens = true; |
|
10277 } |
|
10278 |
|
10279 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
10280 // Keywords "inherit", "initial" and "unset" can not be mixed, so we |
|
10281 // are done. |
|
10282 AppendValue(eCSSProperty_border_image_slice, value); |
|
10283 return true; |
|
10284 } |
|
10285 |
|
10286 // Try parsing "fill" value. |
|
10287 nsCSSValue imageSliceFillValue; |
|
10288 bool hasFill = ParseEnum(imageSliceFillValue, |
|
10289 nsCSSProps::kBorderImageSliceKTable); |
|
10290 |
|
10291 // Parse the box dimensions. |
|
10292 nsCSSValue imageSliceBoxValue; |
|
10293 if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue)) { |
|
10294 if (!hasFill && aConsumedTokens) { |
|
10295 *aConsumedTokens = false; |
|
10296 } |
|
10297 |
|
10298 return false; |
|
10299 } |
|
10300 |
|
10301 // Try parsing "fill" keyword again if the first time failed because keyword |
|
10302 // and slice dimensions can be in any order. |
|
10303 if (!hasFill) { |
|
10304 hasFill = ParseEnum(imageSliceFillValue, |
|
10305 nsCSSProps::kBorderImageSliceKTable); |
|
10306 } |
|
10307 |
|
10308 nsCSSValueList* borderImageSlice = value.SetListValue(); |
|
10309 // Put the box value into the list. |
|
10310 borderImageSlice->mValue = imageSliceBoxValue; |
|
10311 |
|
10312 if (hasFill) { |
|
10313 // Put the "fill" value into the list. |
|
10314 borderImageSlice->mNext = new nsCSSValueList; |
|
10315 borderImageSlice->mNext->mValue = imageSliceFillValue; |
|
10316 } |
|
10317 |
|
10318 AppendValue(eCSSProperty_border_image_slice, value); |
|
10319 return true; |
|
10320 } |
|
10321 |
|
10322 bool |
|
10323 CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit) |
|
10324 { |
|
10325 // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4} |
|
10326 nsCSSValue value; |
|
10327 |
|
10328 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
10329 // Keywords "inherit", "initial" and "unset" can not be mixed, so we |
|
10330 // are done. |
|
10331 AppendValue(eCSSProperty_border_image_width, value); |
|
10332 return true; |
|
10333 } |
|
10334 |
|
10335 // Parse the box dimensions. |
|
10336 if (!ParseGroupedBoxProperty(VARIANT_ALPN, value)) { |
|
10337 return false; |
|
10338 } |
|
10339 |
|
10340 AppendValue(eCSSProperty_border_image_width, value); |
|
10341 return true; |
|
10342 } |
|
10343 |
|
10344 bool |
|
10345 CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit) |
|
10346 { |
|
10347 // border-image-outset: initial | [<length>|<number>]{1,4} |
|
10348 nsCSSValue value; |
|
10349 |
|
10350 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
10351 // Keywords "inherit", "initial" and "unset" can not be mixed, so we |
|
10352 // are done. |
|
10353 AppendValue(eCSSProperty_border_image_outset, value); |
|
10354 return true; |
|
10355 } |
|
10356 |
|
10357 // Parse the box dimensions. |
|
10358 if (!ParseGroupedBoxProperty(VARIANT_LN, value)) { |
|
10359 return false; |
|
10360 } |
|
10361 |
|
10362 AppendValue(eCSSProperty_border_image_outset, value); |
|
10363 return true; |
|
10364 } |
|
10365 |
|
10366 bool |
|
10367 CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit) |
|
10368 { |
|
10369 nsCSSValue value; |
|
10370 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
10371 // Keywords "inherit", "initial" and "unset" can not be mixed, so we |
|
10372 // are done. |
|
10373 AppendValue(eCSSProperty_border_image_repeat, value); |
|
10374 return true; |
|
10375 } |
|
10376 |
|
10377 nsCSSValuePair result; |
|
10378 if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) { |
|
10379 return false; |
|
10380 } |
|
10381 |
|
10382 // optional second keyword, defaults to first |
|
10383 if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) { |
|
10384 result.mYValue = result.mXValue; |
|
10385 } |
|
10386 |
|
10387 value.SetPairValue(&result); |
|
10388 AppendValue(eCSSProperty_border_image_repeat, value); |
|
10389 return true; |
|
10390 } |
|
10391 |
|
10392 bool |
|
10393 CSSParserImpl::ParseBorderImage() |
|
10394 { |
|
10395 nsAutoParseCompoundProperty compound(this); |
|
10396 |
|
10397 // border-image: inherit | initial | |
|
10398 // <border-image-source> || |
|
10399 // <border-image-slice> |
|
10400 // [ / <border-image-width> | |
|
10401 // / <border-image-width>? / <border-image-outset> ]? || |
|
10402 // <border-image-repeat> |
|
10403 |
|
10404 nsCSSValue value; |
|
10405 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
10406 AppendValue(eCSSProperty_border_image_source, value); |
|
10407 AppendValue(eCSSProperty_border_image_slice, value); |
|
10408 AppendValue(eCSSProperty_border_image_width, value); |
|
10409 AppendValue(eCSSProperty_border_image_outset, value); |
|
10410 AppendValue(eCSSProperty_border_image_repeat, value); |
|
10411 // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done. |
|
10412 return true; |
|
10413 } |
|
10414 |
|
10415 // No empty property. |
|
10416 if (CheckEndProperty()) { |
|
10417 return false; |
|
10418 } |
|
10419 |
|
10420 // Shorthand properties are required to set everything they can. |
|
10421 SetBorderImageInitialValues(); |
|
10422 |
|
10423 bool foundSource = false; |
|
10424 bool foundSliceWidthOutset = false; |
|
10425 bool foundRepeat = false; |
|
10426 |
|
10427 // This loop is used to handle the parsing of border-image properties which |
|
10428 // can appear in any order. |
|
10429 nsCSSValue imageSourceValue; |
|
10430 while (!CheckEndProperty()) { |
|
10431 // <border-image-source> |
|
10432 if (!foundSource && ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) { |
|
10433 AppendValue(eCSSProperty_border_image_source, imageSourceValue); |
|
10434 foundSource = true; |
|
10435 continue; |
|
10436 } |
|
10437 |
|
10438 // <border-image-slice> |
|
10439 // ParseBorderImageSlice is weird. It may consume tokens and then return |
|
10440 // false, because it parses a property with two required components that |
|
10441 // can appear in either order. Since the tokens that were consumed cannot |
|
10442 // parse as anything else we care about, this isn't a problem. |
|
10443 if (!foundSliceWidthOutset) { |
|
10444 bool sliceConsumedTokens = false; |
|
10445 if (ParseBorderImageSlice(false, &sliceConsumedTokens)) { |
|
10446 foundSliceWidthOutset = true; |
|
10447 |
|
10448 // [ / <border-image-width>? |
|
10449 if (ExpectSymbol('/', true)) { |
|
10450 bool foundBorderImageWidth = ParseBorderImageWidth(false); |
|
10451 |
|
10452 // [ / <border-image-outset> |
|
10453 if (ExpectSymbol('/', true)) { |
|
10454 if (!ParseBorderImageOutset(false)) { |
|
10455 return false; |
|
10456 } |
|
10457 } else if (!foundBorderImageWidth) { |
|
10458 // If this part has an trailing slash, the whole declaration is |
|
10459 // invalid. |
|
10460 return false; |
|
10461 } |
|
10462 } |
|
10463 |
|
10464 continue; |
|
10465 } else { |
|
10466 // If we consumed some tokens for <border-image-slice> but did not |
|
10467 // successfully parse it, we have an error. |
|
10468 if (sliceConsumedTokens) { |
|
10469 return false; |
|
10470 } |
|
10471 } |
|
10472 } |
|
10473 |
|
10474 // <border-image-repeat> |
|
10475 if (!foundRepeat && ParseBorderImageRepeat(false)) { |
|
10476 foundRepeat = true; |
|
10477 continue; |
|
10478 } |
|
10479 |
|
10480 return false; |
|
10481 } |
|
10482 |
|
10483 return true; |
|
10484 } |
|
10485 |
|
10486 bool |
|
10487 CSSParserImpl::ParseBorderSpacing() |
|
10488 { |
|
10489 nsCSSValue xValue, yValue; |
|
10490 if (!ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr)) { |
|
10491 return false; |
|
10492 } |
|
10493 |
|
10494 // If we have one length, get the optional second length. |
|
10495 // set the second value equal to the first. |
|
10496 if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) { |
|
10497 ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC, nullptr); |
|
10498 } |
|
10499 |
|
10500 if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) { |
|
10501 AppendValue(eCSSProperty_border_spacing, xValue); |
|
10502 } else { |
|
10503 nsCSSValue pair; |
|
10504 pair.SetPairValue(xValue, yValue); |
|
10505 AppendValue(eCSSProperty_border_spacing, pair); |
|
10506 } |
|
10507 return true; |
|
10508 } |
|
10509 |
|
10510 bool |
|
10511 CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[], |
|
10512 bool aSetAllSides) |
|
10513 { |
|
10514 const int32_t numProps = 3; |
|
10515 nsCSSValue values[numProps]; |
|
10516 |
|
10517 int32_t found = ParseChoice(values, aPropIDs, numProps); |
|
10518 if (found < 1) { |
|
10519 return false; |
|
10520 } |
|
10521 |
|
10522 if ((found & 1) == 0) { // Provide default border-width |
|
10523 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated); |
|
10524 } |
|
10525 if ((found & 2) == 0) { // Provide default border-style |
|
10526 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated); |
|
10527 } |
|
10528 if ((found & 4) == 0) { // text color will be used |
|
10529 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated); |
|
10530 } |
|
10531 |
|
10532 if (aSetAllSides) { |
|
10533 static const nsCSSProperty kBorderSources[] = { |
|
10534 eCSSProperty_border_left_color_ltr_source, |
|
10535 eCSSProperty_border_left_color_rtl_source, |
|
10536 eCSSProperty_border_right_color_ltr_source, |
|
10537 eCSSProperty_border_right_color_rtl_source, |
|
10538 eCSSProperty_border_left_style_ltr_source, |
|
10539 eCSSProperty_border_left_style_rtl_source, |
|
10540 eCSSProperty_border_right_style_ltr_source, |
|
10541 eCSSProperty_border_right_style_rtl_source, |
|
10542 eCSSProperty_border_left_width_ltr_source, |
|
10543 eCSSProperty_border_left_width_rtl_source, |
|
10544 eCSSProperty_border_right_width_ltr_source, |
|
10545 eCSSProperty_border_right_width_rtl_source, |
|
10546 eCSSProperty_UNKNOWN |
|
10547 }; |
|
10548 |
|
10549 InitBoxPropsAsPhysical(kBorderSources); |
|
10550 |
|
10551 // Parsing "border" shorthand; set all four sides to the same thing |
|
10552 for (int32_t index = 0; index < 4; index++) { |
|
10553 NS_ASSERTION(numProps == 3, "This code needs updating"); |
|
10554 AppendValue(kBorderWidthIDs[index], values[0]); |
|
10555 AppendValue(kBorderStyleIDs[index], values[1]); |
|
10556 AppendValue(kBorderColorIDs[index], values[2]); |
|
10557 } |
|
10558 |
|
10559 static const nsCSSProperty kBorderColorsProps[] = { |
|
10560 eCSSProperty_border_top_colors, |
|
10561 eCSSProperty_border_right_colors, |
|
10562 eCSSProperty_border_bottom_colors, |
|
10563 eCSSProperty_border_left_colors |
|
10564 }; |
|
10565 |
|
10566 // Set the other properties that the border shorthand sets to their |
|
10567 // initial values. |
|
10568 nsCSSValue extraValue; |
|
10569 switch (values[0].GetUnit()) { |
|
10570 case eCSSUnit_Inherit: |
|
10571 case eCSSUnit_Initial: |
|
10572 case eCSSUnit_Unset: |
|
10573 extraValue = values[0]; |
|
10574 // Set value of border-image properties to initial/inherit/unset |
|
10575 AppendValue(eCSSProperty_border_image_source, extraValue); |
|
10576 AppendValue(eCSSProperty_border_image_slice, extraValue); |
|
10577 AppendValue(eCSSProperty_border_image_width, extraValue); |
|
10578 AppendValue(eCSSProperty_border_image_outset, extraValue); |
|
10579 AppendValue(eCSSProperty_border_image_repeat, extraValue); |
|
10580 break; |
|
10581 default: |
|
10582 extraValue.SetNoneValue(); |
|
10583 SetBorderImageInitialValues(); |
|
10584 break; |
|
10585 } |
|
10586 NS_FOR_CSS_SIDES(side) { |
|
10587 AppendValue(kBorderColorsProps[side], extraValue); |
|
10588 } |
|
10589 } |
|
10590 else { |
|
10591 // Just set our one side |
|
10592 for (int32_t index = 0; index < numProps; index++) { |
|
10593 AppendValue(aPropIDs[index], values[index]); |
|
10594 } |
|
10595 } |
|
10596 return true; |
|
10597 } |
|
10598 |
|
10599 bool |
|
10600 CSSParserImpl::ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[], |
|
10601 int32_t aSourceType) |
|
10602 { |
|
10603 const int32_t numProps = 3; |
|
10604 nsCSSValue values[numProps]; |
|
10605 |
|
10606 int32_t found = ParseChoice(values, aPropIDs, numProps); |
|
10607 if (found < 1) { |
|
10608 return false; |
|
10609 } |
|
10610 |
|
10611 if ((found & 1) == 0) { // Provide default border-width |
|
10612 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated); |
|
10613 } |
|
10614 if ((found & 2) == 0) { // Provide default border-style |
|
10615 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated); |
|
10616 } |
|
10617 if ((found & 4) == 0) { // text color will be used |
|
10618 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated); |
|
10619 } |
|
10620 for (int32_t index = 0; index < numProps; index++) { |
|
10621 const nsCSSProperty* subprops = |
|
10622 nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]); |
|
10623 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN, |
|
10624 "not box property with physical vs. logical cascading"); |
|
10625 AppendValue(subprops[0], values[index]); |
|
10626 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated); |
|
10627 AppendValue(subprops[1], typeVal); |
|
10628 AppendValue(subprops[2], typeVal); |
|
10629 } |
|
10630 return true; |
|
10631 } |
|
10632 |
|
10633 bool |
|
10634 CSSParserImpl::ParseBorderStyle() |
|
10635 { |
|
10636 static const nsCSSProperty kBorderStyleSources[] = { |
|
10637 eCSSProperty_border_left_style_ltr_source, |
|
10638 eCSSProperty_border_left_style_rtl_source, |
|
10639 eCSSProperty_border_right_style_ltr_source, |
|
10640 eCSSProperty_border_right_style_rtl_source, |
|
10641 eCSSProperty_UNKNOWN |
|
10642 }; |
|
10643 |
|
10644 // do this now, in case 4 values weren't specified |
|
10645 InitBoxPropsAsPhysical(kBorderStyleSources); |
|
10646 return ParseBoxProperties(kBorderStyleIDs); |
|
10647 } |
|
10648 |
|
10649 bool |
|
10650 CSSParserImpl::ParseBorderWidth() |
|
10651 { |
|
10652 static const nsCSSProperty kBorderWidthSources[] = { |
|
10653 eCSSProperty_border_left_width_ltr_source, |
|
10654 eCSSProperty_border_left_width_rtl_source, |
|
10655 eCSSProperty_border_right_width_ltr_source, |
|
10656 eCSSProperty_border_right_width_rtl_source, |
|
10657 eCSSProperty_UNKNOWN |
|
10658 }; |
|
10659 |
|
10660 // do this now, in case 4 values weren't specified |
|
10661 InitBoxPropsAsPhysical(kBorderWidthSources); |
|
10662 return ParseBoxProperties(kBorderWidthIDs); |
|
10663 } |
|
10664 |
|
10665 bool |
|
10666 CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty) |
|
10667 { |
|
10668 nsCSSValue value; |
|
10669 // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own |
|
10670 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { |
|
10671 nsCSSValueList *cur = value.SetListValue(); |
|
10672 for (;;) { |
|
10673 if (!ParseVariant(cur->mValue, VARIANT_COLOR | VARIANT_KEYWORD, |
|
10674 nsCSSProps::kBorderColorKTable)) { |
|
10675 return false; |
|
10676 } |
|
10677 if (CheckEndProperty()) { |
|
10678 break; |
|
10679 } |
|
10680 cur->mNext = new nsCSSValueList; |
|
10681 cur = cur->mNext; |
|
10682 } |
|
10683 } |
|
10684 AppendValue(aProperty, value); |
|
10685 return true; |
|
10686 } |
|
10687 |
|
10688 // Parse the top level of a calc() expression. |
|
10689 bool |
|
10690 CSSParserImpl::ParseCalc(nsCSSValue &aValue, int32_t aVariantMask) |
|
10691 { |
|
10692 // Parsing calc expressions requires, in a number of cases, looking |
|
10693 // for a token that is *either* a value of the property or a number. |
|
10694 // This can be done without lookahead when we assume that the property |
|
10695 // values cannot themselves be numbers. |
|
10696 NS_ASSERTION(!(aVariantMask & VARIANT_NUMBER), "unexpected variant mask"); |
|
10697 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask"); |
|
10698 |
|
10699 bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk; |
|
10700 mUnitlessLengthQuirk = false; |
|
10701 |
|
10702 // One-iteration loop so we can break to the error-handling case. |
|
10703 do { |
|
10704 // The toplevel of a calc() is always an nsCSSValue::Array of length 1. |
|
10705 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1); |
|
10706 |
|
10707 if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask)) |
|
10708 break; |
|
10709 |
|
10710 if (!ExpectSymbol(')', true)) |
|
10711 break; |
|
10712 |
|
10713 aValue.SetArrayValue(arr, eCSSUnit_Calc); |
|
10714 mUnitlessLengthQuirk = oldUnitlessLengthQuirk; |
|
10715 return true; |
|
10716 } while (false); |
|
10717 |
|
10718 SkipUntil(')'); |
|
10719 mUnitlessLengthQuirk = oldUnitlessLengthQuirk; |
|
10720 return false; |
|
10721 } |
|
10722 |
|
10723 // We optimize away the <value-expression> production given that |
|
10724 // ParseVariant consumes initial whitespace and we call |
|
10725 // ExpectSymbol(')') with true for aSkipWS. |
|
10726 // * If aVariantMask is VARIANT_NUMBER, this function parses the |
|
10727 // <number-additive-expression> production. |
|
10728 // * If aVariantMask does not contain VARIANT_NUMBER, this function |
|
10729 // parses the <value-additive-expression> production. |
|
10730 // * Otherwise (VARIANT_NUMBER and other bits) this function parses |
|
10731 // whichever one of the productions matches ***and modifies |
|
10732 // aVariantMask*** to reflect which one it has parsed by either |
|
10733 // removing VARIANT_NUMBER or removing all other bits. |
|
10734 // It does so iteratively, but builds the correct recursive |
|
10735 // data structure. |
|
10736 bool |
|
10737 CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue, |
|
10738 int32_t& aVariantMask) |
|
10739 { |
|
10740 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask"); |
|
10741 nsCSSValue *storage = &aValue; |
|
10742 for (;;) { |
|
10743 bool haveWS; |
|
10744 if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS)) |
|
10745 return false; |
|
10746 |
|
10747 if (!haveWS || !GetToken(false)) |
|
10748 return true; |
|
10749 nsCSSUnit unit; |
|
10750 if (mToken.IsSymbol('+')) { |
|
10751 unit = eCSSUnit_Calc_Plus; |
|
10752 } else if (mToken.IsSymbol('-')) { |
|
10753 unit = eCSSUnit_Calc_Minus; |
|
10754 } else { |
|
10755 UngetToken(); |
|
10756 return true; |
|
10757 } |
|
10758 if (!RequireWhitespace()) |
|
10759 return false; |
|
10760 |
|
10761 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2); |
|
10762 arr->Item(0) = aValue; |
|
10763 storage = &arr->Item(1); |
|
10764 aValue.SetArrayValue(arr, unit); |
|
10765 } |
|
10766 } |
|
10767 |
|
10768 struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps, |
|
10769 public mozilla::css::CSSValueInputCalcOps |
|
10770 { |
|
10771 result_type ComputeLeafValue(const nsCSSValue& aValue) |
|
10772 { |
|
10773 NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit"); |
|
10774 return aValue.GetFloatValue(); |
|
10775 } |
|
10776 |
|
10777 float ComputeNumber(const nsCSSValue& aValue) |
|
10778 { |
|
10779 return mozilla::css::ComputeCalc(aValue, *this); |
|
10780 } |
|
10781 }; |
|
10782 |
|
10783 // * If aVariantMask is VARIANT_NUMBER, this function parses the |
|
10784 // <number-multiplicative-expression> production. |
|
10785 // * If aVariantMask does not contain VARIANT_NUMBER, this function |
|
10786 // parses the <value-multiplicative-expression> production. |
|
10787 // * Otherwise (VARIANT_NUMBER and other bits) this function parses |
|
10788 // whichever one of the productions matches ***and modifies |
|
10789 // aVariantMask*** to reflect which one it has parsed by either |
|
10790 // removing VARIANT_NUMBER or removing all other bits. |
|
10791 // It does so iteratively, but builds the correct recursive data |
|
10792 // structure. |
|
10793 // This function always consumes *trailing* whitespace when it returns |
|
10794 // true; whether there was any such whitespace is returned in the |
|
10795 // aHadFinalWS parameter. |
|
10796 bool |
|
10797 CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue, |
|
10798 int32_t& aVariantMask, |
|
10799 bool *aHadFinalWS) |
|
10800 { |
|
10801 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask"); |
|
10802 bool gotValue = false; // already got the part with the unit |
|
10803 bool afterDivision = false; |
|
10804 |
|
10805 nsCSSValue *storage = &aValue; |
|
10806 for (;;) { |
|
10807 int32_t variantMask; |
|
10808 if (afterDivision || gotValue) { |
|
10809 variantMask = VARIANT_NUMBER; |
|
10810 } else { |
|
10811 variantMask = aVariantMask | VARIANT_NUMBER; |
|
10812 } |
|
10813 if (!ParseCalcTerm(*storage, variantMask)) |
|
10814 return false; |
|
10815 NS_ABORT_IF_FALSE(variantMask != 0, |
|
10816 "ParseCalcTerm did not set variantMask appropriately"); |
|
10817 NS_ABORT_IF_FALSE(!(variantMask & VARIANT_NUMBER) || |
|
10818 !(variantMask & ~int32_t(VARIANT_NUMBER)), |
|
10819 "ParseCalcTerm did not set variantMask appropriately"); |
|
10820 |
|
10821 if (variantMask & VARIANT_NUMBER) { |
|
10822 // Simplify the value immediately so we can check for division by |
|
10823 // zero. |
|
10824 ReduceNumberCalcOps ops; |
|
10825 float number = mozilla::css::ComputeCalc(*storage, ops); |
|
10826 if (number == 0.0 && afterDivision) |
|
10827 return false; |
|
10828 storage->SetFloatValue(number, eCSSUnit_Number); |
|
10829 } else { |
|
10830 gotValue = true; |
|
10831 |
|
10832 if (storage != &aValue) { |
|
10833 // Simplify any numbers in the Times_L position (which are |
|
10834 // not simplified by the check above). |
|
10835 NS_ABORT_IF_FALSE(storage == &aValue.GetArrayValue()->Item(1), |
|
10836 "unexpected relationship to current storage"); |
|
10837 nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0); |
|
10838 ReduceNumberCalcOps ops; |
|
10839 float number = mozilla::css::ComputeCalc(leftValue, ops); |
|
10840 leftValue.SetFloatValue(number, eCSSUnit_Number); |
|
10841 } |
|
10842 } |
|
10843 |
|
10844 bool hadWS = RequireWhitespace(); |
|
10845 if (!GetToken(false)) { |
|
10846 *aHadFinalWS = hadWS; |
|
10847 break; |
|
10848 } |
|
10849 nsCSSUnit unit; |
|
10850 if (mToken.IsSymbol('*')) { |
|
10851 unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L; |
|
10852 afterDivision = false; |
|
10853 } else if (mToken.IsSymbol('/')) { |
|
10854 unit = eCSSUnit_Calc_Divided; |
|
10855 afterDivision = true; |
|
10856 } else { |
|
10857 UngetToken(); |
|
10858 *aHadFinalWS = hadWS; |
|
10859 break; |
|
10860 } |
|
10861 |
|
10862 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2); |
|
10863 arr->Item(0) = aValue; |
|
10864 storage = &arr->Item(1); |
|
10865 aValue.SetArrayValue(arr, unit); |
|
10866 } |
|
10867 |
|
10868 // Adjust aVariantMask (see comments above function) to reflect which |
|
10869 // option we took. |
|
10870 if (aVariantMask & VARIANT_NUMBER) { |
|
10871 if (gotValue) { |
|
10872 aVariantMask &= ~int32_t(VARIANT_NUMBER); |
|
10873 } else { |
|
10874 aVariantMask = VARIANT_NUMBER; |
|
10875 } |
|
10876 } else { |
|
10877 if (!gotValue) { |
|
10878 // We had to find a value, but we didn't. |
|
10879 return false; |
|
10880 } |
|
10881 } |
|
10882 |
|
10883 return true; |
|
10884 } |
|
10885 |
|
10886 // * If aVariantMask is VARIANT_NUMBER, this function parses the |
|
10887 // <number-term> production. |
|
10888 // * If aVariantMask does not contain VARIANT_NUMBER, this function |
|
10889 // parses the <value-term> production. |
|
10890 // * Otherwise (VARIANT_NUMBER and other bits) this function parses |
|
10891 // whichever one of the productions matches ***and modifies |
|
10892 // aVariantMask*** to reflect which one it has parsed by either |
|
10893 // removing VARIANT_NUMBER or removing all other bits. |
|
10894 bool |
|
10895 CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask) |
|
10896 { |
|
10897 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask"); |
|
10898 if (!GetToken(true)) |
|
10899 return false; |
|
10900 // Either an additive expression in parentheses... |
|
10901 if (mToken.IsSymbol('(')) { |
|
10902 if (!ParseCalcAdditiveExpression(aValue, aVariantMask) || |
|
10903 !ExpectSymbol(')', true)) { |
|
10904 SkipUntil(')'); |
|
10905 return false; |
|
10906 } |
|
10907 return true; |
|
10908 } |
|
10909 // ... or just a value |
|
10910 UngetToken(); |
|
10911 // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero |
|
10912 // always gets picked up |
|
10913 if (!ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr)) { |
|
10914 return false; |
|
10915 } |
|
10916 // ...and do the VARIANT_NUMBER check ourselves. |
|
10917 if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) { |
|
10918 return false; |
|
10919 } |
|
10920 // If we did the value parsing, we need to adjust aVariantMask to |
|
10921 // reflect which option we took (see above). |
|
10922 if (aVariantMask & VARIANT_NUMBER) { |
|
10923 if (aValue.GetUnit() == eCSSUnit_Number) { |
|
10924 aVariantMask = VARIANT_NUMBER; |
|
10925 } else { |
|
10926 aVariantMask &= ~int32_t(VARIANT_NUMBER); |
|
10927 } |
|
10928 } |
|
10929 return true; |
|
10930 } |
|
10931 |
|
10932 // This function consumes all consecutive whitespace and returns whether |
|
10933 // there was any. |
|
10934 bool |
|
10935 CSSParserImpl::RequireWhitespace() |
|
10936 { |
|
10937 if (!GetToken(false)) |
|
10938 return false; |
|
10939 if (mToken.mType != eCSSToken_Whitespace) { |
|
10940 UngetToken(); |
|
10941 return false; |
|
10942 } |
|
10943 // Skip any additional whitespace tokens. |
|
10944 if (GetToken(true)) { |
|
10945 UngetToken(); |
|
10946 } |
|
10947 return true; |
|
10948 } |
|
10949 |
|
10950 bool |
|
10951 CSSParserImpl::ParseRect(nsCSSProperty aPropID) |
|
10952 { |
|
10953 nsCSSValue val; |
|
10954 if (ParseVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) { |
|
10955 AppendValue(aPropID, val); |
|
10956 return true; |
|
10957 } |
|
10958 |
|
10959 if (! GetToken(true)) { |
|
10960 return false; |
|
10961 } |
|
10962 |
|
10963 if (mToken.mType == eCSSToken_Function && |
|
10964 mToken.mIdent.LowerCaseEqualsLiteral("rect")) { |
|
10965 nsCSSRect& rect = val.SetRectValue(); |
|
10966 bool useCommas; |
|
10967 NS_FOR_CSS_SIDES(side) { |
|
10968 if (! ParseVariant(rect.*(nsCSSRect::sides[side]), |
|
10969 VARIANT_AL, nullptr)) { |
|
10970 return false; |
|
10971 } |
|
10972 if (side == 0) { |
|
10973 useCommas = ExpectSymbol(',', true); |
|
10974 } else if (useCommas && side < 3) { |
|
10975 // Skip optional commas between elements, but only if the first |
|
10976 // separator was a comma. |
|
10977 if (!ExpectSymbol(',', true)) { |
|
10978 return false; |
|
10979 } |
|
10980 } |
|
10981 } |
|
10982 if (!ExpectSymbol(')', true)) { |
|
10983 return false; |
|
10984 } |
|
10985 } else { |
|
10986 UngetToken(); |
|
10987 return false; |
|
10988 } |
|
10989 |
|
10990 AppendValue(aPropID, val); |
|
10991 return true; |
|
10992 } |
|
10993 |
|
10994 bool |
|
10995 CSSParserImpl::ParseColumns() |
|
10996 { |
|
10997 // We use a similar "fake value" hack to ParseListStyle, because |
|
10998 // "auto" is acceptable for both column-count and column-width. |
|
10999 // If the fake "auto" value is found, and one of the real values isn't, |
|
11000 // that means the fake auto value is meant for the real value we didn't |
|
11001 // find. |
|
11002 static const nsCSSProperty columnIDs[] = { |
|
11003 eCSSPropertyExtra_x_auto_value, |
|
11004 eCSSProperty__moz_column_count, |
|
11005 eCSSProperty__moz_column_width |
|
11006 }; |
|
11007 const int32_t numProps = MOZ_ARRAY_LENGTH(columnIDs); |
|
11008 |
|
11009 nsCSSValue values[numProps]; |
|
11010 int32_t found = ParseChoice(values, columnIDs, numProps); |
|
11011 if (found < 1) { |
|
11012 return false; |
|
11013 } |
|
11014 if ((found & (1|2|4)) == (1|2|4) && |
|
11015 values[0].GetUnit() == eCSSUnit_Auto) { |
|
11016 // We filled all 3 values, which is invalid |
|
11017 return false; |
|
11018 } |
|
11019 |
|
11020 if ((found & 2) == 0) { |
|
11021 // Provide auto column-count |
|
11022 values[1].SetAutoValue(); |
|
11023 } |
|
11024 if ((found & 4) == 0) { |
|
11025 // Provide auto column-width |
|
11026 values[2].SetAutoValue(); |
|
11027 } |
|
11028 |
|
11029 // Start at index 1 to skip the fake auto value. |
|
11030 for (int32_t index = 1; index < numProps; index++) { |
|
11031 AppendValue(columnIDs[index], values[index]); |
|
11032 } |
|
11033 return true; |
|
11034 } |
|
11035 |
|
11036 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \ |
|
11037 VARIANT_KEYWORD) |
|
11038 bool |
|
11039 CSSParserImpl::ParseContent() |
|
11040 { |
|
11041 // We need to divide the 'content' keywords into two classes for |
|
11042 // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable. |
|
11043 static const KTableValue kContentListKWs[] = { |
|
11044 eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE, |
|
11045 eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE, |
|
11046 eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE, |
|
11047 eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE, |
|
11048 eCSSKeyword_UNKNOWN,-1 |
|
11049 }; |
|
11050 |
|
11051 static const KTableValue kContentSolitaryKWs[] = { |
|
11052 eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT, |
|
11053 eCSSKeyword_UNKNOWN,-1 |
|
11054 }; |
|
11055 |
|
11056 // Verify that these two lists add up to the size of |
|
11057 // nsCSSProps::kContentKTable. |
|
11058 NS_ABORT_IF_FALSE(nsCSSProps::kContentKTable[ |
|
11059 ArrayLength(kContentListKWs) + |
|
11060 ArrayLength(kContentSolitaryKWs) - 4] == |
|
11061 eCSSKeyword_UNKNOWN && |
|
11062 nsCSSProps::kContentKTable[ |
|
11063 ArrayLength(kContentListKWs) + |
|
11064 ArrayLength(kContentSolitaryKWs) - 3] == -1, |
|
11065 "content keyword tables out of sync"); |
|
11066 |
|
11067 nsCSSValue value; |
|
11068 // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must |
|
11069 // be alone |
|
11070 if (!ParseVariant(value, VARIANT_HMK | VARIANT_NONE, |
|
11071 kContentSolitaryKWs)) { |
|
11072 nsCSSValueList* cur = value.SetListValue(); |
|
11073 for (;;) { |
|
11074 if (!ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs)) { |
|
11075 return false; |
|
11076 } |
|
11077 if (CheckEndProperty()) { |
|
11078 break; |
|
11079 } |
|
11080 cur->mNext = new nsCSSValueList; |
|
11081 cur = cur->mNext; |
|
11082 } |
|
11083 } |
|
11084 AppendValue(eCSSProperty_content, value); |
|
11085 return true; |
|
11086 } |
|
11087 |
|
11088 bool |
|
11089 CSSParserImpl::ParseCounterData(nsCSSProperty aPropID) |
|
11090 { |
|
11091 static const nsCSSKeyword kCounterDataKTable[] = { |
|
11092 eCSSKeyword_none, |
|
11093 eCSSKeyword_UNKNOWN |
|
11094 }; |
|
11095 nsCSSValue value; |
|
11096 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { |
|
11097 if (!GetToken(true)) { |
|
11098 return false; |
|
11099 } |
|
11100 if (mToken.mType != eCSSToken_Ident) { |
|
11101 UngetToken(); |
|
11102 return false; |
|
11103 } |
|
11104 |
|
11105 nsCSSValuePairList *cur = value.SetPairListValue(); |
|
11106 for (;;) { |
|
11107 if (!ParseCustomIdent(cur->mXValue, mToken.mIdent, kCounterDataKTable)) { |
|
11108 return false; |
|
11109 } |
|
11110 if (!GetToken(true)) { |
|
11111 break; |
|
11112 } |
|
11113 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) { |
|
11114 cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer); |
|
11115 } else { |
|
11116 UngetToken(); |
|
11117 } |
|
11118 if (!GetToken(true)) { |
|
11119 break; |
|
11120 } |
|
11121 if (mToken.mType != eCSSToken_Ident) { |
|
11122 UngetToken(); |
|
11123 break; |
|
11124 } |
|
11125 cur->mNext = new nsCSSValuePairList; |
|
11126 cur = cur->mNext; |
|
11127 } |
|
11128 } |
|
11129 AppendValue(aPropID, value); |
|
11130 return true; |
|
11131 } |
|
11132 |
|
11133 bool |
|
11134 CSSParserImpl::ParseCursor() |
|
11135 { |
|
11136 nsCSSValue value; |
|
11137 // 'inherit', 'initial' and 'unset' must be alone |
|
11138 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
11139 nsCSSValueList* cur = value.SetListValue(); |
|
11140 for (;;) { |
|
11141 if (!ParseVariant(cur->mValue, VARIANT_UK, nsCSSProps::kCursorKTable)) { |
|
11142 return false; |
|
11143 } |
|
11144 if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last |
|
11145 break; |
|
11146 } |
|
11147 |
|
11148 // We have a URL, so make a value array with three values. |
|
11149 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3); |
|
11150 val->Item(0) = cur->mValue; |
|
11151 |
|
11152 // Parse optional x and y position of cursor hotspot (css3-ui). |
|
11153 if (ParseVariant(val->Item(1), VARIANT_NUMBER, nullptr)) { |
|
11154 // If we have one number, we must have two. |
|
11155 if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nullptr)) { |
|
11156 return false; |
|
11157 } |
|
11158 } |
|
11159 cur->mValue.SetArrayValue(val, eCSSUnit_Array); |
|
11160 |
|
11161 if (!ExpectSymbol(',', true)) { // url must not be last |
|
11162 return false; |
|
11163 } |
|
11164 cur->mNext = new nsCSSValueList; |
|
11165 cur = cur->mNext; |
|
11166 } |
|
11167 } |
|
11168 AppendValue(eCSSProperty_cursor, value); |
|
11169 return true; |
|
11170 } |
|
11171 |
|
11172 |
|
11173 bool |
|
11174 CSSParserImpl::ParseFont() |
|
11175 { |
|
11176 static const nsCSSProperty fontIDs[] = { |
|
11177 eCSSProperty_font_style, |
|
11178 eCSSProperty_font_variant, |
|
11179 eCSSProperty_font_weight |
|
11180 }; |
|
11181 |
|
11182 // font-variant-alternates enabled ==> layout.css.font-features.enabled is true |
|
11183 bool featuresEnabled = |
|
11184 nsCSSProps::IsEnabled(eCSSProperty_font_variant_alternates); |
|
11185 nsCSSValue family; |
|
11186 if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) { |
|
11187 if (eCSSUnit_Inherit == family.GetUnit() || |
|
11188 eCSSUnit_Initial == family.GetUnit() || |
|
11189 eCSSUnit_Unset == family.GetUnit()) { |
|
11190 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None)); |
|
11191 AppendValue(eCSSProperty_font_family, family); |
|
11192 AppendValue(eCSSProperty_font_style, family); |
|
11193 AppendValue(eCSSProperty_font_variant, family); |
|
11194 AppendValue(eCSSProperty_font_weight, family); |
|
11195 AppendValue(eCSSProperty_font_size, family); |
|
11196 AppendValue(eCSSProperty_line_height, family); |
|
11197 AppendValue(eCSSProperty_font_stretch, family); |
|
11198 AppendValue(eCSSProperty_font_size_adjust, family); |
|
11199 AppendValue(eCSSProperty_font_feature_settings, family); |
|
11200 AppendValue(eCSSProperty_font_language_override, family); |
|
11201 if (featuresEnabled) { |
|
11202 AppendValue(eCSSProperty_font_kerning, family); |
|
11203 AppendValue(eCSSProperty_font_synthesis, family); |
|
11204 AppendValue(eCSSProperty_font_variant_alternates, family); |
|
11205 AppendValue(eCSSProperty_font_variant_caps, family); |
|
11206 AppendValue(eCSSProperty_font_variant_east_asian, family); |
|
11207 AppendValue(eCSSProperty_font_variant_ligatures, family); |
|
11208 AppendValue(eCSSProperty_font_variant_numeric, family); |
|
11209 AppendValue(eCSSProperty_font_variant_position, family); |
|
11210 } |
|
11211 } |
|
11212 else { |
|
11213 AppendValue(eCSSProperty__x_system_font, family); |
|
11214 nsCSSValue systemFont(eCSSUnit_System_Font); |
|
11215 AppendValue(eCSSProperty_font_family, systemFont); |
|
11216 AppendValue(eCSSProperty_font_style, systemFont); |
|
11217 AppendValue(eCSSProperty_font_variant, systemFont); |
|
11218 AppendValue(eCSSProperty_font_weight, systemFont); |
|
11219 AppendValue(eCSSProperty_font_size, systemFont); |
|
11220 AppendValue(eCSSProperty_line_height, systemFont); |
|
11221 AppendValue(eCSSProperty_font_stretch, systemFont); |
|
11222 AppendValue(eCSSProperty_font_size_adjust, systemFont); |
|
11223 AppendValue(eCSSProperty_font_feature_settings, systemFont); |
|
11224 AppendValue(eCSSProperty_font_language_override, systemFont); |
|
11225 if (featuresEnabled) { |
|
11226 AppendValue(eCSSProperty_font_kerning, systemFont); |
|
11227 AppendValue(eCSSProperty_font_synthesis, systemFont); |
|
11228 AppendValue(eCSSProperty_font_variant_alternates, systemFont); |
|
11229 AppendValue(eCSSProperty_font_variant_caps, systemFont); |
|
11230 AppendValue(eCSSProperty_font_variant_east_asian, systemFont); |
|
11231 AppendValue(eCSSProperty_font_variant_ligatures, systemFont); |
|
11232 AppendValue(eCSSProperty_font_variant_numeric, systemFont); |
|
11233 AppendValue(eCSSProperty_font_variant_position, systemFont); |
|
11234 } |
|
11235 } |
|
11236 return true; |
|
11237 } |
|
11238 |
|
11239 // Get optional font-style, font-variant and font-weight (in any order) |
|
11240 const int32_t numProps = 3; |
|
11241 nsCSSValue values[numProps]; |
|
11242 int32_t found = ParseChoice(values, fontIDs, numProps); |
|
11243 if (found < 0 || |
|
11244 eCSSUnit_Inherit == values[0].GetUnit() || |
|
11245 eCSSUnit_Initial == values[0].GetUnit() || |
|
11246 eCSSUnit_Unset == values[0].GetUnit()) { // illegal data |
|
11247 return false; |
|
11248 } |
|
11249 if ((found & 1) == 0) { |
|
11250 // Provide default font-style |
|
11251 values[0].SetIntValue(NS_FONT_STYLE_NORMAL, eCSSUnit_Enumerated); |
|
11252 } |
|
11253 if ((found & 2) == 0) { |
|
11254 // Provide default font-variant |
|
11255 values[1].SetIntValue(NS_FONT_VARIANT_NORMAL, eCSSUnit_Enumerated); |
|
11256 } |
|
11257 if ((found & 4) == 0) { |
|
11258 // Provide default font-weight |
|
11259 values[2].SetIntValue(NS_FONT_WEIGHT_NORMAL, eCSSUnit_Enumerated); |
|
11260 } |
|
11261 |
|
11262 // Get mandatory font-size |
|
11263 nsCSSValue size; |
|
11264 if (! ParseNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP, |
|
11265 nsCSSProps::kFontSizeKTable)) { |
|
11266 return false; |
|
11267 } |
|
11268 |
|
11269 // Get optional "/" line-height |
|
11270 nsCSSValue lineHeight; |
|
11271 if (ExpectSymbol('/', true)) { |
|
11272 if (! ParseNonNegativeVariant(lineHeight, |
|
11273 VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL, |
|
11274 nullptr)) { |
|
11275 return false; |
|
11276 } |
|
11277 } |
|
11278 else { |
|
11279 lineHeight.SetNormalValue(); |
|
11280 } |
|
11281 |
|
11282 // Get final mandatory font-family |
|
11283 nsAutoParseCompoundProperty compound(this); |
|
11284 if (ParseFamily(family)) { |
|
11285 if (eCSSUnit_Inherit != family.GetUnit() && |
|
11286 eCSSUnit_Initial != family.GetUnit() && |
|
11287 eCSSUnit_Unset != family.GetUnit()) { |
|
11288 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None)); |
|
11289 AppendValue(eCSSProperty_font_family, family); |
|
11290 AppendValue(eCSSProperty_font_style, values[0]); |
|
11291 AppendValue(eCSSProperty_font_variant, values[1]); |
|
11292 AppendValue(eCSSProperty_font_weight, values[2]); |
|
11293 AppendValue(eCSSProperty_font_size, size); |
|
11294 AppendValue(eCSSProperty_line_height, lineHeight); |
|
11295 AppendValue(eCSSProperty_font_stretch, |
|
11296 nsCSSValue(NS_FONT_STRETCH_NORMAL, eCSSUnit_Enumerated)); |
|
11297 AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None)); |
|
11298 AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal)); |
|
11299 AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal)); |
|
11300 if (featuresEnabled) { |
|
11301 AppendValue(eCSSProperty_font_kerning, |
|
11302 nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated)); |
|
11303 AppendValue(eCSSProperty_font_synthesis, |
|
11304 nsCSSValue(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE, |
|
11305 eCSSUnit_Enumerated)); |
|
11306 AppendValue(eCSSProperty_font_variant_alternates, |
|
11307 nsCSSValue(eCSSUnit_Normal)); |
|
11308 AppendValue(eCSSProperty_font_variant_caps, nsCSSValue(eCSSUnit_Normal)); |
|
11309 AppendValue(eCSSProperty_font_variant_east_asian, |
|
11310 nsCSSValue(eCSSUnit_Normal)); |
|
11311 AppendValue(eCSSProperty_font_variant_ligatures, |
|
11312 nsCSSValue(eCSSUnit_Normal)); |
|
11313 AppendValue(eCSSProperty_font_variant_numeric, |
|
11314 nsCSSValue(eCSSUnit_Normal)); |
|
11315 AppendValue(eCSSProperty_font_variant_position, |
|
11316 nsCSSValue(eCSSUnit_Normal)); |
|
11317 } |
|
11318 return true; |
|
11319 } |
|
11320 } |
|
11321 return false; |
|
11322 } |
|
11323 |
|
11324 bool |
|
11325 CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue) |
|
11326 { |
|
11327 if (!ParseVariant(aValue, VARIANT_HK | VARIANT_NONE, |
|
11328 nsCSSProps::kFontSynthesisKTable)) { |
|
11329 return false; |
|
11330 } |
|
11331 |
|
11332 // first value 'none' ==> done |
|
11333 if (eCSSUnit_None == aValue.GetUnit() || |
|
11334 eCSSUnit_Initial == aValue.GetUnit() || |
|
11335 eCSSUnit_Inherit == aValue.GetUnit() || |
|
11336 eCSSUnit_Unset == aValue.GetUnit()) |
|
11337 { |
|
11338 return true; |
|
11339 } |
|
11340 |
|
11341 // look for a second value |
|
11342 int32_t intValue = aValue.GetIntValue(); |
|
11343 nsCSSValue nextValue; |
|
11344 |
|
11345 if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) { |
|
11346 int32_t nextIntValue = nextValue.GetIntValue(); |
|
11347 if (nextIntValue & intValue) { |
|
11348 return false; |
|
11349 } |
|
11350 aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated); |
|
11351 } |
|
11352 |
|
11353 return true; |
|
11354 } |
|
11355 |
|
11356 // font-variant-alternates allows for a combination of multiple |
|
11357 // simple enumerated values and functional values. Functional values have |
|
11358 // parameter lists with one or more idents which are later resolved |
|
11359 // based on values defined in @font-feature-value rules. |
|
11360 // |
|
11361 // font-variant-alternates: swash(flowing), historical-forms, styleset(alt-g, alt-m); |
|
11362 // |
|
11363 // So for this the nsCSSValue is set to a pair value, with one |
|
11364 // value for a bitmask of both simple and functional property values |
|
11365 // and another value containing a ValuePairList with lists of idents |
|
11366 // for each functional property value. |
|
11367 // |
|
11368 // pairValue |
|
11369 // o intValue |
|
11370 // NS_FONT_VARIANT_ALTERNATES_SWASH | |
|
11371 // NS_FONT_VARIANT_ALTERNATES_STYLESET |
|
11372 // o valuePairList, each element with |
|
11373 // - intValue - indicates which alternate |
|
11374 // - string or valueList of strings |
|
11375 // |
|
11376 // Note: when only 'historical-forms' is specified, there are no |
|
11377 // functional values to store, in which case the valuePairList is a |
|
11378 // single element dummy list. In all other cases, the length of the |
|
11379 // list will match the number of functional values. |
|
11380 |
|
11381 #define MAX_ALLOWED_FEATURES 512 |
|
11382 |
|
11383 bool |
|
11384 CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature, |
|
11385 nsCSSValue& aValue) |
|
11386 { |
|
11387 if (!GetToken(true)) { |
|
11388 return false; |
|
11389 } |
|
11390 |
|
11391 bool isIdent = (mToken.mType == eCSSToken_Ident); |
|
11392 if (mToken.mType != eCSSToken_Function && !isIdent) { |
|
11393 UngetToken(); |
|
11394 return false; |
|
11395 } |
|
11396 |
|
11397 // ident ==> simple enumerated prop val (e.g. historical-forms) |
|
11398 // function ==> e.g. swash(flowing) styleset(alt-g, alt-m) |
|
11399 |
|
11400 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); |
|
11401 if (!(eCSSKeyword_UNKNOWN < keyword && |
|
11402 nsCSSProps::FindKeyword(keyword, |
|
11403 (isIdent ? |
|
11404 nsCSSProps::kFontVariantAlternatesKTable : |
|
11405 nsCSSProps::kFontVariantAlternatesFuncsKTable), |
|
11406 aWhichFeature))) |
|
11407 { |
|
11408 // failed, pop token |
|
11409 UngetToken(); |
|
11410 return false; |
|
11411 } |
|
11412 |
|
11413 if (isIdent) { |
|
11414 aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated); |
|
11415 return true; |
|
11416 } |
|
11417 |
|
11418 uint16_t maxElems = 1; |
|
11419 if (keyword == eCSSKeyword_styleset || |
|
11420 keyword == eCSSKeyword_character_variant) { |
|
11421 maxElems = MAX_ALLOWED_FEATURES; |
|
11422 } |
|
11423 return ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER, |
|
11424 1, maxElems, aValue); |
|
11425 } |
|
11426 |
|
11427 bool |
|
11428 CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue) |
|
11429 { |
|
11430 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) { |
|
11431 return true; |
|
11432 } |
|
11433 |
|
11434 // iterate through parameters |
|
11435 nsCSSValue listValue; |
|
11436 int32_t feature, featureFlags = 0; |
|
11437 |
|
11438 // if no functional values, this may be a list with a single, unused element |
|
11439 listValue.SetListValue(); |
|
11440 |
|
11441 nsCSSValueList* list = nullptr; |
|
11442 nsCSSValue value; |
|
11443 while (ParseSingleAlternate(feature, value)) { |
|
11444 |
|
11445 // check to make sure value not already set |
|
11446 if (feature == 0 || |
|
11447 feature & featureFlags) { |
|
11448 return false; |
|
11449 } |
|
11450 |
|
11451 featureFlags |= feature; |
|
11452 |
|
11453 // if function, need to add to the list of functions |
|
11454 if (value.GetUnit() == eCSSUnit_Function) { |
|
11455 if (!list) { |
|
11456 list = listValue.GetListValue(); |
|
11457 } else { |
|
11458 list->mNext = new nsCSSValueList; |
|
11459 list = list->mNext; |
|
11460 } |
|
11461 list->mValue = value; |
|
11462 } |
|
11463 } |
|
11464 |
|
11465 if (featureFlags == 0) { |
|
11466 // ParseSingleAlternate failed the first time through the loop. |
|
11467 return false; |
|
11468 } |
|
11469 |
|
11470 nsCSSValue featureValue; |
|
11471 featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated); |
|
11472 aValue.SetPairValue(featureValue, listValue); |
|
11473 |
|
11474 return true; |
|
11475 } |
|
11476 |
|
11477 // aMasks - array of masks for mutually-exclusive property values, |
|
11478 // e.g. proportial-nums, tabular-nums |
|
11479 |
|
11480 bool |
|
11481 CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue, |
|
11482 const KTableValue aKeywordTable[], |
|
11483 const int32_t aMasks[]) |
|
11484 { |
|
11485 // Parse at least one keyword |
|
11486 if (!ParseEnum(aValue, aKeywordTable)) { |
|
11487 return false; |
|
11488 } |
|
11489 |
|
11490 // look for more values |
|
11491 nsCSSValue nextValue; |
|
11492 int32_t mergedValue = aValue.GetIntValue(); |
|
11493 |
|
11494 while (ParseEnum(nextValue, aKeywordTable)) |
|
11495 { |
|
11496 int32_t nextIntValue = nextValue.GetIntValue(); |
|
11497 |
|
11498 // check to make sure value not already set |
|
11499 if (nextIntValue & mergedValue) { |
|
11500 return false; |
|
11501 } |
|
11502 |
|
11503 const int32_t *m = aMasks; |
|
11504 int32_t c = 0; |
|
11505 |
|
11506 while (*m != MASK_END_VALUE) { |
|
11507 if (*m & nextIntValue) { |
|
11508 c = mergedValue & *m; |
|
11509 break; |
|
11510 } |
|
11511 m++; |
|
11512 } |
|
11513 |
|
11514 if (c) { |
|
11515 return false; |
|
11516 } |
|
11517 |
|
11518 mergedValue |= nextIntValue; |
|
11519 } |
|
11520 |
|
11521 aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated); |
|
11522 |
|
11523 return true; |
|
11524 } |
|
11525 |
|
11526 static const int32_t maskEastAsian[] = { |
|
11527 NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK, |
|
11528 NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK, |
|
11529 MASK_END_VALUE |
|
11530 }; |
|
11531 |
|
11532 bool |
|
11533 CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue) |
|
11534 { |
|
11535 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) { |
|
11536 return true; |
|
11537 } |
|
11538 |
|
11539 NS_ASSERTION(maskEastAsian[ArrayLength(maskEastAsian) - 1] == |
|
11540 MASK_END_VALUE, |
|
11541 "incorrectly terminated array"); |
|
11542 |
|
11543 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable, |
|
11544 maskEastAsian); |
|
11545 } |
|
11546 |
|
11547 static const int32_t maskLigatures[] = { |
|
11548 NS_FONT_VARIANT_LIGATURES_COMMON_MASK, |
|
11549 NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK, |
|
11550 NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK, |
|
11551 NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK, |
|
11552 MASK_END_VALUE |
|
11553 }; |
|
11554 |
|
11555 bool |
|
11556 CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue) |
|
11557 { |
|
11558 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) { |
|
11559 return true; |
|
11560 } |
|
11561 |
|
11562 NS_ASSERTION(maskLigatures[ArrayLength(maskLigatures) - 1] == |
|
11563 MASK_END_VALUE, |
|
11564 "incorrectly terminated array"); |
|
11565 |
|
11566 bool parsed = |
|
11567 ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable, |
|
11568 maskLigatures); |
|
11569 |
|
11570 // if none value included, no other values are possible |
|
11571 if (parsed && eCSSUnit_Enumerated == aValue.GetUnit()) { |
|
11572 int32_t val = aValue.GetIntValue(); |
|
11573 if ((val & NS_FONT_VARIANT_LIGATURES_NONE) && |
|
11574 (val & ~int32_t(NS_FONT_VARIANT_LIGATURES_NONE))) { |
|
11575 parsed = false; |
|
11576 } |
|
11577 } |
|
11578 |
|
11579 return parsed; |
|
11580 } |
|
11581 |
|
11582 static const int32_t maskNumeric[] = { |
|
11583 NS_FONT_VARIANT_NUMERIC_FIGURE_MASK, |
|
11584 NS_FONT_VARIANT_NUMERIC_SPACING_MASK, |
|
11585 NS_FONT_VARIANT_NUMERIC_FRACTION_MASK, |
|
11586 MASK_END_VALUE |
|
11587 }; |
|
11588 |
|
11589 bool |
|
11590 CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue) |
|
11591 { |
|
11592 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) { |
|
11593 return true; |
|
11594 } |
|
11595 |
|
11596 NS_ASSERTION(maskNumeric[ArrayLength(maskNumeric) - 1] == |
|
11597 MASK_END_VALUE, |
|
11598 "incorrectly terminated array"); |
|
11599 |
|
11600 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable, |
|
11601 maskNumeric); |
|
11602 } |
|
11603 |
|
11604 bool |
|
11605 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue) |
|
11606 { |
|
11607 if (ParseVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT, |
|
11608 nsCSSProps::kFontWeightKTable)) { |
|
11609 if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value |
|
11610 int32_t intValue = aValue.GetIntValue(); |
|
11611 if ((100 <= intValue) && |
|
11612 (intValue <= 900) && |
|
11613 (0 == (intValue % 100))) { |
|
11614 return true; |
|
11615 } else { |
|
11616 UngetToken(); |
|
11617 return false; |
|
11618 } |
|
11619 } |
|
11620 return true; |
|
11621 } |
|
11622 return false; |
|
11623 } |
|
11624 |
|
11625 bool |
|
11626 CSSParserImpl::ParseOneFamily(nsAString& aFamily, bool& aOneKeyword) |
|
11627 { |
|
11628 if (!GetToken(true)) |
|
11629 return false; |
|
11630 |
|
11631 nsCSSToken* tk = &mToken; |
|
11632 |
|
11633 aOneKeyword = false; |
|
11634 if (eCSSToken_Ident == tk->mType) { |
|
11635 aOneKeyword = true; |
|
11636 aFamily.Append(tk->mIdent); |
|
11637 for (;;) { |
|
11638 if (!GetToken(false)) |
|
11639 break; |
|
11640 |
|
11641 if (eCSSToken_Ident == tk->mType) { |
|
11642 aOneKeyword = false; |
|
11643 // We had at least another keyword before. |
|
11644 // "If a sequence of identifiers is given as a font family name, |
|
11645 // the computed value is the name converted to a string by joining |
|
11646 // all the identifiers in the sequence by single spaces." |
|
11647 // -- CSS 2.1, section 15.3 |
|
11648 // Whitespace tokens do not actually matter, |
|
11649 // identifier tokens can be separated by comments. |
|
11650 aFamily.Append(char16_t(' ')); |
|
11651 aFamily.Append(tk->mIdent); |
|
11652 } else if (eCSSToken_Whitespace != tk->mType) { |
|
11653 UngetToken(); |
|
11654 break; |
|
11655 } |
|
11656 } |
|
11657 return true; |
|
11658 |
|
11659 } else if (eCSSToken_String == tk->mType) { |
|
11660 aFamily.Append(tk->mSymbol); // replace the quotes |
|
11661 aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes? |
|
11662 aFamily.Append(tk->mSymbol); |
|
11663 return true; |
|
11664 |
|
11665 } else { |
|
11666 UngetToken(); |
|
11667 return false; |
|
11668 } |
|
11669 } |
|
11670 |
|
11671 bool |
|
11672 CSSParserImpl::ParseFamily(nsCSSValue& aValue) |
|
11673 { |
|
11674 nsAutoString family; |
|
11675 bool single; |
|
11676 |
|
11677 // keywords only have meaning in the first position |
|
11678 if (!ParseOneFamily(family, single)) |
|
11679 return false; |
|
11680 |
|
11681 // check for keywords, but only when keywords appear by themselves |
|
11682 // i.e. not in compounds such as font-family: default blah; |
|
11683 if (single) { |
|
11684 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family); |
|
11685 if (keyword == eCSSKeyword_inherit) { |
|
11686 aValue.SetInheritValue(); |
|
11687 return true; |
|
11688 } |
|
11689 // 605231 - don't parse unquoted 'default' reserved keyword |
|
11690 if (keyword == eCSSKeyword_default) { |
|
11691 return false; |
|
11692 } |
|
11693 if (keyword == eCSSKeyword_initial) { |
|
11694 aValue.SetInitialValue(); |
|
11695 return true; |
|
11696 } |
|
11697 if (keyword == eCSSKeyword_unset && |
|
11698 nsLayoutUtils::UnsetValueEnabled()) { |
|
11699 aValue.SetUnsetValue(); |
|
11700 return true; |
|
11701 } |
|
11702 if (keyword == eCSSKeyword__moz_use_system_font && |
|
11703 !IsParsingCompoundProperty()) { |
|
11704 aValue.SetSystemFontValue(); |
|
11705 return true; |
|
11706 } |
|
11707 } |
|
11708 |
|
11709 for (;;) { |
|
11710 if (!ExpectSymbol(',', true)) |
|
11711 break; |
|
11712 |
|
11713 family.Append(char16_t(',')); |
|
11714 |
|
11715 nsAutoString nextFamily; |
|
11716 if (!ParseOneFamily(nextFamily, single)) |
|
11717 return false; |
|
11718 |
|
11719 // at this point unquoted keywords are not allowed |
|
11720 // as font family names but can appear within names |
|
11721 if (single) { |
|
11722 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily); |
|
11723 switch (keyword) { |
|
11724 case eCSSKeyword_inherit: |
|
11725 case eCSSKeyword_initial: |
|
11726 case eCSSKeyword_default: |
|
11727 case eCSSKeyword__moz_use_system_font: |
|
11728 return false; |
|
11729 case eCSSKeyword_unset: |
|
11730 if (nsLayoutUtils::UnsetValueEnabled()) { |
|
11731 return false; |
|
11732 } |
|
11733 // fall through |
|
11734 default: |
|
11735 break; |
|
11736 } |
|
11737 } |
|
11738 |
|
11739 family.Append(nextFamily); |
|
11740 } |
|
11741 |
|
11742 if (family.IsEmpty()) { |
|
11743 return false; |
|
11744 } |
|
11745 aValue.SetStringValue(family, eCSSUnit_Families); |
|
11746 return true; |
|
11747 } |
|
11748 |
|
11749 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )* |
|
11750 // uri-src: uri [ 'format(' string ( ',' string )* ')' ] |
|
11751 // local-src: 'local(' ( string | ident ) ')' |
|
11752 |
|
11753 bool |
|
11754 CSSParserImpl::ParseFontSrc(nsCSSValue& aValue) |
|
11755 { |
|
11756 // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>? |
|
11757 InfallibleTArray<nsCSSValue> values; |
|
11758 nsCSSValue cur; |
|
11759 for (;;) { |
|
11760 if (!GetToken(true)) |
|
11761 break; |
|
11762 |
|
11763 if (mToken.mType == eCSSToken_URL) { |
|
11764 SetValueToURL(cur, mToken.mIdent); |
|
11765 values.AppendElement(cur); |
|
11766 if (!ParseFontSrcFormat(values)) |
|
11767 return false; |
|
11768 |
|
11769 } else if (mToken.mType == eCSSToken_Function && |
|
11770 mToken.mIdent.LowerCaseEqualsLiteral("local")) { |
|
11771 // css3-fonts does not specify a formal grammar for local(). |
|
11772 // The text permits both unquoted identifiers and quoted |
|
11773 // strings. We resolve this ambiguity in the spec by |
|
11774 // assuming that the appropriate production is a single |
|
11775 // <family-name>, possibly surrounded by whitespace. |
|
11776 |
|
11777 nsAutoString family; |
|
11778 bool single; |
|
11779 if (!ParseOneFamily(family, single)) { |
|
11780 SkipUntil(')'); |
|
11781 return false; |
|
11782 } |
|
11783 if (!ExpectSymbol(')', true)) { |
|
11784 SkipUntil(')'); |
|
11785 return false; |
|
11786 } |
|
11787 |
|
11788 // XXX: Getting closer... |
|
11789 // the style parameters to the nsFont constructor are ignored, |
|
11790 // because it's only being used to call EnumerateFamilies |
|
11791 nsFont font(family, 0, 0, 0, 0, 0, 0); |
|
11792 ExtractFirstFamilyData dat; |
|
11793 |
|
11794 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat); |
|
11795 if (!dat.mGood) |
|
11796 return false; |
|
11797 |
|
11798 cur.SetStringValue(dat.mFamilyName, eCSSUnit_Local_Font); |
|
11799 values.AppendElement(cur); |
|
11800 } else { |
|
11801 // We don't know what to do with this token; unget it and error out |
|
11802 UngetToken(); |
|
11803 return false; |
|
11804 } |
|
11805 |
|
11806 if (!ExpectSymbol(',', true)) |
|
11807 break; |
|
11808 } |
|
11809 |
|
11810 if (values.Length() == 0) |
|
11811 return false; |
|
11812 |
|
11813 nsRefPtr<nsCSSValue::Array> srcVals |
|
11814 = nsCSSValue::Array::Create(values.Length()); |
|
11815 |
|
11816 uint32_t i; |
|
11817 for (i = 0; i < values.Length(); i++) |
|
11818 srcVals->Item(i) = values[i]; |
|
11819 aValue.SetArrayValue(srcVals, eCSSUnit_Array); |
|
11820 return true; |
|
11821 } |
|
11822 |
|
11823 bool |
|
11824 CSSParserImpl::ParseFontSrcFormat(InfallibleTArray<nsCSSValue> & values) |
|
11825 { |
|
11826 if (!GetToken(true)) |
|
11827 return true; // EOF harmless here |
|
11828 if (mToken.mType != eCSSToken_Function || |
|
11829 !mToken.mIdent.LowerCaseEqualsLiteral("format")) { |
|
11830 UngetToken(); |
|
11831 return true; |
|
11832 } |
|
11833 |
|
11834 do { |
|
11835 if (!GetToken(true)) |
|
11836 return false; // EOF - no need for SkipUntil |
|
11837 |
|
11838 if (mToken.mType != eCSSToken_String) { |
|
11839 UngetToken(); |
|
11840 SkipUntil(')'); |
|
11841 return false; |
|
11842 } |
|
11843 |
|
11844 nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format); |
|
11845 values.AppendElement(cur); |
|
11846 } while (ExpectSymbol(',', true)); |
|
11847 |
|
11848 if (!ExpectSymbol(')', true)) { |
|
11849 SkipUntil(')'); |
|
11850 return false; |
|
11851 } |
|
11852 |
|
11853 return true; |
|
11854 } |
|
11855 |
|
11856 // font-ranges: urange ( ',' urange )* |
|
11857 bool |
|
11858 CSSParserImpl::ParseFontRanges(nsCSSValue& aValue) |
|
11859 { |
|
11860 InfallibleTArray<uint32_t> ranges; |
|
11861 for (;;) { |
|
11862 if (!GetToken(true)) |
|
11863 break; |
|
11864 |
|
11865 if (mToken.mType != eCSSToken_URange) { |
|
11866 UngetToken(); |
|
11867 break; |
|
11868 } |
|
11869 |
|
11870 // An invalid range token is a parsing error, causing the entire |
|
11871 // descriptor to be ignored. |
|
11872 if (!mToken.mIntegerValid) |
|
11873 return false; |
|
11874 |
|
11875 uint32_t low = mToken.mInteger; |
|
11876 uint32_t high = mToken.mInteger2; |
|
11877 |
|
11878 // A range that descends, or a range that is entirely outside the |
|
11879 // current range of Unicode (U+0-10FFFF) is ignored, but does not |
|
11880 // invalidate the descriptor. A range that straddles the high end |
|
11881 // is clipped. |
|
11882 if (low <= 0x10FFFF && low <= high) { |
|
11883 if (high > 0x10FFFF) |
|
11884 high = 0x10FFFF; |
|
11885 |
|
11886 ranges.AppendElement(low); |
|
11887 ranges.AppendElement(high); |
|
11888 } |
|
11889 if (!ExpectSymbol(',', true)) |
|
11890 break; |
|
11891 } |
|
11892 |
|
11893 if (ranges.Length() == 0) |
|
11894 return false; |
|
11895 |
|
11896 nsRefPtr<nsCSSValue::Array> srcVals |
|
11897 = nsCSSValue::Array::Create(ranges.Length()); |
|
11898 |
|
11899 for (uint32_t i = 0; i < ranges.Length(); i++) |
|
11900 srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer); |
|
11901 aValue.SetArrayValue(srcVals, eCSSUnit_Array); |
|
11902 return true; |
|
11903 } |
|
11904 |
|
11905 // font-feature-settings: normal | <feature-tag-value> [, <feature-tag-value>]* |
|
11906 // <feature-tag-value> = <string> [ <integer> | on | off ]? |
|
11907 |
|
11908 // minimum - "tagx", "tagy", "tagz" |
|
11909 // edge error case - "tagx" on 1, "tagx" "tagy", "tagx" -1, "tagx" big |
|
11910 |
|
11911 // pair value is always x = string, y = int |
|
11912 |
|
11913 // font feature tags must be four ASCII characters |
|
11914 #define FEATURE_TAG_LENGTH 4 |
|
11915 |
|
11916 static bool |
|
11917 ValidFontFeatureTag(const nsString& aTag) |
|
11918 { |
|
11919 if (aTag.Length() != FEATURE_TAG_LENGTH) { |
|
11920 return false; |
|
11921 } |
|
11922 uint32_t i; |
|
11923 for (i = 0; i < FEATURE_TAG_LENGTH; i++) { |
|
11924 uint32_t ch = aTag[i]; |
|
11925 if (ch < 0x20 || ch > 0x7e) { |
|
11926 return false; |
|
11927 } |
|
11928 } |
|
11929 return true; |
|
11930 } |
|
11931 |
|
11932 bool |
|
11933 CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue) |
|
11934 { |
|
11935 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) { |
|
11936 return true; |
|
11937 } |
|
11938 |
|
11939 nsCSSValuePairList *cur = aValue.SetPairListValue(); |
|
11940 for (;;) { |
|
11941 // feature tag |
|
11942 if (!GetToken(true)) { |
|
11943 return false; |
|
11944 } |
|
11945 |
|
11946 if (mToken.mType != eCSSToken_String || |
|
11947 !ValidFontFeatureTag(mToken.mIdent)) { |
|
11948 UngetToken(); |
|
11949 return false; |
|
11950 } |
|
11951 cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String); |
|
11952 |
|
11953 if (!GetToken(true)) { |
|
11954 cur->mYValue.SetIntValue(1, eCSSUnit_Integer); |
|
11955 break; |
|
11956 } |
|
11957 |
|
11958 // optional value or on/off keyword |
|
11959 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid && |
|
11960 mToken.mInteger >= 0) { |
|
11961 cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer); |
|
11962 } else if (mToken.mType == eCSSToken_Ident && |
|
11963 mToken.mIdent.LowerCaseEqualsLiteral("on")) { |
|
11964 cur->mYValue.SetIntValue(1, eCSSUnit_Integer); |
|
11965 } else if (mToken.mType == eCSSToken_Ident && |
|
11966 mToken.mIdent.LowerCaseEqualsLiteral("off")) { |
|
11967 cur->mYValue.SetIntValue(0, eCSSUnit_Integer); |
|
11968 } else { |
|
11969 // something other than value/on/off, set default value |
|
11970 cur->mYValue.SetIntValue(1, eCSSUnit_Integer); |
|
11971 UngetToken(); |
|
11972 } |
|
11973 |
|
11974 if (!ExpectSymbol(',', true)) { |
|
11975 break; |
|
11976 } |
|
11977 |
|
11978 cur->mNext = new nsCSSValuePairList; |
|
11979 cur = cur->mNext; |
|
11980 } |
|
11981 |
|
11982 return true; |
|
11983 } |
|
11984 |
|
11985 bool |
|
11986 CSSParserImpl::ParseListStyle() |
|
11987 { |
|
11988 // 'list-style' can accept 'none' for two different subproperties, |
|
11989 // 'list-style-type' and 'list-style-position'. In order to accept |
|
11990 // 'none' as the value of either but still allow another value for |
|
11991 // either, we need to ensure that the first 'none' we find gets |
|
11992 // allocated to a dummy property instead. |
|
11993 static const nsCSSProperty listStyleIDs[] = { |
|
11994 eCSSPropertyExtra_x_none_value, |
|
11995 eCSSProperty_list_style_type, |
|
11996 eCSSProperty_list_style_position, |
|
11997 eCSSProperty_list_style_image |
|
11998 }; |
|
11999 |
|
12000 nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)]; |
|
12001 int32_t found = |
|
12002 ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs)); |
|
12003 if (found < 1) { |
|
12004 return false; |
|
12005 } |
|
12006 |
|
12007 if ((found & (1|2|8)) == (1|2|8)) { |
|
12008 if (values[0].GetUnit() == eCSSUnit_None) { |
|
12009 // We found a 'none' plus another value for both of |
|
12010 // 'list-style-type' and 'list-style-image'. This is a parse |
|
12011 // error, since the 'none' has to count for at least one of them. |
|
12012 return false; |
|
12013 } else { |
|
12014 NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] && |
|
12015 values[0] == values[2] && values[0] == values[3], |
|
12016 "should be a special value"); |
|
12017 } |
|
12018 } |
|
12019 |
|
12020 // Provide default values |
|
12021 if ((found & 2) == 0) { |
|
12022 if (found & 1) { |
|
12023 values[1].SetIntValue(NS_STYLE_LIST_STYLE_NONE, eCSSUnit_Enumerated); |
|
12024 } else { |
|
12025 values[1].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated); |
|
12026 } |
|
12027 } |
|
12028 if ((found & 4) == 0) { |
|
12029 values[2].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, |
|
12030 eCSSUnit_Enumerated); |
|
12031 } |
|
12032 if ((found & 8) == 0) { |
|
12033 values[3].SetNoneValue(); |
|
12034 } |
|
12035 |
|
12036 // Start at 1 to avoid appending fake value. |
|
12037 for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) { |
|
12038 AppendValue(listStyleIDs[index], values[index]); |
|
12039 } |
|
12040 return true; |
|
12041 } |
|
12042 |
|
12043 bool |
|
12044 CSSParserImpl::ParseMargin() |
|
12045 { |
|
12046 static const nsCSSProperty kMarginSideIDs[] = { |
|
12047 eCSSProperty_margin_top, |
|
12048 eCSSProperty_margin_right_value, |
|
12049 eCSSProperty_margin_bottom, |
|
12050 eCSSProperty_margin_left_value |
|
12051 }; |
|
12052 static const nsCSSProperty kMarginSources[] = { |
|
12053 eCSSProperty_margin_left_ltr_source, |
|
12054 eCSSProperty_margin_left_rtl_source, |
|
12055 eCSSProperty_margin_right_ltr_source, |
|
12056 eCSSProperty_margin_right_rtl_source, |
|
12057 eCSSProperty_UNKNOWN |
|
12058 }; |
|
12059 |
|
12060 // do this now, in case 4 values weren't specified |
|
12061 InitBoxPropsAsPhysical(kMarginSources); |
|
12062 return ParseBoxProperties(kMarginSideIDs); |
|
12063 } |
|
12064 |
|
12065 bool |
|
12066 CSSParserImpl::ParseMarks(nsCSSValue& aValue) |
|
12067 { |
|
12068 if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPageMarksKTable)) { |
|
12069 if (eCSSUnit_Enumerated == aValue.GetUnit()) { |
|
12070 if (NS_STYLE_PAGE_MARKS_NONE != aValue.GetIntValue() && |
|
12071 false == CheckEndProperty()) { |
|
12072 nsCSSValue second; |
|
12073 if (ParseEnum(second, nsCSSProps::kPageMarksKTable)) { |
|
12074 // 'none' keyword in conjuction with others is not allowed |
|
12075 if (NS_STYLE_PAGE_MARKS_NONE != second.GetIntValue()) { |
|
12076 aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(), |
|
12077 eCSSUnit_Enumerated); |
|
12078 return true; |
|
12079 } |
|
12080 } |
|
12081 return false; |
|
12082 } |
|
12083 } |
|
12084 return true; |
|
12085 } |
|
12086 return false; |
|
12087 } |
|
12088 |
|
12089 bool |
|
12090 CSSParserImpl::ParseOutline() |
|
12091 { |
|
12092 const int32_t numProps = 3; |
|
12093 static const nsCSSProperty kOutlineIDs[] = { |
|
12094 eCSSProperty_outline_color, |
|
12095 eCSSProperty_outline_style, |
|
12096 eCSSProperty_outline_width |
|
12097 }; |
|
12098 |
|
12099 nsCSSValue values[numProps]; |
|
12100 int32_t found = ParseChoice(values, kOutlineIDs, numProps); |
|
12101 if (found < 1) { |
|
12102 return false; |
|
12103 } |
|
12104 |
|
12105 // Provide default values |
|
12106 if ((found & 1) == 0) { // Provide default outline-color |
|
12107 values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated); |
|
12108 } |
|
12109 if ((found & 2) == 0) { // Provide default outline-style |
|
12110 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated); |
|
12111 } |
|
12112 if ((found & 4) == 0) { // Provide default outline-width |
|
12113 values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated); |
|
12114 } |
|
12115 |
|
12116 int32_t index; |
|
12117 for (index = 0; index < numProps; index++) { |
|
12118 AppendValue(kOutlineIDs[index], values[index]); |
|
12119 } |
|
12120 return true; |
|
12121 } |
|
12122 |
|
12123 bool |
|
12124 CSSParserImpl::ParseOverflow() |
|
12125 { |
|
12126 nsCSSValue overflow; |
|
12127 if (!ParseVariant(overflow, VARIANT_HK, nsCSSProps::kOverflowKTable)) { |
|
12128 return false; |
|
12129 } |
|
12130 |
|
12131 nsCSSValue overflowX(overflow); |
|
12132 nsCSSValue overflowY(overflow); |
|
12133 if (eCSSUnit_Enumerated == overflow.GetUnit()) |
|
12134 switch(overflow.GetIntValue()) { |
|
12135 case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL: |
|
12136 overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated); |
|
12137 overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated); |
|
12138 break; |
|
12139 case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL: |
|
12140 overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated); |
|
12141 overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated); |
|
12142 break; |
|
12143 } |
|
12144 AppendValue(eCSSProperty_overflow_x, overflowX); |
|
12145 AppendValue(eCSSProperty_overflow_y, overflowY); |
|
12146 return true; |
|
12147 } |
|
12148 |
|
12149 bool |
|
12150 CSSParserImpl::ParsePadding() |
|
12151 { |
|
12152 static const nsCSSProperty kPaddingSideIDs[] = { |
|
12153 eCSSProperty_padding_top, |
|
12154 eCSSProperty_padding_right_value, |
|
12155 eCSSProperty_padding_bottom, |
|
12156 eCSSProperty_padding_left_value |
|
12157 }; |
|
12158 static const nsCSSProperty kPaddingSources[] = { |
|
12159 eCSSProperty_padding_left_ltr_source, |
|
12160 eCSSProperty_padding_left_rtl_source, |
|
12161 eCSSProperty_padding_right_ltr_source, |
|
12162 eCSSProperty_padding_right_rtl_source, |
|
12163 eCSSProperty_UNKNOWN |
|
12164 }; |
|
12165 |
|
12166 // do this now, in case 4 values weren't specified |
|
12167 InitBoxPropsAsPhysical(kPaddingSources); |
|
12168 return ParseBoxProperties(kPaddingSideIDs); |
|
12169 } |
|
12170 |
|
12171 bool |
|
12172 CSSParserImpl::ParseQuotes() |
|
12173 { |
|
12174 nsCSSValue value; |
|
12175 if (!ParseVariant(value, VARIANT_HOS, nullptr)) { |
|
12176 return false; |
|
12177 } |
|
12178 if (value.GetUnit() == eCSSUnit_String) { |
|
12179 nsCSSValue open = value; |
|
12180 nsCSSValuePairList* quotes = value.SetPairListValue(); |
|
12181 for (;;) { |
|
12182 quotes->mXValue = open; |
|
12183 // get mandatory close |
|
12184 if (!ParseVariant(quotes->mYValue, VARIANT_STRING, nullptr)) { |
|
12185 return false; |
|
12186 } |
|
12187 // look for another open |
|
12188 if (!ParseVariant(open, VARIANT_STRING, nullptr)) { |
|
12189 break; |
|
12190 } |
|
12191 quotes->mNext = new nsCSSValuePairList; |
|
12192 quotes = quotes->mNext; |
|
12193 } |
|
12194 } |
|
12195 AppendValue(eCSSProperty_quotes, value); |
|
12196 return true; |
|
12197 } |
|
12198 |
|
12199 bool |
|
12200 CSSParserImpl::ParseSize() |
|
12201 { |
|
12202 nsCSSValue width, height; |
|
12203 if (!ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) { |
|
12204 return false; |
|
12205 } |
|
12206 if (width.IsLengthUnit()) { |
|
12207 ParseVariant(height, VARIANT_LENGTH, nullptr); |
|
12208 } |
|
12209 |
|
12210 if (width == height || height.GetUnit() == eCSSUnit_Null) { |
|
12211 AppendValue(eCSSProperty_size, width); |
|
12212 } else { |
|
12213 nsCSSValue pair; |
|
12214 pair.SetPairValue(width, height); |
|
12215 AppendValue(eCSSProperty_size, pair); |
|
12216 } |
|
12217 return true; |
|
12218 } |
|
12219 |
|
12220 bool |
|
12221 CSSParserImpl::ParseTextDecoration() |
|
12222 { |
|
12223 enum { |
|
12224 eDecorationNone = NS_STYLE_TEXT_DECORATION_LINE_NONE, |
|
12225 eDecorationUnderline = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, |
|
12226 eDecorationOverline = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, |
|
12227 eDecorationLineThrough = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, |
|
12228 eDecorationBlink = NS_STYLE_TEXT_DECORATION_LINE_BLINK, |
|
12229 eDecorationPrefAnchors = NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS |
|
12230 }; |
|
12231 static_assert((eDecorationNone ^ eDecorationUnderline ^ |
|
12232 eDecorationOverline ^ eDecorationLineThrough ^ |
|
12233 eDecorationBlink ^ eDecorationPrefAnchors) == |
|
12234 (eDecorationNone | eDecorationUnderline | |
|
12235 eDecorationOverline | eDecorationLineThrough | |
|
12236 eDecorationBlink | eDecorationPrefAnchors), |
|
12237 "text decoration constants need to be bitmasks"); |
|
12238 |
|
12239 static const KTableValue kTextDecorationKTable[] = { |
|
12240 eCSSKeyword_none, eDecorationNone, |
|
12241 eCSSKeyword_underline, eDecorationUnderline, |
|
12242 eCSSKeyword_overline, eDecorationOverline, |
|
12243 eCSSKeyword_line_through, eDecorationLineThrough, |
|
12244 eCSSKeyword_blink, eDecorationBlink, |
|
12245 eCSSKeyword__moz_anchor_decoration, eDecorationPrefAnchors, |
|
12246 eCSSKeyword_UNKNOWN,-1 |
|
12247 }; |
|
12248 |
|
12249 nsCSSValue value; |
|
12250 if (!ParseVariant(value, VARIANT_HK, kTextDecorationKTable)) { |
|
12251 return false; |
|
12252 } |
|
12253 |
|
12254 nsCSSValue line, style, color; |
|
12255 switch (value.GetUnit()) { |
|
12256 case eCSSUnit_Enumerated: { |
|
12257 // We shouldn't accept decoration line style and color via |
|
12258 // text-decoration. |
|
12259 color.SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, |
|
12260 eCSSUnit_Enumerated); |
|
12261 style.SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID, |
|
12262 eCSSUnit_Enumerated); |
|
12263 |
|
12264 int32_t intValue = value.GetIntValue(); |
|
12265 if (intValue == eDecorationNone) { |
|
12266 line.SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE, |
|
12267 eCSSUnit_Enumerated); |
|
12268 break; |
|
12269 } |
|
12270 |
|
12271 // look for more keywords |
|
12272 nsCSSValue keyword; |
|
12273 int32_t index; |
|
12274 for (index = 0; index < 3; index++) { |
|
12275 if (!ParseEnum(keyword, kTextDecorationKTable)) { |
|
12276 break; |
|
12277 } |
|
12278 int32_t newValue = keyword.GetIntValue(); |
|
12279 if (newValue == eDecorationNone || newValue & intValue) { |
|
12280 // 'none' keyword in conjuction with others is not allowed, and |
|
12281 // duplicate keyword is not allowed. |
|
12282 return false; |
|
12283 } |
|
12284 intValue |= newValue; |
|
12285 } |
|
12286 |
|
12287 line.SetIntValue(intValue, eCSSUnit_Enumerated); |
|
12288 break; |
|
12289 } |
|
12290 default: |
|
12291 line = color = style = value; |
|
12292 break; |
|
12293 } |
|
12294 |
|
12295 AppendValue(eCSSProperty_text_decoration_line, line); |
|
12296 AppendValue(eCSSProperty_text_decoration_color, color); |
|
12297 AppendValue(eCSSProperty_text_decoration_style, style); |
|
12298 |
|
12299 return true; |
|
12300 } |
|
12301 |
|
12302 bool |
|
12303 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableValue aTable[]) |
|
12304 { |
|
12305 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) { |
|
12306 // 'inherit', 'initial' and 'unset' must be alone |
|
12307 return true; |
|
12308 } |
|
12309 |
|
12310 nsCSSValue left; |
|
12311 if (!ParseVariant(left, VARIANT_KEYWORD, aTable)) { |
|
12312 return false; |
|
12313 } |
|
12314 |
|
12315 if (!nsLayoutUtils::IsTextAlignTrueValueEnabled()) { |
|
12316 aValue = left; |
|
12317 return true; |
|
12318 } |
|
12319 |
|
12320 nsCSSValue right; |
|
12321 if (ParseVariant(right, VARIANT_KEYWORD, aTable)) { |
|
12322 // 'true' must be combined with some other value than 'true'. |
|
12323 if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE && |
|
12324 right.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) { |
|
12325 return false; |
|
12326 } |
|
12327 aValue.SetPairValue(left, right); |
|
12328 } else { |
|
12329 // Single value 'true' is not allowed. |
|
12330 if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) { |
|
12331 return false; |
|
12332 } |
|
12333 aValue = left; |
|
12334 } |
|
12335 return true; |
|
12336 } |
|
12337 |
|
12338 bool |
|
12339 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue) |
|
12340 { |
|
12341 return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable); |
|
12342 } |
|
12343 |
|
12344 bool |
|
12345 CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue) |
|
12346 { |
|
12347 return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable); |
|
12348 } |
|
12349 |
|
12350 bool |
|
12351 CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue) |
|
12352 { |
|
12353 if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTextDecorationLineKTable)) { |
|
12354 if (eCSSUnit_Enumerated == aValue.GetUnit()) { |
|
12355 int32_t intValue = aValue.GetIntValue(); |
|
12356 if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) { |
|
12357 // look for more keywords |
|
12358 nsCSSValue keyword; |
|
12359 int32_t index; |
|
12360 for (index = 0; index < 3; index++) { |
|
12361 if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) { |
|
12362 int32_t newValue = keyword.GetIntValue(); |
|
12363 if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE || |
|
12364 newValue & intValue) { |
|
12365 // 'none' keyword in conjuction with others is not allowed, and |
|
12366 // duplicate keyword is not allowed. |
|
12367 return false; |
|
12368 } |
|
12369 intValue |= newValue; |
|
12370 } |
|
12371 else { |
|
12372 break; |
|
12373 } |
|
12374 } |
|
12375 aValue.SetIntValue(intValue, eCSSUnit_Enumerated); |
|
12376 } |
|
12377 } |
|
12378 return true; |
|
12379 } |
|
12380 return false; |
|
12381 } |
|
12382 |
|
12383 bool |
|
12384 CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue) |
|
12385 { |
|
12386 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) { |
|
12387 // 'inherit', 'initial' and 'unset' must be alone |
|
12388 return true; |
|
12389 } |
|
12390 |
|
12391 nsCSSValue left; |
|
12392 if (!ParseVariant(left, VARIANT_KEYWORD | VARIANT_STRING, |
|
12393 nsCSSProps::kTextOverflowKTable)) |
|
12394 return false; |
|
12395 |
|
12396 nsCSSValue right; |
|
12397 if (ParseVariant(right, VARIANT_KEYWORD | VARIANT_STRING, |
|
12398 nsCSSProps::kTextOverflowKTable)) |
|
12399 aValue.SetPairValue(left, right); |
|
12400 else { |
|
12401 aValue = left; |
|
12402 } |
|
12403 return true; |
|
12404 } |
|
12405 |
|
12406 bool |
|
12407 CSSParserImpl::ParseTouchAction(nsCSSValue& aValue) |
|
12408 { |
|
12409 // Avaliable values of property touch-action: |
|
12410 // auto | none | [pan-x || pan-y] | manipulation |
|
12411 |
|
12412 if (!ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTouchActionKTable)) { |
|
12413 return false; |
|
12414 } |
|
12415 |
|
12416 // Auto and None keywords aren't allowed in conjunction with others. |
|
12417 // Also inherit, initial and unset values are available. |
|
12418 if (eCSSUnit_Enumerated != aValue.GetUnit()) { |
|
12419 return true; |
|
12420 } |
|
12421 |
|
12422 int32_t intValue = aValue.GetIntValue(); |
|
12423 nsCSSValue nextValue; |
|
12424 if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) { |
|
12425 int32_t nextIntValue = nextValue.GetIntValue(); |
|
12426 |
|
12427 // duplicates aren't allowed. |
|
12428 if (nextIntValue & intValue) { |
|
12429 return false; |
|
12430 } |
|
12431 |
|
12432 // Auto and None and Manipulation is not allowed in conjunction with others. |
|
12433 if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE | |
|
12434 NS_STYLE_TOUCH_ACTION_AUTO | |
|
12435 NS_STYLE_TOUCH_ACTION_MANIPULATION)) { |
|
12436 return false; |
|
12437 } |
|
12438 |
|
12439 aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated); |
|
12440 } |
|
12441 |
|
12442 return true; |
|
12443 } |
|
12444 |
|
12445 bool |
|
12446 CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue) |
|
12447 { |
|
12448 if (!ParseVariant(aValue, VARIANT_HK, |
|
12449 nsCSSProps::kTextCombineUprightKTable)) { |
|
12450 return false; |
|
12451 } |
|
12452 |
|
12453 // if 'digits', need to check for an explicit number [2, 3, 4] |
|
12454 if (eCSSUnit_Enumerated == aValue.GetUnit() && |
|
12455 aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) { |
|
12456 if (!GetToken(true)) { |
|
12457 return true; |
|
12458 } |
|
12459 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) { |
|
12460 switch (mToken.mInteger) { |
|
12461 case 2: // already set, nothing to do |
|
12462 break; |
|
12463 case 3: |
|
12464 aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3, |
|
12465 eCSSUnit_Enumerated); |
|
12466 break; |
|
12467 case 4: |
|
12468 aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4, |
|
12469 eCSSUnit_Enumerated); |
|
12470 break; |
|
12471 default: |
|
12472 // invalid digits value |
|
12473 return false; |
|
12474 } |
|
12475 } else { |
|
12476 UngetToken(); |
|
12477 } |
|
12478 } |
|
12479 return true; |
|
12480 } |
|
12481 |
|
12482 /////////////////////////////////////////////////////// |
|
12483 // transform Parsing Implementation |
|
12484 |
|
12485 /* Reads a function list of arguments and consumes the closing parenthesis. |
|
12486 * Do not call this function directly; it's meant to be called from |
|
12487 * ParseFunction. |
|
12488 */ |
|
12489 bool |
|
12490 CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[], |
|
12491 int32_t aVariantMaskAll, |
|
12492 uint16_t aMinElems, |
|
12493 uint16_t aMaxElems, |
|
12494 InfallibleTArray<nsCSSValue> &aOutput) |
|
12495 { |
|
12496 NS_ASSERTION((aVariantMask && !aVariantMaskAll) || |
|
12497 (!aVariantMask && aVariantMaskAll), |
|
12498 "only one of the two variant mask parameters can be set"); |
|
12499 |
|
12500 for (uint16_t index = 0; index < aMaxElems; ++index) { |
|
12501 nsCSSValue newValue; |
|
12502 int32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index]; |
|
12503 if (!ParseVariant(newValue, m, nullptr)) { |
|
12504 break; |
|
12505 } |
|
12506 |
|
12507 aOutput.AppendElement(newValue); |
|
12508 |
|
12509 if (ExpectSymbol(',', true)) { |
|
12510 // Move on to the next argument if we see a comma. |
|
12511 continue; |
|
12512 } |
|
12513 |
|
12514 if (ExpectSymbol(')', true)) { |
|
12515 // Make sure we've read enough symbols if we see a closing parenthesis. |
|
12516 return (index + 1) >= aMinElems; |
|
12517 } |
|
12518 |
|
12519 // Only a comma or a closing parenthesis is valid after an argument. |
|
12520 break; |
|
12521 } |
|
12522 |
|
12523 // If we're here, we've hit an error without seeing a closing parenthesis or |
|
12524 // we've read too many elements without seeing a closing parenthesis. |
|
12525 SkipUntil(')'); |
|
12526 return false; |
|
12527 } |
|
12528 |
|
12529 /* Parses a function [ input of the form (a [, b]*) ] and stores it |
|
12530 * as an nsCSSValue that holds a function of the form |
|
12531 * function-name arg1 arg2 ... argN |
|
12532 * |
|
12533 * On error, the return value is false. |
|
12534 * |
|
12535 * @param aFunction The name of the function that we're reading. |
|
12536 * @param aAllowedTypes An array of values corresponding to the legal |
|
12537 * types for each element in the function. The zeroth element in the |
|
12538 * array corresponds to the first function parameter, etc. The length |
|
12539 * of this array _must_ be greater than or equal to aMaxElems or the |
|
12540 * behavior is undefined. If not null, aAllowTypesAll must be 0. |
|
12541 * @param aAllowedTypesAll If set, every element tested for these types |
|
12542 * @param aMinElems Minimum number of elements to read. Reading fewer than |
|
12543 * this many elements will result in the function failing. |
|
12544 * @param aMaxElems Maximum number of elements to read. Reading more than |
|
12545 * this many elements will result in the function failing. |
|
12546 * @param aValue (out) The value that was parsed. |
|
12547 */ |
|
12548 bool |
|
12549 CSSParserImpl::ParseFunction(nsCSSKeyword aFunction, |
|
12550 const int32_t aAllowedTypes[], |
|
12551 int32_t aAllowedTypesAll, |
|
12552 uint16_t aMinElems, uint16_t aMaxElems, |
|
12553 nsCSSValue &aValue) |
|
12554 { |
|
12555 NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) || |
|
12556 (!aAllowedTypes && aAllowedTypesAll), |
|
12557 "only one of the two allowed type parameter can be set"); |
|
12558 typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t; |
|
12559 |
|
12560 /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1 |
|
12561 * elements stored in the the nsCSSValue::Array. |
|
12562 */ |
|
12563 static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE; |
|
12564 |
|
12565 /* Read in a list of values as an array, failing if we can't or if |
|
12566 * it's out of bounds. |
|
12567 * |
|
12568 * We reserve 16 entries in the foundValues array in order to avoid |
|
12569 * having to resize the array dynamically when parsing some well-formed |
|
12570 * functions. The number 16 is coming from the number of arguments that |
|
12571 * matrix3d() accepts. |
|
12572 */ |
|
12573 AutoInfallibleTArray<nsCSSValue, 16> foundValues; |
|
12574 if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems, |
|
12575 aMaxElems, foundValues)) { |
|
12576 return false; |
|
12577 } |
|
12578 |
|
12579 /* |
|
12580 * In case the user has given us more than 2^16 - 2 arguments, |
|
12581 * we'll truncate them at 2^16 - 2 arguments. |
|
12582 */ |
|
12583 uint16_t numArgs = std::min(foundValues.Length(), MAX_ALLOWED_ELEMS); |
|
12584 nsRefPtr<nsCSSValue::Array> convertedArray = |
|
12585 aValue.InitFunction(aFunction, numArgs); |
|
12586 |
|
12587 /* Copy things over. */ |
|
12588 for (uint16_t index = 0; index < numArgs; ++index) |
|
12589 convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)]; |
|
12590 |
|
12591 /* Return it! */ |
|
12592 return true; |
|
12593 } |
|
12594 |
|
12595 /** |
|
12596 * Given a token, determines the minimum and maximum number of function |
|
12597 * parameters to read, along with the mask that should be used to read |
|
12598 * those function parameters. If the token isn't a transform function, |
|
12599 * returns an error. |
|
12600 * |
|
12601 * @param aToken The token identifying the function. |
|
12602 * @param aMinElems [out] The minimum number of elements to read. |
|
12603 * @param aMaxElems [out] The maximum number of elements to read |
|
12604 * @param aVariantMask [out] The variant mask to use during parsing |
|
12605 * @return Whether the information was loaded successfully. |
|
12606 */ |
|
12607 static bool GetFunctionParseInformation(nsCSSKeyword aToken, |
|
12608 bool aIsPrefixed, |
|
12609 uint16_t &aMinElems, |
|
12610 uint16_t &aMaxElems, |
|
12611 const int32_t *& aVariantMask) |
|
12612 { |
|
12613 /* These types represent the common variant masks that will be used to |
|
12614 * parse out the individual functions. The order in the enumeration |
|
12615 * must match the order in which the masks are declared. |
|
12616 */ |
|
12617 enum { eLengthPercentCalc, |
|
12618 eLengthCalc, |
|
12619 eTwoLengthPercentCalcs, |
|
12620 eTwoLengthPercentCalcsOneLengthCalc, |
|
12621 eAngle, |
|
12622 eTwoAngles, |
|
12623 eNumber, |
|
12624 ePositiveLength, |
|
12625 eTwoNumbers, |
|
12626 eThreeNumbers, |
|
12627 eThreeNumbersOneAngle, |
|
12628 eMatrix, |
|
12629 eMatrixPrefixed, |
|
12630 eMatrix3d, |
|
12631 eMatrix3dPrefixed, |
|
12632 eNumVariantMasks }; |
|
12633 static const int32_t kMaxElemsPerFunction = 16; |
|
12634 static const int32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = { |
|
12635 {VARIANT_LPCALC}, |
|
12636 {VARIANT_LENGTH|VARIANT_CALC}, |
|
12637 {VARIANT_LPCALC, VARIANT_LPCALC}, |
|
12638 {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LENGTH|VARIANT_CALC}, |
|
12639 {VARIANT_ANGLE_OR_ZERO}, |
|
12640 {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO}, |
|
12641 {VARIANT_NUMBER}, |
|
12642 {VARIANT_LENGTH|VARIANT_POSITIVE_DIMENSION}, |
|
12643 {VARIANT_NUMBER, VARIANT_NUMBER}, |
|
12644 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER}, |
|
12645 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO}, |
|
12646 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, |
|
12647 VARIANT_NUMBER, VARIANT_NUMBER}, |
|
12648 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, |
|
12649 VARIANT_LPNCALC, VARIANT_LPNCALC}, |
|
12650 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, |
|
12651 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, |
|
12652 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, |
|
12653 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER}, |
|
12654 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, |
|
12655 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, |
|
12656 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, |
|
12657 VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}}; |
|
12658 |
|
12659 #ifdef DEBUG |
|
12660 static const uint8_t kVariantMaskLengths[eNumVariantMasks] = |
|
12661 {1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 6, 16, 16}; |
|
12662 #endif |
|
12663 |
|
12664 int32_t variantIndex = eNumVariantMasks; |
|
12665 |
|
12666 switch (aToken) { |
|
12667 case eCSSKeyword_translatex: |
|
12668 case eCSSKeyword_translatey: |
|
12669 /* Exactly one length or percent. */ |
|
12670 variantIndex = eLengthPercentCalc; |
|
12671 aMinElems = 1U; |
|
12672 aMaxElems = 1U; |
|
12673 break; |
|
12674 case eCSSKeyword_translatez: |
|
12675 /* Exactly one length */ |
|
12676 variantIndex = eLengthCalc; |
|
12677 aMinElems = 1U; |
|
12678 aMaxElems = 1U; |
|
12679 break; |
|
12680 case eCSSKeyword_translate3d: |
|
12681 /* Exactly two lengthds or percents and a number */ |
|
12682 variantIndex = eTwoLengthPercentCalcsOneLengthCalc; |
|
12683 aMinElems = 3U; |
|
12684 aMaxElems = 3U; |
|
12685 break; |
|
12686 case eCSSKeyword_scalez: |
|
12687 case eCSSKeyword_scalex: |
|
12688 case eCSSKeyword_scaley: |
|
12689 /* Exactly one scale factor. */ |
|
12690 variantIndex = eNumber; |
|
12691 aMinElems = 1U; |
|
12692 aMaxElems = 1U; |
|
12693 break; |
|
12694 case eCSSKeyword_scale3d: |
|
12695 /* Exactly three scale factors. */ |
|
12696 variantIndex = eThreeNumbers; |
|
12697 aMinElems = 3U; |
|
12698 aMaxElems = 3U; |
|
12699 break; |
|
12700 case eCSSKeyword_rotatex: |
|
12701 case eCSSKeyword_rotatey: |
|
12702 case eCSSKeyword_rotate: |
|
12703 case eCSSKeyword_rotatez: |
|
12704 /* Exactly one angle. */ |
|
12705 variantIndex = eAngle; |
|
12706 aMinElems = 1U; |
|
12707 aMaxElems = 1U; |
|
12708 break; |
|
12709 case eCSSKeyword_rotate3d: |
|
12710 variantIndex = eThreeNumbersOneAngle; |
|
12711 aMinElems = 4U; |
|
12712 aMaxElems = 4U; |
|
12713 break; |
|
12714 case eCSSKeyword_translate: |
|
12715 /* One or two lengths or percents. */ |
|
12716 variantIndex = eTwoLengthPercentCalcs; |
|
12717 aMinElems = 1U; |
|
12718 aMaxElems = 2U; |
|
12719 break; |
|
12720 case eCSSKeyword_skew: |
|
12721 /* Exactly one or two angles. */ |
|
12722 variantIndex = eTwoAngles; |
|
12723 aMinElems = 1U; |
|
12724 aMaxElems = 2U; |
|
12725 break; |
|
12726 case eCSSKeyword_scale: |
|
12727 /* One or two scale factors. */ |
|
12728 variantIndex = eTwoNumbers; |
|
12729 aMinElems = 1U; |
|
12730 aMaxElems = 2U; |
|
12731 break; |
|
12732 case eCSSKeyword_skewx: |
|
12733 /* Exactly one angle. */ |
|
12734 variantIndex = eAngle; |
|
12735 aMinElems = 1U; |
|
12736 aMaxElems = 1U; |
|
12737 break; |
|
12738 case eCSSKeyword_skewy: |
|
12739 /* Exactly one angle. */ |
|
12740 variantIndex = eAngle; |
|
12741 aMinElems = 1U; |
|
12742 aMaxElems = 1U; |
|
12743 break; |
|
12744 case eCSSKeyword_matrix: |
|
12745 /* Six values, all numbers. */ |
|
12746 variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix; |
|
12747 aMinElems = 6U; |
|
12748 aMaxElems = 6U; |
|
12749 break; |
|
12750 case eCSSKeyword_matrix3d: |
|
12751 /* 16 matrix values, all numbers */ |
|
12752 variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d; |
|
12753 aMinElems = 16U; |
|
12754 aMaxElems = 16U; |
|
12755 break; |
|
12756 case eCSSKeyword_perspective: |
|
12757 /* Exactly one scale number. */ |
|
12758 variantIndex = ePositiveLength; |
|
12759 aMinElems = 1U; |
|
12760 aMaxElems = 1U; |
|
12761 break; |
|
12762 default: |
|
12763 /* Oh dear, we didn't match. Report an error. */ |
|
12764 return false; |
|
12765 } |
|
12766 |
|
12767 NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!"); |
|
12768 NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!"); |
|
12769 NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!"); |
|
12770 NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!"); |
|
12771 NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!"); |
|
12772 #ifdef DEBUG |
|
12773 NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex], |
|
12774 "Invalid aMaxElems for this variant mask."); |
|
12775 #endif |
|
12776 |
|
12777 // Convert the index into a mask. |
|
12778 aVariantMask = kVariantMasks[variantIndex]; |
|
12779 |
|
12780 return true; |
|
12781 } |
|
12782 |
|
12783 bool CSSParserImpl::ParseWillChange() |
|
12784 { |
|
12785 nsCSSValue listValue; |
|
12786 nsCSSValueList* currentListValue = listValue.SetListValue(); |
|
12787 bool first = true; |
|
12788 for (;;) { |
|
12789 const uint32_t variantMask = VARIANT_IDENTIFIER | |
|
12790 VARIANT_INHERIT | |
|
12791 VARIANT_NONE | |
|
12792 VARIANT_ALL | |
|
12793 VARIANT_AUTO; |
|
12794 nsCSSValue value; |
|
12795 if (!ParseVariant(value, variantMask, nullptr)) { |
|
12796 return false; |
|
12797 } |
|
12798 |
|
12799 if (value.GetUnit() == eCSSUnit_None || |
|
12800 value.GetUnit() == eCSSUnit_All) |
|
12801 { |
|
12802 return false; |
|
12803 } |
|
12804 |
|
12805 if (value.GetUnit() != eCSSUnit_Ident) { |
|
12806 if (first) { |
|
12807 AppendValue(eCSSProperty_will_change, value); |
|
12808 return true; |
|
12809 } else { |
|
12810 return false; |
|
12811 } |
|
12812 } |
|
12813 |
|
12814 nsString str; |
|
12815 value.GetStringValue(str); |
|
12816 if (str.LowerCaseEqualsLiteral("default")) { |
|
12817 return false; |
|
12818 } |
|
12819 |
|
12820 currentListValue->mValue = value; |
|
12821 |
|
12822 if (!ExpectSymbol(',', true)) { |
|
12823 break; |
|
12824 } |
|
12825 currentListValue->mNext = new nsCSSValueList; |
|
12826 currentListValue = currentListValue->mNext; |
|
12827 first = false; |
|
12828 } |
|
12829 |
|
12830 AppendValue(eCSSProperty_will_change, listValue); |
|
12831 return true; |
|
12832 } |
|
12833 |
|
12834 /* Reads a single transform function from the tokenizer stream, reporting an |
|
12835 * error if something goes wrong. |
|
12836 */ |
|
12837 bool |
|
12838 CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue) |
|
12839 { |
|
12840 if (!GetToken(true)) |
|
12841 return false; |
|
12842 |
|
12843 if (mToken.mType != eCSSToken_Function) { |
|
12844 UngetToken(); |
|
12845 return false; |
|
12846 } |
|
12847 |
|
12848 const int32_t* variantMask; |
|
12849 uint16_t minElems, maxElems; |
|
12850 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); |
|
12851 |
|
12852 if (!GetFunctionParseInformation(keyword, aIsPrefixed, |
|
12853 minElems, maxElems, variantMask)) |
|
12854 return false; |
|
12855 |
|
12856 return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue); |
|
12857 } |
|
12858 |
|
12859 /* Parses a transform property list by continuously reading in properties |
|
12860 * and constructing a matrix from it. |
|
12861 */ |
|
12862 bool CSSParserImpl::ParseTransform(bool aIsPrefixed) |
|
12863 { |
|
12864 nsCSSValue value; |
|
12865 // 'inherit', 'initial', 'unset' and 'none' must be alone |
|
12866 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { |
|
12867 nsCSSValueSharedList* list = new nsCSSValueSharedList; |
|
12868 value.SetSharedListValue(list); |
|
12869 list->mHead = new nsCSSValueList; |
|
12870 nsCSSValueList* cur = list->mHead; |
|
12871 for (;;) { |
|
12872 if (!ParseSingleTransform(aIsPrefixed, cur->mValue)) { |
|
12873 return false; |
|
12874 } |
|
12875 if (CheckEndProperty()) { |
|
12876 break; |
|
12877 } |
|
12878 cur->mNext = new nsCSSValueList; |
|
12879 cur = cur->mNext; |
|
12880 } |
|
12881 } |
|
12882 AppendValue(eCSSProperty_transform, value); |
|
12883 return true; |
|
12884 } |
|
12885 |
|
12886 bool CSSParserImpl::ParseTransformOrigin(bool aPerspective) |
|
12887 { |
|
12888 nsCSSValuePair position; |
|
12889 if (!ParseBoxPositionValues(position, true)) |
|
12890 return false; |
|
12891 |
|
12892 nsCSSProperty prop = eCSSProperty_transform_origin; |
|
12893 if (aPerspective) { |
|
12894 prop = eCSSProperty_perspective_origin; |
|
12895 } |
|
12896 |
|
12897 // Unlike many other uses of pairs, this position should always be stored |
|
12898 // as a pair, even if the values are the same, so it always serializes as |
|
12899 // a pair, and to keep the computation code simple. |
|
12900 if (position.mXValue.GetUnit() == eCSSUnit_Inherit || |
|
12901 position.mXValue.GetUnit() == eCSSUnit_Initial || |
|
12902 position.mXValue.GetUnit() == eCSSUnit_Unset) { |
|
12903 NS_ABORT_IF_FALSE(position.mXValue == position.mYValue, |
|
12904 "inherit/initial/unset only half?"); |
|
12905 AppendValue(prop, position.mXValue); |
|
12906 } else { |
|
12907 nsCSSValue value; |
|
12908 if (aPerspective) { |
|
12909 value.SetPairValue(position.mXValue, position.mYValue); |
|
12910 } else { |
|
12911 nsCSSValue depth; |
|
12912 if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr)) { |
|
12913 depth.SetFloatValue(0.0f, eCSSUnit_Pixel); |
|
12914 } |
|
12915 value.SetTripletValue(position.mXValue, position.mYValue, depth); |
|
12916 } |
|
12917 |
|
12918 AppendValue(prop, value); |
|
12919 } |
|
12920 return true; |
|
12921 } |
|
12922 |
|
12923 /** |
|
12924 * Reads a drop-shadow value. At the moment the Filter Effects specification |
|
12925 * just expects one shadow item. Should this ever change to a list of shadow |
|
12926 * items, use ParseShadowList instead. |
|
12927 */ |
|
12928 bool |
|
12929 CSSParserImpl::ParseDropShadow(nsCSSValue* aValue) |
|
12930 { |
|
12931 // Use nsCSSValueList to reuse the shadow resolving code in |
|
12932 // nsRuleNode and nsComputedDOMStyle. |
|
12933 nsCSSValue shadow; |
|
12934 nsCSSValueList* cur = shadow.SetListValue(); |
|
12935 if (!ParseShadowItem(cur->mValue, false)) |
|
12936 return false; |
|
12937 |
|
12938 if (!ExpectSymbol(')', true)) |
|
12939 return false; |
|
12940 |
|
12941 nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1); |
|
12942 |
|
12943 // Copy things over. |
|
12944 dropShadow->Item(1) = shadow; |
|
12945 |
|
12946 return true; |
|
12947 } |
|
12948 |
|
12949 /** |
|
12950 * Reads a single url or filter function from the tokenizer stream, reporting an |
|
12951 * error if something goes wrong. |
|
12952 */ |
|
12953 bool |
|
12954 CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue) |
|
12955 { |
|
12956 if (ParseVariant(*aValue, VARIANT_URL, nullptr)) { |
|
12957 return true; |
|
12958 } |
|
12959 |
|
12960 if (!nsLayoutUtils::CSSFiltersEnabled()) { |
|
12961 // With CSS Filters disabled, we should only accept an SVG reference filter. |
|
12962 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL); |
|
12963 return false; |
|
12964 } |
|
12965 |
|
12966 if (!GetToken(true)) { |
|
12967 REPORT_UNEXPECTED_EOF(PEFilterEOF); |
|
12968 return false; |
|
12969 } |
|
12970 |
|
12971 if (mToken.mType != eCSSToken_Function) { |
|
12972 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction); |
|
12973 return false; |
|
12974 } |
|
12975 |
|
12976 nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent); |
|
12977 // Parse drop-shadow independently of the other filter functions |
|
12978 // because of its more complex characteristics. |
|
12979 if (functionName == eCSSKeyword_drop_shadow) { |
|
12980 if (ParseDropShadow(aValue)) { |
|
12981 return true; |
|
12982 } else { |
|
12983 // Unrecognized filter function. |
|
12984 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction); |
|
12985 SkipUntil(')'); |
|
12986 return false; |
|
12987 } |
|
12988 } |
|
12989 |
|
12990 // Set up the parsing rules based on the filter function. |
|
12991 int32_t variantMask = VARIANT_PN; |
|
12992 bool rejectNegativeArgument = true; |
|
12993 bool clampArgumentToOne = false; |
|
12994 switch (functionName) { |
|
12995 case eCSSKeyword_blur: |
|
12996 variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION; |
|
12997 // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths. |
|
12998 rejectNegativeArgument = false; |
|
12999 break; |
|
13000 case eCSSKeyword_brightness: |
|
13001 case eCSSKeyword_contrast: |
|
13002 case eCSSKeyword_saturate: |
|
13003 break; |
|
13004 case eCSSKeyword_grayscale: |
|
13005 case eCSSKeyword_invert: |
|
13006 case eCSSKeyword_sepia: |
|
13007 case eCSSKeyword_opacity: |
|
13008 clampArgumentToOne = true; |
|
13009 break; |
|
13010 case eCSSKeyword_hue_rotate: |
|
13011 variantMask = VARIANT_ANGLE; |
|
13012 rejectNegativeArgument = false; |
|
13013 break; |
|
13014 default: |
|
13015 // Unrecognized filter function. |
|
13016 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction); |
|
13017 SkipUntil(')'); |
|
13018 return false; |
|
13019 } |
|
13020 |
|
13021 // Parse the function. |
|
13022 uint16_t minElems = 1U; |
|
13023 uint16_t maxElems = 1U; |
|
13024 uint32_t allVariants = 0; |
|
13025 if (!ParseFunction(functionName, &variantMask, allVariants, |
|
13026 minElems, maxElems, *aValue)) { |
|
13027 REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError); |
|
13028 return false; |
|
13029 } |
|
13030 |
|
13031 // Get the first and only argument to the filter function. |
|
13032 NS_ABORT_IF_FALSE(aValue->GetUnit() == eCSSUnit_Function, |
|
13033 "expected a filter function"); |
|
13034 NS_ABORT_IF_FALSE(aValue->UnitHasArrayValue(), |
|
13035 "filter function should be an array"); |
|
13036 NS_ABORT_IF_FALSE(aValue->GetArrayValue()->Count() == 2, |
|
13037 "filter function should have exactly one argument"); |
|
13038 nsCSSValue& arg = aValue->GetArrayValue()->Item(1); |
|
13039 |
|
13040 if (rejectNegativeArgument && |
|
13041 ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) || |
|
13042 (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) { |
|
13043 REPORT_UNEXPECTED(PEExpectedNonnegativeNP); |
|
13044 return false; |
|
13045 } |
|
13046 |
|
13047 if (clampArgumentToOne) { |
|
13048 if (arg.GetUnit() == eCSSUnit_Number && |
|
13049 arg.GetFloatValue() > 1.0f) { |
|
13050 arg.SetFloatValue(1.0f, arg.GetUnit()); |
|
13051 } else if (arg.GetUnit() == eCSSUnit_Percent && |
|
13052 arg.GetPercentValue() > 1.0f) { |
|
13053 arg.SetPercentValue(1.0f); |
|
13054 } |
|
13055 } |
|
13056 |
|
13057 return true; |
|
13058 } |
|
13059 |
|
13060 /** |
|
13061 * Parses a filter property value by continuously reading in urls and/or filter |
|
13062 * functions and constructing a list. |
|
13063 * |
|
13064 * When CSS Filters are enabled, the filter property accepts one or more SVG |
|
13065 * reference filters and/or CSS filter functions. |
|
13066 * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%); |
|
13067 * |
|
13068 * When CSS Filters are disabled, the filter property only accepts one SVG |
|
13069 * reference filter. |
|
13070 * e.g. filter: url(#my-filter); |
|
13071 */ |
|
13072 bool |
|
13073 CSSParserImpl::ParseFilter() |
|
13074 { |
|
13075 nsCSSValue value; |
|
13076 // 'inherit', 'initial', 'unset' and 'none' must be alone |
|
13077 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { |
|
13078 nsCSSValueList* cur = value.SetListValue(); |
|
13079 while (cur) { |
|
13080 if (!ParseSingleFilter(&cur->mValue)) { |
|
13081 return false; |
|
13082 } |
|
13083 if (CheckEndProperty()) { |
|
13084 break; |
|
13085 } |
|
13086 if (!nsLayoutUtils::CSSFiltersEnabled()) { |
|
13087 // With CSS Filters disabled, we should only accept one SVG reference |
|
13088 // filter. |
|
13089 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); |
|
13090 return false; |
|
13091 } |
|
13092 cur->mNext = new nsCSSValueList; |
|
13093 cur = cur->mNext; |
|
13094 } |
|
13095 } |
|
13096 AppendValue(eCSSProperty_filter, value); |
|
13097 return true; |
|
13098 } |
|
13099 |
|
13100 bool |
|
13101 CSSParserImpl::ParseTransitionProperty() |
|
13102 { |
|
13103 nsCSSValue value; |
|
13104 // 'inherit', 'initial', 'unset' and 'none' must be alone |
|
13105 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { |
|
13106 // Accept a list of arbitrary identifiers. They should be |
|
13107 // CSS properties, but we want to accept any so that we |
|
13108 // accept properties that we don't know about yet, e.g. |
|
13109 // transition-property: invalid-property, left, opacity; |
|
13110 nsCSSValueList* cur = value.SetListValue(); |
|
13111 for (;;) { |
|
13112 if (!ParseVariant(cur->mValue, VARIANT_IDENTIFIER | VARIANT_ALL, nullptr)) { |
|
13113 return false; |
|
13114 } |
|
13115 if (cur->mValue.GetUnit() == eCSSUnit_Ident) { |
|
13116 nsDependentString str(cur->mValue.GetStringBufferValue()); |
|
13117 // Exclude 'none', 'inherit', 'initial' and 'unset' according to the |
|
13118 // same rules as for 'counter-reset' in CSS 2.1. |
|
13119 if (str.LowerCaseEqualsLiteral("none") || |
|
13120 str.LowerCaseEqualsLiteral("inherit") || |
|
13121 str.LowerCaseEqualsLiteral("initial") || |
|
13122 (str.LowerCaseEqualsLiteral("unset") && |
|
13123 nsLayoutUtils::UnsetValueEnabled())) { |
|
13124 return false; |
|
13125 } |
|
13126 } |
|
13127 if (!ExpectSymbol(',', true)) { |
|
13128 break; |
|
13129 } |
|
13130 cur->mNext = new nsCSSValueList; |
|
13131 cur = cur->mNext; |
|
13132 } |
|
13133 } |
|
13134 AppendValue(eCSSProperty_transition_property, value); |
|
13135 return true; |
|
13136 } |
|
13137 |
|
13138 bool |
|
13139 CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue) |
|
13140 { |
|
13141 NS_ASSERTION(!mHavePushBack && |
|
13142 mToken.mType == eCSSToken_Function && |
|
13143 mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"), |
|
13144 "unexpected initial state"); |
|
13145 |
|
13146 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(4); |
|
13147 |
|
13148 float x1, x2, y1, y2; |
|
13149 if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) || |
|
13150 !ParseTransitionTimingFunctionValueComponent(y1, ',', false) || |
|
13151 !ParseTransitionTimingFunctionValueComponent(x2, ',', true) || |
|
13152 !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) { |
|
13153 return false; |
|
13154 } |
|
13155 |
|
13156 val->Item(0).SetFloatValue(x1, eCSSUnit_Number); |
|
13157 val->Item(1).SetFloatValue(y1, eCSSUnit_Number); |
|
13158 val->Item(2).SetFloatValue(x2, eCSSUnit_Number); |
|
13159 val->Item(3).SetFloatValue(y2, eCSSUnit_Number); |
|
13160 |
|
13161 aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier); |
|
13162 |
|
13163 return true; |
|
13164 } |
|
13165 |
|
13166 bool |
|
13167 CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent, |
|
13168 char aStop, |
|
13169 bool aCheckRange) |
|
13170 { |
|
13171 if (!GetToken(true)) { |
|
13172 return false; |
|
13173 } |
|
13174 nsCSSToken* tk = &mToken; |
|
13175 if (tk->mType == eCSSToken_Number) { |
|
13176 float num = tk->mNumber; |
|
13177 if (aCheckRange && (num < 0.0 || num > 1.0)) { |
|
13178 return false; |
|
13179 } |
|
13180 aComponent = num; |
|
13181 if (ExpectSymbol(aStop, true)) { |
|
13182 return true; |
|
13183 } |
|
13184 } |
|
13185 return false; |
|
13186 } |
|
13187 |
|
13188 bool |
|
13189 CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue) |
|
13190 { |
|
13191 NS_ASSERTION(!mHavePushBack && |
|
13192 mToken.mType == eCSSToken_Function && |
|
13193 mToken.mIdent.LowerCaseEqualsLiteral("steps"), |
|
13194 "unexpected initial state"); |
|
13195 |
|
13196 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2); |
|
13197 |
|
13198 if (!ParseOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, nullptr)) { |
|
13199 return false; |
|
13200 } |
|
13201 |
|
13202 int32_t type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END; |
|
13203 if (ExpectSymbol(',', true)) { |
|
13204 if (!GetToken(true)) { |
|
13205 return false; |
|
13206 } |
|
13207 type = -1; |
|
13208 if (mToken.mType == eCSSToken_Ident) { |
|
13209 if (mToken.mIdent.LowerCaseEqualsLiteral("start")) { |
|
13210 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START; |
|
13211 } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) { |
|
13212 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END; |
|
13213 } |
|
13214 } |
|
13215 if (type == -1) { |
|
13216 UngetToken(); |
|
13217 return false; |
|
13218 } |
|
13219 } |
|
13220 val->Item(1).SetIntValue(type, eCSSUnit_Enumerated); |
|
13221 |
|
13222 if (!ExpectSymbol(')', true)) { |
|
13223 return false; |
|
13224 } |
|
13225 |
|
13226 aValue.SetArrayValue(val, eCSSUnit_Steps); |
|
13227 return true; |
|
13228 } |
|
13229 |
|
13230 static nsCSSValueList* |
|
13231 AppendValueToList(nsCSSValue& aContainer, |
|
13232 nsCSSValueList* aTail, |
|
13233 const nsCSSValue& aValue) |
|
13234 { |
|
13235 nsCSSValueList* entry; |
|
13236 if (aContainer.GetUnit() == eCSSUnit_Null) { |
|
13237 NS_ABORT_IF_FALSE(!aTail, "should not have an entry"); |
|
13238 entry = aContainer.SetListValue(); |
|
13239 } else { |
|
13240 NS_ABORT_IF_FALSE(!aTail->mNext, "should not have a next entry"); |
|
13241 NS_ABORT_IF_FALSE(aContainer.GetUnit() == eCSSUnit_List, "not a list"); |
|
13242 entry = new nsCSSValueList; |
|
13243 aTail->mNext = entry; |
|
13244 } |
|
13245 entry->mValue = aValue; |
|
13246 return entry; |
|
13247 } |
|
13248 |
|
13249 CSSParserImpl::ParseAnimationOrTransitionShorthandResult |
|
13250 CSSParserImpl::ParseAnimationOrTransitionShorthand( |
|
13251 const nsCSSProperty* aProperties, |
|
13252 const nsCSSValue* aInitialValues, |
|
13253 nsCSSValue* aValues, |
|
13254 size_t aNumProperties) |
|
13255 { |
|
13256 nsCSSValue tempValue; |
|
13257 // first see if 'inherit', 'initial' or 'unset' is specified. If one is, |
|
13258 // it can be the only thing specified, so don't attempt to parse any |
|
13259 // additional properties |
|
13260 if (ParseVariant(tempValue, VARIANT_INHERIT, nullptr)) { |
|
13261 for (uint32_t i = 0; i < aNumProperties; ++i) { |
|
13262 AppendValue(aProperties[i], tempValue); |
|
13263 } |
|
13264 return eParseAnimationOrTransitionShorthand_Inherit; |
|
13265 } |
|
13266 |
|
13267 static const size_t maxNumProperties = 7; |
|
13268 NS_ABORT_IF_FALSE(aNumProperties <= maxNumProperties, |
|
13269 "can't handle this many properties"); |
|
13270 nsCSSValueList *cur[maxNumProperties]; |
|
13271 bool parsedProperty[maxNumProperties]; |
|
13272 |
|
13273 for (size_t i = 0; i < aNumProperties; ++i) { |
|
13274 cur[i] = nullptr; |
|
13275 } |
|
13276 bool atEOP = false; // at end of property? |
|
13277 for (;;) { // loop over comma-separated transitions or animations |
|
13278 // whether a particular subproperty was specified for this |
|
13279 // transition or animation |
|
13280 bool haveAnyProperty = false; |
|
13281 for (size_t i = 0; i < aNumProperties; ++i) { |
|
13282 parsedProperty[i] = false; |
|
13283 } |
|
13284 for (;;) { // loop over values within a transition or animation |
|
13285 bool foundProperty = false; |
|
13286 // check to see if we're at the end of one full transition or |
|
13287 // animation definition (either because we hit a comma or because |
|
13288 // we hit the end of the property definition) |
|
13289 if (ExpectSymbol(',', true)) |
|
13290 break; |
|
13291 if (CheckEndProperty()) { |
|
13292 atEOP = true; |
|
13293 break; |
|
13294 } |
|
13295 |
|
13296 // else, try to parse the next transition or animation sub-property |
|
13297 for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) { |
|
13298 if (!parsedProperty[i]) { |
|
13299 // if we haven't found this property yet, try to parse it |
|
13300 if (ParseSingleValueProperty(tempValue, aProperties[i])) { |
|
13301 parsedProperty[i] = true; |
|
13302 cur[i] = AppendValueToList(aValues[i], cur[i], tempValue); |
|
13303 foundProperty = true; |
|
13304 haveAnyProperty = true; |
|
13305 break; // out of inner loop; continue looking for next sub-property |
|
13306 } |
|
13307 } |
|
13308 } |
|
13309 if (!foundProperty) { |
|
13310 // We're not at a ',' or at the end of the property, but we couldn't |
|
13311 // parse any of the sub-properties, so the declaration is invalid. |
|
13312 return eParseAnimationOrTransitionShorthand_Error; |
|
13313 } |
|
13314 } |
|
13315 |
|
13316 if (!haveAnyProperty) { |
|
13317 // Got an empty item. |
|
13318 return eParseAnimationOrTransitionShorthand_Error; |
|
13319 } |
|
13320 |
|
13321 // We hit the end of the property or the end of one transition |
|
13322 // or animation definition, add its components to the list. |
|
13323 for (uint32_t i = 0; i < aNumProperties; ++i) { |
|
13324 // If all of the subproperties were not explicitly specified, fill |
|
13325 // in the missing ones with initial values. |
|
13326 if (!parsedProperty[i]) { |
|
13327 cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]); |
|
13328 } |
|
13329 } |
|
13330 |
|
13331 if (atEOP) |
|
13332 break; |
|
13333 // else we just hit a ',' so continue parsing the next compound transition |
|
13334 } |
|
13335 |
|
13336 return eParseAnimationOrTransitionShorthand_Values; |
|
13337 } |
|
13338 |
|
13339 bool |
|
13340 CSSParserImpl::ParseTransition() |
|
13341 { |
|
13342 static const nsCSSProperty kTransitionProperties[] = { |
|
13343 eCSSProperty_transition_duration, |
|
13344 eCSSProperty_transition_timing_function, |
|
13345 // Must check 'transition-delay' after 'transition-duration', since |
|
13346 // that's our assumption about what the spec means for the shorthand |
|
13347 // syntax (the first time given is the duration, and the second |
|
13348 // given is the delay). |
|
13349 eCSSProperty_transition_delay, |
|
13350 // Must check 'transition-property' after |
|
13351 // 'transition-timing-function' since 'transition-property' accepts |
|
13352 // any keyword. |
|
13353 eCSSProperty_transition_property |
|
13354 }; |
|
13355 static const uint32_t numProps = MOZ_ARRAY_LENGTH(kTransitionProperties); |
|
13356 // this is a shorthand property that accepts -property, -delay, |
|
13357 // -duration, and -timing-function with some components missing. |
|
13358 // there can be multiple transitions, separated with commas |
|
13359 |
|
13360 nsCSSValue initialValues[numProps]; |
|
13361 initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds); |
|
13362 initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE, |
|
13363 eCSSUnit_Enumerated); |
|
13364 initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds); |
|
13365 initialValues[3].SetAllValue(); |
|
13366 |
|
13367 nsCSSValue values[numProps]; |
|
13368 |
|
13369 ParseAnimationOrTransitionShorthandResult spres = |
|
13370 ParseAnimationOrTransitionShorthand(kTransitionProperties, |
|
13371 initialValues, values, numProps); |
|
13372 if (spres != eParseAnimationOrTransitionShorthand_Values) { |
|
13373 return spres != eParseAnimationOrTransitionShorthand_Error; |
|
13374 } |
|
13375 |
|
13376 // Make two checks on the list for 'transition-property': |
|
13377 // + If there is more than one item, then none of the items can be |
|
13378 // 'none'. |
|
13379 // + None of the items can be 'inherit', 'initial' or 'unset'. |
|
13380 { |
|
13381 NS_ABORT_IF_FALSE(kTransitionProperties[3] == |
|
13382 eCSSProperty_transition_property, |
|
13383 "array index mismatch"); |
|
13384 nsCSSValueList *l = values[3].GetListValue(); |
|
13385 bool multipleItems = !!l->mNext; |
|
13386 do { |
|
13387 const nsCSSValue& val = l->mValue; |
|
13388 if (val.GetUnit() == eCSSUnit_None) { |
|
13389 if (multipleItems) { |
|
13390 // This is a syntax error. |
|
13391 return false; |
|
13392 } |
|
13393 |
|
13394 // Unbox a solitary 'none'. |
|
13395 values[3].SetNoneValue(); |
|
13396 break; |
|
13397 } |
|
13398 if (val.GetUnit() == eCSSUnit_Ident) { |
|
13399 nsDependentString str(val.GetStringBufferValue()); |
|
13400 if (str.EqualsLiteral("inherit") || |
|
13401 str.EqualsLiteral("initial") || |
|
13402 (str.EqualsLiteral("unset") && |
|
13403 nsLayoutUtils::UnsetValueEnabled())) { |
|
13404 return false; |
|
13405 } |
|
13406 } |
|
13407 } while ((l = l->mNext)); |
|
13408 } |
|
13409 |
|
13410 // Save all parsed transition sub-properties in mTempData |
|
13411 for (uint32_t i = 0; i < numProps; ++i) { |
|
13412 AppendValue(kTransitionProperties[i], values[i]); |
|
13413 } |
|
13414 return true; |
|
13415 } |
|
13416 |
|
13417 bool |
|
13418 CSSParserImpl::ParseAnimation() |
|
13419 { |
|
13420 static const nsCSSProperty kAnimationProperties[] = { |
|
13421 eCSSProperty_animation_duration, |
|
13422 eCSSProperty_animation_timing_function, |
|
13423 // Must check 'animation-delay' after 'animation-duration', since |
|
13424 // that's our assumption about what the spec means for the shorthand |
|
13425 // syntax (the first time given is the duration, and the second |
|
13426 // given is the delay). |
|
13427 eCSSProperty_animation_delay, |
|
13428 eCSSProperty_animation_direction, |
|
13429 eCSSProperty_animation_fill_mode, |
|
13430 eCSSProperty_animation_iteration_count, |
|
13431 // Must check 'animation-name' after 'animation-timing-function', |
|
13432 // 'animation-direction', 'animation-fill-mode', |
|
13433 // 'animation-iteration-count', and 'animation-play-state' since |
|
13434 // 'animation-name' accepts any keyword. |
|
13435 eCSSProperty_animation_name |
|
13436 }; |
|
13437 static const uint32_t numProps = MOZ_ARRAY_LENGTH(kAnimationProperties); |
|
13438 // this is a shorthand property that accepts -property, -delay, |
|
13439 // -duration, and -timing-function with some components missing. |
|
13440 // there can be multiple animations, separated with commas |
|
13441 |
|
13442 nsCSSValue initialValues[numProps]; |
|
13443 initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds); |
|
13444 initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE, |
|
13445 eCSSUnit_Enumerated); |
|
13446 initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds); |
|
13447 initialValues[3].SetIntValue(NS_STYLE_ANIMATION_DIRECTION_NORMAL, eCSSUnit_Enumerated); |
|
13448 initialValues[4].SetIntValue(NS_STYLE_ANIMATION_FILL_MODE_NONE, eCSSUnit_Enumerated); |
|
13449 initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number); |
|
13450 initialValues[6].SetNoneValue(); |
|
13451 |
|
13452 nsCSSValue values[numProps]; |
|
13453 |
|
13454 ParseAnimationOrTransitionShorthandResult spres = |
|
13455 ParseAnimationOrTransitionShorthand(kAnimationProperties, |
|
13456 initialValues, values, numProps); |
|
13457 if (spres != eParseAnimationOrTransitionShorthand_Values) { |
|
13458 return spres != eParseAnimationOrTransitionShorthand_Error; |
|
13459 } |
|
13460 |
|
13461 // Save all parsed animation sub-properties in mTempData |
|
13462 for (uint32_t i = 0; i < numProps; ++i) { |
|
13463 AppendValue(kAnimationProperties[i], values[i]); |
|
13464 } |
|
13465 return true; |
|
13466 } |
|
13467 |
|
13468 bool |
|
13469 CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow) |
|
13470 { |
|
13471 // A shadow list item is an array, with entries in this sequence: |
|
13472 enum { |
|
13473 IndexX, |
|
13474 IndexY, |
|
13475 IndexRadius, |
|
13476 IndexSpread, // only for box-shadow |
|
13477 IndexColor, |
|
13478 IndexInset // only for box-shadow |
|
13479 }; |
|
13480 |
|
13481 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6); |
|
13482 |
|
13483 if (aIsBoxShadow) { |
|
13484 // Optional inset keyword (ignore errors) |
|
13485 ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD, |
|
13486 nsCSSProps::kBoxShadowTypeKTable); |
|
13487 } |
|
13488 |
|
13489 nsCSSValue xOrColor; |
|
13490 bool haveColor = false; |
|
13491 if (!ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC, |
|
13492 nullptr)) { |
|
13493 return false; |
|
13494 } |
|
13495 if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) { |
|
13496 val->Item(IndexX) = xOrColor; |
|
13497 } else { |
|
13498 // Must be a color (as string or color value) |
|
13499 NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident || |
|
13500 xOrColor.GetUnit() == eCSSUnit_EnumColor || |
|
13501 xOrColor.IsNumericColorUnit(), |
|
13502 "Must be a color value"); |
|
13503 val->Item(IndexColor) = xOrColor; |
|
13504 haveColor = true; |
|
13505 |
|
13506 // X coordinate mandatory after color |
|
13507 if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC, |
|
13508 nullptr)) { |
|
13509 return false; |
|
13510 } |
|
13511 } |
|
13512 |
|
13513 // Y coordinate; mandatory |
|
13514 if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC, |
|
13515 nullptr)) { |
|
13516 return false; |
|
13517 } |
|
13518 |
|
13519 // Optional radius. Ignore errors except if they pass a negative |
|
13520 // value which we must reject. If we use ParseNonNegativeVariant |
|
13521 // we can't tell the difference between an unspecified radius |
|
13522 // and a negative radius. |
|
13523 if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC, |
|
13524 nullptr) && |
|
13525 val->Item(IndexRadius).IsLengthUnit() && |
|
13526 val->Item(IndexRadius).GetFloatValue() < 0) { |
|
13527 return false; |
|
13528 } |
|
13529 |
|
13530 if (aIsBoxShadow) { |
|
13531 // Optional spread |
|
13532 ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC, nullptr); |
|
13533 } |
|
13534 |
|
13535 if (!haveColor) { |
|
13536 // Optional color |
|
13537 ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr); |
|
13538 } |
|
13539 |
|
13540 if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) { |
|
13541 // Optional inset keyword |
|
13542 ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD, |
|
13543 nsCSSProps::kBoxShadowTypeKTable); |
|
13544 } |
|
13545 |
|
13546 aValue.SetArrayValue(val, eCSSUnit_Array); |
|
13547 return true; |
|
13548 } |
|
13549 |
|
13550 bool |
|
13551 CSSParserImpl::ParseShadowList(nsCSSProperty aProperty) |
|
13552 { |
|
13553 nsAutoParseCompoundProperty compound(this); |
|
13554 bool isBoxShadow = aProperty == eCSSProperty_box_shadow; |
|
13555 |
|
13556 nsCSSValue value; |
|
13557 // 'inherit', 'initial', 'unset' and 'none' must be alone |
|
13558 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { |
|
13559 nsCSSValueList* cur = value.SetListValue(); |
|
13560 for (;;) { |
|
13561 if (!ParseShadowItem(cur->mValue, isBoxShadow)) { |
|
13562 return false; |
|
13563 } |
|
13564 if (!ExpectSymbol(',', true)) { |
|
13565 break; |
|
13566 } |
|
13567 cur->mNext = new nsCSSValueList; |
|
13568 cur = cur->mNext; |
|
13569 } |
|
13570 } |
|
13571 AppendValue(aProperty, value); |
|
13572 return true; |
|
13573 } |
|
13574 |
|
13575 int32_t |
|
13576 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix) |
|
13577 { |
|
13578 NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here"); |
|
13579 |
|
13580 int32_t nameSpaceID = kNameSpaceID_Unknown; |
|
13581 if (mNameSpaceMap) { |
|
13582 // user-specified identifiers are case-sensitive (bug 416106) |
|
13583 nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix); |
|
13584 if (!prefix) { |
|
13585 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?"); |
|
13586 } |
|
13587 nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix); |
|
13588 } |
|
13589 // else no declared namespaces |
|
13590 |
|
13591 if (nameSpaceID == kNameSpaceID_Unknown) { // unknown prefix, dump it |
|
13592 REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, aPrefix); |
|
13593 } |
|
13594 |
|
13595 return nameSpaceID; |
|
13596 } |
|
13597 |
|
13598 void |
|
13599 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector) |
|
13600 { |
|
13601 if (mNameSpaceMap) { |
|
13602 aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nullptr)); |
|
13603 } else { |
|
13604 aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard |
|
13605 } |
|
13606 } |
|
13607 |
|
13608 bool |
|
13609 CSSParserImpl::ParsePaint(nsCSSProperty aPropID) |
|
13610 { |
|
13611 nsCSSValue x, y; |
|
13612 |
|
13613 if (!ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL | |
|
13614 VARIANT_OPENTYPE_SVG_KEYWORD, |
|
13615 nsCSSProps::kContextPatternKTable)) { |
|
13616 return false; |
|
13617 } |
|
13618 |
|
13619 bool canHaveFallback = x.GetUnit() == eCSSUnit_URL || |
|
13620 x.GetUnit() == eCSSUnit_Enumerated; |
|
13621 if (canHaveFallback) { |
|
13622 if (!ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr)) |
|
13623 y.SetNoneValue(); |
|
13624 } |
|
13625 |
|
13626 if (!canHaveFallback) { |
|
13627 AppendValue(aPropID, x); |
|
13628 } else { |
|
13629 nsCSSValue val; |
|
13630 val.SetPairValue(x, y); |
|
13631 AppendValue(aPropID, val); |
|
13632 } |
|
13633 return true; |
|
13634 } |
|
13635 |
|
13636 bool |
|
13637 CSSParserImpl::ParseDasharray() |
|
13638 { |
|
13639 nsCSSValue value; |
|
13640 |
|
13641 // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own |
|
13642 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE | |
|
13643 VARIANT_OPENTYPE_SVG_KEYWORD, |
|
13644 nsCSSProps::kStrokeContextValueKTable)) { |
|
13645 nsCSSValueList *cur = value.SetListValue(); |
|
13646 for (;;) { |
|
13647 if (!ParseNonNegativeVariant(cur->mValue, VARIANT_LPN, nullptr)) { |
|
13648 return false; |
|
13649 } |
|
13650 if (CheckEndProperty()) { |
|
13651 break; |
|
13652 } |
|
13653 // skip optional commas between elements |
|
13654 (void)ExpectSymbol(',', true); |
|
13655 |
|
13656 cur->mNext = new nsCSSValueList; |
|
13657 cur = cur->mNext; |
|
13658 } |
|
13659 } |
|
13660 AppendValue(eCSSProperty_stroke_dasharray, value); |
|
13661 return true; |
|
13662 } |
|
13663 |
|
13664 bool |
|
13665 CSSParserImpl::ParseMarker() |
|
13666 { |
|
13667 nsCSSValue marker; |
|
13668 if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) { |
|
13669 AppendValue(eCSSProperty_marker_end, marker); |
|
13670 AppendValue(eCSSProperty_marker_mid, marker); |
|
13671 AppendValue(eCSSProperty_marker_start, marker); |
|
13672 return true; |
|
13673 } |
|
13674 return false; |
|
13675 } |
|
13676 |
|
13677 bool |
|
13678 CSSParserImpl::ParsePaintOrder() |
|
13679 { |
|
13680 static_assert |
|
13681 ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) > NS_STYLE_PAINT_ORDER_LAST_VALUE, |
|
13682 "bitfield width insufficient for paint-order constants"); |
|
13683 |
|
13684 static const KTableValue kPaintOrderKTable[] = { |
|
13685 eCSSKeyword_normal, NS_STYLE_PAINT_ORDER_NORMAL, |
|
13686 eCSSKeyword_fill, NS_STYLE_PAINT_ORDER_FILL, |
|
13687 eCSSKeyword_stroke, NS_STYLE_PAINT_ORDER_STROKE, |
|
13688 eCSSKeyword_markers, NS_STYLE_PAINT_ORDER_MARKERS, |
|
13689 eCSSKeyword_UNKNOWN,-1 |
|
13690 }; |
|
13691 |
|
13692 static_assert(MOZ_ARRAY_LENGTH(kPaintOrderKTable) == |
|
13693 2 * (NS_STYLE_PAINT_ORDER_LAST_VALUE + 2), |
|
13694 "missing paint-order values in kPaintOrderKTable"); |
|
13695 |
|
13696 nsCSSValue value; |
|
13697 if (!ParseVariant(value, VARIANT_HK, kPaintOrderKTable)) { |
|
13698 return false; |
|
13699 } |
|
13700 |
|
13701 uint32_t seen = 0; |
|
13702 uint32_t order = 0; |
|
13703 uint32_t position = 0; |
|
13704 |
|
13705 // Ensure that even cast to a signed int32_t when stored in CSSValue, |
|
13706 // we have enough space for the entire paint-order value. |
|
13707 static_assert |
|
13708 (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE < 32, |
|
13709 "seen and order not big enough"); |
|
13710 |
|
13711 if (value.GetUnit() == eCSSUnit_Enumerated) { |
|
13712 uint32_t component = static_cast<uint32_t>(value.GetIntValue()); |
|
13713 if (component != NS_STYLE_PAINT_ORDER_NORMAL) { |
|
13714 bool parsedOK = true; |
|
13715 for (;;) { |
|
13716 if (seen & (1 << component)) { |
|
13717 // Already seen this component. |
|
13718 UngetToken(); |
|
13719 parsedOK = false; |
|
13720 break; |
|
13721 } |
|
13722 seen |= (1 << component); |
|
13723 order |= (component << position); |
|
13724 position += NS_STYLE_PAINT_ORDER_BITWIDTH; |
|
13725 if (!ParseEnum(value, kPaintOrderKTable)) { |
|
13726 break; |
|
13727 } |
|
13728 component = value.GetIntValue(); |
|
13729 if (component == NS_STYLE_PAINT_ORDER_NORMAL) { |
|
13730 // Can't have "normal" in the middle of the list of paint components. |
|
13731 UngetToken(); |
|
13732 parsedOK = false; |
|
13733 break; |
|
13734 } |
|
13735 } |
|
13736 |
|
13737 // Fill in the remaining paint-order components in the order of their |
|
13738 // constant values. |
|
13739 if (parsedOK) { |
|
13740 for (component = 1; |
|
13741 component <= NS_STYLE_PAINT_ORDER_LAST_VALUE; |
|
13742 component++) { |
|
13743 if (!(seen & (1 << component))) { |
|
13744 order |= (component << position); |
|
13745 position += NS_STYLE_PAINT_ORDER_BITWIDTH; |
|
13746 } |
|
13747 } |
|
13748 } |
|
13749 } |
|
13750 |
|
13751 static_assert(NS_STYLE_PAINT_ORDER_NORMAL == 0, |
|
13752 "unexpected value for NS_STYLE_PAINT_ORDER_NORMAL"); |
|
13753 value.SetIntValue(static_cast<int32_t>(order), eCSSUnit_Enumerated); |
|
13754 } |
|
13755 |
|
13756 AppendValue(eCSSProperty_paint_order, value); |
|
13757 return true; |
|
13758 } |
|
13759 |
|
13760 bool |
|
13761 CSSParserImpl::BackslashDropped() |
|
13762 { |
|
13763 return mScanner->GetEOFCharacters() & |
|
13764 nsCSSScanner::eEOFCharacters_DropBackslash; |
|
13765 } |
|
13766 |
|
13767 void |
|
13768 CSSParserImpl::AppendImpliedEOFCharacters(nsAString& aResult) |
|
13769 { |
|
13770 nsCSSScanner::AppendImpliedEOFCharacters(mScanner->GetEOFCharacters(), |
|
13771 aResult); |
|
13772 } |
|
13773 |
|
13774 bool |
|
13775 CSSParserImpl::ParseAll() |
|
13776 { |
|
13777 nsCSSValue value; |
|
13778 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { |
|
13779 return false; |
|
13780 } |
|
13781 |
|
13782 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all) { |
|
13783 AppendValue(*p, value); |
|
13784 } |
|
13785 return true; |
|
13786 } |
|
13787 |
|
13788 bool |
|
13789 CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type* aType, |
|
13790 nsString& aValue) |
|
13791 { |
|
13792 CSSVariableDeclarations::Type type; |
|
13793 nsString variableValue; |
|
13794 bool dropBackslash; |
|
13795 nsString impliedCharacters; |
|
13796 |
|
13797 // Record the token stream while parsing a variable value. |
|
13798 if (!mInSupportsCondition) { |
|
13799 mScanner->StartRecording(); |
|
13800 } |
|
13801 if (!ParseValueWithVariables(&type, &dropBackslash, impliedCharacters, |
|
13802 nullptr, nullptr)) { |
|
13803 if (!mInSupportsCondition) { |
|
13804 mScanner->StopRecording(); |
|
13805 } |
|
13806 return false; |
|
13807 } |
|
13808 |
|
13809 if (!mInSupportsCondition) { |
|
13810 if (type == CSSVariableDeclarations::eTokenStream) { |
|
13811 // This was indeed a token stream value, so store it in variableValue. |
|
13812 mScanner->StopRecording(variableValue); |
|
13813 if (dropBackslash) { |
|
13814 MOZ_ASSERT(!variableValue.IsEmpty() && |
|
13815 variableValue[variableValue.Length() - 1] == '\\'); |
|
13816 variableValue.Truncate(variableValue.Length() - 1); |
|
13817 } |
|
13818 variableValue.Append(impliedCharacters); |
|
13819 } else { |
|
13820 // This was either 'inherit' or 'initial'; we don't need the recorded |
|
13821 // input. |
|
13822 mScanner->StopRecording(); |
|
13823 } |
|
13824 } |
|
13825 |
|
13826 if (mHavePushBack && type == CSSVariableDeclarations::eTokenStream) { |
|
13827 // If we came to the end of a valid variable declaration and a token was |
|
13828 // pushed back, then it would have been ended by '!', ')', ';', ']' or '}'. |
|
13829 // We need to remove it from the recorded variable value. |
|
13830 MOZ_ASSERT(mToken.IsSymbol('!') || |
|
13831 mToken.IsSymbol(')') || |
|
13832 mToken.IsSymbol(';') || |
|
13833 mToken.IsSymbol(']') || |
|
13834 mToken.IsSymbol('}')); |
|
13835 if (!mInSupportsCondition) { |
|
13836 MOZ_ASSERT(!variableValue.IsEmpty()); |
|
13837 MOZ_ASSERT(variableValue[variableValue.Length() - 1] == mToken.mSymbol); |
|
13838 variableValue.Truncate(variableValue.Length() - 1); |
|
13839 } |
|
13840 } |
|
13841 |
|
13842 *aType = type; |
|
13843 aValue = variableValue; |
|
13844 return true; |
|
13845 } |
|
13846 |
|
13847 bool |
|
13848 CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type* aType, |
|
13849 bool* aDropBackslash, |
|
13850 nsString& aImpliedCharacters, |
|
13851 void (*aFunc)(const nsAString&, void*), |
|
13852 void* aData) |
|
13853 { |
|
13854 // A property value is invalid if it contains variable references and also: |
|
13855 // |
|
13856 // * has unbalanced parens, brackets or braces |
|
13857 // * has any BAD_STRING or BAD_URL tokens |
|
13858 // * has any ';' or '!' tokens at the top level of a variable reference's |
|
13859 // fallback |
|
13860 // |
|
13861 // If the property is a custom property (i.e. a variable declaration), then |
|
13862 // it is also invalid if it consists of no tokens, such as: |
|
13863 // |
|
13864 // --invalid:; |
|
13865 // |
|
13866 // Note that is valid for a custom property to have a value that consists |
|
13867 // solely of white space, such as: |
|
13868 // |
|
13869 // --valid: ; |
|
13870 |
|
13871 // Stack of closing characters for currently open constructs. |
|
13872 StopSymbolCharStack stack; |
|
13873 |
|
13874 // Indexes into ')' characters in |stack| that correspond to "var(". This |
|
13875 // is used to stop parsing when we encounter a '!' or ';' at the top level |
|
13876 // of a variable reference's fallback. |
|
13877 nsAutoTArray<uint32_t, 16> references; |
|
13878 |
|
13879 if (!GetToken(false)) { |
|
13880 // Variable value was empty since we reached EOF. |
|
13881 REPORT_UNEXPECTED_EOF(PEVariableEOF); |
|
13882 return false; |
|
13883 } |
|
13884 |
|
13885 if (mToken.mType == eCSSToken_Symbol && |
|
13886 (mToken.mSymbol == '!' || |
|
13887 mToken.mSymbol == ')' || |
|
13888 mToken.mSymbol == ';' || |
|
13889 mToken.mSymbol == ']' || |
|
13890 mToken.mSymbol == '}')) { |
|
13891 // Variable value was empty since we reached the end of the construct. |
|
13892 UngetToken(); |
|
13893 REPORT_UNEXPECTED_TOKEN(PEVariableEmpty); |
|
13894 return false; |
|
13895 } |
|
13896 |
|
13897 if (mToken.mType == eCSSToken_Whitespace) { |
|
13898 if (!GetToken(true)) { |
|
13899 // Variable value was white space only. This is valid. |
|
13900 MOZ_ASSERT(!BackslashDropped()); |
|
13901 *aType = CSSVariableDeclarations::eTokenStream; |
|
13902 *aDropBackslash = false; |
|
13903 AppendImpliedEOFCharacters(aImpliedCharacters); |
|
13904 return true; |
|
13905 } |
|
13906 } |
|
13907 |
|
13908 // Look for 'initial', 'inherit' or 'unset' as the first non-white space |
|
13909 // token. |
|
13910 CSSVariableDeclarations::Type type = CSSVariableDeclarations::eTokenStream; |
|
13911 if (mToken.mType == eCSSToken_Ident) { |
|
13912 if (mToken.mIdent.LowerCaseEqualsLiteral("initial")) { |
|
13913 type = CSSVariableDeclarations::eInitial; |
|
13914 } else if (mToken.mIdent.LowerCaseEqualsLiteral("inherit")) { |
|
13915 type = CSSVariableDeclarations::eInherit; |
|
13916 } else if (mToken.mIdent.LowerCaseEqualsLiteral("unset")) { |
|
13917 type = CSSVariableDeclarations::eUnset; |
|
13918 } |
|
13919 } |
|
13920 |
|
13921 if (type != CSSVariableDeclarations::eTokenStream) { |
|
13922 if (!GetToken(true)) { |
|
13923 // Variable value was 'initial' or 'inherit' followed by EOF. |
|
13924 MOZ_ASSERT(!BackslashDropped()); |
|
13925 *aType = type; |
|
13926 *aDropBackslash = false; |
|
13927 AppendImpliedEOFCharacters(aImpliedCharacters); |
|
13928 return true; |
|
13929 } |
|
13930 UngetToken(); |
|
13931 if (mToken.mType == eCSSToken_Symbol && |
|
13932 (mToken.mSymbol == '!' || |
|
13933 mToken.mSymbol == ')' || |
|
13934 mToken.mSymbol == ';' || |
|
13935 mToken.mSymbol == ']' || |
|
13936 mToken.mSymbol == '}')) { |
|
13937 // Variable value was 'initial' or 'inherit' followed by the end |
|
13938 // of the declaration. |
|
13939 MOZ_ASSERT(!BackslashDropped()); |
|
13940 *aType = type; |
|
13941 *aDropBackslash = false; |
|
13942 return true; |
|
13943 } |
|
13944 } |
|
13945 |
|
13946 do { |
|
13947 switch (mToken.mType) { |
|
13948 case eCSSToken_Symbol: |
|
13949 if (mToken.mSymbol == '(') { |
|
13950 stack.AppendElement(')'); |
|
13951 } else if (mToken.mSymbol == '[') { |
|
13952 stack.AppendElement(']'); |
|
13953 } else if (mToken.mSymbol == '{') { |
|
13954 stack.AppendElement('}'); |
|
13955 } else if (mToken.mSymbol == ';' || |
|
13956 mToken.mSymbol == '!') { |
|
13957 if (stack.IsEmpty()) { |
|
13958 UngetToken(); |
|
13959 MOZ_ASSERT(!BackslashDropped()); |
|
13960 *aType = CSSVariableDeclarations::eTokenStream; |
|
13961 *aDropBackslash = false; |
|
13962 return true; |
|
13963 } else if (!references.IsEmpty() && |
|
13964 references.LastElement() == stack.Length() - 1) { |
|
13965 REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback); |
|
13966 SkipUntilAllOf(stack); |
|
13967 return false; |
|
13968 } |
|
13969 } else if (mToken.mSymbol == ')' || |
|
13970 mToken.mSymbol == ']' || |
|
13971 mToken.mSymbol == '}') { |
|
13972 for (;;) { |
|
13973 if (stack.IsEmpty()) { |
|
13974 UngetToken(); |
|
13975 MOZ_ASSERT(!BackslashDropped()); |
|
13976 *aType = CSSVariableDeclarations::eTokenStream; |
|
13977 *aDropBackslash = false; |
|
13978 return true; |
|
13979 } |
|
13980 char16_t c = stack.LastElement(); |
|
13981 stack.TruncateLength(stack.Length() - 1); |
|
13982 if (!references.IsEmpty() && |
|
13983 references.LastElement() == stack.Length()) { |
|
13984 references.TruncateLength(references.Length() - 1); |
|
13985 } |
|
13986 if (mToken.mSymbol == c) { |
|
13987 break; |
|
13988 } |
|
13989 } |
|
13990 } |
|
13991 break; |
|
13992 |
|
13993 case eCSSToken_Function: |
|
13994 if (mToken.mIdent.LowerCaseEqualsLiteral("var")) { |
|
13995 if (!GetToken(true)) { |
|
13996 // EOF directly after "var(". |
|
13997 REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF); |
|
13998 return false; |
|
13999 } |
|
14000 if (mToken.mType != eCSSToken_Ident || |
|
14001 !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) { |
|
14002 // There must be an identifier directly after the "var(" and |
|
14003 // it must be a custom property name. |
|
14004 UngetToken(); |
|
14005 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName); |
|
14006 SkipUntil(')'); |
|
14007 SkipUntilAllOf(stack); |
|
14008 return false; |
|
14009 } |
|
14010 if (aFunc) { |
|
14011 MOZ_ASSERT(Substring(mToken.mIdent, 0, |
|
14012 CSS_CUSTOM_NAME_PREFIX_LENGTH). |
|
14013 EqualsLiteral("--")); |
|
14014 // remove '--' |
|
14015 const nsDependentSubstring varName = |
|
14016 Substring(mToken.mIdent, CSS_CUSTOM_NAME_PREFIX_LENGTH); |
|
14017 aFunc(varName, aData); |
|
14018 } |
|
14019 if (!GetToken(true)) { |
|
14020 // EOF right after "var(<ident>". |
|
14021 stack.AppendElement(')'); |
|
14022 } else if (mToken.IsSymbol(',')) { |
|
14023 // Variable reference with fallback. |
|
14024 if (!GetToken(false) || mToken.IsSymbol(')')) { |
|
14025 // Comma must be followed by at least one fallback token. |
|
14026 REPORT_UNEXPECTED(PEExpectedVariableFallback); |
|
14027 SkipUntilAllOf(stack); |
|
14028 return false; |
|
14029 } |
|
14030 UngetToken(); |
|
14031 references.AppendElement(stack.Length()); |
|
14032 stack.AppendElement(')'); |
|
14033 } else if (mToken.IsSymbol(')')) { |
|
14034 // Correctly closed variable reference. |
|
14035 } else { |
|
14036 // Malformed variable reference. |
|
14037 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen); |
|
14038 SkipUntil(')'); |
|
14039 SkipUntilAllOf(stack); |
|
14040 return false; |
|
14041 } |
|
14042 } else { |
|
14043 stack.AppendElement(')'); |
|
14044 } |
|
14045 break; |
|
14046 |
|
14047 case eCSSToken_Bad_String: |
|
14048 SkipUntilAllOf(stack); |
|
14049 return false; |
|
14050 |
|
14051 case eCSSToken_Bad_URL: |
|
14052 SkipUntil(')'); |
|
14053 SkipUntilAllOf(stack); |
|
14054 return false; |
|
14055 |
|
14056 default: |
|
14057 break; |
|
14058 } |
|
14059 } while (GetToken(true)); |
|
14060 |
|
14061 // Append any implied closing characters. |
|
14062 *aDropBackslash = BackslashDropped(); |
|
14063 AppendImpliedEOFCharacters(aImpliedCharacters); |
|
14064 uint32_t i = stack.Length(); |
|
14065 while (i--) { |
|
14066 aImpliedCharacters.Append(stack[i]); |
|
14067 } |
|
14068 |
|
14069 *aType = type; |
|
14070 return true; |
|
14071 } |
|
14072 |
|
14073 } // anonymous namespace |
|
14074 |
|
14075 // Recycling of parser implementation objects |
|
14076 |
|
14077 static CSSParserImpl* gFreeList = nullptr; |
|
14078 |
|
14079 nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader, |
|
14080 nsCSSStyleSheet* aSheet) |
|
14081 { |
|
14082 CSSParserImpl *impl = gFreeList; |
|
14083 if (impl) { |
|
14084 gFreeList = impl->mNextFree; |
|
14085 impl->mNextFree = nullptr; |
|
14086 } else { |
|
14087 impl = new CSSParserImpl(); |
|
14088 } |
|
14089 |
|
14090 if (aLoader) { |
|
14091 impl->SetChildLoader(aLoader); |
|
14092 impl->SetQuirkMode(aLoader->GetCompatibilityMode() == |
|
14093 eCompatibility_NavQuirks); |
|
14094 } |
|
14095 if (aSheet) { |
|
14096 impl->SetStyleSheet(aSheet); |
|
14097 } |
|
14098 |
|
14099 mImpl = static_cast<void*>(impl); |
|
14100 } |
|
14101 |
|
14102 nsCSSParser::~nsCSSParser() |
|
14103 { |
|
14104 CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl); |
|
14105 impl->Reset(); |
|
14106 impl->mNextFree = gFreeList; |
|
14107 gFreeList = impl; |
|
14108 } |
|
14109 |
|
14110 /* static */ void |
|
14111 nsCSSParser::Shutdown() |
|
14112 { |
|
14113 CSSParserImpl *tofree = gFreeList; |
|
14114 CSSParserImpl *next; |
|
14115 while (tofree) |
|
14116 { |
|
14117 next = tofree->mNextFree; |
|
14118 delete tofree; |
|
14119 tofree = next; |
|
14120 } |
|
14121 } |
|
14122 |
|
14123 // Wrapper methods |
|
14124 |
|
14125 nsresult |
|
14126 nsCSSParser::SetStyleSheet(nsCSSStyleSheet* aSheet) |
|
14127 { |
|
14128 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14129 SetStyleSheet(aSheet); |
|
14130 } |
|
14131 |
|
14132 nsresult |
|
14133 nsCSSParser::SetQuirkMode(bool aQuirkMode) |
|
14134 { |
|
14135 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14136 SetQuirkMode(aQuirkMode); |
|
14137 } |
|
14138 |
|
14139 nsresult |
|
14140 nsCSSParser::SetChildLoader(mozilla::css::Loader* aChildLoader) |
|
14141 { |
|
14142 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14143 SetChildLoader(aChildLoader); |
|
14144 } |
|
14145 |
|
14146 nsresult |
|
14147 nsCSSParser::ParseSheet(const nsAString& aInput, |
|
14148 nsIURI* aSheetURI, |
|
14149 nsIURI* aBaseURI, |
|
14150 nsIPrincipal* aSheetPrincipal, |
|
14151 uint32_t aLineNumber, |
|
14152 bool aAllowUnsafeRules) |
|
14153 { |
|
14154 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14155 ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber, |
|
14156 aAllowUnsafeRules); |
|
14157 } |
|
14158 |
|
14159 nsresult |
|
14160 nsCSSParser::ParseStyleAttribute(const nsAString& aAttributeValue, |
|
14161 nsIURI* aDocURI, |
|
14162 nsIURI* aBaseURI, |
|
14163 nsIPrincipal* aNodePrincipal, |
|
14164 css::StyleRule** aResult) |
|
14165 { |
|
14166 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14167 ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI, |
|
14168 aNodePrincipal, aResult); |
|
14169 } |
|
14170 |
|
14171 nsresult |
|
14172 nsCSSParser::ParseDeclarations(const nsAString& aBuffer, |
|
14173 nsIURI* aSheetURI, |
|
14174 nsIURI* aBaseURI, |
|
14175 nsIPrincipal* aSheetPrincipal, |
|
14176 css::Declaration* aDeclaration, |
|
14177 bool* aChanged) |
|
14178 { |
|
14179 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14180 ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal, |
|
14181 aDeclaration, aChanged); |
|
14182 } |
|
14183 |
|
14184 nsresult |
|
14185 nsCSSParser::ParseRule(const nsAString& aRule, |
|
14186 nsIURI* aSheetURI, |
|
14187 nsIURI* aBaseURI, |
|
14188 nsIPrincipal* aSheetPrincipal, |
|
14189 css::Rule** aResult) |
|
14190 { |
|
14191 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14192 ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult); |
|
14193 } |
|
14194 |
|
14195 nsresult |
|
14196 nsCSSParser::ParseProperty(const nsCSSProperty aPropID, |
|
14197 const nsAString& aPropValue, |
|
14198 nsIURI* aSheetURI, |
|
14199 nsIURI* aBaseURI, |
|
14200 nsIPrincipal* aSheetPrincipal, |
|
14201 css::Declaration* aDeclaration, |
|
14202 bool* aChanged, |
|
14203 bool aIsImportant, |
|
14204 bool aIsSVGMode) |
|
14205 { |
|
14206 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14207 ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI, |
|
14208 aSheetPrincipal, aDeclaration, aChanged, |
|
14209 aIsImportant, aIsSVGMode); |
|
14210 } |
|
14211 |
|
14212 nsresult |
|
14213 nsCSSParser::ParseVariable(const nsAString& aVariableName, |
|
14214 const nsAString& aPropValue, |
|
14215 nsIURI* aSheetURI, |
|
14216 nsIURI* aBaseURI, |
|
14217 nsIPrincipal* aSheetPrincipal, |
|
14218 css::Declaration* aDeclaration, |
|
14219 bool* aChanged, |
|
14220 bool aIsImportant) |
|
14221 { |
|
14222 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14223 ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI, |
|
14224 aSheetPrincipal, aDeclaration, aChanged, aIsImportant); |
|
14225 } |
|
14226 |
|
14227 void |
|
14228 nsCSSParser::ParseMediaList(const nsSubstring& aBuffer, |
|
14229 nsIURI* aURI, |
|
14230 uint32_t aLineNumber, |
|
14231 nsMediaList* aMediaList, |
|
14232 bool aHTMLMode) |
|
14233 { |
|
14234 static_cast<CSSParserImpl*>(mImpl)-> |
|
14235 ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode); |
|
14236 } |
|
14237 |
|
14238 bool |
|
14239 nsCSSParser::ParseColorString(const nsSubstring& aBuffer, |
|
14240 nsIURI* aURI, |
|
14241 uint32_t aLineNumber, |
|
14242 nsCSSValue& aValue) |
|
14243 { |
|
14244 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14245 ParseColorString(aBuffer, aURI, aLineNumber, aValue); |
|
14246 } |
|
14247 |
|
14248 nsresult |
|
14249 nsCSSParser::ParseSelectorString(const nsSubstring& aSelectorString, |
|
14250 nsIURI* aURI, |
|
14251 uint32_t aLineNumber, |
|
14252 nsCSSSelectorList** aSelectorList) |
|
14253 { |
|
14254 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14255 ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList); |
|
14256 } |
|
14257 |
|
14258 already_AddRefed<nsCSSKeyframeRule> |
|
14259 nsCSSParser::ParseKeyframeRule(const nsSubstring& aBuffer, |
|
14260 nsIURI* aURI, |
|
14261 uint32_t aLineNumber) |
|
14262 { |
|
14263 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14264 ParseKeyframeRule(aBuffer, aURI, aLineNumber); |
|
14265 } |
|
14266 |
|
14267 bool |
|
14268 nsCSSParser::ParseKeyframeSelectorString(const nsSubstring& aSelectorString, |
|
14269 nsIURI* aURI, |
|
14270 uint32_t aLineNumber, |
|
14271 InfallibleTArray<float>& aSelectorList) |
|
14272 { |
|
14273 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14274 ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber, |
|
14275 aSelectorList); |
|
14276 } |
|
14277 |
|
14278 bool |
|
14279 nsCSSParser::EvaluateSupportsDeclaration(const nsAString& aProperty, |
|
14280 const nsAString& aValue, |
|
14281 nsIURI* aDocURL, |
|
14282 nsIURI* aBaseURL, |
|
14283 nsIPrincipal* aDocPrincipal) |
|
14284 { |
|
14285 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14286 EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL, |
|
14287 aDocPrincipal); |
|
14288 } |
|
14289 |
|
14290 bool |
|
14291 nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition, |
|
14292 nsIURI* aDocURL, |
|
14293 nsIURI* aBaseURL, |
|
14294 nsIPrincipal* aDocPrincipal) |
|
14295 { |
|
14296 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14297 EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL, aDocPrincipal); |
|
14298 } |
|
14299 |
|
14300 bool |
|
14301 nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue, |
|
14302 VariableEnumFunc aFunc, |
|
14303 void* aData) |
|
14304 { |
|
14305 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14306 EnumerateVariableReferences(aPropertyValue, aFunc, aData); |
|
14307 } |
|
14308 |
|
14309 bool |
|
14310 nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue, |
|
14311 const CSSVariableValues* aVariables, |
|
14312 nsString& aResult, |
|
14313 nsCSSTokenSerializationType& aFirstToken, |
|
14314 nsCSSTokenSerializationType& aLastToken) |
|
14315 { |
|
14316 return static_cast<CSSParserImpl*>(mImpl)-> |
|
14317 ResolveVariableValue(aPropertyValue, aVariables, |
|
14318 aResult, aFirstToken, aLastToken); |
|
14319 } |
|
14320 |
|
14321 void |
|
14322 nsCSSParser::ParsePropertyWithVariableReferences( |
|
14323 nsCSSProperty aPropertyID, |
|
14324 nsCSSProperty aShorthandPropertyID, |
|
14325 const nsAString& aValue, |
|
14326 const CSSVariableValues* aVariables, |
|
14327 nsRuleData* aRuleData, |
|
14328 nsIURI* aDocURL, |
|
14329 nsIURI* aBaseURL, |
|
14330 nsIPrincipal* aDocPrincipal, |
|
14331 nsCSSStyleSheet* aSheet, |
|
14332 uint32_t aLineNumber, |
|
14333 uint32_t aLineOffset) |
|
14334 { |
|
14335 static_cast<CSSParserImpl*>(mImpl)-> |
|
14336 ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID, |
|
14337 aValue, aVariables, aRuleData, aDocURL, |
|
14338 aBaseURL, aDocPrincipal, aSheet, |
|
14339 aLineNumber, aLineOffset); |
|
14340 } |