layout/style/nsCSSParser.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=78: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
michael@0 8
michael@0 9 #include "mozilla/ArrayUtils.h"
michael@0 10 #include "mozilla/DebugOnly.h"
michael@0 11
michael@0 12 #include "nsCSSParser.h"
michael@0 13 #include "nsCSSProps.h"
michael@0 14 #include "nsCSSKeywords.h"
michael@0 15 #include "nsCSSScanner.h"
michael@0 16 #include "mozilla/css/ErrorReporter.h"
michael@0 17 #include "mozilla/css/Loader.h"
michael@0 18 #include "mozilla/css/StyleRule.h"
michael@0 19 #include "mozilla/css/ImportRule.h"
michael@0 20 #include "nsCSSRules.h"
michael@0 21 #include "mozilla/css/NameSpaceRule.h"
michael@0 22 #include "nsTArray.h"
michael@0 23 #include "nsCSSStyleSheet.h"
michael@0 24 #include "mozilla/css/Declaration.h"
michael@0 25 #include "nsStyleConsts.h"
michael@0 26 #include "nsNetUtil.h"
michael@0 27 #include "nsCOMPtr.h"
michael@0 28 #include "nsString.h"
michael@0 29 #include "nsIAtom.h"
michael@0 30 #include "nsColor.h"
michael@0 31 #include "nsCSSPseudoClasses.h"
michael@0 32 #include "nsCSSPseudoElements.h"
michael@0 33 #include "nsNameSpaceManager.h"
michael@0 34 #include "nsXMLNameSpaceMap.h"
michael@0 35 #include "nsError.h"
michael@0 36 #include "nsIMediaList.h"
michael@0 37 #include "nsStyleUtil.h"
michael@0 38 #include "nsIPrincipal.h"
michael@0 39 #include "prprf.h"
michael@0 40 #include "nsContentUtils.h"
michael@0 41 #include "nsAutoPtr.h"
michael@0 42 #include "CSSCalc.h"
michael@0 43 #include "nsMediaFeatures.h"
michael@0 44 #include "nsLayoutUtils.h"
michael@0 45 #include "mozilla/Preferences.h"
michael@0 46 #include "nsRuleData.h"
michael@0 47 #include "mozilla/CSSVariableValues.h"
michael@0 48 #include "mozilla/dom/URL.h"
michael@0 49
michael@0 50 using namespace mozilla;
michael@0 51
michael@0 52 typedef nsCSSProps::KTableValue KTableValue;
michael@0 53
michael@0 54 const uint32_t
michael@0 55 nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
michael@0 56 #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \
michael@0 57 stylestruct_, stylestructoffset_, animtype_) \
michael@0 58 parsevariant_,
michael@0 59 #include "nsCSSPropList.h"
michael@0 60 #undef CSS_PROP
michael@0 61 };
michael@0 62
michael@0 63 // Maximum number of repetitions for the repeat() function
michael@0 64 // in the grid-template-columns and grid-template-rows properties,
michael@0 65 // to limit high memory usage from small stylesheets.
michael@0 66 // Must be a positive integer. Should be large-ish.
michael@0 67 #define GRID_TEMPLATE_MAX_REPETITIONS 10000
michael@0 68
michael@0 69 // End-of-array marker for mask arguments to ParseBitmaskValues
michael@0 70 #define MASK_END_VALUE (-1)
michael@0 71
michael@0 72 MOZ_BEGIN_ENUM_CLASS(CSSParseResult, int32_t)
michael@0 73 // Parsed something successfully:
michael@0 74 Ok,
michael@0 75 // Did not find what we were looking for, but did not consume any token:
michael@0 76 NotFound,
michael@0 77 // Unexpected token or token value, too late for UngetToken() to be enough:
michael@0 78 Error
michael@0 79 MOZ_END_ENUM_CLASS(CSSParseResult)
michael@0 80
michael@0 81 namespace {
michael@0 82
michael@0 83 // Rule processing function
michael@0 84 typedef void (* RuleAppendFunc) (css::Rule* aRule, void* aData);
michael@0 85 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer);
michael@0 86 static void AppendRuleToSheet(css::Rule* aRule, void* aParser);
michael@0 87
michael@0 88 struct CSSParserInputState {
michael@0 89 nsCSSScannerPosition mPosition;
michael@0 90 nsCSSToken mToken;
michael@0 91 bool mHavePushBack;
michael@0 92 };
michael@0 93
michael@0 94 // Your basic top-down recursive descent style parser
michael@0 95 // The exposed methods and members of this class are precisely those
michael@0 96 // needed by nsCSSParser, far below.
michael@0 97 class CSSParserImpl {
michael@0 98 public:
michael@0 99 CSSParserImpl();
michael@0 100 ~CSSParserImpl();
michael@0 101
michael@0 102 nsresult SetStyleSheet(nsCSSStyleSheet* aSheet);
michael@0 103
michael@0 104 nsresult SetQuirkMode(bool aQuirkMode);
michael@0 105
michael@0 106 nsresult SetChildLoader(mozilla::css::Loader* aChildLoader);
michael@0 107
michael@0 108 // Clears everything set by the above Set*() functions.
michael@0 109 void Reset();
michael@0 110
michael@0 111 nsresult ParseSheet(const nsAString& aInput,
michael@0 112 nsIURI* aSheetURI,
michael@0 113 nsIURI* aBaseURI,
michael@0 114 nsIPrincipal* aSheetPrincipal,
michael@0 115 uint32_t aLineNumber,
michael@0 116 bool aAllowUnsafeRules);
michael@0 117
michael@0 118 nsresult ParseStyleAttribute(const nsAString& aAttributeValue,
michael@0 119 nsIURI* aDocURL,
michael@0 120 nsIURI* aBaseURL,
michael@0 121 nsIPrincipal* aNodePrincipal,
michael@0 122 css::StyleRule** aResult);
michael@0 123
michael@0 124 nsresult ParseDeclarations(const nsAString& aBuffer,
michael@0 125 nsIURI* aSheetURL,
michael@0 126 nsIURI* aBaseURL,
michael@0 127 nsIPrincipal* aSheetPrincipal,
michael@0 128 css::Declaration* aDeclaration,
michael@0 129 bool* aChanged);
michael@0 130
michael@0 131 nsresult ParseRule(const nsAString& aRule,
michael@0 132 nsIURI* aSheetURL,
michael@0 133 nsIURI* aBaseURL,
michael@0 134 nsIPrincipal* aSheetPrincipal,
michael@0 135 css::Rule** aResult);
michael@0 136
michael@0 137 nsresult ParseProperty(const nsCSSProperty aPropID,
michael@0 138 const nsAString& aPropValue,
michael@0 139 nsIURI* aSheetURL,
michael@0 140 nsIURI* aBaseURL,
michael@0 141 nsIPrincipal* aSheetPrincipal,
michael@0 142 css::Declaration* aDeclaration,
michael@0 143 bool* aChanged,
michael@0 144 bool aIsImportant,
michael@0 145 bool aIsSVGMode);
michael@0 146
michael@0 147 void ParseMediaList(const nsSubstring& aBuffer,
michael@0 148 nsIURI* aURL, // for error reporting
michael@0 149 uint32_t aLineNumber, // for error reporting
michael@0 150 nsMediaList* aMediaList,
michael@0 151 bool aHTMLMode);
michael@0 152
michael@0 153 nsresult ParseVariable(const nsAString& aVariableName,
michael@0 154 const nsAString& aPropValue,
michael@0 155 nsIURI* aSheetURL,
michael@0 156 nsIURI* aBaseURL,
michael@0 157 nsIPrincipal* aSheetPrincipal,
michael@0 158 css::Declaration* aDeclaration,
michael@0 159 bool* aChanged,
michael@0 160 bool aIsImportant);
michael@0 161
michael@0 162 bool ParseColorString(const nsSubstring& aBuffer,
michael@0 163 nsIURI* aURL, // for error reporting
michael@0 164 uint32_t aLineNumber, // for error reporting
michael@0 165 nsCSSValue& aValue);
michael@0 166
michael@0 167 nsresult ParseSelectorString(const nsSubstring& aSelectorString,
michael@0 168 nsIURI* aURL, // for error reporting
michael@0 169 uint32_t aLineNumber, // for error reporting
michael@0 170 nsCSSSelectorList **aSelectorList);
michael@0 171
michael@0 172 already_AddRefed<nsCSSKeyframeRule>
michael@0 173 ParseKeyframeRule(const nsSubstring& aBuffer,
michael@0 174 nsIURI* aURL,
michael@0 175 uint32_t aLineNumber);
michael@0 176
michael@0 177 bool ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
michael@0 178 nsIURI* aURL, // for error reporting
michael@0 179 uint32_t aLineNumber, // for error reporting
michael@0 180 InfallibleTArray<float>& aSelectorList);
michael@0 181
michael@0 182 bool EvaluateSupportsDeclaration(const nsAString& aProperty,
michael@0 183 const nsAString& aValue,
michael@0 184 nsIURI* aDocURL,
michael@0 185 nsIURI* aBaseURL,
michael@0 186 nsIPrincipal* aDocPrincipal);
michael@0 187
michael@0 188 bool EvaluateSupportsCondition(const nsAString& aCondition,
michael@0 189 nsIURI* aDocURL,
michael@0 190 nsIURI* aBaseURL,
michael@0 191 nsIPrincipal* aDocPrincipal);
michael@0 192
michael@0 193 typedef nsCSSParser::VariableEnumFunc VariableEnumFunc;
michael@0 194
michael@0 195 /**
michael@0 196 * Parses a CSS token stream value and invokes a callback function for each
michael@0 197 * variable reference that is encountered.
michael@0 198 *
michael@0 199 * @param aPropertyValue The CSS token stream value.
michael@0 200 * @param aFunc The callback function to invoke; its parameters are the
michael@0 201 * variable name found and the aData argument passed in to this function.
michael@0 202 * @param aData User data to pass in to the callback.
michael@0 203 * @return Whether aPropertyValue could be parsed as a valid CSS token stream
michael@0 204 * value (e.g., without syntactic errors in variable references).
michael@0 205 */
michael@0 206 bool EnumerateVariableReferences(const nsAString& aPropertyValue,
michael@0 207 VariableEnumFunc aFunc,
michael@0 208 void* aData);
michael@0 209
michael@0 210 /**
michael@0 211 * Parses aPropertyValue as a CSS token stream value and resolves any
michael@0 212 * variable references using the variables in aVariables.
michael@0 213 *
michael@0 214 * @param aPropertyValue The CSS token stream value.
michael@0 215 * @param aVariables The set of variable values to use when resolving variable
michael@0 216 * references.
michael@0 217 * @param aResult Out parameter that gets the resolved value.
michael@0 218 * @param aFirstToken Out parameter that gets the type of the first token in
michael@0 219 * aResult.
michael@0 220 * @param aLastToken Out parameter that gets the type of the last token in
michael@0 221 * aResult.
michael@0 222 * @return Whether aResult could be parsed successfully and variable reference
michael@0 223 * substitution succeeded.
michael@0 224 */
michael@0 225 bool ResolveVariableValue(const nsAString& aPropertyValue,
michael@0 226 const CSSVariableValues* aVariables,
michael@0 227 nsString& aResult,
michael@0 228 nsCSSTokenSerializationType& aFirstToken,
michael@0 229 nsCSSTokenSerializationType& aLastToken);
michael@0 230
michael@0 231 /**
michael@0 232 * Parses a string as a CSS token stream value for particular property,
michael@0 233 * resolving any variable references. The parsed property value is stored
michael@0 234 * in the specified nsRuleData object. If aShorthandPropertyID has a value
michael@0 235 * other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
michael@0 236 * otherwise, aPropertyID will be parsed. Either way, only aPropertyID,
michael@0 237 * a longhand property, will be copied over to the rule data.
michael@0 238 *
michael@0 239 * If the property cannot be parsed, it will be treated as if 'initial' or
michael@0 240 * 'inherit' were specified, for non-inherited and inherited properties
michael@0 241 * respectively.
michael@0 242 *
michael@0 243 * @param aPropertyID The ID of the longhand property whose value is to be
michael@0 244 * copied to the rule data.
michael@0 245 * @param aShorthandPropertyID The ID of the shorthand property to be parsed.
michael@0 246 * If a longhand property is to be parsed, aPropertyID is that property,
michael@0 247 * and aShorthandPropertyID must be eCSSProperty_UNKNOWN.
michael@0 248 * @param aValue The CSS token stream value.
michael@0 249 * @param aVariables The set of variable values to use when resolving variable
michael@0 250 * references.
michael@0 251 * @param aRuleData The rule data object into which parsed property value for
michael@0 252 * aPropertyID will be stored.
michael@0 253 */
michael@0 254 void ParsePropertyWithVariableReferences(nsCSSProperty aPropertyID,
michael@0 255 nsCSSProperty aShorthandPropertyID,
michael@0 256 const nsAString& aValue,
michael@0 257 const CSSVariableValues* aVariables,
michael@0 258 nsRuleData* aRuleData,
michael@0 259 nsIURI* aDocURL,
michael@0 260 nsIURI* aBaseURL,
michael@0 261 nsIPrincipal* aDocPrincipal,
michael@0 262 nsCSSStyleSheet* aSheet,
michael@0 263 uint32_t aLineNumber,
michael@0 264 uint32_t aLineOffset);
michael@0 265
michael@0 266 nsCSSProperty LookupEnabledProperty(const nsAString& aProperty) {
michael@0 267 static_assert(nsCSSProps::eEnabledForAllContent == 0,
michael@0 268 "nsCSSProps::eEnabledForAllContent should be zero for "
michael@0 269 "this bitfield to work");
michael@0 270 nsCSSProps::EnabledState enabledState = nsCSSProps::eEnabledForAllContent;
michael@0 271 if (mUnsafeRulesEnabled) {
michael@0 272 enabledState |= nsCSSProps::eEnabledInUASheets;
michael@0 273 }
michael@0 274 if (mIsChromeOrCertifiedApp) {
michael@0 275 enabledState |= nsCSSProps::eEnabledInChromeOrCertifiedApp;
michael@0 276 }
michael@0 277 return nsCSSProps::LookupProperty(aProperty, enabledState);
michael@0 278 }
michael@0 279
michael@0 280 protected:
michael@0 281 class nsAutoParseCompoundProperty;
michael@0 282 friend class nsAutoParseCompoundProperty;
michael@0 283
michael@0 284 class nsAutoFailingSupportsRule;
michael@0 285 friend class nsAutoFailingSupportsRule;
michael@0 286
michael@0 287 class nsAutoSuppressErrors;
michael@0 288 friend class nsAutoSuppressErrors;
michael@0 289
michael@0 290 void AppendRule(css::Rule* aRule);
michael@0 291 friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule
michael@0 292
michael@0 293 /**
michael@0 294 * This helper class automatically calls SetParsingCompoundProperty in its
michael@0 295 * constructor and takes care of resetting it to false in its destructor.
michael@0 296 */
michael@0 297 class nsAutoParseCompoundProperty {
michael@0 298 public:
michael@0 299 nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
michael@0 300 {
michael@0 301 NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
michael@0 302 "already parsing compound property");
michael@0 303 NS_ASSERTION(aParser, "Null parser?");
michael@0 304 aParser->SetParsingCompoundProperty(true);
michael@0 305 }
michael@0 306
michael@0 307 ~nsAutoParseCompoundProperty()
michael@0 308 {
michael@0 309 mParser->SetParsingCompoundProperty(false);
michael@0 310 }
michael@0 311 private:
michael@0 312 CSSParserImpl* mParser;
michael@0 313 };
michael@0 314
michael@0 315 /**
michael@0 316 * This helper class conditionally sets mInFailingSupportsRule to
michael@0 317 * true if aCondition = false, and resets it to its original value in its
michael@0 318 * destructor. If we are already somewhere within a failing @supports
michael@0 319 * rule, passing in aCondition = true does not change mInFailingSupportsRule.
michael@0 320 */
michael@0 321 class nsAutoFailingSupportsRule {
michael@0 322 public:
michael@0 323 nsAutoFailingSupportsRule(CSSParserImpl* aParser,
michael@0 324 bool aCondition)
michael@0 325 : mParser(aParser),
michael@0 326 mOriginalValue(aParser->mInFailingSupportsRule)
michael@0 327 {
michael@0 328 if (!aCondition) {
michael@0 329 mParser->mInFailingSupportsRule = true;
michael@0 330 }
michael@0 331 }
michael@0 332
michael@0 333 ~nsAutoFailingSupportsRule()
michael@0 334 {
michael@0 335 mParser->mInFailingSupportsRule = mOriginalValue;
michael@0 336 }
michael@0 337
michael@0 338 private:
michael@0 339 CSSParserImpl* mParser;
michael@0 340 bool mOriginalValue;
michael@0 341 };
michael@0 342
michael@0 343 /**
michael@0 344 * Auto class to set aParser->mSuppressErrors to the specified value
michael@0 345 * and restore it to its original value later.
michael@0 346 */
michael@0 347 class nsAutoSuppressErrors {
michael@0 348 public:
michael@0 349 nsAutoSuppressErrors(CSSParserImpl* aParser,
michael@0 350 bool aSuppressErrors = true)
michael@0 351 : mParser(aParser),
michael@0 352 mOriginalValue(aParser->mSuppressErrors)
michael@0 353 {
michael@0 354 mParser->mSuppressErrors = aSuppressErrors;
michael@0 355 }
michael@0 356
michael@0 357 ~nsAutoSuppressErrors()
michael@0 358 {
michael@0 359 mParser->mSuppressErrors = mOriginalValue;
michael@0 360 }
michael@0 361
michael@0 362 private:
michael@0 363 CSSParserImpl* mParser;
michael@0 364 bool mOriginalValue;
michael@0 365 };
michael@0 366
michael@0 367 // the caller must hold on to aString until parsing is done
michael@0 368 void InitScanner(nsCSSScanner& aScanner,
michael@0 369 css::ErrorReporter& aReporter,
michael@0 370 nsIURI* aSheetURI, nsIURI* aBaseURI,
michael@0 371 nsIPrincipal* aSheetPrincipal);
michael@0 372 void ReleaseScanner(void);
michael@0 373 bool IsSVGMode() const {
michael@0 374 return mScanner->IsSVGMode();
michael@0 375 }
michael@0 376
michael@0 377 /**
michael@0 378 * Saves the current input state, which includes any currently pushed
michael@0 379 * back token, and the current position of the scanner.
michael@0 380 */
michael@0 381 void SaveInputState(CSSParserInputState& aState);
michael@0 382
michael@0 383 /**
michael@0 384 * Restores the saved input state by pushing back any saved pushback
michael@0 385 * token and calling RestoreSavedPosition on the scanner.
michael@0 386 */
michael@0 387 void RestoreSavedInputState(const CSSParserInputState& aState);
michael@0 388
michael@0 389 bool GetToken(bool aSkipWS);
michael@0 390 void UngetToken();
michael@0 391 bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum);
michael@0 392
michael@0 393 bool ExpectSymbol(char16_t aSymbol, bool aSkipWS);
michael@0 394 bool ExpectEndProperty();
michael@0 395 bool CheckEndProperty();
michael@0 396 nsSubstring* NextIdent();
michael@0 397
michael@0 398 // returns true when the stop symbol is found, and false for EOF
michael@0 399 bool SkipUntil(char16_t aStopSymbol);
michael@0 400 void SkipUntilOneOf(const char16_t* aStopSymbolChars);
michael@0 401 // For each character in aStopSymbolChars from the end of the array
michael@0 402 // to the start, calls SkipUntil with that character.
michael@0 403 typedef nsAutoTArray<char16_t, 16> StopSymbolCharStack;
michael@0 404 void SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars);
michael@0 405 // returns true if the stop symbol or EOF is found, and false for an
michael@0 406 // unexpected ')', ']' or '}'; this not safe to call outside variable
michael@0 407 // resolution, as it doesn't handle mismatched content
michael@0 408 bool SkipBalancedContentUntil(char16_t aStopSymbol);
michael@0 409
michael@0 410 void SkipRuleSet(bool aInsideBraces);
michael@0 411 bool SkipAtRule(bool aInsideBlock);
michael@0 412 bool SkipDeclaration(bool aCheckForBraces);
michael@0 413
michael@0 414 void PushGroup(css::GroupRule* aRule);
michael@0 415 void PopGroup();
michael@0 416
michael@0 417 bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData,
michael@0 418 bool aInsideBraces = false);
michael@0 419 bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData,
michael@0 420 bool aInAtRule);
michael@0 421 bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
michael@0 422 bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
michael@0 423 bool ParseURLOrString(nsString& aURL);
michael@0 424 bool GatherMedia(nsMediaList* aMedia,
michael@0 425 bool aInAtRule);
michael@0 426 bool ParseMediaQuery(bool aInAtRule, nsMediaQuery **aQuery,
michael@0 427 bool *aHitStop);
michael@0 428 bool ParseMediaQueryExpression(nsMediaQuery* aQuery);
michael@0 429 void ProcessImport(const nsString& aURLSpec,
michael@0 430 nsMediaList* aMedia,
michael@0 431 RuleAppendFunc aAppendFunc,
michael@0 432 void* aProcessData);
michael@0 433 bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc,
michael@0 434 void* aProcessData);
michael@0 435 bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
michael@0 436 bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
michael@0 437 bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
michael@0 438 void ProcessNameSpace(const nsString& aPrefix,
michael@0 439 const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
michael@0 440 void* aProcessData);
michael@0 441
michael@0 442 bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
michael@0 443 bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
michael@0 444 void* aProcessData);
michael@0 445 bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule);
michael@0 446 bool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
michael@0 447 bool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
michael@0 448 nsCSSValue& aValue);
michael@0 449
michael@0 450 bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
michael@0 451 bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData);
michael@0 452 already_AddRefed<nsCSSKeyframeRule> ParseKeyframeRule();
michael@0 453 bool ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList);
michael@0 454
michael@0 455 bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData);
michael@0 456 bool ParseSupportsCondition(bool& aConditionMet);
michael@0 457 bool ParseSupportsConditionNegation(bool& aConditionMet);
michael@0 458 bool ParseSupportsConditionInParens(bool& aConditionMet);
michael@0 459 bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet);
michael@0 460 bool ParseSupportsConditionTerms(bool& aConditionMet);
michael@0 461 enum SupportsConditionTermOperator { eAnd, eOr };
michael@0 462 bool ParseSupportsConditionTermsAfterOperator(
michael@0 463 bool& aConditionMet,
michael@0 464 SupportsConditionTermOperator aOperator);
michael@0 465
michael@0 466 /**
michael@0 467 * Parses the current input stream for a CSS token stream value and resolves
michael@0 468 * any variable references using the variables in aVariables.
michael@0 469 *
michael@0 470 * @param aVariables The set of variable values to use when resolving variable
michael@0 471 * references.
michael@0 472 * @param aResult Out parameter that, if the function returns true, will be
michael@0 473 * replaced with the resolved value.
michael@0 474 * @return Whether aResult could be parsed successfully and variable reference
michael@0 475 * substitution succeeded.
michael@0 476 */
michael@0 477 bool ResolveValueWithVariableReferences(
michael@0 478 const CSSVariableValues* aVariables,
michael@0 479 nsString& aResult,
michael@0 480 nsCSSTokenSerializationType& aResultFirstToken,
michael@0 481 nsCSSTokenSerializationType& aResultLastToken);
michael@0 482 // Helper function for ResolveValueWithVariableReferences.
michael@0 483 bool ResolveValueWithVariableReferencesRec(
michael@0 484 nsString& aResult,
michael@0 485 nsCSSTokenSerializationType& aResultFirstToken,
michael@0 486 nsCSSTokenSerializationType& aResultLastToken,
michael@0 487 const CSSVariableValues* aVariables);
michael@0 488
michael@0 489 enum nsSelectorParsingStatus {
michael@0 490 // we have parsed a selector and we saw a token that cannot be
michael@0 491 // part of a selector:
michael@0 492 eSelectorParsingStatus_Done,
michael@0 493 // we should continue parsing the selector:
michael@0 494 eSelectorParsingStatus_Continue,
michael@0 495 // we saw an unexpected token or token value,
michael@0 496 // or we saw end-of-file with an unfinished selector:
michael@0 497 eSelectorParsingStatus_Error
michael@0 498 };
michael@0 499 nsSelectorParsingStatus ParseIDSelector(int32_t& aDataMask,
michael@0 500 nsCSSSelector& aSelector);
michael@0 501
michael@0 502 nsSelectorParsingStatus ParseClassSelector(int32_t& aDataMask,
michael@0 503 nsCSSSelector& aSelector);
michael@0 504
michael@0 505 // aPseudoElement and aPseudoElementArgs are the location where
michael@0 506 // pseudo-elements (as opposed to pseudo-classes) are stored;
michael@0 507 // pseudo-classes are stored on aSelector. aPseudoElement and
michael@0 508 // aPseudoElementArgs must be non-null iff !aIsNegated.
michael@0 509 nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask,
michael@0 510 nsCSSSelector& aSelector,
michael@0 511 bool aIsNegated,
michael@0 512 nsIAtom** aPseudoElement,
michael@0 513 nsAtomList** aPseudoElementArgs,
michael@0 514 nsCSSPseudoElements::Type* aPseudoElementType);
michael@0 515
michael@0 516 nsSelectorParsingStatus ParseAttributeSelector(int32_t& aDataMask,
michael@0 517 nsCSSSelector& aSelector);
michael@0 518
michael@0 519 nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask,
michael@0 520 nsCSSSelector& aSelector,
michael@0 521 bool aIsNegated);
michael@0 522
michael@0 523 nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
michael@0 524 nsCSSPseudoClasses::Type aType);
michael@0 525
michael@0 526 nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
michael@0 527 nsCSSPseudoClasses::Type aType);
michael@0 528
michael@0 529 nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
michael@0 530 nsCSSPseudoClasses::Type aType);
michael@0 531
michael@0 532 nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask,
michael@0 533 nsCSSSelector& aSelector);
michael@0 534
michael@0 535 // If aStopChar is non-zero, the selector list is done when we hit
michael@0 536 // aStopChar. Otherwise, it's done when we hit EOF.
michael@0 537 bool ParseSelectorList(nsCSSSelectorList*& aListHead,
michael@0 538 char16_t aStopChar);
michael@0 539 bool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
michael@0 540 bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator);
michael@0 541
michael@0 542 enum {
michael@0 543 eParseDeclaration_InBraces = 1 << 0,
michael@0 544 eParseDeclaration_AllowImportant = 1 << 1
michael@0 545 };
michael@0 546 enum nsCSSContextType {
michael@0 547 eCSSContext_General,
michael@0 548 eCSSContext_Page
michael@0 549 };
michael@0 550
michael@0 551 css::Declaration* ParseDeclarationBlock(uint32_t aFlags,
michael@0 552 nsCSSContextType aContext = eCSSContext_General);
michael@0 553 bool ParseDeclaration(css::Declaration* aDeclaration,
michael@0 554 uint32_t aFlags,
michael@0 555 bool aMustCallValueAppended,
michael@0 556 bool* aChanged,
michael@0 557 nsCSSContextType aContext = eCSSContext_General);
michael@0 558
michael@0 559 bool ParseProperty(nsCSSProperty aPropID);
michael@0 560 bool ParsePropertyByFunction(nsCSSProperty aPropID);
michael@0 561 bool ParseSingleValueProperty(nsCSSValue& aValue,
michael@0 562 nsCSSProperty aPropID);
michael@0 563
michael@0 564 enum PriorityParsingStatus {
michael@0 565 ePriority_None,
michael@0 566 ePriority_Important,
michael@0 567 ePriority_Error
michael@0 568 };
michael@0 569 PriorityParsingStatus ParsePriority();
michael@0 570
michael@0 571 #ifdef MOZ_XUL
michael@0 572 bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs);
michael@0 573 #endif
michael@0 574
michael@0 575 void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties);
michael@0 576
michael@0 577 // Property specific parsing routines
michael@0 578 bool ParseBackground();
michael@0 579
michael@0 580 struct BackgroundParseState {
michael@0 581 nsCSSValue& mColor;
michael@0 582 nsCSSValueList* mImage;
michael@0 583 nsCSSValuePairList* mRepeat;
michael@0 584 nsCSSValueList* mAttachment;
michael@0 585 nsCSSValueList* mClip;
michael@0 586 nsCSSValueList* mOrigin;
michael@0 587 nsCSSValueList* mPosition;
michael@0 588 nsCSSValuePairList* mSize;
michael@0 589 BackgroundParseState(
michael@0 590 nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
michael@0 591 nsCSSValueList* aAttachment, nsCSSValueList* aClip,
michael@0 592 nsCSSValueList* aOrigin, nsCSSValueList* aPosition,
michael@0 593 nsCSSValuePairList* aSize) :
michael@0 594 mColor(aColor), mImage(aImage), mRepeat(aRepeat),
michael@0 595 mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
michael@0 596 mPosition(aPosition), mSize(aSize) {};
michael@0 597 };
michael@0 598
michael@0 599 bool ParseBackgroundItem(BackgroundParseState& aState);
michael@0 600
michael@0 601 bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id
michael@0 602 bool ParseBackgroundRepeat();
michael@0 603 bool ParseBackgroundRepeatValues(nsCSSValuePair& aValue);
michael@0 604 bool ParseBackgroundPosition();
michael@0 605
michael@0 606 // ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
michael@0 607 // which is still used by some properties. See ParseBackgroundPositionValues
michael@0 608 // for the css3-background syntax.
michael@0 609 bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit,
michael@0 610 bool aAllowExplicitCenter = true); // deprecated
michael@0 611 bool ParseBackgroundPositionValues(nsCSSValue& aOut, bool aAcceptsInherit);
michael@0 612
michael@0 613 bool ParseBackgroundSize();
michael@0 614 bool ParseBackgroundSizeValues(nsCSSValuePair& aOut);
michael@0 615 bool ParseBorderColor();
michael@0 616 bool ParseBorderColors(nsCSSProperty aProperty);
michael@0 617 void SetBorderImageInitialValues();
michael@0 618 bool ParseBorderImageRepeat(bool aAcceptsInherit);
michael@0 619 // If ParseBorderImageSlice returns false, aConsumedTokens indicates
michael@0 620 // whether or not any tokens were consumed (in other words, was the property
michael@0 621 // in error or just not present). If ParseBorderImageSlice returns true
michael@0 622 // aConsumedTokens is always true.
michael@0 623 bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens);
michael@0 624 bool ParseBorderImageWidth(bool aAcceptsInherit);
michael@0 625 bool ParseBorderImageOutset(bool aAcceptsInherit);
michael@0 626 bool ParseBorderImage();
michael@0 627 bool ParseBorderSpacing();
michael@0 628 bool ParseBorderSide(const nsCSSProperty aPropIDs[],
michael@0 629 bool aSetAllSides);
michael@0 630 bool ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
michael@0 631 int32_t aSourceType);
michael@0 632 bool ParseBorderStyle();
michael@0 633 bool ParseBorderWidth();
michael@0 634
michael@0 635 bool ParseCalc(nsCSSValue &aValue, int32_t aVariantMask);
michael@0 636 bool ParseCalcAdditiveExpression(nsCSSValue& aValue,
michael@0 637 int32_t& aVariantMask);
michael@0 638 bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
michael@0 639 int32_t& aVariantMask,
michael@0 640 bool *aHadFinalWS);
michael@0 641 bool ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask);
michael@0 642 bool RequireWhitespace();
michael@0 643
michael@0 644 // For "flex" shorthand property, defined in CSS Flexbox spec
michael@0 645 bool ParseFlex();
michael@0 646 // For "flex-flow" shorthand property, defined in CSS Flexbox spec
michael@0 647 bool ParseFlexFlow();
michael@0 648
michael@0 649 // CSS Grid
michael@0 650 bool ParseGridAutoFlow();
michael@0 651
michael@0 652 // Parse a <line-names> expression.
michael@0 653 // If successful, either leave aValue untouched,
michael@0 654 // to indicate that we parsed the empty list,
michael@0 655 // or set it to a eCSSUnit_List of eCSSUnit_Ident.
michael@0 656 //
michael@0 657 // To parse an optional <line-names> (ie. if not finding an open paren
michael@0 658 // is considered the same as an empty list),
michael@0 659 // treat CSSParseResult::NotFound the same as CSSParseResult::Ok.
michael@0 660 //
michael@0 661 // If aValue is already a eCSSUnit_List, append to that list.
michael@0 662 CSSParseResult ParseGridLineNames(nsCSSValue& aValue);
michael@0 663 bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr);
michael@0 664 bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue);
michael@0 665 bool ParseGridTrackBreadth(nsCSSValue& aValue);
michael@0 666 CSSParseResult ParseGridTrackSize(nsCSSValue& aValue);
michael@0 667 bool ParseGridAutoColumnsRows(nsCSSProperty aPropID);
michael@0 668 bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr);
michael@0 669
michael@0 670 // Assuming a [ <line-names>? ] has already been parsed,
michael@0 671 // parse the rest of a <track-list>.
michael@0 672 //
michael@0 673 // This exists because [ <line-names>? ] is ambiguous in the
michael@0 674 // 'grid-template' shorthand: it can be either the start of a <track-list>,
michael@0 675 // or of the intertwined syntax that sets both
michael@0 676 // grid-template-rows and grid-template-areas.
michael@0 677 //
michael@0 678 // On success, |aValue| will be a list of odd length >= 3,
michael@0 679 // starting with a <line-names> (which is itself a list)
michael@0 680 // and alternating between that and <track-size>.
michael@0 681 bool ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
michael@0 682 const nsCSSValue& aFirstLineNames);
michael@0 683 bool ParseGridTemplateColumnsRows(nsCSSProperty aPropID);
michael@0 684
michael@0 685 // |aAreaIndices| is a lookup table to help us parse faster,
michael@0 686 // mapping area names to indices in |aResult.mNamedAreas|.
michael@0 687 bool ParseGridTemplateAreasLine(const nsAutoString& aInput,
michael@0 688 css::GridTemplateAreasValue* aResult,
michael@0 689 nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices);
michael@0 690 bool ParseGridTemplateAreas();
michael@0 691 bool ParseGridTemplate();
michael@0 692 bool ParseGridTemplateAfterSlash(bool aColumnsIsTrackList);
michael@0 693 bool ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames);
michael@0 694 bool ParseGrid();
michael@0 695 bool ParseGridShorthandAutoProps();
michael@0 696 bool ParseGridLine(nsCSSValue& aValue);
michael@0 697 bool ParseGridAutoPosition();
michael@0 698 bool ParseGridColumnRowStartEnd(nsCSSProperty aPropID);
michael@0 699 bool ParseGridColumnRow(nsCSSProperty aStartPropID,
michael@0 700 nsCSSProperty aEndPropID);
michael@0 701 bool ParseGridArea();
michael@0 702
michael@0 703 // for 'clip' and '-moz-image-region'
michael@0 704 bool ParseRect(nsCSSProperty aPropID);
michael@0 705 bool ParseColumns();
michael@0 706 bool ParseContent();
michael@0 707 bool ParseCounterData(nsCSSProperty aPropID);
michael@0 708 bool ParseCursor();
michael@0 709 bool ParseFont();
michael@0 710 bool ParseFontSynthesis(nsCSSValue& aValue);
michael@0 711 bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue);
michael@0 712 bool ParseFontVariantAlternates(nsCSSValue& aValue);
michael@0 713 bool ParseBitmaskValues(nsCSSValue& aValue,
michael@0 714 const KTableValue aKeywordTable[],
michael@0 715 const int32_t aMasks[]);
michael@0 716 bool ParseFontVariantEastAsian(nsCSSValue& aValue);
michael@0 717 bool ParseFontVariantLigatures(nsCSSValue& aValue);
michael@0 718 bool ParseFontVariantNumeric(nsCSSValue& aValue);
michael@0 719 bool ParseFontWeight(nsCSSValue& aValue);
michael@0 720 bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword);
michael@0 721 bool ParseFamily(nsCSSValue& aValue);
michael@0 722 bool ParseFontFeatureSettings(nsCSSValue& aValue);
michael@0 723 bool ParseFontSrc(nsCSSValue& aValue);
michael@0 724 bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
michael@0 725 bool ParseFontRanges(nsCSSValue& aValue);
michael@0 726 bool ParseListStyle();
michael@0 727 bool ParseMargin();
michael@0 728 bool ParseMarks(nsCSSValue& aValue);
michael@0 729 bool ParseTransform(bool aIsPrefixed);
michael@0 730 bool ParseOutline();
michael@0 731 bool ParseOverflow();
michael@0 732 bool ParsePadding();
michael@0 733 bool ParseQuotes();
michael@0 734 bool ParseSize();
michael@0 735 bool ParseTextAlign(nsCSSValue& aValue,
michael@0 736 const KTableValue aTable[]);
michael@0 737 bool ParseTextAlign(nsCSSValue& aValue);
michael@0 738 bool ParseTextAlignLast(nsCSSValue& aValue);
michael@0 739 bool ParseTextDecoration();
michael@0 740 bool ParseTextDecorationLine(nsCSSValue& aValue);
michael@0 741 bool ParseTextCombineUpright(nsCSSValue& aValue);
michael@0 742 bool ParseTextOverflow(nsCSSValue& aValue);
michael@0 743 bool ParseTouchAction(nsCSSValue& aValue);
michael@0 744
michael@0 745 bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow);
michael@0 746 bool ParseShadowList(nsCSSProperty aProperty);
michael@0 747 bool ParseTransitionProperty();
michael@0 748 bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue);
michael@0 749 bool ParseTransitionTimingFunctionValueComponent(float& aComponent,
michael@0 750 char aStop,
michael@0 751 bool aCheckRange);
michael@0 752 bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue);
michael@0 753 enum ParseAnimationOrTransitionShorthandResult {
michael@0 754 eParseAnimationOrTransitionShorthand_Values,
michael@0 755 eParseAnimationOrTransitionShorthand_Inherit,
michael@0 756 eParseAnimationOrTransitionShorthand_Error
michael@0 757 };
michael@0 758 ParseAnimationOrTransitionShorthandResult
michael@0 759 ParseAnimationOrTransitionShorthand(const nsCSSProperty* aProperties,
michael@0 760 const nsCSSValue* aInitialValues,
michael@0 761 nsCSSValue* aValues,
michael@0 762 size_t aNumProperties);
michael@0 763 bool ParseTransition();
michael@0 764 bool ParseAnimation();
michael@0 765 bool ParseWillChange();
michael@0 766
michael@0 767 bool ParsePaint(nsCSSProperty aPropID);
michael@0 768 bool ParseDasharray();
michael@0 769 bool ParseMarker();
michael@0 770 bool ParsePaintOrder();
michael@0 771 bool ParseAll();
michael@0 772
michael@0 773 /**
michael@0 774 * Parses a variable value from a custom property declaration.
michael@0 775 *
michael@0 776 * @param aType Out parameter into which will be stored the type of variable
michael@0 777 * value, indicating whether the parsed value was a token stream or one of
michael@0 778 * the CSS-wide keywords.
michael@0 779 * @param aValue Out parameter into which will be stored the token stream
michael@0 780 * as a string, if the parsed custom property did take a token stream.
michael@0 781 * @return Whether parsing succeeded.
michael@0 782 */
michael@0 783 bool ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
michael@0 784 nsString& aValue);
michael@0 785
michael@0 786 /**
michael@0 787 * Parses a CSS variable value. This could be 'initial', 'inherit', 'unset'
michael@0 788 * or a token stream, which may or may not include variable references.
michael@0 789 *
michael@0 790 * @param aType Out parameter into which the type of the variable value
michael@0 791 * will be stored.
michael@0 792 * @param aDropBackslash Out parameter indicating whether during variable
michael@0 793 * value parsing there was a trailing backslash before EOF that must
michael@0 794 * be dropped when serializing the variable value.
michael@0 795 * @param aImpliedCharacters Out parameter appended to which will be any
michael@0 796 * characters that were implied when encountering EOF and which
michael@0 797 * must be included at the end of the serialized variable value.
michael@0 798 * @param aFunc A callback function to invoke when a variable reference
michael@0 799 * is encountered. May be null. Arguments are the variable name
michael@0 800 * and the aData argument passed in to this function.
michael@0 801 * @param User data to pass in to the callback.
michael@0 802 * @return Whether parsing succeeded.
michael@0 803 */
michael@0 804 bool ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
michael@0 805 bool* aDropBackslash,
michael@0 806 nsString& aImpliedCharacters,
michael@0 807 void (*aFunc)(const nsAString&, void*),
michael@0 808 void* aData);
michael@0 809
michael@0 810 /**
michael@0 811 * Returns whether the scanner dropped a backslash just before EOF.
michael@0 812 */
michael@0 813 bool BackslashDropped();
michael@0 814
michael@0 815 /**
michael@0 816 * Calls AppendImpliedEOFCharacters on mScanner.
michael@0 817 */
michael@0 818 void AppendImpliedEOFCharacters(nsAString& aResult);
michael@0 819
michael@0 820 // Reused utility parsing routines
michael@0 821 void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
michael@0 822 bool ParseBoxProperties(const nsCSSProperty aPropIDs[]);
michael@0 823 bool ParseGroupedBoxProperty(int32_t aVariantMask,
michael@0 824 nsCSSValue& aValue);
michael@0 825 bool ParseDirectionalBoxProperty(nsCSSProperty aProperty,
michael@0 826 int32_t aSourceType);
michael@0 827 bool ParseBoxCornerRadius(const nsCSSProperty aPropID);
michael@0 828 bool ParseBoxCornerRadii(const nsCSSProperty aPropIDs[]);
michael@0 829 int32_t ParseChoice(nsCSSValue aValues[],
michael@0 830 const nsCSSProperty aPropIDs[], int32_t aNumIDs);
michael@0 831 bool ParseColor(nsCSSValue& aValue);
michael@0 832 bool ParseNumberColorComponent(uint8_t& aComponent, char aStop);
michael@0 833 bool ParsePercentageColorComponent(float& aComponent, char aStop);
michael@0 834 // ParseHSLColor parses everything starting with the opening '('
michael@0 835 // up through and including the aStop char.
michael@0 836 bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
michael@0 837 char aStop);
michael@0 838 // ParseColorOpacity will enforce that the color ends with a ')'
michael@0 839 // after the opacity
michael@0 840 bool ParseColorOpacity(uint8_t& aOpacity);
michael@0 841 bool ParseColorOpacity(float& aOpacity);
michael@0 842 bool ParseEnum(nsCSSValue& aValue,
michael@0 843 const KTableValue aKeywordTable[]);
michael@0 844 bool ParseVariant(nsCSSValue& aValue,
michael@0 845 int32_t aVariantMask,
michael@0 846 const KTableValue aKeywordTable[]);
michael@0 847 bool ParseNonNegativeVariant(nsCSSValue& aValue,
michael@0 848 int32_t aVariantMask,
michael@0 849 const KTableValue aKeywordTable[]);
michael@0 850 bool ParseOneOrLargerVariant(nsCSSValue& aValue,
michael@0 851 int32_t aVariantMask,
michael@0 852 const KTableValue aKeywordTable[]);
michael@0 853
michael@0 854 // http://dev.w3.org/csswg/css-values/#custom-idents
michael@0 855 // Parse an identifier that is none of:
michael@0 856 // * a CSS-wide keyword
michael@0 857 // * "default"
michael@0 858 // * a keyword in |aExcludedKeywords|
michael@0 859 // * a keyword in |aPropertyKTable|
michael@0 860 //
michael@0 861 // |aExcludedKeywords| is an array of nsCSSKeyword
michael@0 862 // that ends with a eCSSKeyword_UNKNOWN marker.
michael@0 863 //
michael@0 864 // |aPropertyKTable| can be used if some of the keywords to exclude
michael@0 865 // also appear in an existing nsCSSProps::KTableValue,
michael@0 866 // to avoid duplicating them.
michael@0 867 bool ParseCustomIdent(nsCSSValue& aValue,
michael@0 868 const nsAutoString& aIdentValue,
michael@0 869 const nsCSSKeyword aExcludedKeywords[] = nullptr,
michael@0 870 const nsCSSProps::KTableValue aPropertyKTable[] = nullptr);
michael@0 871 bool ParseCounter(nsCSSValue& aValue);
michael@0 872 bool ParseAttr(nsCSSValue& aValue);
michael@0 873 bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL);
michael@0 874 bool TranslateDimension(nsCSSValue& aValue, int32_t aVariantMask,
michael@0 875 float aNumber, const nsString& aUnit);
michael@0 876 bool ParseImageOrientation(nsCSSValue& aAngle);
michael@0 877 bool ParseImageRect(nsCSSValue& aImage);
michael@0 878 bool ParseElement(nsCSSValue& aValue);
michael@0 879 bool ParseColorStop(nsCSSValueGradient* aGradient);
michael@0 880 bool ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating,
michael@0 881 bool aIsLegacy);
michael@0 882 bool ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating,
michael@0 883 bool aIsLegacy);
michael@0 884 bool IsLegacyGradientLine(const nsCSSTokenType& aType,
michael@0 885 const nsString& aId);
michael@0 886 bool ParseGradientColorStops(nsCSSValueGradient* aGradient,
michael@0 887 nsCSSValue& aValue);
michael@0 888
michael@0 889 void SetParsingCompoundProperty(bool aBool) {
michael@0 890 mParsingCompoundProperty = aBool;
michael@0 891 }
michael@0 892 bool IsParsingCompoundProperty(void) const {
michael@0 893 return mParsingCompoundProperty;
michael@0 894 }
michael@0 895
michael@0 896 /* Functions for transform Parsing */
michael@0 897 bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue);
michael@0 898 bool ParseFunction(nsCSSKeyword aFunction, const int32_t aAllowedTypes[],
michael@0 899 int32_t aVariantMaskAll, uint16_t aMinElems,
michael@0 900 uint16_t aMaxElems, nsCSSValue &aValue);
michael@0 901 bool ParseFunctionInternals(const int32_t aVariantMask[],
michael@0 902 int32_t aVariantMaskAll,
michael@0 903 uint16_t aMinElems,
michael@0 904 uint16_t aMaxElems,
michael@0 905 InfallibleTArray<nsCSSValue>& aOutput);
michael@0 906
michael@0 907 /* Functions for transform-origin/perspective-origin Parsing */
michael@0 908 bool ParseTransformOrigin(bool aPerspective);
michael@0 909
michael@0 910 /* Functions for filter parsing */
michael@0 911 bool ParseFilter();
michael@0 912 bool ParseSingleFilter(nsCSSValue* aValue);
michael@0 913 bool ParseDropShadow(nsCSSValue* aValue);
michael@0 914
michael@0 915 /* Find and return the namespace ID associated with aPrefix.
michael@0 916 If aPrefix has not been declared in an @namespace rule, returns
michael@0 917 kNameSpaceID_Unknown. */
michael@0 918 int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
michael@0 919
michael@0 920 /* Find the correct default namespace, and set it on aSelector. */
michael@0 921 void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
michael@0 922
michael@0 923 // Current token. The value is valid after calling GetToken and invalidated
michael@0 924 // by UngetToken.
michael@0 925 nsCSSToken mToken;
michael@0 926
michael@0 927 // Our scanner.
michael@0 928 nsCSSScanner* mScanner;
michael@0 929
michael@0 930 // Our error reporter.
michael@0 931 css::ErrorReporter* mReporter;
michael@0 932
michael@0 933 // The URI to be used as a base for relative URIs.
michael@0 934 nsCOMPtr<nsIURI> mBaseURI;
michael@0 935
michael@0 936 // The URI to be used as an HTTP "Referer" and for error reporting.
michael@0 937 nsCOMPtr<nsIURI> mSheetURI;
michael@0 938
michael@0 939 // The principal of the sheet involved
michael@0 940 nsCOMPtr<nsIPrincipal> mSheetPrincipal;
michael@0 941
michael@0 942 // The sheet we're parsing into
michael@0 943 nsRefPtr<nsCSSStyleSheet> mSheet;
michael@0 944
michael@0 945 // Used for @import rules
michael@0 946 mozilla::css::Loader* mChildLoader; // not ref counted, it owns us
michael@0 947
michael@0 948 // Sheet section we're in. This is used to enforce correct ordering of the
michael@0 949 // various rule types (eg the fact that a @charset rule must come before
michael@0 950 // anything else). Note that there are checks of similar things in various
michael@0 951 // places in nsCSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
michael@0 952 enum nsCSSSection {
michael@0 953 eCSSSection_Charset,
michael@0 954 eCSSSection_Import,
michael@0 955 eCSSSection_NameSpace,
michael@0 956 eCSSSection_General
michael@0 957 };
michael@0 958 nsCSSSection mSection;
michael@0 959
michael@0 960 nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it
michael@0 961
michael@0 962 // After an UngetToken is done this flag is true. The next call to
michael@0 963 // GetToken clears the flag.
michael@0 964 bool mHavePushBack : 1;
michael@0 965
michael@0 966 // True if we are in quirks mode; false in standards or almost standards mode
michael@0 967 bool mNavQuirkMode : 1;
michael@0 968
michael@0 969 // True when the hashless color quirk applies.
michael@0 970 bool mHashlessColorQuirk : 1;
michael@0 971
michael@0 972 // True when the unitless length quirk applies.
michael@0 973 bool mUnitlessLengthQuirk : 1;
michael@0 974
michael@0 975 // True if unsafe rules should be allowed
michael@0 976 bool mUnsafeRulesEnabled : 1;
michael@0 977
michael@0 978 // True if we are in parsing rules for Chrome or Certified App content,
michael@0 979 // in which case CSS properties with the
michael@0 980 // CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP
michael@0 981 // flag should be allowed.
michael@0 982 bool mIsChromeOrCertifiedApp : 1;
michael@0 983
michael@0 984 // True if viewport units should be allowed.
michael@0 985 bool mViewportUnitsEnabled : 1;
michael@0 986
michael@0 987 // True for parsing media lists for HTML attributes, where we have to
michael@0 988 // ignore CSS comments.
michael@0 989 bool mHTMLMediaMode : 1;
michael@0 990
michael@0 991 // This flag is set when parsing a non-box shorthand; it's used to not apply
michael@0 992 // some quirks during shorthand parsing
michael@0 993 bool mParsingCompoundProperty : 1;
michael@0 994
michael@0 995 // True if we are in the middle of parsing an @supports condition.
michael@0 996 // This is used to avoid recording the input stream when variable references
michael@0 997 // are encountered in a property declaration in the @supports condition.
michael@0 998 bool mInSupportsCondition : 1;
michael@0 999
michael@0 1000 // True if we are somewhere within a @supports rule whose condition is
michael@0 1001 // false.
michael@0 1002 bool mInFailingSupportsRule : 1;
michael@0 1003
michael@0 1004 // True if we will suppress all parse errors (except unexpected EOFs).
michael@0 1005 // This is used to prevent errors for declarations inside a failing
michael@0 1006 // @supports rule.
michael@0 1007 bool mSuppressErrors : 1;
michael@0 1008
michael@0 1009 // Stack of rule groups; used for @media and such.
michael@0 1010 InfallibleTArray<nsRefPtr<css::GroupRule> > mGroupStack;
michael@0 1011
michael@0 1012 // During the parsing of a property (which may be a shorthand), the data
michael@0 1013 // are stored in |mTempData|. (It is needed to ensure that parser
michael@0 1014 // errors cause the data to be ignored, and to ensure that a
michael@0 1015 // non-'!important' declaration does not override an '!important'
michael@0 1016 // one.)
michael@0 1017 nsCSSExpandedDataBlock mTempData;
michael@0 1018
michael@0 1019 // All data from successfully parsed properties are placed into |mData|.
michael@0 1020 nsCSSExpandedDataBlock mData;
michael@0 1021
michael@0 1022 public:
michael@0 1023 // Used from nsCSSParser constructors and destructors
michael@0 1024 CSSParserImpl* mNextFree;
michael@0 1025 };
michael@0 1026
michael@0 1027 static void AssignRuleToPointer(css::Rule* aRule, void* aPointer)
michael@0 1028 {
michael@0 1029 css::Rule **pointer = static_cast<css::Rule**>(aPointer);
michael@0 1030 NS_ADDREF(*pointer = aRule);
michael@0 1031 }
michael@0 1032
michael@0 1033 static void AppendRuleToSheet(css::Rule* aRule, void* aParser)
michael@0 1034 {
michael@0 1035 CSSParserImpl* parser = (CSSParserImpl*) aParser;
michael@0 1036 parser->AppendRule(aRule);
michael@0 1037 }
michael@0 1038
michael@0 1039 #define REPORT_UNEXPECTED(msg_) \
michael@0 1040 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_); }
michael@0 1041
michael@0 1042 #define REPORT_UNEXPECTED_P(msg_, param_) \
michael@0 1043 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_); }
michael@0 1044
michael@0 1045 #define REPORT_UNEXPECTED_TOKEN(msg_) \
michael@0 1046 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken); }
michael@0 1047
michael@0 1048 #define REPORT_UNEXPECTED_TOKEN_CHAR(msg_, ch_) \
michael@0 1049 { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken, ch_); }
michael@0 1050
michael@0 1051 #define REPORT_UNEXPECTED_EOF(lf_) \
michael@0 1052 mReporter->ReportUnexpectedEOF(#lf_)
michael@0 1053
michael@0 1054 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
michael@0 1055 mReporter->ReportUnexpectedEOF(ch_)
michael@0 1056
michael@0 1057 #define OUTPUT_ERROR() \
michael@0 1058 mReporter->OutputError()
michael@0 1059
michael@0 1060 #define OUTPUT_ERROR_WITH_POSITION(linenum_, lineoff_) \
michael@0 1061 mReporter->OutputError(linenum_, lineoff_)
michael@0 1062
michael@0 1063 #define CLEAR_ERROR() \
michael@0 1064 mReporter->ClearError()
michael@0 1065
michael@0 1066 CSSParserImpl::CSSParserImpl()
michael@0 1067 : mToken(),
michael@0 1068 mScanner(nullptr),
michael@0 1069 mReporter(nullptr),
michael@0 1070 mChildLoader(nullptr),
michael@0 1071 mSection(eCSSSection_Charset),
michael@0 1072 mNameSpaceMap(nullptr),
michael@0 1073 mHavePushBack(false),
michael@0 1074 mNavQuirkMode(false),
michael@0 1075 mHashlessColorQuirk(false),
michael@0 1076 mUnitlessLengthQuirk(false),
michael@0 1077 mUnsafeRulesEnabled(false),
michael@0 1078 mIsChromeOrCertifiedApp(false),
michael@0 1079 mViewportUnitsEnabled(true),
michael@0 1080 mHTMLMediaMode(false),
michael@0 1081 mParsingCompoundProperty(false),
michael@0 1082 mInSupportsCondition(false),
michael@0 1083 mInFailingSupportsRule(false),
michael@0 1084 mSuppressErrors(false),
michael@0 1085 mNextFree(nullptr)
michael@0 1086 {
michael@0 1087 }
michael@0 1088
michael@0 1089 CSSParserImpl::~CSSParserImpl()
michael@0 1090 {
michael@0 1091 mData.AssertInitialState();
michael@0 1092 mTempData.AssertInitialState();
michael@0 1093 }
michael@0 1094
michael@0 1095 nsresult
michael@0 1096 CSSParserImpl::SetStyleSheet(nsCSSStyleSheet* aSheet)
michael@0 1097 {
michael@0 1098 if (aSheet != mSheet) {
michael@0 1099 // Switch to using the new sheet, if any
michael@0 1100 mGroupStack.Clear();
michael@0 1101 mSheet = aSheet;
michael@0 1102 if (mSheet) {
michael@0 1103 mNameSpaceMap = mSheet->GetNameSpaceMap();
michael@0 1104 } else {
michael@0 1105 mNameSpaceMap = nullptr;
michael@0 1106 }
michael@0 1107 } else if (mSheet) {
michael@0 1108 mNameSpaceMap = mSheet->GetNameSpaceMap();
michael@0 1109 }
michael@0 1110
michael@0 1111 return NS_OK;
michael@0 1112 }
michael@0 1113
michael@0 1114 nsresult
michael@0 1115 CSSParserImpl::SetQuirkMode(bool aQuirkMode)
michael@0 1116 {
michael@0 1117 mNavQuirkMode = aQuirkMode;
michael@0 1118 return NS_OK;
michael@0 1119 }
michael@0 1120
michael@0 1121 nsresult
michael@0 1122 CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader)
michael@0 1123 {
michael@0 1124 mChildLoader = aChildLoader; // not ref counted, it owns us
michael@0 1125 return NS_OK;
michael@0 1126 }
michael@0 1127
michael@0 1128 void
michael@0 1129 CSSParserImpl::Reset()
michael@0 1130 {
michael@0 1131 NS_ASSERTION(!mScanner, "resetting with scanner active");
michael@0 1132 SetStyleSheet(nullptr);
michael@0 1133 SetQuirkMode(false);
michael@0 1134 SetChildLoader(nullptr);
michael@0 1135 }
michael@0 1136
michael@0 1137 void
michael@0 1138 CSSParserImpl::InitScanner(nsCSSScanner& aScanner,
michael@0 1139 css::ErrorReporter& aReporter,
michael@0 1140 nsIURI* aSheetURI, nsIURI* aBaseURI,
michael@0 1141 nsIPrincipal* aSheetPrincipal)
michael@0 1142 {
michael@0 1143 NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
michael@0 1144 NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
michael@0 1145 NS_PRECONDITION(!mScanner, "already have scanner");
michael@0 1146
michael@0 1147 mScanner = &aScanner;
michael@0 1148 mReporter = &aReporter;
michael@0 1149 mScanner->SetErrorReporter(mReporter);
michael@0 1150
michael@0 1151 mBaseURI = aBaseURI;
michael@0 1152 mSheetURI = aSheetURI;
michael@0 1153 mSheetPrincipal = aSheetPrincipal;
michael@0 1154 mHavePushBack = false;
michael@0 1155 }
michael@0 1156
michael@0 1157 void
michael@0 1158 CSSParserImpl::ReleaseScanner()
michael@0 1159 {
michael@0 1160 mScanner = nullptr;
michael@0 1161 mReporter = nullptr;
michael@0 1162 mBaseURI = nullptr;
michael@0 1163 mSheetURI = nullptr;
michael@0 1164 mSheetPrincipal = nullptr;
michael@0 1165 }
michael@0 1166
michael@0 1167 nsresult
michael@0 1168 CSSParserImpl::ParseSheet(const nsAString& aInput,
michael@0 1169 nsIURI* aSheetURI,
michael@0 1170 nsIURI* aBaseURI,
michael@0 1171 nsIPrincipal* aSheetPrincipal,
michael@0 1172 uint32_t aLineNumber,
michael@0 1173 bool aAllowUnsafeRules)
michael@0 1174 {
michael@0 1175 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
michael@0 1176 NS_PRECONDITION(aBaseURI, "need base URI");
michael@0 1177 NS_PRECONDITION(aSheetURI, "need sheet URI");
michael@0 1178 NS_PRECONDITION(mSheet, "Must have sheet to parse into");
michael@0 1179 NS_ENSURE_STATE(mSheet);
michael@0 1180
michael@0 1181 #ifdef DEBUG
michael@0 1182 nsIURI* uri = mSheet->GetSheetURI();
michael@0 1183 bool equal;
michael@0 1184 NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
michael@0 1185 "Sheet URI does not match passed URI");
michael@0 1186 NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
michael@0 1187 &equal)) &&
michael@0 1188 equal,
michael@0 1189 "Sheet principal does not match passed principal");
michael@0 1190 #endif
michael@0 1191
michael@0 1192 nsCSSScanner scanner(aInput, aLineNumber);
michael@0 1193 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
michael@0 1194 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
michael@0 1195
michael@0 1196 int32_t ruleCount = mSheet->StyleRuleCount();
michael@0 1197 if (0 < ruleCount) {
michael@0 1198 const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1);
michael@0 1199 if (lastRule) {
michael@0 1200 switch (lastRule->GetType()) {
michael@0 1201 case css::Rule::CHARSET_RULE:
michael@0 1202 case css::Rule::IMPORT_RULE:
michael@0 1203 mSection = eCSSSection_Import;
michael@0 1204 break;
michael@0 1205 case css::Rule::NAMESPACE_RULE:
michael@0 1206 mSection = eCSSSection_NameSpace;
michael@0 1207 break;
michael@0 1208 default:
michael@0 1209 mSection = eCSSSection_General;
michael@0 1210 break;
michael@0 1211 }
michael@0 1212 }
michael@0 1213 }
michael@0 1214 else {
michael@0 1215 mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
michael@0 1216 }
michael@0 1217
michael@0 1218 mUnsafeRulesEnabled = aAllowUnsafeRules;
michael@0 1219 mIsChromeOrCertifiedApp =
michael@0 1220 dom::IsChromeURI(aSheetURI) ||
michael@0 1221 aSheetPrincipal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
michael@0 1222
michael@0 1223 nsCSSToken* tk = &mToken;
michael@0 1224 for (;;) {
michael@0 1225 // Get next non-whitespace token
michael@0 1226 if (!GetToken(true)) {
michael@0 1227 OUTPUT_ERROR();
michael@0 1228 break;
michael@0 1229 }
michael@0 1230 if (eCSSToken_HTMLComment == tk->mType) {
michael@0 1231 continue; // legal here only
michael@0 1232 }
michael@0 1233 if (eCSSToken_AtKeyword == tk->mType) {
michael@0 1234 ParseAtRule(AppendRuleToSheet, this, false);
michael@0 1235 continue;
michael@0 1236 }
michael@0 1237 UngetToken();
michael@0 1238 if (ParseRuleSet(AppendRuleToSheet, this)) {
michael@0 1239 mSection = eCSSSection_General;
michael@0 1240 }
michael@0 1241 }
michael@0 1242 ReleaseScanner();
michael@0 1243
michael@0 1244 mUnsafeRulesEnabled = false;
michael@0 1245 mIsChromeOrCertifiedApp = false;
michael@0 1246
michael@0 1247 // XXX check for low level errors
michael@0 1248 return NS_OK;
michael@0 1249 }
michael@0 1250
michael@0 1251 /**
michael@0 1252 * Determines whether the identifier contained in the given string is a
michael@0 1253 * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
michael@0 1254 */
michael@0 1255 static bool
michael@0 1256 NonMozillaVendorIdentifier(const nsAString& ident)
michael@0 1257 {
michael@0 1258 return (ident.First() == char16_t('-') &&
michael@0 1259 !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
michael@0 1260 ident.First() == char16_t('_');
michael@0 1261
michael@0 1262 }
michael@0 1263
michael@0 1264 nsresult
michael@0 1265 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
michael@0 1266 nsIURI* aDocURI,
michael@0 1267 nsIURI* aBaseURI,
michael@0 1268 nsIPrincipal* aNodePrincipal,
michael@0 1269 css::StyleRule** aResult)
michael@0 1270 {
michael@0 1271 NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
michael@0 1272 NS_PRECONDITION(aBaseURI, "need base URI");
michael@0 1273
michael@0 1274 // XXX line number?
michael@0 1275 nsCSSScanner scanner(aAttributeValue, 0);
michael@0 1276 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI);
michael@0 1277 InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal);
michael@0 1278
michael@0 1279 mSection = eCSSSection_General;
michael@0 1280
michael@0 1281 uint32_t parseFlags = eParseDeclaration_AllowImportant;
michael@0 1282
michael@0 1283 css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
michael@0 1284 if (declaration) {
michael@0 1285 // Create a style rule for the declaration
michael@0 1286 NS_ADDREF(*aResult = new css::StyleRule(nullptr, declaration));
michael@0 1287 } else {
michael@0 1288 *aResult = nullptr;
michael@0 1289 }
michael@0 1290
michael@0 1291 ReleaseScanner();
michael@0 1292
michael@0 1293 // XXX check for low level errors
michael@0 1294 return NS_OK;
michael@0 1295 }
michael@0 1296
michael@0 1297 nsresult
michael@0 1298 CSSParserImpl::ParseDeclarations(const nsAString& aBuffer,
michael@0 1299 nsIURI* aSheetURI,
michael@0 1300 nsIURI* aBaseURI,
michael@0 1301 nsIPrincipal* aSheetPrincipal,
michael@0 1302 css::Declaration* aDeclaration,
michael@0 1303 bool* aChanged)
michael@0 1304 {
michael@0 1305 *aChanged = false;
michael@0 1306
michael@0 1307 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
michael@0 1308
michael@0 1309 nsCSSScanner scanner(aBuffer, 0);
michael@0 1310 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
michael@0 1311 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
michael@0 1312
michael@0 1313 mSection = eCSSSection_General;
michael@0 1314
michael@0 1315 mData.AssertInitialState();
michael@0 1316 aDeclaration->ClearData();
michael@0 1317 // We could check if it was already empty, but...
michael@0 1318 *aChanged = true;
michael@0 1319
michael@0 1320 for (;;) {
michael@0 1321 // If we cleared the old decl, then we want to be calling
michael@0 1322 // ValueAppended as we parse.
michael@0 1323 if (!ParseDeclaration(aDeclaration, eParseDeclaration_AllowImportant,
michael@0 1324 true, aChanged)) {
michael@0 1325 if (!SkipDeclaration(false)) {
michael@0 1326 break;
michael@0 1327 }
michael@0 1328 }
michael@0 1329 }
michael@0 1330
michael@0 1331 aDeclaration->CompressFrom(&mData);
michael@0 1332 ReleaseScanner();
michael@0 1333 return NS_OK;
michael@0 1334 }
michael@0 1335
michael@0 1336 nsresult
michael@0 1337 CSSParserImpl::ParseRule(const nsAString& aRule,
michael@0 1338 nsIURI* aSheetURI,
michael@0 1339 nsIURI* aBaseURI,
michael@0 1340 nsIPrincipal* aSheetPrincipal,
michael@0 1341 css::Rule** aResult)
michael@0 1342 {
michael@0 1343 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
michael@0 1344 NS_PRECONDITION(aBaseURI, "need base URI");
michael@0 1345
michael@0 1346 *aResult = nullptr;
michael@0 1347
michael@0 1348 nsCSSScanner scanner(aRule, 0);
michael@0 1349 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
michael@0 1350 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
michael@0 1351
michael@0 1352 mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
michael@0 1353
michael@0 1354 nsCSSToken* tk = &mToken;
michael@0 1355 // Get first non-whitespace token
michael@0 1356 nsresult rv = NS_OK;
michael@0 1357 if (!GetToken(true)) {
michael@0 1358 REPORT_UNEXPECTED(PEParseRuleWSOnly);
michael@0 1359 OUTPUT_ERROR();
michael@0 1360 rv = NS_ERROR_DOM_SYNTAX_ERR;
michael@0 1361 } else {
michael@0 1362 if (eCSSToken_AtKeyword == tk->mType) {
michael@0 1363 // FIXME: perhaps aInsideBlock should be true when we are?
michael@0 1364 ParseAtRule(AssignRuleToPointer, aResult, false);
michael@0 1365 } else {
michael@0 1366 UngetToken();
michael@0 1367 ParseRuleSet(AssignRuleToPointer, aResult);
michael@0 1368 }
michael@0 1369
michael@0 1370 if (*aResult && GetToken(true)) {
michael@0 1371 // garbage after rule
michael@0 1372 REPORT_UNEXPECTED_TOKEN(PERuleTrailing);
michael@0 1373 NS_RELEASE(*aResult);
michael@0 1374 }
michael@0 1375
michael@0 1376 if (!*aResult) {
michael@0 1377 rv = NS_ERROR_DOM_SYNTAX_ERR;
michael@0 1378 OUTPUT_ERROR();
michael@0 1379 }
michael@0 1380 }
michael@0 1381
michael@0 1382 ReleaseScanner();
michael@0 1383 return rv;
michael@0 1384 }
michael@0 1385
michael@0 1386 // See Bug 723197
michael@0 1387 #ifdef _MSC_VER
michael@0 1388 #pragma optimize( "", off )
michael@0 1389 #pragma warning( push )
michael@0 1390 #pragma warning( disable : 4748 )
michael@0 1391 #endif
michael@0 1392
michael@0 1393 nsresult
michael@0 1394 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
michael@0 1395 const nsAString& aPropValue,
michael@0 1396 nsIURI* aSheetURI,
michael@0 1397 nsIURI* aBaseURI,
michael@0 1398 nsIPrincipal* aSheetPrincipal,
michael@0 1399 css::Declaration* aDeclaration,
michael@0 1400 bool* aChanged,
michael@0 1401 bool aIsImportant,
michael@0 1402 bool aIsSVGMode)
michael@0 1403 {
michael@0 1404 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
michael@0 1405 NS_PRECONDITION(aBaseURI, "need base URI");
michael@0 1406 NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
michael@0 1407
michael@0 1408 mData.AssertInitialState();
michael@0 1409 mTempData.AssertInitialState();
michael@0 1410 aDeclaration->AssertMutable();
michael@0 1411
michael@0 1412 nsCSSScanner scanner(aPropValue, 0);
michael@0 1413 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
michael@0 1414 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
michael@0 1415 mSection = eCSSSection_General;
michael@0 1416 scanner.SetSVGMode(aIsSVGMode);
michael@0 1417
michael@0 1418 *aChanged = false;
michael@0 1419
michael@0 1420 // Check for unknown or preffed off properties
michael@0 1421 if (eCSSProperty_UNKNOWN == aPropID ||
michael@0 1422 !(nsCSSProps::IsEnabled(aPropID) ||
michael@0 1423 (mUnsafeRulesEnabled &&
michael@0 1424 nsCSSProps::PropHasFlags(aPropID,
michael@0 1425 CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS)))) {
michael@0 1426 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
michael@0 1427 REPORT_UNEXPECTED_P(PEUnknownProperty, propName);
michael@0 1428 REPORT_UNEXPECTED(PEDeclDropped);
michael@0 1429 OUTPUT_ERROR();
michael@0 1430 ReleaseScanner();
michael@0 1431 return NS_OK;
michael@0 1432 }
michael@0 1433
michael@0 1434 bool parsedOK = ParseProperty(aPropID);
michael@0 1435 // We should now be at EOF
michael@0 1436 if (parsedOK && GetToken(true)) {
michael@0 1437 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
michael@0 1438 parsedOK = false;
michael@0 1439 }
michael@0 1440
michael@0 1441 if (!parsedOK) {
michael@0 1442 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
michael@0 1443 REPORT_UNEXPECTED_P(PEValueParsingError, propName);
michael@0 1444 REPORT_UNEXPECTED(PEDeclDropped);
michael@0 1445 OUTPUT_ERROR();
michael@0 1446 mTempData.ClearProperty(aPropID);
michael@0 1447 } else {
michael@0 1448
michael@0 1449 // We know we don't need to force a ValueAppended call for the new
michael@0 1450 // value. So if we are not processing a shorthand, and there's
michael@0 1451 // already a value for this property in the declaration at the
michael@0 1452 // same importance level, then we can just copy our parsed value
michael@0 1453 // directly into the declaration without going through the whole
michael@0 1454 // expand/compress thing.
michael@0 1455 if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData,
michael@0 1456 aChanged)) {
michael@0 1457 // Do it the slow way
michael@0 1458 aDeclaration->ExpandTo(&mData);
michael@0 1459 *aChanged = mData.TransferFromBlock(mTempData, aPropID, aIsImportant,
michael@0 1460 true, false, aDeclaration);
michael@0 1461 aDeclaration->CompressFrom(&mData);
michael@0 1462 }
michael@0 1463 CLEAR_ERROR();
michael@0 1464 }
michael@0 1465
michael@0 1466 mTempData.AssertInitialState();
michael@0 1467
michael@0 1468 ReleaseScanner();
michael@0 1469 return NS_OK;
michael@0 1470 }
michael@0 1471
michael@0 1472 nsresult
michael@0 1473 CSSParserImpl::ParseVariable(const nsAString& aVariableName,
michael@0 1474 const nsAString& aPropValue,
michael@0 1475 nsIURI* aSheetURI,
michael@0 1476 nsIURI* aBaseURI,
michael@0 1477 nsIPrincipal* aSheetPrincipal,
michael@0 1478 css::Declaration* aDeclaration,
michael@0 1479 bool* aChanged,
michael@0 1480 bool aIsImportant)
michael@0 1481 {
michael@0 1482 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
michael@0 1483 NS_PRECONDITION(aBaseURI, "need base URI");
michael@0 1484 NS_PRECONDITION(aDeclaration, "Need declaration to parse into!");
michael@0 1485 NS_PRECONDITION(nsLayoutUtils::CSSVariablesEnabled(),
michael@0 1486 "expected Variables to be enabled");
michael@0 1487
michael@0 1488 mData.AssertInitialState();
michael@0 1489 mTempData.AssertInitialState();
michael@0 1490 aDeclaration->AssertMutable();
michael@0 1491
michael@0 1492 nsCSSScanner scanner(aPropValue, 0);
michael@0 1493 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI);
michael@0 1494 InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal);
michael@0 1495 mSection = eCSSSection_General;
michael@0 1496
michael@0 1497 *aChanged = false;
michael@0 1498
michael@0 1499 CSSVariableDeclarations::Type variableType;
michael@0 1500 nsString variableValue;
michael@0 1501
michael@0 1502 bool parsedOK = ParseVariableDeclaration(&variableType, variableValue);
michael@0 1503
michael@0 1504 // We should now be at EOF
michael@0 1505 if (parsedOK && GetToken(true)) {
michael@0 1506 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
michael@0 1507 parsedOK = false;
michael@0 1508 }
michael@0 1509
michael@0 1510 if (!parsedOK) {
michael@0 1511 REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("--") +
michael@0 1512 aVariableName);
michael@0 1513 REPORT_UNEXPECTED(PEDeclDropped);
michael@0 1514 OUTPUT_ERROR();
michael@0 1515 } else {
michael@0 1516 CLEAR_ERROR();
michael@0 1517 aDeclaration->AddVariableDeclaration(aVariableName, variableType,
michael@0 1518 variableValue, aIsImportant, true);
michael@0 1519 *aChanged = true;
michael@0 1520 }
michael@0 1521
michael@0 1522 mTempData.AssertInitialState();
michael@0 1523
michael@0 1524 ReleaseScanner();
michael@0 1525 return NS_OK;
michael@0 1526 }
michael@0 1527
michael@0 1528 #ifdef _MSC_VER
michael@0 1529 #pragma warning( pop )
michael@0 1530 #pragma optimize( "", on )
michael@0 1531 #endif
michael@0 1532
michael@0 1533 void
michael@0 1534 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
michael@0 1535 nsIURI* aURI, // for error reporting
michael@0 1536 uint32_t aLineNumber, // for error reporting
michael@0 1537 nsMediaList* aMediaList,
michael@0 1538 bool aHTMLMode)
michael@0 1539 {
michael@0 1540 // XXX Are there cases where the caller wants to keep what it already
michael@0 1541 // has in case of parser error? If GatherMedia ever changes to return
michael@0 1542 // a value other than true, we probably should avoid modifying aMediaList.
michael@0 1543 aMediaList->Clear();
michael@0 1544
michael@0 1545 // fake base URI since media lists don't have URIs in them
michael@0 1546 nsCSSScanner scanner(aBuffer, aLineNumber);
michael@0 1547 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
michael@0 1548 InitScanner(scanner, reporter, aURI, aURI, nullptr);
michael@0 1549
michael@0 1550 mHTMLMediaMode = aHTMLMode;
michael@0 1551
michael@0 1552 // XXXldb We need to make the scanner not skip CSS comments! (Or
michael@0 1553 // should we?)
michael@0 1554
michael@0 1555 // For aHTMLMode, we used to follow the parsing rules in
michael@0 1556 // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
michael@0 1557 // which wouldn't work for media queries since they remove all but the
michael@0 1558 // first word. However, they're changed in
michael@0 1559 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
michael@0 1560 // (as of 2008-05-29) which says that the media attribute just points
michael@0 1561 // to a media query. (The main substative difference is the relative
michael@0 1562 // precedence of commas and paretheses.)
michael@0 1563
michael@0 1564 DebugOnly<bool> parsedOK = GatherMedia(aMediaList, false);
michael@0 1565 NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid "
michael@0 1566 "trashing aMediaList");
michael@0 1567
michael@0 1568 CLEAR_ERROR();
michael@0 1569 ReleaseScanner();
michael@0 1570 mHTMLMediaMode = false;
michael@0 1571 }
michael@0 1572
michael@0 1573 bool
michael@0 1574 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
michael@0 1575 nsIURI* aURI, // for error reporting
michael@0 1576 uint32_t aLineNumber, // for error reporting
michael@0 1577 nsCSSValue& aValue)
michael@0 1578 {
michael@0 1579 nsCSSScanner scanner(aBuffer, aLineNumber);
michael@0 1580 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
michael@0 1581 InitScanner(scanner, reporter, aURI, aURI, nullptr);
michael@0 1582
michael@0 1583 // Parse a color, and check that there's nothing else after it.
michael@0 1584 bool colorParsed = ParseColor(aValue) && !GetToken(true);
michael@0 1585 OUTPUT_ERROR();
michael@0 1586 ReleaseScanner();
michael@0 1587 return colorParsed;
michael@0 1588 }
michael@0 1589
michael@0 1590 nsresult
michael@0 1591 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
michael@0 1592 nsIURI* aURI, // for error reporting
michael@0 1593 uint32_t aLineNumber, // for error reporting
michael@0 1594 nsCSSSelectorList **aSelectorList)
michael@0 1595 {
michael@0 1596 nsCSSScanner scanner(aSelectorString, aLineNumber);
michael@0 1597 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
michael@0 1598 InitScanner(scanner, reporter, aURI, aURI, nullptr);
michael@0 1599
michael@0 1600 bool success = ParseSelectorList(*aSelectorList, char16_t(0));
michael@0 1601
michael@0 1602 // We deliberately do not call OUTPUT_ERROR here, because all our
michael@0 1603 // callers map a failure return to a JS exception, and if that JS
michael@0 1604 // exception is caught, people don't want to see parser diagnostics;
michael@0 1605 // see e.g. http://bugs.jquery.com/ticket/7535
michael@0 1606 // It would be nice to be able to save the parser diagnostics into
michael@0 1607 // the exception, so that if it _isn't_ caught we can report them
michael@0 1608 // along with the usual uncaught-exception message, but we don't
michael@0 1609 // have any way to do that at present; see bug 631621.
michael@0 1610 CLEAR_ERROR();
michael@0 1611 ReleaseScanner();
michael@0 1612
michael@0 1613 if (success) {
michael@0 1614 NS_ASSERTION(*aSelectorList, "Should have list!");
michael@0 1615 return NS_OK;
michael@0 1616 }
michael@0 1617
michael@0 1618 NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
michael@0 1619
michael@0 1620 return NS_ERROR_DOM_SYNTAX_ERR;
michael@0 1621 }
michael@0 1622
michael@0 1623
michael@0 1624 already_AddRefed<nsCSSKeyframeRule>
michael@0 1625 CSSParserImpl::ParseKeyframeRule(const nsSubstring& aBuffer,
michael@0 1626 nsIURI* aURI,
michael@0 1627 uint32_t aLineNumber)
michael@0 1628 {
michael@0 1629 nsCSSScanner scanner(aBuffer, aLineNumber);
michael@0 1630 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
michael@0 1631 InitScanner(scanner, reporter, aURI, aURI, nullptr);
michael@0 1632
michael@0 1633 nsRefPtr<nsCSSKeyframeRule> result = ParseKeyframeRule();
michael@0 1634 if (GetToken(true)) {
michael@0 1635 // extra garbage at the end
michael@0 1636 result = nullptr;
michael@0 1637 }
michael@0 1638
michael@0 1639 OUTPUT_ERROR();
michael@0 1640 ReleaseScanner();
michael@0 1641
michael@0 1642 return result.forget();
michael@0 1643 }
michael@0 1644
michael@0 1645 bool
michael@0 1646 CSSParserImpl::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
michael@0 1647 nsIURI* aURI, // for error reporting
michael@0 1648 uint32_t aLineNumber, // for error reporting
michael@0 1649 InfallibleTArray<float>& aSelectorList)
michael@0 1650 {
michael@0 1651 NS_ABORT_IF_FALSE(aSelectorList.IsEmpty(), "given list should start empty");
michael@0 1652
michael@0 1653 nsCSSScanner scanner(aSelectorString, aLineNumber);
michael@0 1654 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
michael@0 1655 InitScanner(scanner, reporter, aURI, aURI, nullptr);
michael@0 1656
michael@0 1657 bool success = ParseKeyframeSelectorList(aSelectorList) &&
michael@0 1658 // must consume entire input string
michael@0 1659 !GetToken(true);
michael@0 1660
michael@0 1661 OUTPUT_ERROR();
michael@0 1662 ReleaseScanner();
michael@0 1663
michael@0 1664 if (success) {
michael@0 1665 NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty");
michael@0 1666 } else {
michael@0 1667 aSelectorList.Clear();
michael@0 1668 }
michael@0 1669
michael@0 1670 return success;
michael@0 1671 }
michael@0 1672
michael@0 1673 bool
michael@0 1674 CSSParserImpl::EvaluateSupportsDeclaration(const nsAString& aProperty,
michael@0 1675 const nsAString& aValue,
michael@0 1676 nsIURI* aDocURL,
michael@0 1677 nsIURI* aBaseURL,
michael@0 1678 nsIPrincipal* aDocPrincipal)
michael@0 1679 {
michael@0 1680 nsCSSProperty propID = LookupEnabledProperty(aProperty);
michael@0 1681 if (propID == eCSSProperty_UNKNOWN) {
michael@0 1682 return false;
michael@0 1683 }
michael@0 1684
michael@0 1685 nsCSSScanner scanner(aValue, 0);
michael@0 1686 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
michael@0 1687 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
michael@0 1688 nsAutoSuppressErrors suppressErrors(this);
michael@0 1689
michael@0 1690 bool parsedOK;
michael@0 1691
michael@0 1692 if (propID == eCSSPropertyExtra_variable) {
michael@0 1693 MOZ_ASSERT(Substring(aProperty, 0,
michael@0 1694 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
michael@0 1695 const nsDependentSubstring varName =
michael@0 1696 Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH); // remove '--'
michael@0 1697 CSSVariableDeclarations::Type variableType;
michael@0 1698 nsString variableValue;
michael@0 1699 parsedOK = ParseVariableDeclaration(&variableType, variableValue) &&
michael@0 1700 !GetToken(true);
michael@0 1701 } else {
michael@0 1702 parsedOK = ParseProperty(propID) && !GetToken(true);
michael@0 1703
michael@0 1704 mTempData.ClearProperty(propID);
michael@0 1705 mTempData.AssertInitialState();
michael@0 1706 }
michael@0 1707
michael@0 1708 CLEAR_ERROR();
michael@0 1709 ReleaseScanner();
michael@0 1710
michael@0 1711 return parsedOK;
michael@0 1712 }
michael@0 1713
michael@0 1714 bool
michael@0 1715 CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration,
michael@0 1716 nsIURI* aDocURL,
michael@0 1717 nsIURI* aBaseURL,
michael@0 1718 nsIPrincipal* aDocPrincipal)
michael@0 1719 {
michael@0 1720 nsCSSScanner scanner(aDeclaration, 0);
michael@0 1721 css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL);
michael@0 1722 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
michael@0 1723 nsAutoSuppressErrors suppressErrors(this);
michael@0 1724
michael@0 1725 bool conditionMet;
michael@0 1726 bool parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true);
michael@0 1727
michael@0 1728 CLEAR_ERROR();
michael@0 1729 ReleaseScanner();
michael@0 1730
michael@0 1731 return parsedOK && conditionMet;
michael@0 1732 }
michael@0 1733
michael@0 1734 bool
michael@0 1735 CSSParserImpl::EnumerateVariableReferences(const nsAString& aPropertyValue,
michael@0 1736 VariableEnumFunc aFunc,
michael@0 1737 void* aData)
michael@0 1738 {
michael@0 1739 nsCSSScanner scanner(aPropertyValue, 0);
michael@0 1740 css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
michael@0 1741 InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
michael@0 1742 nsAutoSuppressErrors suppressErrors(this);
michael@0 1743
michael@0 1744 CSSVariableDeclarations::Type type;
michael@0 1745 bool dropBackslash;
michael@0 1746 nsString impliedCharacters;
michael@0 1747 bool result = ParseValueWithVariables(&type, &dropBackslash,
michael@0 1748 impliedCharacters, aFunc, aData) &&
michael@0 1749 !GetToken(true);
michael@0 1750
michael@0 1751 ReleaseScanner();
michael@0 1752
michael@0 1753 return result;
michael@0 1754 }
michael@0 1755
michael@0 1756 static bool
michael@0 1757 SeparatorRequiredBetweenTokens(nsCSSTokenSerializationType aToken1,
michael@0 1758 nsCSSTokenSerializationType aToken2)
michael@0 1759 {
michael@0 1760 // The two lines marked with (*) do not correspond to entries in
michael@0 1761 // the table in the css-syntax spec but which we need to handle,
michael@0 1762 // as we treat them as whole tokens.
michael@0 1763 switch (aToken1) {
michael@0 1764 case eCSSTokenSerialization_Ident:
michael@0 1765 return aToken2 == eCSSTokenSerialization_Ident ||
michael@0 1766 aToken2 == eCSSTokenSerialization_Function ||
michael@0 1767 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
michael@0 1768 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
michael@0 1769 aToken2 == eCSSTokenSerialization_Number ||
michael@0 1770 aToken2 == eCSSTokenSerialization_Percentage ||
michael@0 1771 aToken2 == eCSSTokenSerialization_Dimension ||
michael@0 1772 aToken2 == eCSSTokenSerialization_URange ||
michael@0 1773 aToken2 == eCSSTokenSerialization_CDC ||
michael@0 1774 aToken2 == eCSSTokenSerialization_Symbol_OpenParen;
michael@0 1775 case eCSSTokenSerialization_AtKeyword_or_Hash:
michael@0 1776 case eCSSTokenSerialization_Dimension:
michael@0 1777 return aToken2 == eCSSTokenSerialization_Ident ||
michael@0 1778 aToken2 == eCSSTokenSerialization_Function ||
michael@0 1779 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
michael@0 1780 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
michael@0 1781 aToken2 == eCSSTokenSerialization_Number ||
michael@0 1782 aToken2 == eCSSTokenSerialization_Percentage ||
michael@0 1783 aToken2 == eCSSTokenSerialization_Dimension ||
michael@0 1784 aToken2 == eCSSTokenSerialization_URange ||
michael@0 1785 aToken2 == eCSSTokenSerialization_CDC;
michael@0 1786 case eCSSTokenSerialization_Symbol_Hash:
michael@0 1787 return aToken2 == eCSSTokenSerialization_Ident ||
michael@0 1788 aToken2 == eCSSTokenSerialization_Function ||
michael@0 1789 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
michael@0 1790 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
michael@0 1791 aToken2 == eCSSTokenSerialization_Number ||
michael@0 1792 aToken2 == eCSSTokenSerialization_Percentage ||
michael@0 1793 aToken2 == eCSSTokenSerialization_Dimension ||
michael@0 1794 aToken2 == eCSSTokenSerialization_URange;
michael@0 1795 case eCSSTokenSerialization_Symbol_Minus:
michael@0 1796 case eCSSTokenSerialization_Number:
michael@0 1797 return aToken2 == eCSSTokenSerialization_Ident ||
michael@0 1798 aToken2 == eCSSTokenSerialization_Function ||
michael@0 1799 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
michael@0 1800 aToken2 == eCSSTokenSerialization_Number ||
michael@0 1801 aToken2 == eCSSTokenSerialization_Percentage ||
michael@0 1802 aToken2 == eCSSTokenSerialization_Dimension ||
michael@0 1803 aToken2 == eCSSTokenSerialization_URange;
michael@0 1804 case eCSSTokenSerialization_Symbol_At:
michael@0 1805 return aToken2 == eCSSTokenSerialization_Ident ||
michael@0 1806 aToken2 == eCSSTokenSerialization_Function ||
michael@0 1807 aToken2 == eCSSTokenSerialization_URL_or_BadURL ||
michael@0 1808 aToken2 == eCSSTokenSerialization_Symbol_Minus ||
michael@0 1809 aToken2 == eCSSTokenSerialization_URange;
michael@0 1810 case eCSSTokenSerialization_URange:
michael@0 1811 return aToken2 == eCSSTokenSerialization_Ident ||
michael@0 1812 aToken2 == eCSSTokenSerialization_Function ||
michael@0 1813 aToken2 == eCSSTokenSerialization_Number ||
michael@0 1814 aToken2 == eCSSTokenSerialization_Percentage ||
michael@0 1815 aToken2 == eCSSTokenSerialization_Dimension ||
michael@0 1816 aToken2 == eCSSTokenSerialization_Symbol_Question;
michael@0 1817 case eCSSTokenSerialization_Symbol_Dot_or_Plus:
michael@0 1818 return aToken2 == eCSSTokenSerialization_Number ||
michael@0 1819 aToken2 == eCSSTokenSerialization_Percentage ||
michael@0 1820 aToken2 == eCSSTokenSerialization_Dimension;
michael@0 1821 case eCSSTokenSerialization_Symbol_Assorted:
michael@0 1822 case eCSSTokenSerialization_Symbol_Asterisk:
michael@0 1823 return aToken2 == eCSSTokenSerialization_Symbol_Equals;
michael@0 1824 case eCSSTokenSerialization_Symbol_Bar:
michael@0 1825 return aToken2 == eCSSTokenSerialization_Symbol_Equals ||
michael@0 1826 aToken2 == eCSSTokenSerialization_Symbol_Bar ||
michael@0 1827 aToken2 == eCSSTokenSerialization_DashMatch; // (*)
michael@0 1828 case eCSSTokenSerialization_Symbol_Slash:
michael@0 1829 return aToken2 == eCSSTokenSerialization_Symbol_Asterisk ||
michael@0 1830 aToken2 == eCSSTokenSerialization_ContainsMatch; // (*)
michael@0 1831 default:
michael@0 1832 MOZ_ASSERT(aToken1 == eCSSTokenSerialization_Nothing ||
michael@0 1833 aToken1 == eCSSTokenSerialization_Whitespace ||
michael@0 1834 aToken1 == eCSSTokenSerialization_Percentage ||
michael@0 1835 aToken1 == eCSSTokenSerialization_URL_or_BadURL ||
michael@0 1836 aToken1 == eCSSTokenSerialization_Function ||
michael@0 1837 aToken1 == eCSSTokenSerialization_CDC ||
michael@0 1838 aToken1 == eCSSTokenSerialization_Symbol_OpenParen ||
michael@0 1839 aToken1 == eCSSTokenSerialization_Symbol_Question ||
michael@0 1840 aToken1 == eCSSTokenSerialization_Symbol_Assorted ||
michael@0 1841 aToken1 == eCSSTokenSerialization_Symbol_Asterisk ||
michael@0 1842 aToken1 == eCSSTokenSerialization_Symbol_Equals ||
michael@0 1843 aToken1 == eCSSTokenSerialization_Symbol_Bar ||
michael@0 1844 aToken1 == eCSSTokenSerialization_Symbol_Slash ||
michael@0 1845 aToken1 == eCSSTokenSerialization_Other ||
michael@0 1846 "unexpected nsCSSTokenSerializationType value");
michael@0 1847 return false;
michael@0 1848 }
michael@0 1849 }
michael@0 1850
michael@0 1851 /**
michael@0 1852 * Appends aValue to aResult, possibly inserting an empty CSS
michael@0 1853 * comment between the two to ensure that tokens from both strings
michael@0 1854 * remain separated.
michael@0 1855 */
michael@0 1856 static void
michael@0 1857 AppendTokens(nsAString& aResult,
michael@0 1858 nsCSSTokenSerializationType& aResultFirstToken,
michael@0 1859 nsCSSTokenSerializationType& aResultLastToken,
michael@0 1860 nsCSSTokenSerializationType aValueFirstToken,
michael@0 1861 nsCSSTokenSerializationType aValueLastToken,
michael@0 1862 const nsAString& aValue)
michael@0 1863 {
michael@0 1864 if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
michael@0 1865 aResult.AppendLiteral("/**/");
michael@0 1866 }
michael@0 1867 aResult.Append(aValue);
michael@0 1868 if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
michael@0 1869 aResultFirstToken = aValueFirstToken;
michael@0 1870 }
michael@0 1871 if (aValueLastToken != eCSSTokenSerialization_Nothing) {
michael@0 1872 aResultLastToken = aValueLastToken;
michael@0 1873 }
michael@0 1874 }
michael@0 1875
michael@0 1876 /**
michael@0 1877 * Stops the given scanner recording, and appends the recorded result
michael@0 1878 * to aResult, possibly inserting an empty CSS comment between the two to
michael@0 1879 * ensure that tokens from both strings remain separated.
michael@0 1880 */
michael@0 1881 static void
michael@0 1882 StopRecordingAndAppendTokens(nsString& aResult,
michael@0 1883 nsCSSTokenSerializationType& aResultFirstToken,
michael@0 1884 nsCSSTokenSerializationType& aResultLastToken,
michael@0 1885 nsCSSTokenSerializationType aValueFirstToken,
michael@0 1886 nsCSSTokenSerializationType aValueLastToken,
michael@0 1887 nsCSSScanner* aScanner)
michael@0 1888 {
michael@0 1889 if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) {
michael@0 1890 aResult.AppendLiteral("/**/");
michael@0 1891 }
michael@0 1892 aScanner->StopRecording(aResult);
michael@0 1893 if (aResultFirstToken == eCSSTokenSerialization_Nothing) {
michael@0 1894 aResultFirstToken = aValueFirstToken;
michael@0 1895 }
michael@0 1896 if (aValueLastToken != eCSSTokenSerialization_Nothing) {
michael@0 1897 aResultLastToken = aValueLastToken;
michael@0 1898 }
michael@0 1899 }
michael@0 1900
michael@0 1901 bool
michael@0 1902 CSSParserImpl::ResolveValueWithVariableReferencesRec(
michael@0 1903 nsString& aResult,
michael@0 1904 nsCSSTokenSerializationType& aResultFirstToken,
michael@0 1905 nsCSSTokenSerializationType& aResultLastToken,
michael@0 1906 const CSSVariableValues* aVariables)
michael@0 1907 {
michael@0 1908 // This function assumes we are already recording, and will leave the scanner
michael@0 1909 // recording when it returns.
michael@0 1910 MOZ_ASSERT(mScanner->IsRecording());
michael@0 1911 MOZ_ASSERT(aResult.IsEmpty());
michael@0 1912
michael@0 1913 // Stack of closing characters for currently open constructs.
michael@0 1914 nsAutoTArray<char16_t, 16> stack;
michael@0 1915
michael@0 1916 // The resolved value for this ResolveValueWithVariableReferencesRec call.
michael@0 1917 nsString value;
michael@0 1918
michael@0 1919 // The length of the scanner's recording before the currently parsed token.
michael@0 1920 // This is used so that when we encounter a "var(" token, we can strip
michael@0 1921 // it off the end of the recording, regardless of how long the token was.
michael@0 1922 // (With escapes, it could be longer than four characters.)
michael@0 1923 uint32_t lengthBeforeVar = 0;
michael@0 1924
michael@0 1925 // Tracking the type of token that appears at the start and end of |value|
michael@0 1926 // and that appears at the start and end of the scanner recording. These are
michael@0 1927 // used to determine whether we need to insert "/**/" when pasting token
michael@0 1928 // streams together.
michael@0 1929 nsCSSTokenSerializationType valueFirstToken = eCSSTokenSerialization_Nothing,
michael@0 1930 valueLastToken = eCSSTokenSerialization_Nothing,
michael@0 1931 recFirstToken = eCSSTokenSerialization_Nothing,
michael@0 1932 recLastToken = eCSSTokenSerialization_Nothing;
michael@0 1933
michael@0 1934 #define UPDATE_RECORDING_TOKENS(type) \
michael@0 1935 if (recFirstToken == eCSSTokenSerialization_Nothing) { \
michael@0 1936 recFirstToken = type; \
michael@0 1937 } \
michael@0 1938 recLastToken = type;
michael@0 1939
michael@0 1940 while (GetToken(false)) {
michael@0 1941 switch (mToken.mType) {
michael@0 1942 case eCSSToken_Symbol: {
michael@0 1943 nsCSSTokenSerializationType type = eCSSTokenSerialization_Other;
michael@0 1944 if (mToken.mSymbol == '(') {
michael@0 1945 stack.AppendElement(')');
michael@0 1946 type = eCSSTokenSerialization_Symbol_OpenParen;
michael@0 1947 } else if (mToken.mSymbol == '[') {
michael@0 1948 stack.AppendElement(']');
michael@0 1949 } else if (mToken.mSymbol == '{') {
michael@0 1950 stack.AppendElement('}');
michael@0 1951 } else if (mToken.mSymbol == ';') {
michael@0 1952 if (stack.IsEmpty()) {
michael@0 1953 // A ';' that is at the top level of the value or at the top level
michael@0 1954 // of a variable reference's fallback is invalid.
michael@0 1955 return false;
michael@0 1956 }
michael@0 1957 } else if (mToken.mSymbol == '!') {
michael@0 1958 if (stack.IsEmpty()) {
michael@0 1959 // An '!' that is at the top level of the value or at the top level
michael@0 1960 // of a variable reference's fallback is invalid.
michael@0 1961 return false;
michael@0 1962 }
michael@0 1963 } else if (mToken.mSymbol == ')' &&
michael@0 1964 stack.IsEmpty()) {
michael@0 1965 // We're closing a "var(".
michael@0 1966 nsString finalTokens;
michael@0 1967 mScanner->StopRecording(finalTokens);
michael@0 1968 MOZ_ASSERT(finalTokens[finalTokens.Length() - 1] == ')');
michael@0 1969 finalTokens.Truncate(finalTokens.Length() - 1);
michael@0 1970 aResult.Append(value);
michael@0 1971
michael@0 1972 AppendTokens(aResult, valueFirstToken, valueLastToken,
michael@0 1973 recFirstToken, recLastToken, finalTokens);
michael@0 1974
michael@0 1975 mScanner->StartRecording();
michael@0 1976 UngetToken();
michael@0 1977 aResultFirstToken = valueFirstToken;
michael@0 1978 aResultLastToken = valueLastToken;
michael@0 1979 return true;
michael@0 1980 } else if (mToken.mSymbol == ')' ||
michael@0 1981 mToken.mSymbol == ']' ||
michael@0 1982 mToken.mSymbol == '}') {
michael@0 1983 if (stack.IsEmpty() ||
michael@0 1984 stack.LastElement() != mToken.mSymbol) {
michael@0 1985 // A mismatched closing bracket is invalid.
michael@0 1986 return false;
michael@0 1987 }
michael@0 1988 stack.TruncateLength(stack.Length() - 1);
michael@0 1989 } else if (mToken.mSymbol == '#') {
michael@0 1990 type = eCSSTokenSerialization_Symbol_Hash;
michael@0 1991 } else if (mToken.mSymbol == '@') {
michael@0 1992 type = eCSSTokenSerialization_Symbol_At;
michael@0 1993 } else if (mToken.mSymbol == '.' ||
michael@0 1994 mToken.mSymbol == '+') {
michael@0 1995 type = eCSSTokenSerialization_Symbol_Dot_or_Plus;
michael@0 1996 } else if (mToken.mSymbol == '-') {
michael@0 1997 type = eCSSTokenSerialization_Symbol_Minus;
michael@0 1998 } else if (mToken.mSymbol == '?') {
michael@0 1999 type = eCSSTokenSerialization_Symbol_Question;
michael@0 2000 } else if (mToken.mSymbol == '$' ||
michael@0 2001 mToken.mSymbol == '^' ||
michael@0 2002 mToken.mSymbol == '~') {
michael@0 2003 type = eCSSTokenSerialization_Symbol_Assorted;
michael@0 2004 } else if (mToken.mSymbol == '=') {
michael@0 2005 type = eCSSTokenSerialization_Symbol_Equals;
michael@0 2006 } else if (mToken.mSymbol == '|') {
michael@0 2007 type = eCSSTokenSerialization_Symbol_Bar;
michael@0 2008 } else if (mToken.mSymbol == '/') {
michael@0 2009 type = eCSSTokenSerialization_Symbol_Slash;
michael@0 2010 } else if (mToken.mSymbol == '*') {
michael@0 2011 type = eCSSTokenSerialization_Symbol_Asterisk;
michael@0 2012 }
michael@0 2013 UPDATE_RECORDING_TOKENS(type);
michael@0 2014 break;
michael@0 2015 }
michael@0 2016
michael@0 2017 case eCSSToken_Function:
michael@0 2018 if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
michael@0 2019 // Save the tokens before the "var(" to our resolved value.
michael@0 2020 nsString recording;
michael@0 2021 mScanner->StopRecording(recording);
michael@0 2022 recording.Truncate(lengthBeforeVar);
michael@0 2023 AppendTokens(value, valueFirstToken, valueLastToken,
michael@0 2024 recFirstToken, recLastToken, recording);
michael@0 2025 recFirstToken = eCSSTokenSerialization_Nothing;
michael@0 2026 recLastToken = eCSSTokenSerialization_Nothing;
michael@0 2027
michael@0 2028 if (!GetToken(true) ||
michael@0 2029 mToken.mType != eCSSToken_Ident ||
michael@0 2030 !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
michael@0 2031 // "var(" must be followed by an identifier, and it must be a
michael@0 2032 // custom property name.
michael@0 2033 return false;
michael@0 2034 }
michael@0 2035
michael@0 2036 // Turn the custom property name into a variable name by removing the
michael@0 2037 // '--' prefix.
michael@0 2038 MOZ_ASSERT(Substring(mToken.mIdent, 0,
michael@0 2039 CSS_CUSTOM_NAME_PREFIX_LENGTH).
michael@0 2040 EqualsLiteral("--"));
michael@0 2041 nsDependentString variableName(mToken.mIdent,
michael@0 2042 CSS_CUSTOM_NAME_PREFIX_LENGTH);
michael@0 2043
michael@0 2044 // Get the value of the identified variable. Note that we
michael@0 2045 // check if the variable value is the empty string, as that means
michael@0 2046 // that the variable was invalid at computed value time due to
michael@0 2047 // unresolveable variable references or cycles.
michael@0 2048 nsString variableValue;
michael@0 2049 nsCSSTokenSerializationType varFirstToken, varLastToken;
michael@0 2050 bool valid = aVariables->Get(variableName, variableValue,
michael@0 2051 varFirstToken, varLastToken) &&
michael@0 2052 !variableValue.IsEmpty();
michael@0 2053
michael@0 2054 if (!GetToken(true) ||
michael@0 2055 mToken.IsSymbol(')')) {
michael@0 2056 mScanner->StartRecording();
michael@0 2057 if (!valid) {
michael@0 2058 // Invalid variable with no fallback.
michael@0 2059 return false;
michael@0 2060 }
michael@0 2061 // Valid variable with no fallback.
michael@0 2062 AppendTokens(value, valueFirstToken, valueLastToken,
michael@0 2063 varFirstToken, varLastToken, variableValue);
michael@0 2064 } else if (mToken.IsSymbol(',')) {
michael@0 2065 mScanner->StartRecording();
michael@0 2066 if (!GetToken(false) ||
michael@0 2067 mToken.IsSymbol(')')) {
michael@0 2068 // Comma must be followed by at least one fallback token.
michael@0 2069 return false;
michael@0 2070 }
michael@0 2071 UngetToken();
michael@0 2072 if (valid) {
michael@0 2073 // Valid variable with ignored fallback.
michael@0 2074 mScanner->StopRecording();
michael@0 2075 AppendTokens(value, valueFirstToken, valueLastToken,
michael@0 2076 varFirstToken, varLastToken, variableValue);
michael@0 2077 bool ok = SkipBalancedContentUntil(')');
michael@0 2078 mScanner->StartRecording();
michael@0 2079 if (!ok) {
michael@0 2080 return false;
michael@0 2081 }
michael@0 2082 } else {
michael@0 2083 nsString fallback;
michael@0 2084 if (!ResolveValueWithVariableReferencesRec(fallback,
michael@0 2085 varFirstToken,
michael@0 2086 varLastToken,
michael@0 2087 aVariables)) {
michael@0 2088 // Fallback value had invalid tokens or an invalid variable reference
michael@0 2089 // that itself had no fallback.
michael@0 2090 return false;
michael@0 2091 }
michael@0 2092 AppendTokens(value, valueFirstToken, valueLastToken,
michael@0 2093 varFirstToken, varLastToken, fallback);
michael@0 2094 // Now we're either at the pushed back ')' that finished the
michael@0 2095 // fallback or at EOF.
michael@0 2096 DebugOnly<bool> gotToken = GetToken(false);
michael@0 2097 MOZ_ASSERT(!gotToken || mToken.IsSymbol(')'));
michael@0 2098 }
michael@0 2099 } else {
michael@0 2100 // Expected ',' or ')' after the variable name.
michael@0 2101 mScanner->StartRecording();
michael@0 2102 return false;
michael@0 2103 }
michael@0 2104 } else {
michael@0 2105 stack.AppendElement(')');
michael@0 2106 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function);
michael@0 2107 }
michael@0 2108 break;
michael@0 2109
michael@0 2110 case eCSSToken_Bad_String:
michael@0 2111 case eCSSToken_Bad_URL:
michael@0 2112 return false;
michael@0 2113
michael@0 2114 case eCSSToken_Whitespace:
michael@0 2115 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace);
michael@0 2116 break;
michael@0 2117
michael@0 2118 case eCSSToken_AtKeyword:
michael@0 2119 case eCSSToken_Hash:
michael@0 2120 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash);
michael@0 2121 break;
michael@0 2122
michael@0 2123 case eCSSToken_Number:
michael@0 2124 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number);
michael@0 2125 break;
michael@0 2126
michael@0 2127 case eCSSToken_Dimension:
michael@0 2128 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension);
michael@0 2129 break;
michael@0 2130
michael@0 2131 case eCSSToken_Ident:
michael@0 2132 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident);
michael@0 2133 break;
michael@0 2134
michael@0 2135 case eCSSToken_Percentage:
michael@0 2136 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage);
michael@0 2137 break;
michael@0 2138
michael@0 2139 case eCSSToken_URange:
michael@0 2140 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange);
michael@0 2141 break;
michael@0 2142
michael@0 2143 case eCSSToken_URL:
michael@0 2144 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL);
michael@0 2145 break;
michael@0 2146
michael@0 2147 case eCSSToken_HTMLComment:
michael@0 2148 if (mToken.mIdent[0] == '-') {
michael@0 2149 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC);
michael@0 2150 } else {
michael@0 2151 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
michael@0 2152 }
michael@0 2153 break;
michael@0 2154
michael@0 2155 case eCSSToken_Dashmatch:
michael@0 2156 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch);
michael@0 2157 break;
michael@0 2158
michael@0 2159 case eCSSToken_Containsmatch:
michael@0 2160 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch);
michael@0 2161 break;
michael@0 2162
michael@0 2163 default:
michael@0 2164 NS_NOTREACHED("unexpected token type");
michael@0 2165 // fall through
michael@0 2166 case eCSSToken_ID:
michael@0 2167 case eCSSToken_String:
michael@0 2168 case eCSSToken_Includes:
michael@0 2169 case eCSSToken_Beginsmatch:
michael@0 2170 case eCSSToken_Endsmatch:
michael@0 2171 UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other);
michael@0 2172 break;
michael@0 2173 }
michael@0 2174
michael@0 2175 lengthBeforeVar = mScanner->RecordingLength();
michael@0 2176 }
michael@0 2177
michael@0 2178 #undef UPDATE_RECORDING_TOKENS
michael@0 2179
michael@0 2180 aResult.Append(value);
michael@0 2181 StopRecordingAndAppendTokens(aResult, valueFirstToken, valueLastToken,
michael@0 2182 recFirstToken, recLastToken, mScanner);
michael@0 2183
michael@0 2184 // Append any implicitly closed brackets.
michael@0 2185 if (!stack.IsEmpty()) {
michael@0 2186 do {
michael@0 2187 aResult.Append(stack.LastElement());
michael@0 2188 stack.TruncateLength(stack.Length() - 1);
michael@0 2189 } while (!stack.IsEmpty());
michael@0 2190 valueLastToken = eCSSTokenSerialization_Other;
michael@0 2191 }
michael@0 2192
michael@0 2193 mScanner->StartRecording();
michael@0 2194 aResultFirstToken = valueFirstToken;
michael@0 2195 aResultLastToken = valueLastToken;
michael@0 2196 return true;
michael@0 2197 }
michael@0 2198
michael@0 2199 bool
michael@0 2200 CSSParserImpl::ResolveValueWithVariableReferences(
michael@0 2201 const CSSVariableValues* aVariables,
michael@0 2202 nsString& aResult,
michael@0 2203 nsCSSTokenSerializationType& aFirstToken,
michael@0 2204 nsCSSTokenSerializationType& aLastToken)
michael@0 2205 {
michael@0 2206 aResult.Truncate(0);
michael@0 2207
michael@0 2208 // Start recording before we read the first token.
michael@0 2209 mScanner->StartRecording();
michael@0 2210
michael@0 2211 if (!GetToken(false)) {
michael@0 2212 // Value was empty since we reached EOF.
michael@0 2213 mScanner->StopRecording();
michael@0 2214 return false;
michael@0 2215 }
michael@0 2216
michael@0 2217 UngetToken();
michael@0 2218
michael@0 2219 nsString value;
michael@0 2220 nsCSSTokenSerializationType firstToken, lastToken;
michael@0 2221 bool ok = ResolveValueWithVariableReferencesRec(value, firstToken, lastToken, aVariables) &&
michael@0 2222 !GetToken(true);
michael@0 2223
michael@0 2224 mScanner->StopRecording();
michael@0 2225
michael@0 2226 if (ok) {
michael@0 2227 aResult = value;
michael@0 2228 aFirstToken = firstToken;
michael@0 2229 aLastToken = lastToken;
michael@0 2230 }
michael@0 2231 return ok;
michael@0 2232 }
michael@0 2233
michael@0 2234 bool
michael@0 2235 CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue,
michael@0 2236 const CSSVariableValues* aVariables,
michael@0 2237 nsString& aResult,
michael@0 2238 nsCSSTokenSerializationType& aFirstToken,
michael@0 2239 nsCSSTokenSerializationType& aLastToken)
michael@0 2240 {
michael@0 2241 nsCSSScanner scanner(aPropertyValue, 0);
michael@0 2242
michael@0 2243 // At this point, we know that aPropertyValue is syntactically correct
michael@0 2244 // for a token stream that has variable references. We also won't be
michael@0 2245 // interpreting any of the stream as we parse it, apart from expanding
michael@0 2246 // var() references, so we don't need a base URL etc. or any useful
michael@0 2247 // error reporting.
michael@0 2248 css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr);
michael@0 2249 InitScanner(scanner, reporter, nullptr, nullptr, nullptr);
michael@0 2250
michael@0 2251 bool valid = ResolveValueWithVariableReferences(aVariables, aResult,
michael@0 2252 aFirstToken, aLastToken);
michael@0 2253
michael@0 2254 ReleaseScanner();
michael@0 2255 return valid;
michael@0 2256 }
michael@0 2257
michael@0 2258 void
michael@0 2259 CSSParserImpl::ParsePropertyWithVariableReferences(
michael@0 2260 nsCSSProperty aPropertyID,
michael@0 2261 nsCSSProperty aShorthandPropertyID,
michael@0 2262 const nsAString& aValue,
michael@0 2263 const CSSVariableValues* aVariables,
michael@0 2264 nsRuleData* aRuleData,
michael@0 2265 nsIURI* aDocURL,
michael@0 2266 nsIURI* aBaseURL,
michael@0 2267 nsIPrincipal* aDocPrincipal,
michael@0 2268 nsCSSStyleSheet* aSheet,
michael@0 2269 uint32_t aLineNumber,
michael@0 2270 uint32_t aLineOffset)
michael@0 2271 {
michael@0 2272 mTempData.AssertInitialState();
michael@0 2273
michael@0 2274 bool valid;
michael@0 2275 nsString expandedValue;
michael@0 2276
michael@0 2277 // Resolve any variable references in the property value.
michael@0 2278 {
michael@0 2279 nsCSSScanner scanner(aValue, 0);
michael@0 2280 css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
michael@0 2281 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
michael@0 2282
michael@0 2283 nsCSSTokenSerializationType firstToken, lastToken;
michael@0 2284 valid = ResolveValueWithVariableReferences(aVariables, expandedValue,
michael@0 2285 firstToken, lastToken);
michael@0 2286 if (!valid) {
michael@0 2287 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropertyID));
michael@0 2288 REPORT_UNEXPECTED(PEInvalidVariableReference);
michael@0 2289 REPORT_UNEXPECTED_P(PEValueParsingError, propName);
michael@0 2290 if (nsCSSProps::IsInherited(aPropertyID)) {
michael@0 2291 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
michael@0 2292 } else {
michael@0 2293 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
michael@0 2294 }
michael@0 2295 OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
michael@0 2296 }
michael@0 2297 ReleaseScanner();
michael@0 2298 }
michael@0 2299
michael@0 2300 nsCSSProperty propertyToParse =
michael@0 2301 aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID :
michael@0 2302 aPropertyID;
michael@0 2303
michael@0 2304 // Parse the property with that resolved value.
michael@0 2305 if (valid) {
michael@0 2306 nsCSSScanner scanner(expandedValue, 0);
michael@0 2307 css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL);
michael@0 2308 InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal);
michael@0 2309 valid = ParseProperty(propertyToParse);
michael@0 2310 if (valid && GetToken(true)) {
michael@0 2311 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
michael@0 2312 valid = false;
michael@0 2313 }
michael@0 2314 if (!valid) {
michael@0 2315 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(
michael@0 2316 propertyToParse));
michael@0 2317 REPORT_UNEXPECTED_P(PEValueWithVariablesParsingError, propName);
michael@0 2318 if (nsCSSProps::IsInherited(aPropertyID)) {
michael@0 2319 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit);
michael@0 2320 } else {
michael@0 2321 REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial);
michael@0 2322 }
michael@0 2323 OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset);
michael@0 2324 }
michael@0 2325 ReleaseScanner();
michael@0 2326 }
michael@0 2327
michael@0 2328 // If the property could not be parsed with the resolved value, then we
michael@0 2329 // treat it as if the value were 'initial' or 'inherit', depending on whether
michael@0 2330 // the property is an inherited property.
michael@0 2331 if (!valid) {
michael@0 2332 nsCSSValue defaultValue;
michael@0 2333 if (nsCSSProps::IsInherited(aPropertyID)) {
michael@0 2334 defaultValue.SetInheritValue();
michael@0 2335 } else {
michael@0 2336 defaultValue.SetInitialValue();
michael@0 2337 }
michael@0 2338 mTempData.AddLonghandProperty(aPropertyID, defaultValue);
michael@0 2339 }
michael@0 2340
michael@0 2341 // Copy the property value into the rule data.
michael@0 2342 mTempData.MapRuleInfoInto(aPropertyID, aRuleData);
michael@0 2343
michael@0 2344 mTempData.ClearProperty(propertyToParse);
michael@0 2345 mTempData.AssertInitialState();
michael@0 2346 }
michael@0 2347
michael@0 2348 //----------------------------------------------------------------------
michael@0 2349
michael@0 2350 bool
michael@0 2351 CSSParserImpl::GetToken(bool aSkipWS)
michael@0 2352 {
michael@0 2353 if (mHavePushBack) {
michael@0 2354 mHavePushBack = false;
michael@0 2355 if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) {
michael@0 2356 return true;
michael@0 2357 }
michael@0 2358 }
michael@0 2359 return mScanner->Next(mToken, aSkipWS);
michael@0 2360 }
michael@0 2361
michael@0 2362 void
michael@0 2363 CSSParserImpl::UngetToken()
michael@0 2364 {
michael@0 2365 NS_PRECONDITION(!mHavePushBack, "double pushback");
michael@0 2366 mHavePushBack = true;
michael@0 2367 }
michael@0 2368
michael@0 2369 bool
michael@0 2370 CSSParserImpl::GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum)
michael@0 2371 {
michael@0 2372 // Peek at next token so that mScanner updates line and column vals
michael@0 2373 if (!GetToken(aSkipWS)) {
michael@0 2374 return false;
michael@0 2375 }
michael@0 2376 UngetToken();
michael@0 2377 // The scanner uses one-indexing for line numbers but zero-indexing
michael@0 2378 // for column numbers.
michael@0 2379 *linenum = mScanner->GetLineNumber();
michael@0 2380 *colnum = 1 + mScanner->GetColumnNumber();
michael@0 2381 return true;
michael@0 2382 }
michael@0 2383
michael@0 2384 bool
michael@0 2385 CSSParserImpl::ExpectSymbol(char16_t aSymbol,
michael@0 2386 bool aSkipWS)
michael@0 2387 {
michael@0 2388 if (!GetToken(aSkipWS)) {
michael@0 2389 // CSS2.1 specifies that all "open constructs" are to be closed at
michael@0 2390 // EOF. It simplifies higher layers if we claim to have found an
michael@0 2391 // ), ], }, or ; if we encounter EOF while looking for one of them.
michael@0 2392 // Do still issue a diagnostic, to aid debugging.
michael@0 2393 if (aSymbol == ')' || aSymbol == ']' ||
michael@0 2394 aSymbol == '}' || aSymbol == ';') {
michael@0 2395 REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
michael@0 2396 return true;
michael@0 2397 }
michael@0 2398 else
michael@0 2399 return false;
michael@0 2400 }
michael@0 2401 if (mToken.IsSymbol(aSymbol)) {
michael@0 2402 return true;
michael@0 2403 }
michael@0 2404 UngetToken();
michael@0 2405 return false;
michael@0 2406 }
michael@0 2407
michael@0 2408 // Checks to see if we're at the end of a property. If an error occurs during
michael@0 2409 // the check, does not signal a parse error.
michael@0 2410 bool
michael@0 2411 CSSParserImpl::CheckEndProperty()
michael@0 2412 {
michael@0 2413 if (!GetToken(true)) {
michael@0 2414 return true; // properties may end with eof
michael@0 2415 }
michael@0 2416 if ((eCSSToken_Symbol == mToken.mType) &&
michael@0 2417 ((';' == mToken.mSymbol) ||
michael@0 2418 ('!' == mToken.mSymbol) ||
michael@0 2419 ('}' == mToken.mSymbol) ||
michael@0 2420 (')' == mToken.mSymbol))) {
michael@0 2421 // XXX need to verify that ! is only followed by "important [;|}]
michael@0 2422 // XXX this requires a multi-token pushback buffer
michael@0 2423 UngetToken();
michael@0 2424 return true;
michael@0 2425 }
michael@0 2426 UngetToken();
michael@0 2427 return false;
michael@0 2428 }
michael@0 2429
michael@0 2430 // Checks if we're at the end of a property, raising an error if we're not.
michael@0 2431 bool
michael@0 2432 CSSParserImpl::ExpectEndProperty()
michael@0 2433 {
michael@0 2434 if (CheckEndProperty())
michael@0 2435 return true;
michael@0 2436
michael@0 2437 // If we're here, we read something incorrect, so we should report it.
michael@0 2438 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
michael@0 2439 return false;
michael@0 2440 }
michael@0 2441
michael@0 2442 // Parses the priority suffix on a property, which at present may be
michael@0 2443 // either '!important' or nothing.
michael@0 2444 CSSParserImpl::PriorityParsingStatus
michael@0 2445 CSSParserImpl::ParsePriority()
michael@0 2446 {
michael@0 2447 if (!GetToken(true)) {
michael@0 2448 return ePriority_None; // properties may end with EOF
michael@0 2449 }
michael@0 2450 if (!mToken.IsSymbol('!')) {
michael@0 2451 UngetToken();
michael@0 2452 return ePriority_None; // dunno what it is, but it's not a priority
michael@0 2453 }
michael@0 2454
michael@0 2455 if (!GetToken(true)) {
michael@0 2456 // EOF is not ok after !
michael@0 2457 REPORT_UNEXPECTED_EOF(PEImportantEOF);
michael@0 2458 return ePriority_Error;
michael@0 2459 }
michael@0 2460
michael@0 2461 if (mToken.mType != eCSSToken_Ident ||
michael@0 2462 !mToken.mIdent.LowerCaseEqualsLiteral("important")) {
michael@0 2463 REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
michael@0 2464 UngetToken();
michael@0 2465 return ePriority_Error;
michael@0 2466 }
michael@0 2467
michael@0 2468 return ePriority_Important;
michael@0 2469 }
michael@0 2470
michael@0 2471 nsSubstring*
michael@0 2472 CSSParserImpl::NextIdent()
michael@0 2473 {
michael@0 2474 // XXX Error reporting?
michael@0 2475 if (!GetToken(true)) {
michael@0 2476 return nullptr;
michael@0 2477 }
michael@0 2478 if (eCSSToken_Ident != mToken.mType) {
michael@0 2479 UngetToken();
michael@0 2480 return nullptr;
michael@0 2481 }
michael@0 2482 return &mToken.mIdent;
michael@0 2483 }
michael@0 2484
michael@0 2485 bool
michael@0 2486 CSSParserImpl::SkipAtRule(bool aInsideBlock)
michael@0 2487 {
michael@0 2488 for (;;) {
michael@0 2489 if (!GetToken(true)) {
michael@0 2490 REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2);
michael@0 2491 return false;
michael@0 2492 }
michael@0 2493 if (eCSSToken_Symbol == mToken.mType) {
michael@0 2494 char16_t symbol = mToken.mSymbol;
michael@0 2495 if (symbol == ';') {
michael@0 2496 break;
michael@0 2497 }
michael@0 2498 if (aInsideBlock && symbol == '}') {
michael@0 2499 // The closing } doesn't belong to us.
michael@0 2500 UngetToken();
michael@0 2501 break;
michael@0 2502 }
michael@0 2503 if (symbol == '{') {
michael@0 2504 SkipUntil('}');
michael@0 2505 break;
michael@0 2506 } else if (symbol == '(') {
michael@0 2507 SkipUntil(')');
michael@0 2508 } else if (symbol == '[') {
michael@0 2509 SkipUntil(']');
michael@0 2510 }
michael@0 2511 } else if (eCSSToken_Function == mToken.mType ||
michael@0 2512 eCSSToken_Bad_URL == mToken.mType) {
michael@0 2513 SkipUntil(')');
michael@0 2514 }
michael@0 2515 }
michael@0 2516 return true;
michael@0 2517 }
michael@0 2518
michael@0 2519 bool
michael@0 2520 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
michael@0 2521 void* aData,
michael@0 2522 bool aInAtRule)
michael@0 2523 {
michael@0 2524
michael@0 2525 nsCSSSection newSection;
michael@0 2526 bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*);
michael@0 2527
michael@0 2528 if ((mSection <= eCSSSection_Charset) &&
michael@0 2529 (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
michael@0 2530 parseFunc = &CSSParserImpl::ParseCharsetRule;
michael@0 2531 newSection = eCSSSection_Import; // only one charset allowed
michael@0 2532
michael@0 2533 } else if ((mSection <= eCSSSection_Import) &&
michael@0 2534 mToken.mIdent.LowerCaseEqualsLiteral("import")) {
michael@0 2535 parseFunc = &CSSParserImpl::ParseImportRule;
michael@0 2536 newSection = eCSSSection_Import;
michael@0 2537
michael@0 2538 } else if ((mSection <= eCSSSection_NameSpace) &&
michael@0 2539 mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
michael@0 2540 parseFunc = &CSSParserImpl::ParseNameSpaceRule;
michael@0 2541 newSection = eCSSSection_NameSpace;
michael@0 2542
michael@0 2543 } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
michael@0 2544 parseFunc = &CSSParserImpl::ParseMediaRule;
michael@0 2545 newSection = eCSSSection_General;
michael@0 2546
michael@0 2547 } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
michael@0 2548 parseFunc = &CSSParserImpl::ParseMozDocumentRule;
michael@0 2549 newSection = eCSSSection_General;
michael@0 2550
michael@0 2551 } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
michael@0 2552 parseFunc = &CSSParserImpl::ParseFontFaceRule;
michael@0 2553 newSection = eCSSSection_General;
michael@0 2554
michael@0 2555 } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values") &&
michael@0 2556 nsCSSFontFeatureValuesRule::PrefEnabled()) {
michael@0 2557 parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule;
michael@0 2558 newSection = eCSSSection_General;
michael@0 2559
michael@0 2560 } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
michael@0 2561 parseFunc = &CSSParserImpl::ParsePageRule;
michael@0 2562 newSection = eCSSSection_General;
michael@0 2563
michael@0 2564 } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation) &&
michael@0 2565 mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) ||
michael@0 2566 mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) {
michael@0 2567 parseFunc = &CSSParserImpl::ParseKeyframesRule;
michael@0 2568 newSection = eCSSSection_General;
michael@0 2569
michael@0 2570 } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports") &&
michael@0 2571 CSSSupportsRule::PrefEnabled()) {
michael@0 2572 parseFunc = &CSSParserImpl::ParseSupportsRule;
michael@0 2573 newSection = eCSSSection_General;
michael@0 2574
michael@0 2575 } else {
michael@0 2576 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
michael@0 2577 REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
michael@0 2578 OUTPUT_ERROR();
michael@0 2579 }
michael@0 2580 // Skip over unsupported at rule, don't advance section
michael@0 2581 return SkipAtRule(aInAtRule);
michael@0 2582 }
michael@0 2583
michael@0 2584 // Inside of @-rules, only the rules that can occur anywhere
michael@0 2585 // are allowed.
michael@0 2586 bool unnestable = aInAtRule && newSection != eCSSSection_General;
michael@0 2587 if (unnestable) {
michael@0 2588 REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule);
michael@0 2589 }
michael@0 2590
michael@0 2591 if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) {
michael@0 2592 // Skip over invalid at rule, don't advance section
michael@0 2593 OUTPUT_ERROR();
michael@0 2594 return SkipAtRule(aInAtRule);
michael@0 2595 }
michael@0 2596
michael@0 2597 // Nested @-rules don't affect the top-level rule chain requirement
michael@0 2598 if (!aInAtRule) {
michael@0 2599 mSection = newSection;
michael@0 2600 }
michael@0 2601
michael@0 2602 return true;
michael@0 2603 }
michael@0 2604
michael@0 2605 bool
michael@0 2606 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
michael@0 2607 void* aData)
michael@0 2608 {
michael@0 2609 if (!GetToken(true)) {
michael@0 2610 REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
michael@0 2611 return false;
michael@0 2612 }
michael@0 2613
michael@0 2614 if (eCSSToken_String != mToken.mType) {
michael@0 2615 UngetToken();
michael@0 2616 REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
michael@0 2617 return false;
michael@0 2618 }
michael@0 2619
michael@0 2620 nsAutoString charset = mToken.mIdent;
michael@0 2621
michael@0 2622 if (!ExpectSymbol(';', true)) {
michael@0 2623 return false;
michael@0 2624 }
michael@0 2625
michael@0 2626 nsRefPtr<css::CharsetRule> rule = new css::CharsetRule(charset);
michael@0 2627 (*aAppendFunc)(rule, aData);
michael@0 2628
michael@0 2629 return true;
michael@0 2630 }
michael@0 2631
michael@0 2632 bool
michael@0 2633 CSSParserImpl::ParseURLOrString(nsString& aURL)
michael@0 2634 {
michael@0 2635 if (!GetToken(true)) {
michael@0 2636 return false;
michael@0 2637 }
michael@0 2638 if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) {
michael@0 2639 aURL = mToken.mIdent;
michael@0 2640 return true;
michael@0 2641 }
michael@0 2642 UngetToken();
michael@0 2643 return false;
michael@0 2644 }
michael@0 2645
michael@0 2646 bool
michael@0 2647 CSSParserImpl::ParseMediaQuery(bool aInAtRule,
michael@0 2648 nsMediaQuery **aQuery,
michael@0 2649 bool *aHitStop)
michael@0 2650 {
michael@0 2651 *aQuery = nullptr;
michael@0 2652 *aHitStop = false;
michael@0 2653
michael@0 2654 // "If the comma-separated list is the empty list it is assumed to
michael@0 2655 // specify the media query 'all'." (css3-mediaqueries, section
michael@0 2656 // "Media Queries")
michael@0 2657 if (!GetToken(true)) {
michael@0 2658 *aHitStop = true;
michael@0 2659 // expected termination by EOF
michael@0 2660 if (!aInAtRule)
michael@0 2661 return true;
michael@0 2662
michael@0 2663 // unexpected termination by EOF
michael@0 2664 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
michael@0 2665 return true;
michael@0 2666 }
michael@0 2667
michael@0 2668 if (eCSSToken_Symbol == mToken.mType && aInAtRule &&
michael@0 2669 (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) {
michael@0 2670 *aHitStop = true;
michael@0 2671 UngetToken();
michael@0 2672 return true;
michael@0 2673 }
michael@0 2674 UngetToken();
michael@0 2675
michael@0 2676 nsMediaQuery* query = new nsMediaQuery;
michael@0 2677 *aQuery = query;
michael@0 2678
michael@0 2679 if (ExpectSymbol('(', true)) {
michael@0 2680 // we got an expression without a media type
michael@0 2681 UngetToken(); // so ParseMediaQueryExpression can handle it
michael@0 2682 query->SetType(nsGkAtoms::all);
michael@0 2683 query->SetTypeOmitted();
michael@0 2684 // Just parse the first expression here.
michael@0 2685 if (!ParseMediaQueryExpression(query)) {
michael@0 2686 OUTPUT_ERROR();
michael@0 2687 query->SetHadUnknownExpression();
michael@0 2688 }
michael@0 2689 } else {
michael@0 2690 nsCOMPtr<nsIAtom> mediaType;
michael@0 2691 bool gotNotOrOnly = false;
michael@0 2692 for (;;) {
michael@0 2693 if (!GetToken(true)) {
michael@0 2694 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
michael@0 2695 return false;
michael@0 2696 }
michael@0 2697 if (eCSSToken_Ident != mToken.mType) {
michael@0 2698 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
michael@0 2699 UngetToken();
michael@0 2700 return false;
michael@0 2701 }
michael@0 2702 // case insensitive from CSS - must be lower cased
michael@0 2703 nsContentUtils::ASCIIToLower(mToken.mIdent);
michael@0 2704 mediaType = do_GetAtom(mToken.mIdent);
michael@0 2705 if (!mediaType) {
michael@0 2706 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
michael@0 2707 }
michael@0 2708 if (!gotNotOrOnly && mediaType == nsGkAtoms::_not) {
michael@0 2709 gotNotOrOnly = true;
michael@0 2710 query->SetNegated();
michael@0 2711 } else if (!gotNotOrOnly && mediaType == nsGkAtoms::only) {
michael@0 2712 gotNotOrOnly = true;
michael@0 2713 query->SetHasOnly();
michael@0 2714 } else if (mediaType == nsGkAtoms::_not ||
michael@0 2715 mediaType == nsGkAtoms::only ||
michael@0 2716 mediaType == nsGkAtoms::_and ||
michael@0 2717 mediaType == nsGkAtoms::_or) {
michael@0 2718 REPORT_UNEXPECTED_TOKEN(PEGatherMediaReservedMediaType);
michael@0 2719 UngetToken();
michael@0 2720 return false;
michael@0 2721 } else {
michael@0 2722 // valid media type
michael@0 2723 break;
michael@0 2724 }
michael@0 2725 }
michael@0 2726 query->SetType(mediaType);
michael@0 2727 }
michael@0 2728
michael@0 2729 for (;;) {
michael@0 2730 if (!GetToken(true)) {
michael@0 2731 *aHitStop = true;
michael@0 2732 // expected termination by EOF
michael@0 2733 if (!aInAtRule)
michael@0 2734 break;
michael@0 2735
michael@0 2736 // unexpected termination by EOF
michael@0 2737 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
michael@0 2738 break;
michael@0 2739 }
michael@0 2740
michael@0 2741 if (eCSSToken_Symbol == mToken.mType && aInAtRule &&
michael@0 2742 (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) {
michael@0 2743 *aHitStop = true;
michael@0 2744 UngetToken();
michael@0 2745 break;
michael@0 2746 }
michael@0 2747 if (eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
michael@0 2748 // Done with the expressions for this query
michael@0 2749 break;
michael@0 2750 }
michael@0 2751 if (eCSSToken_Ident != mToken.mType ||
michael@0 2752 !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
michael@0 2753 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
michael@0 2754 UngetToken();
michael@0 2755 return false;
michael@0 2756 }
michael@0 2757 if (!ParseMediaQueryExpression(query)) {
michael@0 2758 OUTPUT_ERROR();
michael@0 2759 query->SetHadUnknownExpression();
michael@0 2760 }
michael@0 2761 }
michael@0 2762 return true;
michael@0 2763 }
michael@0 2764
michael@0 2765 // Returns false only when there is a low-level error in the scanner
michael@0 2766 // (out-of-memory).
michael@0 2767 bool
michael@0 2768 CSSParserImpl::GatherMedia(nsMediaList* aMedia,
michael@0 2769 bool aInAtRule)
michael@0 2770 {
michael@0 2771 for (;;) {
michael@0 2772 nsAutoPtr<nsMediaQuery> query;
michael@0 2773 bool hitStop;
michael@0 2774 if (!ParseMediaQuery(aInAtRule, getter_Transfers(query),
michael@0 2775 &hitStop)) {
michael@0 2776 NS_ASSERTION(!hitStop, "should return true when hit stop");
michael@0 2777 OUTPUT_ERROR();
michael@0 2778 if (query) {
michael@0 2779 query->SetHadUnknownExpression();
michael@0 2780 }
michael@0 2781 if (aInAtRule) {
michael@0 2782 const char16_t stopChars[] =
michael@0 2783 { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) };
michael@0 2784 SkipUntilOneOf(stopChars);
michael@0 2785 } else {
michael@0 2786 SkipUntil(',');
michael@0 2787 }
michael@0 2788 // Rely on SkipUntilOneOf leaving mToken around as the last token read.
michael@0 2789 if (mToken.mType == eCSSToken_Symbol && aInAtRule &&
michael@0 2790 (mToken.mSymbol == '{' || mToken.mSymbol == ';' || mToken.mSymbol == '}')) {
michael@0 2791 UngetToken();
michael@0 2792 hitStop = true;
michael@0 2793 }
michael@0 2794 }
michael@0 2795 if (query) {
michael@0 2796 aMedia->AppendQuery(query);
michael@0 2797 }
michael@0 2798 if (hitStop) {
michael@0 2799 break;
michael@0 2800 }
michael@0 2801 }
michael@0 2802 return true;
michael@0 2803 }
michael@0 2804
michael@0 2805 bool
michael@0 2806 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
michael@0 2807 {
michael@0 2808 if (!ExpectSymbol('(', true)) {
michael@0 2809 REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
michael@0 2810 return false;
michael@0 2811 }
michael@0 2812 if (! GetToken(true)) {
michael@0 2813 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
michael@0 2814 return false;
michael@0 2815 }
michael@0 2816 if (eCSSToken_Ident != mToken.mType) {
michael@0 2817 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
michael@0 2818 UngetToken();
michael@0 2819 SkipUntil(')');
michael@0 2820 return false;
michael@0 2821 }
michael@0 2822
michael@0 2823 nsMediaExpression *expr = aQuery->NewExpression();
michael@0 2824
michael@0 2825 // case insensitive from CSS - must be lower cased
michael@0 2826 nsContentUtils::ASCIIToLower(mToken.mIdent);
michael@0 2827 const char16_t *featureString;
michael@0 2828 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
michael@0 2829 expr->mRange = nsMediaExpression::eMin;
michael@0 2830 featureString = mToken.mIdent.get() + 4;
michael@0 2831 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
michael@0 2832 expr->mRange = nsMediaExpression::eMax;
michael@0 2833 featureString = mToken.mIdent.get() + 4;
michael@0 2834 } else {
michael@0 2835 expr->mRange = nsMediaExpression::eEqual;
michael@0 2836 featureString = mToken.mIdent.get();
michael@0 2837 }
michael@0 2838
michael@0 2839 nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
michael@0 2840 if (!mediaFeatureAtom) {
michael@0 2841 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
michael@0 2842 }
michael@0 2843 const nsMediaFeature *feature = nsMediaFeatures::features;
michael@0 2844 for (; feature->mName; ++feature) {
michael@0 2845 if (*(feature->mName) == mediaFeatureAtom) {
michael@0 2846 break;
michael@0 2847 }
michael@0 2848 }
michael@0 2849 if (!feature->mName ||
michael@0 2850 (expr->mRange != nsMediaExpression::eEqual &&
michael@0 2851 feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
michael@0 2852 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
michael@0 2853 SkipUntil(')');
michael@0 2854 return false;
michael@0 2855 }
michael@0 2856 expr->mFeature = feature;
michael@0 2857
michael@0 2858 if (!GetToken(true) || mToken.IsSymbol(')')) {
michael@0 2859 // Query expressions for any feature can be given without a value.
michael@0 2860 // However, min/max prefixes are not allowed.
michael@0 2861 if (expr->mRange != nsMediaExpression::eEqual) {
michael@0 2862 REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
michael@0 2863 return false;
michael@0 2864 }
michael@0 2865 expr->mValue.Reset();
michael@0 2866 return true;
michael@0 2867 }
michael@0 2868
michael@0 2869 if (!mToken.IsSymbol(':')) {
michael@0 2870 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
michael@0 2871 UngetToken();
michael@0 2872 SkipUntil(')');
michael@0 2873 return false;
michael@0 2874 }
michael@0 2875
michael@0 2876 bool rv = false;
michael@0 2877 switch (feature->mValueType) {
michael@0 2878 case nsMediaFeature::eLength:
michael@0 2879 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_LENGTH, nullptr);
michael@0 2880 break;
michael@0 2881 case nsMediaFeature::eInteger:
michael@0 2882 case nsMediaFeature::eBoolInteger:
michael@0 2883 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_INTEGER, nullptr);
michael@0 2884 // Enforce extra restrictions for eBoolInteger
michael@0 2885 if (rv &&
michael@0 2886 feature->mValueType == nsMediaFeature::eBoolInteger &&
michael@0 2887 expr->mValue.GetIntValue() > 1)
michael@0 2888 rv = false;
michael@0 2889 break;
michael@0 2890 case nsMediaFeature::eFloat:
michael@0 2891 rv = ParseNonNegativeVariant(expr->mValue, VARIANT_NUMBER, nullptr);
michael@0 2892 break;
michael@0 2893 case nsMediaFeature::eIntRatio:
michael@0 2894 {
michael@0 2895 // Two integers separated by '/', with optional whitespace on
michael@0 2896 // either side of the '/'.
michael@0 2897 nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
michael@0 2898 expr->mValue.SetArrayValue(a, eCSSUnit_Array);
michael@0 2899 // We don't bother with ParseNonNegativeVariant since we have to
michael@0 2900 // check for != 0 as well; no need to worry about the UngetToken
michael@0 2901 // since we're throwing out up to the next ')' anyway.
michael@0 2902 rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nullptr) &&
michael@0 2903 a->Item(0).GetIntValue() > 0 &&
michael@0 2904 ExpectSymbol('/', true) &&
michael@0 2905 ParseVariant(a->Item(1), VARIANT_INTEGER, nullptr) &&
michael@0 2906 a->Item(1).GetIntValue() > 0;
michael@0 2907 }
michael@0 2908 break;
michael@0 2909 case nsMediaFeature::eResolution:
michael@0 2910 rv = GetToken(true);
michael@0 2911 if (!rv)
michael@0 2912 break;
michael@0 2913 rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f;
michael@0 2914 if (!rv) {
michael@0 2915 UngetToken();
michael@0 2916 break;
michael@0 2917 }
michael@0 2918 // No worries about whether unitless zero is allowed, since the
michael@0 2919 // value must be positive (and we checked that above).
michael@0 2920 NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied");
michael@0 2921 if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
michael@0 2922 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
michael@0 2923 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) {
michael@0 2924 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel);
michael@0 2925 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
michael@0 2926 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
michael@0 2927 } else {
michael@0 2928 rv = false;
michael@0 2929 }
michael@0 2930 break;
michael@0 2931 case nsMediaFeature::eEnumerated:
michael@0 2932 rv = ParseVariant(expr->mValue, VARIANT_KEYWORD,
michael@0 2933 feature->mData.mKeywordTable);
michael@0 2934 break;
michael@0 2935 case nsMediaFeature::eIdent:
michael@0 2936 rv = ParseVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr);
michael@0 2937 break;
michael@0 2938 }
michael@0 2939 if (!rv || !ExpectSymbol(')', true)) {
michael@0 2940 REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
michael@0 2941 SkipUntil(')');
michael@0 2942 return false;
michael@0 2943 }
michael@0 2944
michael@0 2945 return true;
michael@0 2946 }
michael@0 2947
michael@0 2948 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
michael@0 2949 bool
michael@0 2950 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
michael@0 2951 {
michael@0 2952 nsRefPtr<nsMediaList> media = new nsMediaList();
michael@0 2953
michael@0 2954 nsAutoString url;
michael@0 2955 if (!ParseURLOrString(url)) {
michael@0 2956 REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
michael@0 2957 return false;
michael@0 2958 }
michael@0 2959
michael@0 2960 if (!ExpectSymbol(';', true)) {
michael@0 2961 if (!GatherMedia(media, true) ||
michael@0 2962 !ExpectSymbol(';', true)) {
michael@0 2963 REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
michael@0 2964 // don't advance section, simply ignore invalid @import
michael@0 2965 return false;
michael@0 2966 }
michael@0 2967
michael@0 2968 // Safe to assert this, since we ensured that there is something
michael@0 2969 // other than the ';' coming after the @import's url() token.
michael@0 2970 NS_ASSERTION(media->Length() != 0, "media list must be nonempty");
michael@0 2971 }
michael@0 2972
michael@0 2973 ProcessImport(url, media, aAppendFunc, aData);
michael@0 2974 return true;
michael@0 2975 }
michael@0 2976
michael@0 2977
michael@0 2978 void
michael@0 2979 CSSParserImpl::ProcessImport(const nsString& aURLSpec,
michael@0 2980 nsMediaList* aMedia,
michael@0 2981 RuleAppendFunc aAppendFunc,
michael@0 2982 void* aData)
michael@0 2983 {
michael@0 2984 nsRefPtr<css::ImportRule> rule = new css::ImportRule(aMedia, aURLSpec);
michael@0 2985 (*aAppendFunc)(rule, aData);
michael@0 2986
michael@0 2987 // Diagnose bad URIs even if we don't have a child loader.
michael@0 2988 nsCOMPtr<nsIURI> url;
michael@0 2989 // Charset will be deduced from mBaseURI, which is more or less correct.
michael@0 2990 nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI);
michael@0 2991
michael@0 2992 if (NS_FAILED(rv)) {
michael@0 2993 if (rv == NS_ERROR_MALFORMED_URI) {
michael@0 2994 // import url is bad
michael@0 2995 REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec);
michael@0 2996 OUTPUT_ERROR();
michael@0 2997 }
michael@0 2998 return;
michael@0 2999 }
michael@0 3000
michael@0 3001 if (mChildLoader) {
michael@0 3002 mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
michael@0 3003 }
michael@0 3004 }
michael@0 3005
michael@0 3006 // Parse the {} part of an @media or @-moz-document rule.
michael@0 3007 bool
michael@0 3008 CSSParserImpl::ParseGroupRule(css::GroupRule* aRule,
michael@0 3009 RuleAppendFunc aAppendFunc,
michael@0 3010 void* aData)
michael@0 3011 {
michael@0 3012 // XXXbz this could use better error reporting throughout the method
michael@0 3013 if (!ExpectSymbol('{', true)) {
michael@0 3014 return false;
michael@0 3015 }
michael@0 3016
michael@0 3017 // push rule on stack, loop over children
michael@0 3018 PushGroup(aRule);
michael@0 3019 nsCSSSection holdSection = mSection;
michael@0 3020 mSection = eCSSSection_General;
michael@0 3021
michael@0 3022 for (;;) {
michael@0 3023 // Get next non-whitespace token
michael@0 3024 if (! GetToken(true)) {
michael@0 3025 REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2);
michael@0 3026 break;
michael@0 3027 }
michael@0 3028 if (mToken.IsSymbol('}')) { // done!
michael@0 3029 UngetToken();
michael@0 3030 break;
michael@0 3031 }
michael@0 3032 if (eCSSToken_AtKeyword == mToken.mType) {
michael@0 3033 // Parse for nested rules
michael@0 3034 ParseAtRule(aAppendFunc, aData, true);
michael@0 3035 continue;
michael@0 3036 }
michael@0 3037 UngetToken();
michael@0 3038 ParseRuleSet(AppendRuleToSheet, this, true);
michael@0 3039 }
michael@0 3040 PopGroup();
michael@0 3041
michael@0 3042 if (!ExpectSymbol('}', true)) {
michael@0 3043 mSection = holdSection;
michael@0 3044 return false;
michael@0 3045 }
michael@0 3046 (*aAppendFunc)(aRule, aData);
michael@0 3047 return true;
michael@0 3048 }
michael@0 3049
michael@0 3050 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
michael@0 3051 bool
michael@0 3052 CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
michael@0 3053 {
michael@0 3054 nsRefPtr<nsMediaList> media = new nsMediaList();
michael@0 3055
michael@0 3056 if (GatherMedia(media, true)) {
michael@0 3057 // XXXbz this could use better error reporting throughout the method
michael@0 3058 nsRefPtr<css::MediaRule> rule = new css::MediaRule();
michael@0 3059 // Append first, so when we do SetMedia() the rule
michael@0 3060 // knows what its stylesheet is.
michael@0 3061 if (ParseGroupRule(rule, aAppendFunc, aData)) {
michael@0 3062 rule->SetMedia(media);
michael@0 3063 return true;
michael@0 3064 }
michael@0 3065 }
michael@0 3066
michael@0 3067 return false;
michael@0 3068 }
michael@0 3069
michael@0 3070 // Parse a @-moz-document rule. This is like an @media rule, but instead
michael@0 3071 // of a medium it has a nonempty list of items where each item is either
michael@0 3072 // url(), url-prefix(), or domain().
michael@0 3073 bool
michael@0 3074 CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
michael@0 3075 {
michael@0 3076 css::DocumentRule::URL *urls = nullptr;
michael@0 3077 css::DocumentRule::URL **next = &urls;
michael@0 3078 do {
michael@0 3079 if (!GetToken(true)) {
michael@0 3080 REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF);
michael@0 3081 delete urls;
michael@0 3082 return false;
michael@0 3083 }
michael@0 3084
michael@0 3085 if (!(eCSSToken_URL == mToken.mType ||
michael@0 3086 (eCSSToken_Function == mToken.mType &&
michael@0 3087 (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
michael@0 3088 mToken.mIdent.LowerCaseEqualsLiteral("domain") ||
michael@0 3089 mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) {
michael@0 3090 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc2);
michael@0 3091 UngetToken();
michael@0 3092 delete urls;
michael@0 3093 return false;
michael@0 3094 }
michael@0 3095 css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL;
michael@0 3096 next = &cur->next;
michael@0 3097 if (mToken.mType == eCSSToken_URL) {
michael@0 3098 cur->func = css::DocumentRule::eURL;
michael@0 3099 CopyUTF16toUTF8(mToken.mIdent, cur->url);
michael@0 3100 } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) {
michael@0 3101 // regexp() is different from url-prefix() and domain() (but
michael@0 3102 // probably the way they *should* have been* in that it requires a
michael@0 3103 // string argument, and doesn't try to behave like url().
michael@0 3104 cur->func = css::DocumentRule::eRegExp;
michael@0 3105 GetToken(true);
michael@0 3106 // copy before we know it's valid (but before ExpectSymbol changes
michael@0 3107 // mToken.mIdent)
michael@0 3108 CopyUTF16toUTF8(mToken.mIdent, cur->url);
michael@0 3109 if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) {
michael@0 3110 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString);
michael@0 3111 SkipUntil(')');
michael@0 3112 delete urls;
michael@0 3113 return false;
michael@0 3114 }
michael@0 3115 } else {
michael@0 3116 if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
michael@0 3117 cur->func = css::DocumentRule::eURLPrefix;
michael@0 3118 } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
michael@0 3119 cur->func = css::DocumentRule::eDomain;
michael@0 3120 }
michael@0 3121
michael@0 3122 NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point");
michael@0 3123 if (!mScanner->NextURL(mToken) || mToken.mType != eCSSToken_URL) {
michael@0 3124 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
michael@0 3125 SkipUntil(')');
michael@0 3126 delete urls;
michael@0 3127 return false;
michael@0 3128 }
michael@0 3129
michael@0 3130 // We could try to make the URL (as long as it's not domain())
michael@0 3131 // canonical and absolute with NS_NewURI and GetSpec, but I'm
michael@0 3132 // inclined to think we shouldn't.
michael@0 3133 CopyUTF16toUTF8(mToken.mIdent, cur->url);
michael@0 3134 }
michael@0 3135 } while (ExpectSymbol(',', true));
michael@0 3136
michael@0 3137 nsRefPtr<css::DocumentRule> rule = new css::DocumentRule();
michael@0 3138 rule->SetURLs(urls);
michael@0 3139
michael@0 3140 return ParseGroupRule(rule, aAppendFunc, aData);
michael@0 3141 }
michael@0 3142
michael@0 3143 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
michael@0 3144 bool
michael@0 3145 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
michael@0 3146 {
michael@0 3147 if (!GetToken(true)) {
michael@0 3148 REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
michael@0 3149 return false;
michael@0 3150 }
michael@0 3151
michael@0 3152 nsAutoString prefix;
michael@0 3153 nsAutoString url;
michael@0 3154
michael@0 3155 if (eCSSToken_Ident == mToken.mType) {
michael@0 3156 prefix = mToken.mIdent;
michael@0 3157 // user-specified identifiers are case-sensitive (bug 416106)
michael@0 3158 } else {
michael@0 3159 UngetToken();
michael@0 3160 }
michael@0 3161
michael@0 3162 if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) {
michael@0 3163 if (mHavePushBack) {
michael@0 3164 REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
michael@0 3165 } else {
michael@0 3166 REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
michael@0 3167 }
michael@0 3168 return false;
michael@0 3169 }
michael@0 3170
michael@0 3171 ProcessNameSpace(prefix, url, aAppendFunc, aData);
michael@0 3172 return true;
michael@0 3173 }
michael@0 3174
michael@0 3175 void
michael@0 3176 CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
michael@0 3177 const nsString& aURLSpec,
michael@0 3178 RuleAppendFunc aAppendFunc,
michael@0 3179 void* aData)
michael@0 3180 {
michael@0 3181 nsCOMPtr<nsIAtom> prefix;
michael@0 3182
michael@0 3183 if (!aPrefix.IsEmpty()) {
michael@0 3184 prefix = do_GetAtom(aPrefix);
michael@0 3185 if (!prefix) {
michael@0 3186 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
michael@0 3187 }
michael@0 3188 }
michael@0 3189
michael@0 3190 nsRefPtr<css::NameSpaceRule> rule = new css::NameSpaceRule(prefix, aURLSpec);
michael@0 3191 (*aAppendFunc)(rule, aData);
michael@0 3192
michael@0 3193 // If this was the first namespace rule encountered, it will trigger
michael@0 3194 // creation of a namespace map.
michael@0 3195 if (!mNameSpaceMap) {
michael@0 3196 mNameSpaceMap = mSheet->GetNameSpaceMap();
michael@0 3197 }
michael@0 3198 }
michael@0 3199
michael@0 3200 // font-face-rule: '@font-face' '{' font-description '}'
michael@0 3201 // font-description: font-descriptor+
michael@0 3202 bool
michael@0 3203 CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
michael@0 3204 {
michael@0 3205 if (!ExpectSymbol('{', true)) {
michael@0 3206 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart);
michael@0 3207 return false;
michael@0 3208 }
michael@0 3209
michael@0 3210 nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule());
michael@0 3211
michael@0 3212 for (;;) {
michael@0 3213 if (!GetToken(true)) {
michael@0 3214 REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
michael@0 3215 break;
michael@0 3216 }
michael@0 3217 if (mToken.IsSymbol('}')) { // done!
michael@0 3218 UngetToken();
michael@0 3219 break;
michael@0 3220 }
michael@0 3221
michael@0 3222 // ignore extra semicolons
michael@0 3223 if (mToken.IsSymbol(';'))
michael@0 3224 continue;
michael@0 3225
michael@0 3226 if (!ParseFontDescriptor(rule)) {
michael@0 3227 REPORT_UNEXPECTED(PEDeclSkipped);
michael@0 3228 OUTPUT_ERROR();
michael@0 3229 if (!SkipDeclaration(true))
michael@0 3230 break;
michael@0 3231 }
michael@0 3232 }
michael@0 3233 if (!ExpectSymbol('}', true)) {
michael@0 3234 REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd);
michael@0 3235 return false;
michael@0 3236 }
michael@0 3237 (*aAppendFunc)(rule, aData);
michael@0 3238 return true;
michael@0 3239 }
michael@0 3240
michael@0 3241 // font-descriptor: font-family-desc
michael@0 3242 // | font-style-desc
michael@0 3243 // | font-weight-desc
michael@0 3244 // | font-stretch-desc
michael@0 3245 // | font-src-desc
michael@0 3246 // | unicode-range-desc
michael@0 3247 //
michael@0 3248 // All font-*-desc productions follow the pattern
michael@0 3249 // IDENT ':' value ';'
michael@0 3250 //
michael@0 3251 // On entry to this function, mToken is the IDENT.
michael@0 3252
michael@0 3253 bool
michael@0 3254 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
michael@0 3255 {
michael@0 3256 if (eCSSToken_Ident != mToken.mType) {
michael@0 3257 REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
michael@0 3258 return false;
michael@0 3259 }
michael@0 3260
michael@0 3261 nsString descName = mToken.mIdent;
michael@0 3262 if (!ExpectSymbol(':', true)) {
michael@0 3263 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
michael@0 3264 OUTPUT_ERROR();
michael@0 3265 return false;
michael@0 3266 }
michael@0 3267
michael@0 3268 nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
michael@0 3269 nsCSSValue value;
michael@0 3270
michael@0 3271 if (descID == eCSSFontDesc_UNKNOWN) {
michael@0 3272 if (NonMozillaVendorIdentifier(descName)) {
michael@0 3273 // silently skip other vendors' extensions
michael@0 3274 SkipDeclaration(true);
michael@0 3275 return true;
michael@0 3276 } else {
michael@0 3277 REPORT_UNEXPECTED_P(PEUnknownFontDesc, descName);
michael@0 3278 return false;
michael@0 3279 }
michael@0 3280 }
michael@0 3281
michael@0 3282 if (!ParseFontDescriptorValue(descID, value)) {
michael@0 3283 REPORT_UNEXPECTED_P(PEValueParsingError, descName);
michael@0 3284 return false;
michael@0 3285 }
michael@0 3286
michael@0 3287 if (!ExpectEndProperty())
michael@0 3288 return false;
michael@0 3289
michael@0 3290 aRule->SetDesc(descID, value);
michael@0 3291 return true;
michael@0 3292 }
michael@0 3293
michael@0 3294 // @font-feature-values <font-family># {
michael@0 3295 // @<feature-type> {
michael@0 3296 // <feature-ident> : <feature-index>+;
michael@0 3297 // <feature-ident> : <feature-index>+;
michael@0 3298 // ...
michael@0 3299 // }
michael@0 3300 // ...
michael@0 3301 // }
michael@0 3302
michael@0 3303 bool
michael@0 3304 CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
michael@0 3305 void* aData)
michael@0 3306 {
michael@0 3307 nsRefPtr<nsCSSFontFeatureValuesRule>
michael@0 3308 valuesRule(new nsCSSFontFeatureValuesRule());
michael@0 3309
michael@0 3310 // parse family list
michael@0 3311 nsCSSValue familyValue;
michael@0 3312
michael@0 3313 if (!ParseFamily(familyValue) ||
michael@0 3314 familyValue.GetUnit() != eCSSUnit_Families)
michael@0 3315 {
michael@0 3316 REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily);
michael@0 3317 return false;
michael@0 3318 }
michael@0 3319
michael@0 3320 // add family to rule
michael@0 3321 nsAutoString familyList;
michael@0 3322 bool hasGeneric;
michael@0 3323 familyValue.GetStringValue(familyList);
michael@0 3324 valuesRule->SetFamilyList(familyList, hasGeneric);
michael@0 3325
michael@0 3326 // family list has generic ==> parse error
michael@0 3327 if (hasGeneric) {
michael@0 3328 REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList);
michael@0 3329 return false;
michael@0 3330 }
michael@0 3331
michael@0 3332 // open brace
michael@0 3333 if (!ExpectSymbol('{', true)) {
michael@0 3334 REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart);
michael@0 3335 return false;
michael@0 3336 }
michael@0 3337
michael@0 3338 // list of sets of feature values, each set bound to a specific
michael@0 3339 // feature-type (e.g. swash, annotation)
michael@0 3340 for (;;) {
michael@0 3341 if (!GetToken(true)) {
michael@0 3342 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
michael@0 3343 break;
michael@0 3344 }
michael@0 3345 if (mToken.IsSymbol('}')) { // done!
michael@0 3346 UngetToken();
michael@0 3347 break;
michael@0 3348 }
michael@0 3349
michael@0 3350 if (!ParseFontFeatureValueSet(valuesRule)) {
michael@0 3351 if (!SkipAtRule(false)) {
michael@0 3352 break;
michael@0 3353 }
michael@0 3354 }
michael@0 3355 }
michael@0 3356 if (!ExpectSymbol('}', true)) {
michael@0 3357 REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd);
michael@0 3358 SkipUntil('}');
michael@0 3359 return false;
michael@0 3360 }
michael@0 3361
michael@0 3362 (*aAppendFunc)(valuesRule, aData);
michael@0 3363 return true;
michael@0 3364 }
michael@0 3365
michael@0 3366 #define NUMVALUES_NO_LIMIT 0xFFFF
michael@0 3367
michael@0 3368 // parse a single value set containing name-value pairs for a single feature type
michael@0 3369 // @<feature-type> { [ <feature-ident> : <feature-index>+ ; ]* }
michael@0 3370 // Ex: @swash { flowing: 1; delicate: 2; }
michael@0 3371 bool
michael@0 3372 CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule
michael@0 3373 *aFeatureValuesRule)
michael@0 3374 {
michael@0 3375 // -- @keyword (e.g. swash, styleset)
michael@0 3376 if (eCSSToken_AtKeyword != mToken.mType) {
michael@0 3377 REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt);
michael@0 3378 OUTPUT_ERROR();
michael@0 3379 UngetToken();
michael@0 3380 return false;
michael@0 3381 }
michael@0 3382
michael@0 3383 // which font-specific variant of font-variant-alternates
michael@0 3384 int32_t whichVariant;
michael@0 3385 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
michael@0 3386 if (keyword == eCSSKeyword_UNKNOWN ||
michael@0 3387 !nsCSSProps::FindKeyword(keyword,
michael@0 3388 nsCSSProps::kFontVariantAlternatesFuncsKTable,
michael@0 3389 whichVariant))
michael@0 3390 {
michael@0 3391 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
michael@0 3392 REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue);
michael@0 3393 OUTPUT_ERROR();
michael@0 3394 }
michael@0 3395 UngetToken();
michael@0 3396 return false;
michael@0 3397 }
michael@0 3398
michael@0 3399 nsAutoString featureType(mToken.mIdent);
michael@0 3400
michael@0 3401 // open brace
michael@0 3402 if (!ExpectSymbol('{', true)) {
michael@0 3403 REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart);
michael@0 3404 return false;
michael@0 3405 }
michael@0 3406
michael@0 3407 // styleset and character-variant can be multi-valued, otherwise single value
michael@0 3408 int32_t limitNumValues;
michael@0 3409
michael@0 3410 switch (keyword) {
michael@0 3411 case eCSSKeyword_styleset:
michael@0 3412 limitNumValues = NUMVALUES_NO_LIMIT;
michael@0 3413 break;
michael@0 3414 case eCSSKeyword_character_variant:
michael@0 3415 limitNumValues = 2;
michael@0 3416 break;
michael@0 3417 default:
michael@0 3418 limitNumValues = 1;
michael@0 3419 break;
michael@0 3420 }
michael@0 3421
michael@0 3422 // -- ident integer+ [, ident integer+]
michael@0 3423 nsAutoTArray<gfxFontFeatureValueSet::ValueList, 5> values;
michael@0 3424
michael@0 3425 // list of font-feature-values-declaration's
michael@0 3426 for (;;) {
michael@0 3427 nsAutoString valueId;
michael@0 3428
michael@0 3429 if (!GetToken(true)) {
michael@0 3430 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
michael@0 3431 break;
michael@0 3432 }
michael@0 3433
michael@0 3434 // ignore extra semicolons
michael@0 3435 if (mToken.IsSymbol(';')) {
michael@0 3436 continue;
michael@0 3437 }
michael@0 3438
michael@0 3439 // close brace ==> done
michael@0 3440 if (mToken.IsSymbol('}')) {
michael@0 3441 break;
michael@0 3442 }
michael@0 3443
michael@0 3444 // ident
michael@0 3445 if (eCSSToken_Ident != mToken.mType) {
michael@0 3446 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent);
michael@0 3447 if (!SkipDeclaration(true)) {
michael@0 3448 break;
michael@0 3449 }
michael@0 3450 continue;
michael@0 3451 }
michael@0 3452
michael@0 3453 valueId.Assign(mToken.mIdent);
michael@0 3454
michael@0 3455 // colon
michael@0 3456 if (!ExpectSymbol(':', true)) {
michael@0 3457 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
michael@0 3458 OUTPUT_ERROR();
michael@0 3459 if (!SkipDeclaration(true)) {
michael@0 3460 break;
michael@0 3461 }
michael@0 3462 continue;
michael@0 3463 }
michael@0 3464
michael@0 3465 // value list
michael@0 3466 nsAutoTArray<uint32_t,4> featureSelectors;
michael@0 3467
michael@0 3468 nsCSSValue intValue;
michael@0 3469 while (ParseNonNegativeVariant(intValue, VARIANT_INTEGER, nullptr)) {
michael@0 3470 featureSelectors.AppendElement(uint32_t(intValue.GetIntValue()));
michael@0 3471 }
michael@0 3472
michael@0 3473 int32_t numValues = featureSelectors.Length();
michael@0 3474
michael@0 3475 if (numValues == 0) {
michael@0 3476 REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue);
michael@0 3477 OUTPUT_ERROR();
michael@0 3478 if (!SkipDeclaration(true)) {
michael@0 3479 break;
michael@0 3480 }
michael@0 3481 continue;
michael@0 3482 }
michael@0 3483
michael@0 3484 if (numValues > limitNumValues) {
michael@0 3485 REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType);
michael@0 3486 OUTPUT_ERROR();
michael@0 3487 if (!SkipDeclaration(true)) {
michael@0 3488 break;
michael@0 3489 }
michael@0 3490 continue;
michael@0 3491 }
michael@0 3492
michael@0 3493 if (!GetToken(true)) {
michael@0 3494 REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF);
michael@0 3495 gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
michael@0 3496 values.AppendElement(v);
michael@0 3497 break;
michael@0 3498 }
michael@0 3499
michael@0 3500 // ';' or '}' to end definition
michael@0 3501 if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) {
michael@0 3502 REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing);
michael@0 3503 OUTPUT_ERROR();
michael@0 3504 if (!SkipDeclaration(true)) {
michael@0 3505 break;
michael@0 3506 }
michael@0 3507 continue;
michael@0 3508 }
michael@0 3509
michael@0 3510 gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors);
michael@0 3511 values.AppendElement(v);
michael@0 3512
michael@0 3513 if (mToken.IsSymbol('}')) {
michael@0 3514 break;
michael@0 3515 }
michael@0 3516 }
michael@0 3517
michael@0 3518 aFeatureValuesRule->AddValueList(whichVariant, values);
michael@0 3519 return true;
michael@0 3520 }
michael@0 3521
michael@0 3522 bool
michael@0 3523 CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData)
michael@0 3524 {
michael@0 3525 if (!GetToken(true)) {
michael@0 3526 REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF);
michael@0 3527 return false;
michael@0 3528 }
michael@0 3529
michael@0 3530 if (mToken.mType != eCSSToken_Ident) {
michael@0 3531 REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName);
michael@0 3532 UngetToken();
michael@0 3533 return false;
michael@0 3534 }
michael@0 3535 nsString name(mToken.mIdent);
michael@0 3536
michael@0 3537 if (!ExpectSymbol('{', true)) {
michael@0 3538 REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace);
michael@0 3539 return false;
michael@0 3540 }
michael@0 3541
michael@0 3542 nsRefPtr<nsCSSKeyframesRule> rule = new nsCSSKeyframesRule(name);
michael@0 3543
michael@0 3544 while (!ExpectSymbol('}', true)) {
michael@0 3545 nsRefPtr<nsCSSKeyframeRule> kid = ParseKeyframeRule();
michael@0 3546 if (kid) {
michael@0 3547 rule->AppendStyleRule(kid);
michael@0 3548 } else {
michael@0 3549 OUTPUT_ERROR();
michael@0 3550 SkipRuleSet(true);
michael@0 3551 }
michael@0 3552 }
michael@0 3553
michael@0 3554 (*aAppendFunc)(rule, aData);
michael@0 3555 return true;
michael@0 3556 }
michael@0 3557
michael@0 3558 bool
michael@0 3559 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
michael@0 3560 {
michael@0 3561 // TODO: There can be page selectors after @page such as ":first", ":left".
michael@0 3562 uint32_t parseFlags = eParseDeclaration_InBraces |
michael@0 3563 eParseDeclaration_AllowImportant;
michael@0 3564
michael@0 3565 // Forbid viewport units in @page rules. See bug 811391.
michael@0 3566 NS_ABORT_IF_FALSE(mViewportUnitsEnabled,
michael@0 3567 "Viewport units should be enabled outside of @page rules.");
michael@0 3568 mViewportUnitsEnabled = false;
michael@0 3569 nsAutoPtr<css::Declaration> declaration(
michael@0 3570 ParseDeclarationBlock(parseFlags,
michael@0 3571 eCSSContext_Page));
michael@0 3572 mViewportUnitsEnabled = true;
michael@0 3573
michael@0 3574 if (!declaration) {
michael@0 3575 return false;
michael@0 3576 }
michael@0 3577
michael@0 3578 // Takes ownership of declaration.
michael@0 3579 nsRefPtr<nsCSSPageRule> rule = new nsCSSPageRule(declaration);
michael@0 3580
michael@0 3581 (*aAppendFunc)(rule, aData);
michael@0 3582 return true;
michael@0 3583 }
michael@0 3584
michael@0 3585 already_AddRefed<nsCSSKeyframeRule>
michael@0 3586 CSSParserImpl::ParseKeyframeRule()
michael@0 3587 {
michael@0 3588 InfallibleTArray<float> selectorList;
michael@0 3589 if (!ParseKeyframeSelectorList(selectorList)) {
michael@0 3590 REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
michael@0 3591 return nullptr;
michael@0 3592 }
michael@0 3593
michael@0 3594 // Ignore !important in keyframe rules
michael@0 3595 uint32_t parseFlags = eParseDeclaration_InBraces;
michael@0 3596 nsAutoPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags));
michael@0 3597 if (!declaration) {
michael@0 3598 return nullptr;
michael@0 3599 }
michael@0 3600
michael@0 3601 // Takes ownership of declaration, and steals contents of selectorList.
michael@0 3602 nsRefPtr<nsCSSKeyframeRule> rule =
michael@0 3603 new nsCSSKeyframeRule(selectorList, declaration);
michael@0 3604
michael@0 3605 return rule.forget();
michael@0 3606 }
michael@0 3607
michael@0 3608 bool
michael@0 3609 CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList)
michael@0 3610 {
michael@0 3611 for (;;) {
michael@0 3612 if (!GetToken(true)) {
michael@0 3613 // The first time through the loop, this means we got an empty
michael@0 3614 // list. Otherwise, it means we have a trailing comma.
michael@0 3615 return false;
michael@0 3616 }
michael@0 3617 float value;
michael@0 3618 switch (mToken.mType) {
michael@0 3619 case eCSSToken_Percentage:
michael@0 3620 value = mToken.mNumber;
michael@0 3621 break;
michael@0 3622 case eCSSToken_Ident:
michael@0 3623 if (mToken.mIdent.LowerCaseEqualsLiteral("from")) {
michael@0 3624 value = 0.0f;
michael@0 3625 break;
michael@0 3626 }
michael@0 3627 if (mToken.mIdent.LowerCaseEqualsLiteral("to")) {
michael@0 3628 value = 1.0f;
michael@0 3629 break;
michael@0 3630 }
michael@0 3631 // fall through
michael@0 3632 default:
michael@0 3633 UngetToken();
michael@0 3634 // The first time through the loop, this means we got an empty
michael@0 3635 // list. Otherwise, it means we have a trailing comma.
michael@0 3636 return false;
michael@0 3637 }
michael@0 3638 aSelectorList.AppendElement(value);
michael@0 3639 if (!ExpectSymbol(',', true)) {
michael@0 3640 return true;
michael@0 3641 }
michael@0 3642 }
michael@0 3643 }
michael@0 3644
michael@0 3645 // supports_rule
michael@0 3646 // : "@supports" supports_condition group_rule_body
michael@0 3647 // ;
michael@0 3648 bool
michael@0 3649 CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData)
michael@0 3650 {
michael@0 3651 bool conditionMet = false;
michael@0 3652 nsString condition;
michael@0 3653
michael@0 3654 mScanner->StartRecording();
michael@0 3655 bool parsed = ParseSupportsCondition(conditionMet);
michael@0 3656
michael@0 3657 if (!parsed) {
michael@0 3658 mScanner->StopRecording();
michael@0 3659 return false;
michael@0 3660 }
michael@0 3661
michael@0 3662 if (!ExpectSymbol('{', true)) {
michael@0 3663 REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart);
michael@0 3664 mScanner->StopRecording();
michael@0 3665 return false;
michael@0 3666 }
michael@0 3667
michael@0 3668 UngetToken();
michael@0 3669 mScanner->StopRecording(condition);
michael@0 3670
michael@0 3671 // Remove the "{" that would follow the condition.
michael@0 3672 if (condition.Length() != 0) {
michael@0 3673 condition.Truncate(condition.Length() - 1);
michael@0 3674 }
michael@0 3675
michael@0 3676 // Remove spaces from the start and end of the recorded supports condition.
michael@0 3677 condition.Trim(" ", true, true, false);
michael@0 3678
michael@0 3679 // Record whether we are in a failing @supports, so that property parse
michael@0 3680 // errors don't get reported.
michael@0 3681 nsAutoFailingSupportsRule failing(this, conditionMet);
michael@0 3682
michael@0 3683 nsRefPtr<css::GroupRule> rule = new CSSSupportsRule(conditionMet, condition);
michael@0 3684 return ParseGroupRule(rule, aAppendFunc, aProcessData);
michael@0 3685 }
michael@0 3686
michael@0 3687 // supports_condition
michael@0 3688 // : supports_condition_in_parens supports_condition_terms
michael@0 3689 // | supports_condition_negation
michael@0 3690 // ;
michael@0 3691 bool
michael@0 3692 CSSParserImpl::ParseSupportsCondition(bool& aConditionMet)
michael@0 3693 {
michael@0 3694 mInSupportsCondition = true;
michael@0 3695
michael@0 3696 if (!GetToken(true)) {
michael@0 3697 REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2);
michael@0 3698 return false;
michael@0 3699 }
michael@0 3700
michael@0 3701 UngetToken();
michael@0 3702
michael@0 3703 mScanner->ClearSeenBadToken();
michael@0 3704
michael@0 3705 if (mToken.IsSymbol('(') ||
michael@0 3706 mToken.mType == eCSSToken_Function ||
michael@0 3707 mToken.mType == eCSSToken_URL ||
michael@0 3708 mToken.mType == eCSSToken_Bad_URL) {
michael@0 3709 bool result = ParseSupportsConditionInParens(aConditionMet) &&
michael@0 3710 ParseSupportsConditionTerms(aConditionMet) &&
michael@0 3711 !mScanner->SeenBadToken();
michael@0 3712 mInSupportsCondition = false;
michael@0 3713 return result;
michael@0 3714 }
michael@0 3715
michael@0 3716 if (mToken.mType == eCSSToken_Ident &&
michael@0 3717 mToken.mIdent.LowerCaseEqualsLiteral("not")) {
michael@0 3718 bool result = ParseSupportsConditionNegation(aConditionMet) &&
michael@0 3719 !mScanner->SeenBadToken();
michael@0 3720 mInSupportsCondition = false;
michael@0 3721 return result;
michael@0 3722 }
michael@0 3723
michael@0 3724 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart);
michael@0 3725 mInSupportsCondition = false;
michael@0 3726 return false;
michael@0 3727 }
michael@0 3728
michael@0 3729 // supports_condition_negation
michael@0 3730 // : 'not' S+ supports_condition_in_parens
michael@0 3731 // ;
michael@0 3732 bool
michael@0 3733 CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet)
michael@0 3734 {
michael@0 3735 if (!GetToken(true)) {
michael@0 3736 REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF);
michael@0 3737 return false;
michael@0 3738 }
michael@0 3739
michael@0 3740 if (mToken.mType != eCSSToken_Ident ||
michael@0 3741 !mToken.mIdent.LowerCaseEqualsLiteral("not")) {
michael@0 3742 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot);
michael@0 3743 return false;
michael@0 3744 }
michael@0 3745
michael@0 3746 if (!RequireWhitespace()) {
michael@0 3747 REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
michael@0 3748 return false;
michael@0 3749 }
michael@0 3750
michael@0 3751 if (ParseSupportsConditionInParens(aConditionMet)) {
michael@0 3752 aConditionMet = !aConditionMet;
michael@0 3753 return true;
michael@0 3754 }
michael@0 3755
michael@0 3756 return false;
michael@0 3757 }
michael@0 3758
michael@0 3759 // supports_condition_in_parens
michael@0 3760 // : '(' S* supports_condition_in_parens_inside_parens ')' S*
michael@0 3761 // | general_enclosed
michael@0 3762 // ;
michael@0 3763 bool
michael@0 3764 CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet)
michael@0 3765 {
michael@0 3766 if (!GetToken(true)) {
michael@0 3767 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF);
michael@0 3768 return false;
michael@0 3769 }
michael@0 3770
michael@0 3771 if (mToken.mType == eCSSToken_URL) {
michael@0 3772 aConditionMet = false;
michael@0 3773 return true;
michael@0 3774 }
michael@0 3775
michael@0 3776 if (mToken.mType == eCSSToken_Function ||
michael@0 3777 mToken.mType == eCSSToken_Bad_URL) {
michael@0 3778 if (!SkipUntil(')')) {
michael@0 3779 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
michael@0 3780 return false;
michael@0 3781 }
michael@0 3782 aConditionMet = false;
michael@0 3783 return true;
michael@0 3784 }
michael@0 3785
michael@0 3786 if (!mToken.IsSymbol('(')) {
michael@0 3787 REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction);
michael@0 3788 UngetToken();
michael@0 3789 return false;
michael@0 3790 }
michael@0 3791
michael@0 3792 if (!ParseSupportsConditionInParensInsideParens(aConditionMet)) {
michael@0 3793 if (!SkipUntil(')')) {
michael@0 3794 REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF);
michael@0 3795 return false;
michael@0 3796 }
michael@0 3797 aConditionMet = false;
michael@0 3798 return true;
michael@0 3799 }
michael@0 3800
michael@0 3801 if (!(ExpectSymbol(')', true))) {
michael@0 3802 SkipUntil(')');
michael@0 3803 aConditionMet = false;
michael@0 3804 return true;
michael@0 3805 }
michael@0 3806
michael@0 3807 return true;
michael@0 3808 }
michael@0 3809
michael@0 3810 // supports_condition_in_parens_inside_parens
michael@0 3811 // : core_declaration
michael@0 3812 // | supports_condition_negation
michael@0 3813 // | supports_condition_in_parens supports_condition_terms
michael@0 3814 // ;
michael@0 3815 bool
michael@0 3816 CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet)
michael@0 3817 {
michael@0 3818 if (!GetToken(true)) {
michael@0 3819 return false;
michael@0 3820 }
michael@0 3821
michael@0 3822 if (mToken.mType == eCSSToken_Ident) {
michael@0 3823 if (!mToken.mIdent.LowerCaseEqualsLiteral("not")) {
michael@0 3824 nsAutoString propertyName = mToken.mIdent;
michael@0 3825 if (!ExpectSymbol(':', true)) {
michael@0 3826 return false;
michael@0 3827 }
michael@0 3828
michael@0 3829 nsCSSProperty propID = LookupEnabledProperty(propertyName);
michael@0 3830 if (propID == eCSSProperty_UNKNOWN) {
michael@0 3831 if (ExpectSymbol(')', true)) {
michael@0 3832 UngetToken();
michael@0 3833 return false;
michael@0 3834 }
michael@0 3835 aConditionMet = false;
michael@0 3836 SkipUntil(')');
michael@0 3837 UngetToken();
michael@0 3838 } else if (propID == eCSSPropertyExtra_variable) {
michael@0 3839 if (ExpectSymbol(')', false)) {
michael@0 3840 UngetToken();
michael@0 3841 return false;
michael@0 3842 }
michael@0 3843 CSSVariableDeclarations::Type variableType;
michael@0 3844 nsString variableValue;
michael@0 3845 aConditionMet =
michael@0 3846 ParseVariableDeclaration(&variableType, variableValue) &&
michael@0 3847 ParsePriority() != ePriority_Error;
michael@0 3848 if (!aConditionMet) {
michael@0 3849 SkipUntil(')');
michael@0 3850 UngetToken();
michael@0 3851 }
michael@0 3852 } else {
michael@0 3853 if (ExpectSymbol(')', true)) {
michael@0 3854 UngetToken();
michael@0 3855 return false;
michael@0 3856 }
michael@0 3857 aConditionMet = ParseProperty(propID) &&
michael@0 3858 ParsePriority() != ePriority_Error;
michael@0 3859 if (!aConditionMet) {
michael@0 3860 SkipUntil(')');
michael@0 3861 UngetToken();
michael@0 3862 }
michael@0 3863 mTempData.ClearProperty(propID);
michael@0 3864 mTempData.AssertInitialState();
michael@0 3865 }
michael@0 3866 return true;
michael@0 3867 }
michael@0 3868
michael@0 3869 UngetToken();
michael@0 3870 return ParseSupportsConditionNegation(aConditionMet);
michael@0 3871 }
michael@0 3872
michael@0 3873 UngetToken();
michael@0 3874 return ParseSupportsConditionInParens(aConditionMet) &&
michael@0 3875 ParseSupportsConditionTerms(aConditionMet);
michael@0 3876 }
michael@0 3877
michael@0 3878 // supports_condition_terms
michael@0 3879 // : S+ 'and' supports_condition_terms_after_operator('and')
michael@0 3880 // | S+ 'or' supports_condition_terms_after_operator('or')
michael@0 3881 // |
michael@0 3882 // ;
michael@0 3883 bool
michael@0 3884 CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet)
michael@0 3885 {
michael@0 3886 if (!RequireWhitespace() || !GetToken(false)) {
michael@0 3887 return true;
michael@0 3888 }
michael@0 3889
michael@0 3890 if (mToken.mType != eCSSToken_Ident) {
michael@0 3891 UngetToken();
michael@0 3892 return true;
michael@0 3893 }
michael@0 3894
michael@0 3895 if (mToken.mIdent.LowerCaseEqualsLiteral("and")) {
michael@0 3896 return ParseSupportsConditionTermsAfterOperator(aConditionMet, eAnd);
michael@0 3897 }
michael@0 3898
michael@0 3899 if (mToken.mIdent.LowerCaseEqualsLiteral("or")) {
michael@0 3900 return ParseSupportsConditionTermsAfterOperator(aConditionMet, eOr);
michael@0 3901 }
michael@0 3902
michael@0 3903 UngetToken();
michael@0 3904 return true;
michael@0 3905 }
michael@0 3906
michael@0 3907 // supports_condition_terms_after_operator(operator)
michael@0 3908 // : S+ supports_condition_in_parens ( <operator> supports_condition_in_parens )*
michael@0 3909 // ;
michael@0 3910 bool
michael@0 3911 CSSParserImpl::ParseSupportsConditionTermsAfterOperator(
michael@0 3912 bool& aConditionMet,
michael@0 3913 CSSParserImpl::SupportsConditionTermOperator aOperator)
michael@0 3914 {
michael@0 3915 if (!RequireWhitespace()) {
michael@0 3916 REPORT_UNEXPECTED(PESupportsWhitespaceRequired);
michael@0 3917 return false;
michael@0 3918 }
michael@0 3919
michael@0 3920 const char* token = aOperator == eAnd ? "and" : "or";
michael@0 3921 for (;;) {
michael@0 3922 bool termConditionMet = false;
michael@0 3923 if (!ParseSupportsConditionInParens(termConditionMet)) {
michael@0 3924 return false;
michael@0 3925 }
michael@0 3926 aConditionMet = aOperator == eAnd ? aConditionMet && termConditionMet :
michael@0 3927 aConditionMet || termConditionMet;
michael@0 3928
michael@0 3929 if (!GetToken(true)) {
michael@0 3930 return true;
michael@0 3931 }
michael@0 3932
michael@0 3933 if (mToken.mType != eCSSToken_Ident ||
michael@0 3934 !mToken.mIdent.LowerCaseEqualsASCII(token)) {
michael@0 3935 UngetToken();
michael@0 3936 return true;
michael@0 3937 }
michael@0 3938 }
michael@0 3939 }
michael@0 3940
michael@0 3941 bool
michael@0 3942 CSSParserImpl::SkipUntil(char16_t aStopSymbol)
michael@0 3943 {
michael@0 3944 nsCSSToken* tk = &mToken;
michael@0 3945 nsAutoTArray<char16_t, 16> stack;
michael@0 3946 stack.AppendElement(aStopSymbol);
michael@0 3947 for (;;) {
michael@0 3948 if (!GetToken(true)) {
michael@0 3949 return false;
michael@0 3950 }
michael@0 3951 if (eCSSToken_Symbol == tk->mType) {
michael@0 3952 char16_t symbol = tk->mSymbol;
michael@0 3953 uint32_t stackTopIndex = stack.Length() - 1;
michael@0 3954 if (symbol == stack.ElementAt(stackTopIndex)) {
michael@0 3955 stack.RemoveElementAt(stackTopIndex);
michael@0 3956 if (stackTopIndex == 0) {
michael@0 3957 return true;
michael@0 3958 }
michael@0 3959
michael@0 3960 // Just handle out-of-memory by parsing incorrectly. It's
michael@0 3961 // highly unlikely we're dealing with a legitimate style sheet
michael@0 3962 // anyway.
michael@0 3963 } else if ('{' == symbol) {
michael@0 3964 stack.AppendElement('}');
michael@0 3965 } else if ('[' == symbol) {
michael@0 3966 stack.AppendElement(']');
michael@0 3967 } else if ('(' == symbol) {
michael@0 3968 stack.AppendElement(')');
michael@0 3969 }
michael@0 3970 } else if (eCSSToken_Function == tk->mType ||
michael@0 3971 eCSSToken_Bad_URL == tk->mType) {
michael@0 3972 stack.AppendElement(')');
michael@0 3973 }
michael@0 3974 }
michael@0 3975 }
michael@0 3976
michael@0 3977 bool
michael@0 3978 CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol)
michael@0 3979 {
michael@0 3980 nsCSSToken* tk = &mToken;
michael@0 3981 nsAutoTArray<char16_t, 16> stack;
michael@0 3982 stack.AppendElement(aStopSymbol);
michael@0 3983 for (;;) {
michael@0 3984 if (!GetToken(true)) {
michael@0 3985 return true;
michael@0 3986 }
michael@0 3987 if (eCSSToken_Symbol == tk->mType) {
michael@0 3988 char16_t symbol = tk->mSymbol;
michael@0 3989 uint32_t stackTopIndex = stack.Length() - 1;
michael@0 3990 if (symbol == stack.ElementAt(stackTopIndex)) {
michael@0 3991 stack.RemoveElementAt(stackTopIndex);
michael@0 3992 if (stackTopIndex == 0) {
michael@0 3993 return true;
michael@0 3994 }
michael@0 3995
michael@0 3996 // Just handle out-of-memory by parsing incorrectly. It's
michael@0 3997 // highly unlikely we're dealing with a legitimate style sheet
michael@0 3998 // anyway.
michael@0 3999 } else if ('{' == symbol) {
michael@0 4000 stack.AppendElement('}');
michael@0 4001 } else if ('[' == symbol) {
michael@0 4002 stack.AppendElement(']');
michael@0 4003 } else if ('(' == symbol) {
michael@0 4004 stack.AppendElement(')');
michael@0 4005 } else if (')' == symbol ||
michael@0 4006 ']' == symbol ||
michael@0 4007 '}' == symbol) {
michael@0 4008 UngetToken();
michael@0 4009 return false;
michael@0 4010 }
michael@0 4011 } else if (eCSSToken_Function == tk->mType ||
michael@0 4012 eCSSToken_Bad_URL == tk->mType) {
michael@0 4013 stack.AppendElement(')');
michael@0 4014 }
michael@0 4015 }
michael@0 4016 }
michael@0 4017
michael@0 4018 void
michael@0 4019 CSSParserImpl::SkipUntilOneOf(const char16_t* aStopSymbolChars)
michael@0 4020 {
michael@0 4021 nsCSSToken* tk = &mToken;
michael@0 4022 nsDependentString stopSymbolChars(aStopSymbolChars);
michael@0 4023 for (;;) {
michael@0 4024 if (!GetToken(true)) {
michael@0 4025 break;
michael@0 4026 }
michael@0 4027 if (eCSSToken_Symbol == tk->mType) {
michael@0 4028 char16_t symbol = tk->mSymbol;
michael@0 4029 if (stopSymbolChars.FindChar(symbol) != -1) {
michael@0 4030 break;
michael@0 4031 } else if ('{' == symbol) {
michael@0 4032 SkipUntil('}');
michael@0 4033 } else if ('[' == symbol) {
michael@0 4034 SkipUntil(']');
michael@0 4035 } else if ('(' == symbol) {
michael@0 4036 SkipUntil(')');
michael@0 4037 }
michael@0 4038 } else if (eCSSToken_Function == tk->mType ||
michael@0 4039 eCSSToken_Bad_URL == tk->mType) {
michael@0 4040 SkipUntil(')');
michael@0 4041 }
michael@0 4042 }
michael@0 4043 }
michael@0 4044
michael@0 4045 void
michael@0 4046 CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars)
michael@0 4047 {
michael@0 4048 uint32_t i = aStopSymbolChars.Length();
michael@0 4049 while (i--) {
michael@0 4050 SkipUntil(aStopSymbolChars[i]);
michael@0 4051 }
michael@0 4052 }
michael@0 4053
michael@0 4054 bool
michael@0 4055 CSSParserImpl::SkipDeclaration(bool aCheckForBraces)
michael@0 4056 {
michael@0 4057 nsCSSToken* tk = &mToken;
michael@0 4058 for (;;) {
michael@0 4059 if (!GetToken(true)) {
michael@0 4060 if (aCheckForBraces) {
michael@0 4061 REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
michael@0 4062 }
michael@0 4063 return false;
michael@0 4064 }
michael@0 4065 if (eCSSToken_Symbol == tk->mType) {
michael@0 4066 char16_t symbol = tk->mSymbol;
michael@0 4067 if (';' == symbol) {
michael@0 4068 break;
michael@0 4069 }
michael@0 4070 if (aCheckForBraces) {
michael@0 4071 if ('}' == symbol) {
michael@0 4072 UngetToken();
michael@0 4073 break;
michael@0 4074 }
michael@0 4075 }
michael@0 4076 if ('{' == symbol) {
michael@0 4077 SkipUntil('}');
michael@0 4078 } else if ('(' == symbol) {
michael@0 4079 SkipUntil(')');
michael@0 4080 } else if ('[' == symbol) {
michael@0 4081 SkipUntil(']');
michael@0 4082 }
michael@0 4083 } else if (eCSSToken_Function == tk->mType ||
michael@0 4084 eCSSToken_Bad_URL == tk->mType) {
michael@0 4085 SkipUntil(')');
michael@0 4086 }
michael@0 4087 }
michael@0 4088 return true;
michael@0 4089 }
michael@0 4090
michael@0 4091 void
michael@0 4092 CSSParserImpl::SkipRuleSet(bool aInsideBraces)
michael@0 4093 {
michael@0 4094 nsCSSToken* tk = &mToken;
michael@0 4095 for (;;) {
michael@0 4096 if (!GetToken(true)) {
michael@0 4097 REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
michael@0 4098 break;
michael@0 4099 }
michael@0 4100 if (eCSSToken_Symbol == tk->mType) {
michael@0 4101 char16_t symbol = tk->mSymbol;
michael@0 4102 if ('}' == symbol && aInsideBraces) {
michael@0 4103 // leave block closer for higher-level grammar to consume
michael@0 4104 UngetToken();
michael@0 4105 break;
michael@0 4106 } else if ('{' == symbol) {
michael@0 4107 SkipUntil('}');
michael@0 4108 break;
michael@0 4109 } else if ('(' == symbol) {
michael@0 4110 SkipUntil(')');
michael@0 4111 } else if ('[' == symbol) {
michael@0 4112 SkipUntil(']');
michael@0 4113 }
michael@0 4114 } else if (eCSSToken_Function == tk->mType ||
michael@0 4115 eCSSToken_Bad_URL == tk->mType) {
michael@0 4116 SkipUntil(')');
michael@0 4117 }
michael@0 4118 }
michael@0 4119 }
michael@0 4120
michael@0 4121 void
michael@0 4122 CSSParserImpl::PushGroup(css::GroupRule* aRule)
michael@0 4123 {
michael@0 4124 mGroupStack.AppendElement(aRule);
michael@0 4125 }
michael@0 4126
michael@0 4127 void
michael@0 4128 CSSParserImpl::PopGroup()
michael@0 4129 {
michael@0 4130 uint32_t count = mGroupStack.Length();
michael@0 4131 if (0 < count) {
michael@0 4132 mGroupStack.RemoveElementAt(count - 1);
michael@0 4133 }
michael@0 4134 }
michael@0 4135
michael@0 4136 void
michael@0 4137 CSSParserImpl::AppendRule(css::Rule* aRule)
michael@0 4138 {
michael@0 4139 uint32_t count = mGroupStack.Length();
michael@0 4140 if (0 < count) {
michael@0 4141 mGroupStack[count - 1]->AppendStyleRule(aRule);
michael@0 4142 }
michael@0 4143 else {
michael@0 4144 mSheet->AppendStyleRule(aRule);
michael@0 4145 }
michael@0 4146 }
michael@0 4147
michael@0 4148 bool
michael@0 4149 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData,
michael@0 4150 bool aInsideBraces)
michael@0 4151 {
michael@0 4152 // First get the list of selectors for the rule
michael@0 4153 nsCSSSelectorList* slist = nullptr;
michael@0 4154 uint32_t linenum, colnum;
michael@0 4155 if (!GetNextTokenLocation(true, &linenum, &colnum) ||
michael@0 4156 !ParseSelectorList(slist, char16_t('{'))) {
michael@0 4157 REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
michael@0 4158 OUTPUT_ERROR();
michael@0 4159 SkipRuleSet(aInsideBraces);
michael@0 4160 return false;
michael@0 4161 }
michael@0 4162 NS_ASSERTION(nullptr != slist, "null selector list");
michael@0 4163 CLEAR_ERROR();
michael@0 4164
michael@0 4165 // Next parse the declaration block
michael@0 4166 uint32_t parseFlags = eParseDeclaration_InBraces |
michael@0 4167 eParseDeclaration_AllowImportant;
michael@0 4168 css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
michael@0 4169 if (nullptr == declaration) {
michael@0 4170 delete slist;
michael@0 4171 return false;
michael@0 4172 }
michael@0 4173
michael@0 4174 #if 0
michael@0 4175 slist->Dump();
michael@0 4176 fputs("{\n", stdout);
michael@0 4177 declaration->List();
michael@0 4178 fputs("}\n", stdout);
michael@0 4179 #endif
michael@0 4180
michael@0 4181 // Translate the selector list and declaration block into style data
michael@0 4182
michael@0 4183 nsRefPtr<css::StyleRule> rule = new css::StyleRule(slist, declaration);
michael@0 4184 rule->SetLineNumberAndColumnNumber(linenum, colnum);
michael@0 4185 (*aAppendFunc)(rule, aData);
michael@0 4186
michael@0 4187 return true;
michael@0 4188 }
michael@0 4189
michael@0 4190 bool
michael@0 4191 CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
michael@0 4192 char16_t aStopChar)
michael@0 4193 {
michael@0 4194 nsCSSSelectorList* list = nullptr;
michael@0 4195 if (! ParseSelectorGroup(list)) {
michael@0 4196 // must have at least one selector group
michael@0 4197 aListHead = nullptr;
michael@0 4198 return false;
michael@0 4199 }
michael@0 4200 NS_ASSERTION(nullptr != list, "no selector list");
michael@0 4201 aListHead = list;
michael@0 4202
michael@0 4203 // After that there must either be a "," or a "{" (the latter if
michael@0 4204 // StopChar is nonzero)
michael@0 4205 nsCSSToken* tk = &mToken;
michael@0 4206 for (;;) {
michael@0 4207 if (! GetToken(true)) {
michael@0 4208 if (aStopChar == char16_t(0)) {
michael@0 4209 return true;
michael@0 4210 }
michael@0 4211
michael@0 4212 REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
michael@0 4213 break;
michael@0 4214 }
michael@0 4215
michael@0 4216 if (eCSSToken_Symbol == tk->mType) {
michael@0 4217 if (',' == tk->mSymbol) {
michael@0 4218 nsCSSSelectorList* newList = nullptr;
michael@0 4219 // Another selector group must follow
michael@0 4220 if (! ParseSelectorGroup(newList)) {
michael@0 4221 break;
michael@0 4222 }
michael@0 4223 // add new list to the end of the selector list
michael@0 4224 list->mNext = newList;
michael@0 4225 list = newList;
michael@0 4226 continue;
michael@0 4227 } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) {
michael@0 4228 UngetToken();
michael@0 4229 return true;
michael@0 4230 }
michael@0 4231 }
michael@0 4232 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
michael@0 4233 UngetToken();
michael@0 4234 break;
michael@0 4235 }
michael@0 4236
michael@0 4237 delete aListHead;
michael@0 4238 aListHead = nullptr;
michael@0 4239 return false;
michael@0 4240 }
michael@0 4241
michael@0 4242 static bool IsUniversalSelector(const nsCSSSelector& aSelector)
michael@0 4243 {
michael@0 4244 return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
michael@0 4245 (aSelector.mLowercaseTag == nullptr) &&
michael@0 4246 (aSelector.mIDList == nullptr) &&
michael@0 4247 (aSelector.mClassList == nullptr) &&
michael@0 4248 (aSelector.mAttrList == nullptr) &&
michael@0 4249 (aSelector.mNegations == nullptr) &&
michael@0 4250 (aSelector.mPseudoClassList == nullptr));
michael@0 4251 }
michael@0 4252
michael@0 4253 bool
michael@0 4254 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
michael@0 4255 {
michael@0 4256 char16_t combinator = 0;
michael@0 4257 nsAutoPtr<nsCSSSelectorList> list(new nsCSSSelectorList());
michael@0 4258
michael@0 4259 for (;;) {
michael@0 4260 if (!ParseSelector(list, combinator)) {
michael@0 4261 return false;
michael@0 4262 }
michael@0 4263
michael@0 4264 // Look for a combinator.
michael@0 4265 if (!GetToken(false)) {
michael@0 4266 break; // EOF ok here
michael@0 4267 }
michael@0 4268
michael@0 4269 combinator = char16_t(0);
michael@0 4270 if (mToken.mType == eCSSToken_Whitespace) {
michael@0 4271 if (!GetToken(true)) {
michael@0 4272 break; // EOF ok here
michael@0 4273 }
michael@0 4274 combinator = char16_t(' ');
michael@0 4275 }
michael@0 4276
michael@0 4277 if (mToken.mType != eCSSToken_Symbol) {
michael@0 4278 UngetToken(); // not a combinator
michael@0 4279 } else {
michael@0 4280 char16_t symbol = mToken.mSymbol;
michael@0 4281 if (symbol == '+' || symbol == '>' || symbol == '~') {
michael@0 4282 combinator = mToken.mSymbol;
michael@0 4283 } else {
michael@0 4284 UngetToken(); // not a combinator
michael@0 4285 if (symbol == ',' || symbol == '{' || symbol == ')') {
michael@0 4286 break; // end of selector group
michael@0 4287 }
michael@0 4288 }
michael@0 4289 }
michael@0 4290
michael@0 4291 if (!combinator) {
michael@0 4292 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
michael@0 4293 return false;
michael@0 4294 }
michael@0 4295 }
michael@0 4296
michael@0 4297 aList = list.forget();
michael@0 4298 return true;
michael@0 4299 }
michael@0 4300
michael@0 4301 #define SEL_MASK_NSPACE 0x01
michael@0 4302 #define SEL_MASK_ELEM 0x02
michael@0 4303 #define SEL_MASK_ID 0x04
michael@0 4304 #define SEL_MASK_CLASS 0x08
michael@0 4305 #define SEL_MASK_ATTRIB 0x10
michael@0 4306 #define SEL_MASK_PCLASS 0x20
michael@0 4307 #define SEL_MASK_PELEM 0x40
michael@0 4308
michael@0 4309 //
michael@0 4310 // Parses an ID selector #name
michael@0 4311 //
michael@0 4312 CSSParserImpl::nsSelectorParsingStatus
michael@0 4313 CSSParserImpl::ParseIDSelector(int32_t& aDataMask,
michael@0 4314 nsCSSSelector& aSelector)
michael@0 4315 {
michael@0 4316 NS_ASSERTION(!mToken.mIdent.IsEmpty(),
michael@0 4317 "Empty mIdent in eCSSToken_ID token?");
michael@0 4318 aDataMask |= SEL_MASK_ID;
michael@0 4319 aSelector.AddID(mToken.mIdent);
michael@0 4320 return eSelectorParsingStatus_Continue;
michael@0 4321 }
michael@0 4322
michael@0 4323 //
michael@0 4324 // Parses a class selector .name
michael@0 4325 //
michael@0 4326 CSSParserImpl::nsSelectorParsingStatus
michael@0 4327 CSSParserImpl::ParseClassSelector(int32_t& aDataMask,
michael@0 4328 nsCSSSelector& aSelector)
michael@0 4329 {
michael@0 4330 if (! GetToken(false)) { // get ident
michael@0 4331 REPORT_UNEXPECTED_EOF(PEClassSelEOF);
michael@0 4332 return eSelectorParsingStatus_Error;
michael@0 4333 }
michael@0 4334 if (eCSSToken_Ident != mToken.mType) { // malformed selector
michael@0 4335 REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
michael@0 4336 UngetToken();
michael@0 4337 return eSelectorParsingStatus_Error;
michael@0 4338 }
michael@0 4339 aDataMask |= SEL_MASK_CLASS;
michael@0 4340
michael@0 4341 aSelector.AddClass(mToken.mIdent);
michael@0 4342
michael@0 4343 return eSelectorParsingStatus_Continue;
michael@0 4344 }
michael@0 4345
michael@0 4346 //
michael@0 4347 // Parse a type element selector or a universal selector
michael@0 4348 // namespace|type or namespace|* or *|* or *
michael@0 4349 //
michael@0 4350 CSSParserImpl::nsSelectorParsingStatus
michael@0 4351 CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
michael@0 4352 nsCSSSelector& aSelector,
michael@0 4353 bool aIsNegated)
michael@0 4354 {
michael@0 4355 nsAutoString buffer;
michael@0 4356 if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
michael@0 4357 if (ExpectSymbol('|', false)) { // was namespace
michael@0 4358 aDataMask |= SEL_MASK_NSPACE;
michael@0 4359 aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
michael@0 4360
michael@0 4361 if (! GetToken(false)) {
michael@0 4362 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
michael@0 4363 return eSelectorParsingStatus_Error;
michael@0 4364 }
michael@0 4365 if (eCSSToken_Ident == mToken.mType) { // element name
michael@0 4366 aDataMask |= SEL_MASK_ELEM;
michael@0 4367
michael@0 4368 aSelector.SetTag(mToken.mIdent);
michael@0 4369 }
michael@0 4370 else if (mToken.IsSymbol('*')) { // universal selector
michael@0 4371 aDataMask |= SEL_MASK_ELEM;
michael@0 4372 // don't set tag
michael@0 4373 }
michael@0 4374 else {
michael@0 4375 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
michael@0 4376 UngetToken();
michael@0 4377 return eSelectorParsingStatus_Error;
michael@0 4378 }
michael@0 4379 }
michael@0 4380 else { // was universal element selector
michael@0 4381 SetDefaultNamespaceOnSelector(aSelector);
michael@0 4382 aDataMask |= SEL_MASK_ELEM;
michael@0 4383 // don't set any tag in the selector
michael@0 4384 }
michael@0 4385 if (! GetToken(false)) { // premature eof is ok (here!)
michael@0 4386 return eSelectorParsingStatus_Done;
michael@0 4387 }
michael@0 4388 }
michael@0 4389 else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
michael@0 4390 buffer = mToken.mIdent; // hang on to ident
michael@0 4391
michael@0 4392 if (ExpectSymbol('|', false)) { // was namespace
michael@0 4393 aDataMask |= SEL_MASK_NSPACE;
michael@0 4394 int32_t nameSpaceID = GetNamespaceIdForPrefix(buffer);
michael@0 4395 if (nameSpaceID == kNameSpaceID_Unknown) {
michael@0 4396 return eSelectorParsingStatus_Error;
michael@0 4397 }
michael@0 4398 aSelector.SetNameSpace(nameSpaceID);
michael@0 4399
michael@0 4400 if (! GetToken(false)) {
michael@0 4401 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
michael@0 4402 return eSelectorParsingStatus_Error;
michael@0 4403 }
michael@0 4404 if (eCSSToken_Ident == mToken.mType) { // element name
michael@0 4405 aDataMask |= SEL_MASK_ELEM;
michael@0 4406 aSelector.SetTag(mToken.mIdent);
michael@0 4407 }
michael@0 4408 else if (mToken.IsSymbol('*')) { // universal selector
michael@0 4409 aDataMask |= SEL_MASK_ELEM;
michael@0 4410 // don't set tag
michael@0 4411 }
michael@0 4412 else {
michael@0 4413 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
michael@0 4414 UngetToken();
michael@0 4415 return eSelectorParsingStatus_Error;
michael@0 4416 }
michael@0 4417 }
michael@0 4418 else { // was element name
michael@0 4419 SetDefaultNamespaceOnSelector(aSelector);
michael@0 4420 aSelector.SetTag(buffer);
michael@0 4421
michael@0 4422 aDataMask |= SEL_MASK_ELEM;
michael@0 4423 }
michael@0 4424 if (! GetToken(false)) { // premature eof is ok (here!)
michael@0 4425 return eSelectorParsingStatus_Done;
michael@0 4426 }
michael@0 4427 }
michael@0 4428 else if (mToken.IsSymbol('|')) { // No namespace
michael@0 4429 aDataMask |= SEL_MASK_NSPACE;
michael@0 4430 aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
michael@0 4431
michael@0 4432 // get mandatory tag
michael@0 4433 if (! GetToken(false)) {
michael@0 4434 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
michael@0 4435 return eSelectorParsingStatus_Error;
michael@0 4436 }
michael@0 4437 if (eCSSToken_Ident == mToken.mType) { // element name
michael@0 4438 aDataMask |= SEL_MASK_ELEM;
michael@0 4439 aSelector.SetTag(mToken.mIdent);
michael@0 4440 }
michael@0 4441 else if (mToken.IsSymbol('*')) { // universal selector
michael@0 4442 aDataMask |= SEL_MASK_ELEM;
michael@0 4443 // don't set tag
michael@0 4444 }
michael@0 4445 else {
michael@0 4446 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
michael@0 4447 UngetToken();
michael@0 4448 return eSelectorParsingStatus_Error;
michael@0 4449 }
michael@0 4450 if (! GetToken(false)) { // premature eof is ok (here!)
michael@0 4451 return eSelectorParsingStatus_Done;
michael@0 4452 }
michael@0 4453 }
michael@0 4454 else {
michael@0 4455 SetDefaultNamespaceOnSelector(aSelector);
michael@0 4456 }
michael@0 4457
michael@0 4458 if (aIsNegated) {
michael@0 4459 // restore last token read in case of a negated type selector
michael@0 4460 UngetToken();
michael@0 4461 }
michael@0 4462 return eSelectorParsingStatus_Continue;
michael@0 4463 }
michael@0 4464
michael@0 4465 //
michael@0 4466 // Parse attribute selectors [attr], [attr=value], [attr|=value],
michael@0 4467 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
michael@0 4468 //
michael@0 4469 CSSParserImpl::nsSelectorParsingStatus
michael@0 4470 CSSParserImpl::ParseAttributeSelector(int32_t& aDataMask,
michael@0 4471 nsCSSSelector& aSelector)
michael@0 4472 {
michael@0 4473 if (! GetToken(true)) { // premature EOF
michael@0 4474 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
michael@0 4475 return eSelectorParsingStatus_Error;
michael@0 4476 }
michael@0 4477
michael@0 4478 int32_t nameSpaceID = kNameSpaceID_None;
michael@0 4479 nsAutoString attr;
michael@0 4480 if (mToken.IsSymbol('*')) { // wildcard namespace
michael@0 4481 nameSpaceID = kNameSpaceID_Unknown;
michael@0 4482 if (ExpectSymbol('|', false)) {
michael@0 4483 if (! GetToken(false)) { // premature EOF
michael@0 4484 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
michael@0 4485 return eSelectorParsingStatus_Error;
michael@0 4486 }
michael@0 4487 if (eCSSToken_Ident == mToken.mType) { // attr name
michael@0 4488 attr = mToken.mIdent;
michael@0 4489 }
michael@0 4490 else {
michael@0 4491 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
michael@0 4492 UngetToken();
michael@0 4493 return eSelectorParsingStatus_Error;
michael@0 4494 }
michael@0 4495 }
michael@0 4496 else {
michael@0 4497 REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
michael@0 4498 return eSelectorParsingStatus_Error;
michael@0 4499 }
michael@0 4500 }
michael@0 4501 else if (mToken.IsSymbol('|')) { // NO namespace
michael@0 4502 if (! GetToken(false)) { // premature EOF
michael@0 4503 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
michael@0 4504 return eSelectorParsingStatus_Error;
michael@0 4505 }
michael@0 4506 if (eCSSToken_Ident == mToken.mType) { // attr name
michael@0 4507 attr = mToken.mIdent;
michael@0 4508 }
michael@0 4509 else {
michael@0 4510 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
michael@0 4511 UngetToken();
michael@0 4512 return eSelectorParsingStatus_Error;
michael@0 4513 }
michael@0 4514 }
michael@0 4515 else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
michael@0 4516 attr = mToken.mIdent; // hang on to it
michael@0 4517 if (ExpectSymbol('|', false)) { // was a namespace
michael@0 4518 nameSpaceID = GetNamespaceIdForPrefix(attr);
michael@0 4519 if (nameSpaceID == kNameSpaceID_Unknown) {
michael@0 4520 return eSelectorParsingStatus_Error;
michael@0 4521 }
michael@0 4522 if (! GetToken(false)) { // premature EOF
michael@0 4523 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
michael@0 4524 return eSelectorParsingStatus_Error;
michael@0 4525 }
michael@0 4526 if (eCSSToken_Ident == mToken.mType) { // attr name
michael@0 4527 attr = mToken.mIdent;
michael@0 4528 }
michael@0 4529 else {
michael@0 4530 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
michael@0 4531 UngetToken();
michael@0 4532 return eSelectorParsingStatus_Error;
michael@0 4533 }
michael@0 4534 }
michael@0 4535 }
michael@0 4536 else { // malformed
michael@0 4537 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
michael@0 4538 UngetToken();
michael@0 4539 return eSelectorParsingStatus_Error;
michael@0 4540 }
michael@0 4541
michael@0 4542 if (! GetToken(true)) { // premature EOF
michael@0 4543 REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
michael@0 4544 return eSelectorParsingStatus_Error;
michael@0 4545 }
michael@0 4546 if ((eCSSToken_Symbol == mToken.mType) ||
michael@0 4547 (eCSSToken_Includes == mToken.mType) ||
michael@0 4548 (eCSSToken_Dashmatch == mToken.mType) ||
michael@0 4549 (eCSSToken_Beginsmatch == mToken.mType) ||
michael@0 4550 (eCSSToken_Endsmatch == mToken.mType) ||
michael@0 4551 (eCSSToken_Containsmatch == mToken.mType)) {
michael@0 4552 uint8_t func;
michael@0 4553 if (eCSSToken_Includes == mToken.mType) {
michael@0 4554 func = NS_ATTR_FUNC_INCLUDES;
michael@0 4555 }
michael@0 4556 else if (eCSSToken_Dashmatch == mToken.mType) {
michael@0 4557 func = NS_ATTR_FUNC_DASHMATCH;
michael@0 4558 }
michael@0 4559 else if (eCSSToken_Beginsmatch == mToken.mType) {
michael@0 4560 func = NS_ATTR_FUNC_BEGINSMATCH;
michael@0 4561 }
michael@0 4562 else if (eCSSToken_Endsmatch == mToken.mType) {
michael@0 4563 func = NS_ATTR_FUNC_ENDSMATCH;
michael@0 4564 }
michael@0 4565 else if (eCSSToken_Containsmatch == mToken.mType) {
michael@0 4566 func = NS_ATTR_FUNC_CONTAINSMATCH;
michael@0 4567 }
michael@0 4568 else if (']' == mToken.mSymbol) {
michael@0 4569 aDataMask |= SEL_MASK_ATTRIB;
michael@0 4570 aSelector.AddAttribute(nameSpaceID, attr);
michael@0 4571 func = NS_ATTR_FUNC_SET;
michael@0 4572 }
michael@0 4573 else if ('=' == mToken.mSymbol) {
michael@0 4574 func = NS_ATTR_FUNC_EQUALS;
michael@0 4575 }
michael@0 4576 else {
michael@0 4577 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
michael@0 4578 UngetToken(); // bad function
michael@0 4579 return eSelectorParsingStatus_Error;
michael@0 4580 }
michael@0 4581 if (NS_ATTR_FUNC_SET != func) { // get value
michael@0 4582 if (! GetToken(true)) { // premature EOF
michael@0 4583 REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
michael@0 4584 return eSelectorParsingStatus_Error;
michael@0 4585 }
michael@0 4586 if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
michael@0 4587 nsAutoString value(mToken.mIdent);
michael@0 4588 if (! GetToken(true)) { // premature EOF
michael@0 4589 REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
michael@0 4590 return eSelectorParsingStatus_Error;
michael@0 4591 }
michael@0 4592 if (mToken.IsSymbol(']')) {
michael@0 4593 bool isCaseSensitive = true;
michael@0 4594
michael@0 4595 // For cases when this style sheet is applied to an HTML
michael@0 4596 // element in an HTML document, and the attribute selector is
michael@0 4597 // for a non-namespaced attribute, then check to see if it's
michael@0 4598 // one of the known attributes whose VALUE is
michael@0 4599 // case-insensitive.
michael@0 4600 if (nameSpaceID == kNameSpaceID_None) {
michael@0 4601 static const char* caseInsensitiveHTMLAttribute[] = {
michael@0 4602 // list based on http://www.w3.org/TR/html4/
michael@0 4603 "lang",
michael@0 4604 "dir",
michael@0 4605 "http-equiv",
michael@0 4606 "text",
michael@0 4607 "link",
michael@0 4608 "vlink",
michael@0 4609 "alink",
michael@0 4610 "compact",
michael@0 4611 "align",
michael@0 4612 "frame",
michael@0 4613 "rules",
michael@0 4614 "valign",
michael@0 4615 "scope",
michael@0 4616 "axis",
michael@0 4617 "nowrap",
michael@0 4618 "hreflang",
michael@0 4619 "rel",
michael@0 4620 "rev",
michael@0 4621 "charset",
michael@0 4622 "codetype",
michael@0 4623 "declare",
michael@0 4624 "valuetype",
michael@0 4625 "shape",
michael@0 4626 "nohref",
michael@0 4627 "media",
michael@0 4628 "bgcolor",
michael@0 4629 "clear",
michael@0 4630 "color",
michael@0 4631 "face",
michael@0 4632 "noshade",
michael@0 4633 "noresize",
michael@0 4634 "scrolling",
michael@0 4635 "target",
michael@0 4636 "method",
michael@0 4637 "enctype",
michael@0 4638 "accept-charset",
michael@0 4639 "accept",
michael@0 4640 "checked",
michael@0 4641 "multiple",
michael@0 4642 "selected",
michael@0 4643 "disabled",
michael@0 4644 "readonly",
michael@0 4645 "language",
michael@0 4646 "defer",
michael@0 4647 "type",
michael@0 4648 // additional attributes not in HTML4
michael@0 4649 "direction", // marquee
michael@0 4650 nullptr
michael@0 4651 };
michael@0 4652 short i = 0;
michael@0 4653 const char* htmlAttr;
michael@0 4654 while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
michael@0 4655 if (attr.LowerCaseEqualsASCII(htmlAttr)) {
michael@0 4656 isCaseSensitive = false;
michael@0 4657 break;
michael@0 4658 }
michael@0 4659 }
michael@0 4660 }
michael@0 4661 aDataMask |= SEL_MASK_ATTRIB;
michael@0 4662 aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
michael@0 4663 }
michael@0 4664 else {
michael@0 4665 REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
michael@0 4666 UngetToken();
michael@0 4667 return eSelectorParsingStatus_Error;
michael@0 4668 }
michael@0 4669 }
michael@0 4670 else {
michael@0 4671 REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
michael@0 4672 UngetToken();
michael@0 4673 return eSelectorParsingStatus_Error;
michael@0 4674 }
michael@0 4675 }
michael@0 4676 }
michael@0 4677 else {
michael@0 4678 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
michael@0 4679 UngetToken(); // bad dog, no biscut!
michael@0 4680 return eSelectorParsingStatus_Error;
michael@0 4681 }
michael@0 4682 return eSelectorParsingStatus_Continue;
michael@0 4683 }
michael@0 4684
michael@0 4685 //
michael@0 4686 // Parse pseudo-classes and pseudo-elements
michael@0 4687 //
michael@0 4688 CSSParserImpl::nsSelectorParsingStatus
michael@0 4689 CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
michael@0 4690 nsCSSSelector& aSelector,
michael@0 4691 bool aIsNegated,
michael@0 4692 nsIAtom** aPseudoElement,
michael@0 4693 nsAtomList** aPseudoElementArgs,
michael@0 4694 nsCSSPseudoElements::Type* aPseudoElementType)
michael@0 4695 {
michael@0 4696 NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs),
michael@0 4697 "expected location to store pseudo element");
michael@0 4698 NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs),
michael@0 4699 "negated selectors shouldn't have a place to store "
michael@0 4700 "pseudo elements");
michael@0 4701 if (! GetToken(false)) { // premature eof
michael@0 4702 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
michael@0 4703 return eSelectorParsingStatus_Error;
michael@0 4704 }
michael@0 4705
michael@0 4706 // First, find out whether we are parsing a CSS3 pseudo-element
michael@0 4707 bool parsingPseudoElement = false;
michael@0 4708 if (mToken.IsSymbol(':')) {
michael@0 4709 parsingPseudoElement = true;
michael@0 4710 if (! GetToken(false)) { // premature eof
michael@0 4711 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
michael@0 4712 return eSelectorParsingStatus_Error;
michael@0 4713 }
michael@0 4714 }
michael@0 4715
michael@0 4716 // Do some sanity-checking on the token
michael@0 4717 if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
michael@0 4718 // malformed selector
michael@0 4719 REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
michael@0 4720 UngetToken();
michael@0 4721 return eSelectorParsingStatus_Error;
michael@0 4722 }
michael@0 4723
michael@0 4724 // OK, now we know we have an mIdent. Atomize it. All the atoms, for
michael@0 4725 // pseudo-classes as well as pseudo-elements, start with a single ':'.
michael@0 4726 nsAutoString buffer;
michael@0 4727 buffer.Append(char16_t(':'));
michael@0 4728 buffer.Append(mToken.mIdent);
michael@0 4729 nsContentUtils::ASCIIToLower(buffer);
michael@0 4730 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
michael@0 4731 if (!pseudo) {
michael@0 4732 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
michael@0 4733 }
michael@0 4734
michael@0 4735 // stash away some info about this pseudo so we only have to get it once.
michael@0 4736 bool isTreePseudo = false;
michael@0 4737 nsCSSPseudoElements::Type pseudoElementType =
michael@0 4738 nsCSSPseudoElements::GetPseudoType(pseudo);
michael@0 4739 nsCSSPseudoClasses::Type pseudoClassType =
michael@0 4740 nsCSSPseudoClasses::GetPseudoType(pseudo);
michael@0 4741 bool pseudoClassIsUserAction =
michael@0 4742 nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
michael@0 4743
michael@0 4744 if (!mUnsafeRulesEnabled &&
michael@0 4745 pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount &&
michael@0 4746 nsCSSPseudoElements::PseudoElementIsChromeOnly(pseudoElementType)) {
michael@0 4747 // This pseudo-element is not exposed to content.
michael@0 4748 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
michael@0 4749 UngetToken();
michael@0 4750 return eSelectorParsingStatus_Error;
michael@0 4751 }
michael@0 4752
michael@0 4753 // We currently allow :-moz-placeholder and ::-moz-placeholder. We have to
michael@0 4754 // be a bit stricter regarding the pseudo-element parsing rules.
michael@0 4755 if (pseudoElementType == nsCSSPseudoElements::ePseudo_mozPlaceholder &&
michael@0 4756 pseudoClassType == nsCSSPseudoClasses::ePseudoClass_mozPlaceholder) {
michael@0 4757 if (parsingPseudoElement) {
michael@0 4758 pseudoClassType = nsCSSPseudoClasses::ePseudoClass_NotPseudoClass;
michael@0 4759 } else {
michael@0 4760 pseudoElementType = nsCSSPseudoElements::ePseudo_NotPseudoElement;
michael@0 4761 }
michael@0 4762 }
michael@0 4763
michael@0 4764 #ifdef MOZ_XUL
michael@0 4765 isTreePseudo = (pseudoElementType == nsCSSPseudoElements::ePseudo_XULTree);
michael@0 4766 // If a tree pseudo-element is using the function syntax, it will
michael@0 4767 // get isTree set here and will pass the check below that only
michael@0 4768 // allows functions if they are in our list of things allowed to be
michael@0 4769 // functions. If it is _not_ using the function syntax, isTree will
michael@0 4770 // be false, and it will still pass that check. So the tree
michael@0 4771 // pseudo-elements are allowed to be either functions or not, as
michael@0 4772 // desired.
michael@0 4773 bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
michael@0 4774 #endif
michael@0 4775 bool isPseudoElement =
michael@0 4776 (pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount);
michael@0 4777 // anonymous boxes are only allowed if they're the tree boxes or we have
michael@0 4778 // enabled unsafe rules
michael@0 4779 bool isAnonBox = isTreePseudo ||
michael@0 4780 (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox &&
michael@0 4781 mUnsafeRulesEnabled);
michael@0 4782 bool isPseudoClass =
michael@0 4783 (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass);
michael@0 4784
michael@0 4785 NS_ASSERTION(!isPseudoClass ||
michael@0 4786 pseudoElementType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
michael@0 4787 "Why is this atom both a pseudo-class and a pseudo-element?");
michael@0 4788 NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1,
michael@0 4789 "Shouldn't be more than one of these");
michael@0 4790
michael@0 4791 if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
michael@0 4792 // Not a pseudo-class, not a pseudo-element.... forget it
michael@0 4793 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
michael@0 4794 UngetToken();
michael@0 4795 return eSelectorParsingStatus_Error;
michael@0 4796 }
michael@0 4797
michael@0 4798 // If it's a function token, it better be on our "ok" list, and if the name
michael@0 4799 // is that of a function pseudo it better be a function token
michael@0 4800 if ((eCSSToken_Function == mToken.mType) !=
michael@0 4801 (
michael@0 4802 #ifdef MOZ_XUL
michael@0 4803 isTree ||
michael@0 4804 #endif
michael@0 4805 nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType ||
michael@0 4806 nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
michael@0 4807 nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
michael@0 4808 nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
michael@0 4809 // There are no other function pseudos
michael@0 4810 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
michael@0 4811 UngetToken();
michael@0 4812 return eSelectorParsingStatus_Error;
michael@0 4813 }
michael@0 4814
michael@0 4815 // If it starts with "::", it better be a pseudo-element
michael@0 4816 if (parsingPseudoElement &&
michael@0 4817 !isPseudoElement &&
michael@0 4818 !isAnonBox) {
michael@0 4819 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
michael@0 4820 UngetToken();
michael@0 4821 return eSelectorParsingStatus_Error;
michael@0 4822 }
michael@0 4823
michael@0 4824 if (!parsingPseudoElement &&
michael@0 4825 nsCSSPseudoClasses::ePseudoClass_notPseudo == pseudoClassType) {
michael@0 4826 if (aIsNegated) { // :not() can't be itself negated
michael@0 4827 REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
michael@0 4828 UngetToken();
michael@0 4829 return eSelectorParsingStatus_Error;
michael@0 4830 }
michael@0 4831 // CSS 3 Negation pseudo-class takes one simple selector as argument
michael@0 4832 nsSelectorParsingStatus parsingStatus =
michael@0 4833 ParseNegatedSimpleSelector(aDataMask, aSelector);
michael@0 4834 if (eSelectorParsingStatus_Continue != parsingStatus) {
michael@0 4835 return parsingStatus;
michael@0 4836 }
michael@0 4837 }
michael@0 4838 else if (!parsingPseudoElement && isPseudoClass) {
michael@0 4839 if (aSelector.IsPseudoElement()) {
michael@0 4840 nsCSSPseudoElements::Type type = aSelector.PseudoType();
michael@0 4841 if (!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
michael@0 4842 // We only allow user action pseudo-classes on certain pseudo-elements.
michael@0 4843 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
michael@0 4844 UngetToken();
michael@0 4845 return eSelectorParsingStatus_Error;
michael@0 4846 }
michael@0 4847 if (!pseudoClassIsUserAction) {
michael@0 4848 // CSS 4 Selectors says that pseudo-elements can only be followed by
michael@0 4849 // a user action pseudo-class.
michael@0 4850 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
michael@0 4851 UngetToken();
michael@0 4852 return eSelectorParsingStatus_Error;
michael@0 4853 }
michael@0 4854 }
michael@0 4855 aDataMask |= SEL_MASK_PCLASS;
michael@0 4856 if (eCSSToken_Function == mToken.mType) {
michael@0 4857 nsSelectorParsingStatus parsingStatus;
michael@0 4858 if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
michael@0 4859 parsingStatus =
michael@0 4860 ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
michael@0 4861 }
michael@0 4862 else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) {
michael@0 4863 parsingStatus =
michael@0 4864 ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType);
michael@0 4865 }
michael@0 4866 else {
michael@0 4867 NS_ABORT_IF_FALSE(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
michael@0 4868 "unexpected pseudo with function token");
michael@0 4869 parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
michael@0 4870 pseudoClassType);
michael@0 4871 }
michael@0 4872 if (eSelectorParsingStatus_Continue != parsingStatus) {
michael@0 4873 if (eSelectorParsingStatus_Error == parsingStatus) {
michael@0 4874 SkipUntil(')');
michael@0 4875 }
michael@0 4876 return parsingStatus;
michael@0 4877 }
michael@0 4878 }
michael@0 4879 else {
michael@0 4880 aSelector.AddPseudoClass(pseudoClassType);
michael@0 4881 }
michael@0 4882 }
michael@0 4883 else if (isPseudoElement || isAnonBox) {
michael@0 4884 // Pseudo-element. Make some more sanity checks.
michael@0 4885
michael@0 4886 if (aIsNegated) { // pseudo-elements can't be negated
michael@0 4887 REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
michael@0 4888 UngetToken();
michael@0 4889 return eSelectorParsingStatus_Error;
michael@0 4890 }
michael@0 4891 // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
michael@0 4892 // to have a single ':' on them. Others (CSS3+ pseudo-elements and
michael@0 4893 // various -moz-* pseudo-elements) must have |parsingPseudoElement|
michael@0 4894 // set.
michael@0 4895 if (!parsingPseudoElement &&
michael@0 4896 !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
michael@0 4897 #ifdef MOZ_XUL
michael@0 4898 && !isTreePseudo
michael@0 4899 #endif
michael@0 4900 ) {
michael@0 4901 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
michael@0 4902 UngetToken();
michael@0 4903 return eSelectorParsingStatus_Error;
michael@0 4904 }
michael@0 4905
michael@0 4906 if (0 == (aDataMask & SEL_MASK_PELEM)) {
michael@0 4907 aDataMask |= SEL_MASK_PELEM;
michael@0 4908 NS_ADDREF(*aPseudoElement = pseudo);
michael@0 4909 *aPseudoElementType = pseudoElementType;
michael@0 4910
michael@0 4911 #ifdef MOZ_XUL
michael@0 4912 if (isTree) {
michael@0 4913 // We have encountered a pseudoelement of the form
michael@0 4914 // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each
michael@0 4915 // item in the list to the pseudoclass list. They will be pulled
michael@0 4916 // from the list later along with the pseudo-element.
michael@0 4917 if (!ParseTreePseudoElement(aPseudoElementArgs)) {
michael@0 4918 return eSelectorParsingStatus_Error;
michael@0 4919 }
michael@0 4920 }
michael@0 4921 #endif
michael@0 4922
michael@0 4923 // Pseudo-elements can only be followed by user action pseudo-classes
michael@0 4924 // or be the end of the selector. So the next non-whitespace token must
michael@0 4925 // be ':', '{' or ',' or EOF.
michael@0 4926 if (!GetToken(true)) { // premature eof is ok (here!)
michael@0 4927 return eSelectorParsingStatus_Done;
michael@0 4928 }
michael@0 4929 if (parsingPseudoElement && mToken.IsSymbol(':')) {
michael@0 4930 UngetToken();
michael@0 4931 return eSelectorParsingStatus_Continue;
michael@0 4932 }
michael@0 4933 if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
michael@0 4934 UngetToken();
michael@0 4935 return eSelectorParsingStatus_Done;
michael@0 4936 }
michael@0 4937 REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC);
michael@0 4938 UngetToken();
michael@0 4939 return eSelectorParsingStatus_Error;
michael@0 4940 }
michael@0 4941 else { // multiple pseudo elements, not legal
michael@0 4942 REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
michael@0 4943 UngetToken();
michael@0 4944 return eSelectorParsingStatus_Error;
michael@0 4945 }
michael@0 4946 }
michael@0 4947 #ifdef DEBUG
michael@0 4948 else {
michael@0 4949 // We should never end up here. Indeed, if we ended up here, we know (from
michael@0 4950 // the current if/else cascade) that !isPseudoElement and !isAnonBox. But
michael@0 4951 // then due to our earlier check we know that isPseudoClass. Since we
michael@0 4952 // didn't fall into the isPseudoClass case in this cascade, we must have
michael@0 4953 // parsingPseudoElement. But we've already checked the
michael@0 4954 // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
michael@0 4955 // it's happened.
michael@0 4956 NS_NOTREACHED("How did this happen?");
michael@0 4957 }
michael@0 4958 #endif
michael@0 4959 return eSelectorParsingStatus_Continue;
michael@0 4960 }
michael@0 4961
michael@0 4962 //
michael@0 4963 // Parse the argument of a negation pseudo-class :not()
michael@0 4964 //
michael@0 4965 CSSParserImpl::nsSelectorParsingStatus
michael@0 4966 CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask,
michael@0 4967 nsCSSSelector& aSelector)
michael@0 4968 {
michael@0 4969 if (! GetToken(true)) { // premature eof
michael@0 4970 REPORT_UNEXPECTED_EOF(PENegationEOF);
michael@0 4971 return eSelectorParsingStatus_Error;
michael@0 4972 }
michael@0 4973
michael@0 4974 if (mToken.IsSymbol(')')) {
michael@0 4975 REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
michael@0 4976 return eSelectorParsingStatus_Error;
michael@0 4977 }
michael@0 4978
michael@0 4979 // Create a new nsCSSSelector and add it to the end of
michael@0 4980 // aSelector.mNegations.
michael@0 4981 // Given the current parsing rules, every selector in mNegations
michael@0 4982 // contains only one simple selector (css3 definition) within it.
michael@0 4983 // This could easily change in future versions of CSS, and the only
michael@0 4984 // thing we need to change to support that is this parsing code and the
michael@0 4985 // serialization code for nsCSSSelector.
michael@0 4986 nsCSSSelector *newSel = new nsCSSSelector();
michael@0 4987 nsCSSSelector* negations = &aSelector;
michael@0 4988 while (negations->mNegations) {
michael@0 4989 negations = negations->mNegations;
michael@0 4990 }
michael@0 4991 negations->mNegations = newSel;
michael@0 4992
michael@0 4993 nsSelectorParsingStatus parsingStatus;
michael@0 4994 if (eCSSToken_ID == mToken.mType) { // #id
michael@0 4995 parsingStatus = ParseIDSelector(aDataMask, *newSel);
michael@0 4996 }
michael@0 4997 else if (mToken.IsSymbol('.')) { // .class
michael@0 4998 parsingStatus = ParseClassSelector(aDataMask, *newSel);
michael@0 4999 }
michael@0 5000 else if (mToken.IsSymbol(':')) { // :pseudo
michael@0 5001 parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true,
michael@0 5002 nullptr, nullptr, nullptr);
michael@0 5003 }
michael@0 5004 else if (mToken.IsSymbol('[')) { // [attribute
michael@0 5005 parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
michael@0 5006 if (eSelectorParsingStatus_Error == parsingStatus) {
michael@0 5007 // Skip forward to the matching ']'
michael@0 5008 SkipUntil(']');
michael@0 5009 }
michael@0 5010 }
michael@0 5011 else {
michael@0 5012 // then it should be a type element or universal selector
michael@0 5013 parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true);
michael@0 5014 }
michael@0 5015 if (eSelectorParsingStatus_Error == parsingStatus) {
michael@0 5016 REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
michael@0 5017 SkipUntil(')');
michael@0 5018 return parsingStatus;
michael@0 5019 }
michael@0 5020 // close the parenthesis
michael@0 5021 if (!ExpectSymbol(')', true)) {
michael@0 5022 REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
michael@0 5023 SkipUntil(')');
michael@0 5024 return eSelectorParsingStatus_Error;
michael@0 5025 }
michael@0 5026
michael@0 5027 NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown ||
michael@0 5028 (!newSel->mIDList && !newSel->mClassList &&
michael@0 5029 !newSel->mPseudoClassList && !newSel->mAttrList),
michael@0 5030 "Need to fix the serialization code to deal with this");
michael@0 5031
michael@0 5032 return eSelectorParsingStatus_Continue;
michael@0 5033 }
michael@0 5034
michael@0 5035 //
michael@0 5036 // Parse the argument of a pseudo-class that has an ident arg
michael@0 5037 //
michael@0 5038 CSSParserImpl::nsSelectorParsingStatus
michael@0 5039 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
michael@0 5040 nsCSSPseudoClasses::Type aType)
michael@0 5041 {
michael@0 5042 if (! GetToken(true)) { // premature eof
michael@0 5043 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
michael@0 5044 return eSelectorParsingStatus_Error;
michael@0 5045 }
michael@0 5046 // We expect an identifier with a language abbreviation
michael@0 5047 if (eCSSToken_Ident != mToken.mType) {
michael@0 5048 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
michael@0 5049 UngetToken();
michael@0 5050 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5051 }
michael@0 5052
michael@0 5053 // -moz-locale-dir and -moz-dir can only have values of 'ltr' or 'rtl'.
michael@0 5054 if (aType == nsCSSPseudoClasses::ePseudoClass_mozLocaleDir ||
michael@0 5055 aType == nsCSSPseudoClasses::ePseudoClass_dir) {
michael@0 5056 nsContentUtils::ASCIIToLower(mToken.mIdent); // case insensitive
michael@0 5057 if (!mToken.mIdent.EqualsLiteral("ltr") &&
michael@0 5058 !mToken.mIdent.EqualsLiteral("rtl")) {
michael@0 5059 REPORT_UNEXPECTED_TOKEN(PEBadDirValue);
michael@0 5060 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5061 }
michael@0 5062 }
michael@0 5063
michael@0 5064 // Add the pseudo with the language parameter
michael@0 5065 aSelector.AddPseudoClass(aType, mToken.mIdent.get());
michael@0 5066
michael@0 5067 // close the parenthesis
michael@0 5068 if (!ExpectSymbol(')', true)) {
michael@0 5069 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
michael@0 5070 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5071 }
michael@0 5072
michael@0 5073 return eSelectorParsingStatus_Continue;
michael@0 5074 }
michael@0 5075
michael@0 5076 CSSParserImpl::nsSelectorParsingStatus
michael@0 5077 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
michael@0 5078 nsCSSPseudoClasses::Type aType)
michael@0 5079 {
michael@0 5080 int32_t numbers[2] = { 0, 0 };
michael@0 5081 int32_t sign[2] = { 1, 1 };
michael@0 5082 bool hasSign[2] = { false, false };
michael@0 5083 bool lookForB = true;
michael@0 5084
michael@0 5085 // Follow the whitespace rules as proposed in
michael@0 5086 // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
michael@0 5087
michael@0 5088 if (! GetToken(true)) {
michael@0 5089 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
michael@0 5090 return eSelectorParsingStatus_Error;
michael@0 5091 }
michael@0 5092
michael@0 5093 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
michael@0 5094 hasSign[0] = true;
michael@0 5095 if (mToken.IsSymbol('-')) {
michael@0 5096 sign[0] = -1;
michael@0 5097 }
michael@0 5098 if (! GetToken(false)) {
michael@0 5099 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
michael@0 5100 return eSelectorParsingStatus_Error;
michael@0 5101 }
michael@0 5102 }
michael@0 5103
michael@0 5104 if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
michael@0 5105 // The CSS tokenization doesn't handle :nth-child() containing - well:
michael@0 5106 // 2n-1 is a dimension
michael@0 5107 // n-1 is an identifier
michael@0 5108 // The easiest way to deal with that is to push everything from the
michael@0 5109 // minus on back onto the scanner's pushback buffer.
michael@0 5110 uint32_t truncAt = 0;
michael@0 5111 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
michael@0 5112 truncAt = 1;
michael@0 5113 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-")) && !hasSign[0]) {
michael@0 5114 truncAt = 2;
michael@0 5115 }
michael@0 5116 if (truncAt != 0) {
michael@0 5117 mScanner->Backup(mToken.mIdent.Length() - truncAt);
michael@0 5118 mToken.mIdent.Truncate(truncAt);
michael@0 5119 }
michael@0 5120 }
michael@0 5121
michael@0 5122 if (eCSSToken_Ident == mToken.mType) {
michael@0 5123 if (mToken.mIdent.LowerCaseEqualsLiteral("odd") && !hasSign[0]) {
michael@0 5124 numbers[0] = 2;
michael@0 5125 numbers[1] = 1;
michael@0 5126 lookForB = false;
michael@0 5127 }
michael@0 5128 else if (mToken.mIdent.LowerCaseEqualsLiteral("even") && !hasSign[0]) {
michael@0 5129 numbers[0] = 2;
michael@0 5130 numbers[1] = 0;
michael@0 5131 lookForB = false;
michael@0 5132 }
michael@0 5133 else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) {
michael@0 5134 numbers[0] = sign[0];
michael@0 5135 }
michael@0 5136 else if (mToken.mIdent.LowerCaseEqualsLiteral("-n") && !hasSign[0]) {
michael@0 5137 numbers[0] = -1;
michael@0 5138 }
michael@0 5139 else {
michael@0 5140 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
michael@0 5141 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5142 }
michael@0 5143 }
michael@0 5144 else if (eCSSToken_Number == mToken.mType) {
michael@0 5145 if (!mToken.mIntegerValid) {
michael@0 5146 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
michael@0 5147 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5148 }
michael@0 5149 // for +-an case
michael@0 5150 if (mToken.mHasSign && hasSign[0]) {
michael@0 5151 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
michael@0 5152 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5153 }
michael@0 5154 int32_t intValue = mToken.mInteger * sign[0];
michael@0 5155 // for -a/**/n case
michael@0 5156 if (! GetToken(false)) {
michael@0 5157 numbers[1] = intValue;
michael@0 5158 lookForB = false;
michael@0 5159 }
michael@0 5160 else {
michael@0 5161 if (eCSSToken_Ident == mToken.mType && mToken.mIdent.LowerCaseEqualsLiteral("n")) {
michael@0 5162 numbers[0] = intValue;
michael@0 5163 }
michael@0 5164 else if (eCSSToken_Ident == mToken.mType && StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
michael@0 5165 numbers[0] = intValue;
michael@0 5166 mScanner->Backup(mToken.mIdent.Length() - 1);
michael@0 5167 }
michael@0 5168 else {
michael@0 5169 UngetToken();
michael@0 5170 numbers[1] = intValue;
michael@0 5171 lookForB = false;
michael@0 5172 }
michael@0 5173 }
michael@0 5174 }
michael@0 5175 else if (eCSSToken_Dimension == mToken.mType) {
michael@0 5176 if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) {
michael@0 5177 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
michael@0 5178 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5179 }
michael@0 5180 // for +-an case
michael@0 5181 if ( mToken.mHasSign && hasSign[0] ) {
michael@0 5182 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
michael@0 5183 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5184 }
michael@0 5185 numbers[0] = mToken.mInteger * sign[0];
michael@0 5186 }
michael@0 5187 // XXX If it's a ')', is that valid? (as 0n+0)
michael@0 5188 else {
michael@0 5189 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
michael@0 5190 UngetToken();
michael@0 5191 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5192 }
michael@0 5193
michael@0 5194 if (! GetToken(true)) {
michael@0 5195 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
michael@0 5196 return eSelectorParsingStatus_Error;
michael@0 5197 }
michael@0 5198 if (lookForB && !mToken.IsSymbol(')')) {
michael@0 5199 // The '+' or '-' sign can optionally be separated by whitespace.
michael@0 5200 // If it is separated by whitespace from what follows it, it appears
michael@0 5201 // as a separate token rather than part of the number token.
michael@0 5202 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
michael@0 5203 hasSign[1] = true;
michael@0 5204 if (mToken.IsSymbol('-')) {
michael@0 5205 sign[1] = -1;
michael@0 5206 }
michael@0 5207 if (! GetToken(true)) {
michael@0 5208 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
michael@0 5209 return eSelectorParsingStatus_Error;
michael@0 5210 }
michael@0 5211 }
michael@0 5212 if (eCSSToken_Number != mToken.mType ||
michael@0 5213 !mToken.mIntegerValid || mToken.mHasSign == hasSign[1]) {
michael@0 5214 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
michael@0 5215 UngetToken();
michael@0 5216 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5217 }
michael@0 5218 numbers[1] = mToken.mInteger * sign[1];
michael@0 5219 if (! GetToken(true)) {
michael@0 5220 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
michael@0 5221 return eSelectorParsingStatus_Error;
michael@0 5222 }
michael@0 5223 }
michael@0 5224 if (!mToken.IsSymbol(')')) {
michael@0 5225 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
michael@0 5226 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5227 }
michael@0 5228 aSelector.AddPseudoClass(aType, numbers);
michael@0 5229 return eSelectorParsingStatus_Continue;
michael@0 5230 }
michael@0 5231
michael@0 5232 //
michael@0 5233 // Parse the argument of a pseudo-class that has a selector list argument.
michael@0 5234 // Such selector lists cannot contain combinators, but can contain
michael@0 5235 // anything that goes between a pair of combinators.
michael@0 5236 //
michael@0 5237 CSSParserImpl::nsSelectorParsingStatus
michael@0 5238 CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector,
michael@0 5239 nsCSSPseudoClasses::Type aType)
michael@0 5240 {
michael@0 5241 nsAutoPtr<nsCSSSelectorList> slist;
michael@0 5242 if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) {
michael@0 5243 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5244 }
michael@0 5245
michael@0 5246 // Check that none of the selectors in the list have combinators or
michael@0 5247 // pseudo-elements.
michael@0 5248 for (nsCSSSelectorList *l = slist; l; l = l->mNext) {
michael@0 5249 nsCSSSelector *s = l->mSelectors;
michael@0 5250 if (s->mNext || s->IsPseudoElement()) {
michael@0 5251 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5252 }
michael@0 5253 }
michael@0 5254
michael@0 5255 // Add the pseudo with the selector list parameter
michael@0 5256 aSelector.AddPseudoClass(aType, slist.forget());
michael@0 5257
michael@0 5258 // close the parenthesis
michael@0 5259 if (!ExpectSymbol(')', true)) {
michael@0 5260 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
michael@0 5261 return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')')
michael@0 5262 }
michael@0 5263
michael@0 5264 return eSelectorParsingStatus_Continue;
michael@0 5265 }
michael@0 5266
michael@0 5267
michael@0 5268 /**
michael@0 5269 * This is the format for selectors:
michael@0 5270 * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
michael@0 5271 */
michael@0 5272 bool
michael@0 5273 CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
michael@0 5274 char16_t aPrevCombinator)
michael@0 5275 {
michael@0 5276 if (! GetToken(true)) {
michael@0 5277 REPORT_UNEXPECTED_EOF(PESelectorEOF);
michael@0 5278 return false;
michael@0 5279 }
michael@0 5280
michael@0 5281 nsCSSSelector* selector = aList->AddSelector(aPrevCombinator);
michael@0 5282 nsCOMPtr<nsIAtom> pseudoElement;
michael@0 5283 nsAutoPtr<nsAtomList> pseudoElementArgs;
michael@0 5284 nsCSSPseudoElements::Type pseudoElementType =
michael@0 5285 nsCSSPseudoElements::ePseudo_NotPseudoElement;
michael@0 5286
michael@0 5287 int32_t dataMask = 0;
michael@0 5288 nsSelectorParsingStatus parsingStatus =
michael@0 5289 ParseTypeOrUniversalSelector(dataMask, *selector, false);
michael@0 5290
michael@0 5291 while (parsingStatus == eSelectorParsingStatus_Continue) {
michael@0 5292 if (eCSSToken_ID == mToken.mType) { // #id
michael@0 5293 parsingStatus = ParseIDSelector(dataMask, *selector);
michael@0 5294 }
michael@0 5295 else if (mToken.IsSymbol('.')) { // .class
michael@0 5296 parsingStatus = ParseClassSelector(dataMask, *selector);
michael@0 5297 }
michael@0 5298 else if (mToken.IsSymbol(':')) { // :pseudo
michael@0 5299 parsingStatus = ParsePseudoSelector(dataMask, *selector, false,
michael@0 5300 getter_AddRefs(pseudoElement),
michael@0 5301 getter_Transfers(pseudoElementArgs),
michael@0 5302 &pseudoElementType);
michael@0 5303 if (pseudoElement &&
michael@0 5304 pseudoElementType != nsCSSPseudoElements::ePseudo_AnonBox) {
michael@0 5305 // Pseudo-elements other than anonymous boxes are represented with
michael@0 5306 // a special ':' combinator.
michael@0 5307
michael@0 5308 aList->mWeight += selector->CalcWeight();
michael@0 5309
michael@0 5310 selector = aList->AddSelector(':');
michael@0 5311
michael@0 5312 selector->mLowercaseTag.swap(pseudoElement);
michael@0 5313 selector->mClassList = pseudoElementArgs.forget();
michael@0 5314 selector->SetPseudoType(pseudoElementType);
michael@0 5315 }
michael@0 5316 }
michael@0 5317 else if (mToken.IsSymbol('[')) { // [attribute
michael@0 5318 parsingStatus = ParseAttributeSelector(dataMask, *selector);
michael@0 5319 if (eSelectorParsingStatus_Error == parsingStatus) {
michael@0 5320 SkipUntil(']');
michael@0 5321 }
michael@0 5322 }
michael@0 5323 else { // not a selector token, we're done
michael@0 5324 parsingStatus = eSelectorParsingStatus_Done;
michael@0 5325 UngetToken();
michael@0 5326 break;
michael@0 5327 }
michael@0 5328
michael@0 5329 if (parsingStatus != eSelectorParsingStatus_Continue) {
michael@0 5330 break;
michael@0 5331 }
michael@0 5332
michael@0 5333 if (! GetToken(false)) { // premature eof is ok (here!)
michael@0 5334 parsingStatus = eSelectorParsingStatus_Done;
michael@0 5335 break;
michael@0 5336 }
michael@0 5337 }
michael@0 5338
michael@0 5339 if (parsingStatus == eSelectorParsingStatus_Error) {
michael@0 5340 return false;
michael@0 5341 }
michael@0 5342
michael@0 5343 if (!dataMask) {
michael@0 5344 if (selector->mNext) {
michael@0 5345 REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
michael@0 5346 } else {
michael@0 5347 REPORT_UNEXPECTED(PESelectorGroupNoSelector);
michael@0 5348 }
michael@0 5349 return false;
michael@0 5350 }
michael@0 5351
michael@0 5352 if (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox) {
michael@0 5353 // We got an anonymous box pseudo-element; it must be the only
michael@0 5354 // thing in this selector group.
michael@0 5355 if (selector->mNext || !IsUniversalSelector(*selector)) {
michael@0 5356 REPORT_UNEXPECTED(PEAnonBoxNotAlone);
michael@0 5357 return false;
michael@0 5358 }
michael@0 5359
michael@0 5360 // Rewrite the current selector as this pseudo-element.
michael@0 5361 // It does not contribute to selector weight.
michael@0 5362 selector->mLowercaseTag.swap(pseudoElement);
michael@0 5363 selector->mClassList = pseudoElementArgs.forget();
michael@0 5364 selector->SetPseudoType(pseudoElementType);
michael@0 5365 return true;
michael@0 5366 }
michael@0 5367
michael@0 5368 aList->mWeight += selector->CalcWeight();
michael@0 5369
michael@0 5370 return true;
michael@0 5371 }
michael@0 5372
michael@0 5373 css::Declaration*
michael@0 5374 CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext)
michael@0 5375 {
michael@0 5376 bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
michael@0 5377
michael@0 5378 if (checkForBraces) {
michael@0 5379 if (!ExpectSymbol('{', true)) {
michael@0 5380 REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
michael@0 5381 OUTPUT_ERROR();
michael@0 5382 return nullptr;
michael@0 5383 }
michael@0 5384 }
michael@0 5385 css::Declaration* declaration = new css::Declaration();
michael@0 5386 mData.AssertInitialState();
michael@0 5387 if (declaration) {
michael@0 5388 for (;;) {
michael@0 5389 bool changed;
michael@0 5390 if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) {
michael@0 5391 if (!SkipDeclaration(checkForBraces)) {
michael@0 5392 break;
michael@0 5393 }
michael@0 5394 if (checkForBraces) {
michael@0 5395 if (ExpectSymbol('}', true)) {
michael@0 5396 break;
michael@0 5397 }
michael@0 5398 }
michael@0 5399 // Since the skipped declaration didn't end the block we parse
michael@0 5400 // the next declaration.
michael@0 5401 }
michael@0 5402 }
michael@0 5403 declaration->CompressFrom(&mData);
michael@0 5404 }
michael@0 5405 return declaration;
michael@0 5406 }
michael@0 5407
michael@0 5408 bool
michael@0 5409 CSSParserImpl::ParseColor(nsCSSValue& aValue)
michael@0 5410 {
michael@0 5411 if (!GetToken(true)) {
michael@0 5412 REPORT_UNEXPECTED_EOF(PEColorEOF);
michael@0 5413 return false;
michael@0 5414 }
michael@0 5415
michael@0 5416 nsCSSToken* tk = &mToken;
michael@0 5417 nscolor rgba;
michael@0 5418 switch (tk->mType) {
michael@0 5419 case eCSSToken_ID:
michael@0 5420 case eCSSToken_Hash:
michael@0 5421 // #xxyyzz
michael@0 5422 if (NS_HexToRGB(tk->mIdent, &rgba)) {
michael@0 5423 MOZ_ASSERT(tk->mIdent.Length() == 3 || tk->mIdent.Length() == 6,
michael@0 5424 "unexpected hex color length");
michael@0 5425 nsCSSUnit unit = tk->mIdent.Length() == 3 ?
michael@0 5426 eCSSUnit_ShortHexColor :
michael@0 5427 eCSSUnit_HexColor;
michael@0 5428 aValue.SetIntegerColorValue(rgba, unit);
michael@0 5429 return true;
michael@0 5430 }
michael@0 5431 break;
michael@0 5432
michael@0 5433 case eCSSToken_Ident:
michael@0 5434 if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
michael@0 5435 aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
michael@0 5436 return true;
michael@0 5437 }
michael@0 5438 else {
michael@0 5439 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
michael@0 5440 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
michael@0 5441 int32_t value;
michael@0 5442 if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
michael@0 5443 aValue.SetIntValue(value, eCSSUnit_EnumColor);
michael@0 5444 return true;
michael@0 5445 }
michael@0 5446 }
michael@0 5447 }
michael@0 5448 break;
michael@0 5449 case eCSSToken_Function:
michael@0 5450 if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
michael@0 5451 // rgb ( component , component , component )
michael@0 5452 if (GetToken(true)) {
michael@0 5453 UngetToken();
michael@0 5454 }
michael@0 5455 if (mToken.mType == eCSSToken_Number) {
michael@0 5456 uint8_t r, g, b;
michael@0 5457 if (ParseNumberColorComponent(r, ',') &&
michael@0 5458 ParseNumberColorComponent(g, ',') &&
michael@0 5459 ParseNumberColorComponent(b, ')')) {
michael@0 5460 aValue.SetIntegerColorValue(NS_RGB(r, g, b), eCSSUnit_RGBColor);
michael@0 5461 return true;
michael@0 5462 }
michael@0 5463 } else {
michael@0 5464 float r, g, b;
michael@0 5465 if (ParsePercentageColorComponent(r, ',') &&
michael@0 5466 ParsePercentageColorComponent(g, ',') &&
michael@0 5467 ParsePercentageColorComponent(b, ')')) {
michael@0 5468 aValue.SetFloatColorValue(r, g, b, 1.0f,
michael@0 5469 eCSSUnit_PercentageRGBColor);
michael@0 5470 return true;
michael@0 5471 }
michael@0 5472 }
michael@0 5473 SkipUntil(')');
michael@0 5474 return false;
michael@0 5475 }
michael@0 5476 else if (mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
michael@0 5477 // rgba ( component , component , component , opacity )
michael@0 5478 if (GetToken(true)) {
michael@0 5479 UngetToken();
michael@0 5480 }
michael@0 5481 if (mToken.mType == eCSSToken_Number) {
michael@0 5482 uint8_t r, g, b, a;
michael@0 5483 if (ParseNumberColorComponent(r, ',') &&
michael@0 5484 ParseNumberColorComponent(g, ',') &&
michael@0 5485 ParseNumberColorComponent(b, ',') &&
michael@0 5486 ParseColorOpacity(a)) {
michael@0 5487 aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a),
michael@0 5488 eCSSUnit_RGBAColor);
michael@0 5489 return true;
michael@0 5490 }
michael@0 5491 } else {
michael@0 5492 float r, g, b, a;
michael@0 5493 if (ParsePercentageColorComponent(r, ',') &&
michael@0 5494 ParsePercentageColorComponent(g, ',') &&
michael@0 5495 ParsePercentageColorComponent(b, ',') &&
michael@0 5496 ParseColorOpacity(a)) {
michael@0 5497 aValue.SetFloatColorValue(r, g, b, a, eCSSUnit_PercentageRGBAColor);
michael@0 5498 return true;
michael@0 5499 }
michael@0 5500 }
michael@0 5501 SkipUntil(')');
michael@0 5502 return false;
michael@0 5503 }
michael@0 5504 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
michael@0 5505 // hsl ( hue , saturation , lightness )
michael@0 5506 // "hue" is a number, "saturation" and "lightness" are percentages.
michael@0 5507 float h, s, l;
michael@0 5508 if (ParseHSLColor(h, s, l, ')')) {
michael@0 5509 aValue.SetFloatColorValue(h, s, l, 1.0f, eCSSUnit_HSLColor);
michael@0 5510 return true;
michael@0 5511 }
michael@0 5512 SkipUntil(')');
michael@0 5513 return false;
michael@0 5514 }
michael@0 5515 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
michael@0 5516 // hsla ( hue , saturation , lightness , opacity )
michael@0 5517 // "hue" is a number, "saturation" and "lightness" are percentages,
michael@0 5518 // "opacity" is a number.
michael@0 5519 float h, s, l, a;
michael@0 5520 if (ParseHSLColor(h, s, l, ',') &&
michael@0 5521 ParseColorOpacity(a)) {
michael@0 5522 aValue.SetFloatColorValue(h, s, l, a, eCSSUnit_HSLAColor);
michael@0 5523 return true;
michael@0 5524 }
michael@0 5525 SkipUntil(')');
michael@0 5526 return false;
michael@0 5527 }
michael@0 5528 break;
michael@0 5529 default:
michael@0 5530 break;
michael@0 5531 }
michael@0 5532
michael@0 5533 // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
michael@0 5534 if (mHashlessColorQuirk) {
michael@0 5535 // - If the string starts with 'a-f', the nsCSSScanner builds the
michael@0 5536 // token as a eCSSToken_Ident and we can parse the string as a
michael@0 5537 // 'xxyyzz' RGB color.
michael@0 5538 // - If it only contains '0-9' digits, the token is a
michael@0 5539 // eCSSToken_Number and it must be converted back to a 6
michael@0 5540 // characters string to be parsed as a RGB color.
michael@0 5541 // - If it starts with '0-9' and contains any 'a-f', the token is a
michael@0 5542 // eCSSToken_Dimension, the mNumber part must be converted back to
michael@0 5543 // a string and the mIdent part must be appended to that string so
michael@0 5544 // that the resulting string has 6 characters.
michael@0 5545 // Note: This is a hack for Nav compatibility. Do not attempt to
michael@0 5546 // simplify it by hacking into the ncCSSScanner. This would be very
michael@0 5547 // bad.
michael@0 5548 nsAutoString str;
michael@0 5549 char buffer[20];
michael@0 5550 switch (tk->mType) {
michael@0 5551 case eCSSToken_Ident:
michael@0 5552 str.Assign(tk->mIdent);
michael@0 5553 break;
michael@0 5554
michael@0 5555 case eCSSToken_Number:
michael@0 5556 if (tk->mIntegerValid) {
michael@0 5557 PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
michael@0 5558 str.AssignWithConversion(buffer);
michael@0 5559 }
michael@0 5560 break;
michael@0 5561
michael@0 5562 case eCSSToken_Dimension:
michael@0 5563 if (tk->mIdent.Length() <= 6) {
michael@0 5564 PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
michael@0 5565 nsAutoString temp;
michael@0 5566 temp.AssignWithConversion(buffer);
michael@0 5567 temp.Right(str, 6 - tk->mIdent.Length());
michael@0 5568 str.Append(tk->mIdent);
michael@0 5569 }
michael@0 5570 break;
michael@0 5571 default:
michael@0 5572 // There is a whole bunch of cases that are
michael@0 5573 // not handled by this switch. Ignore them.
michael@0 5574 break;
michael@0 5575 }
michael@0 5576 if (NS_HexToRGB(str, &rgba)) {
michael@0 5577 aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor);
michael@0 5578 return true;
michael@0 5579 }
michael@0 5580 }
michael@0 5581
michael@0 5582 // It's not a color
michael@0 5583 REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
michael@0 5584 UngetToken();
michael@0 5585 return false;
michael@0 5586 }
michael@0 5587
michael@0 5588 bool
michael@0 5589 CSSParserImpl::ParseNumberColorComponent(uint8_t& aComponent, char aStop)
michael@0 5590 {
michael@0 5591 if (!GetToken(true)) {
michael@0 5592 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
michael@0 5593 return false;
michael@0 5594 }
michael@0 5595
michael@0 5596 if (mToken.mType != eCSSToken_Number || !mToken.mIntegerValid) {
michael@0 5597 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
michael@0 5598 UngetToken();
michael@0 5599 return false;
michael@0 5600 }
michael@0 5601
michael@0 5602 float value = mToken.mNumber;
michael@0 5603 if (value < 0.0f) value = 0.0f;
michael@0 5604 if (value > 255.0f) value = 255.0f;
michael@0 5605
michael@0 5606 if (ExpectSymbol(aStop, true)) {
michael@0 5607 aComponent = NSToIntRound(value);
michael@0 5608 return true;
michael@0 5609 }
michael@0 5610 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
michael@0 5611 return false;
michael@0 5612 }
michael@0 5613
michael@0 5614 bool
michael@0 5615 CSSParserImpl::ParsePercentageColorComponent(float& aComponent, char aStop)
michael@0 5616 {
michael@0 5617 if (!GetToken(true)) {
michael@0 5618 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
michael@0 5619 return false;
michael@0 5620 }
michael@0 5621
michael@0 5622 if (mToken.mType != eCSSToken_Percentage) {
michael@0 5623 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
michael@0 5624 UngetToken();
michael@0 5625 return false;
michael@0 5626 }
michael@0 5627
michael@0 5628 float value = mToken.mNumber;
michael@0 5629 if (value < 0.0f) value = 0.0f;
michael@0 5630 if (value > 1.0f) value = 1.0f;
michael@0 5631
michael@0 5632 if (ExpectSymbol(aStop, true)) {
michael@0 5633 aComponent = value;
michael@0 5634 return true;
michael@0 5635 }
michael@0 5636 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
michael@0 5637 return false;
michael@0 5638 }
michael@0 5639
michael@0 5640
michael@0 5641 bool
michael@0 5642 CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness,
michael@0 5643 char aStop)
michael@0 5644 {
michael@0 5645 float h, s, l;
michael@0 5646
michael@0 5647 // Get the hue
michael@0 5648 if (!GetToken(true)) {
michael@0 5649 REPORT_UNEXPECTED_EOF(PEColorHueEOF);
michael@0 5650 return false;
michael@0 5651 }
michael@0 5652 if (mToken.mType != eCSSToken_Number) {
michael@0 5653 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
michael@0 5654 UngetToken();
michael@0 5655 return false;
michael@0 5656 }
michael@0 5657 h = mToken.mNumber;
michael@0 5658 h /= 360.0f;
michael@0 5659 // hue values are wraparound
michael@0 5660 h = h - floor(h);
michael@0 5661
michael@0 5662 if (!ExpectSymbol(',', true)) {
michael@0 5663 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
michael@0 5664 return false;
michael@0 5665 }
michael@0 5666
michael@0 5667 // Get the saturation
michael@0 5668 if (!GetToken(true)) {
michael@0 5669 REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
michael@0 5670 return false;
michael@0 5671 }
michael@0 5672 if (mToken.mType != eCSSToken_Percentage) {
michael@0 5673 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
michael@0 5674 UngetToken();
michael@0 5675 return false;
michael@0 5676 }
michael@0 5677 s = mToken.mNumber;
michael@0 5678 if (s < 0.0f) s = 0.0f;
michael@0 5679 if (s > 1.0f) s = 1.0f;
michael@0 5680
michael@0 5681 if (!ExpectSymbol(',', true)) {
michael@0 5682 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
michael@0 5683 return false;
michael@0 5684 }
michael@0 5685
michael@0 5686 // Get the lightness
michael@0 5687 if (!GetToken(true)) {
michael@0 5688 REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
michael@0 5689 return false;
michael@0 5690 }
michael@0 5691 if (mToken.mType != eCSSToken_Percentage) {
michael@0 5692 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
michael@0 5693 UngetToken();
michael@0 5694 return false;
michael@0 5695 }
michael@0 5696 l = mToken.mNumber;
michael@0 5697 if (l < 0.0f) l = 0.0f;
michael@0 5698 if (l > 1.0f) l = 1.0f;
michael@0 5699
michael@0 5700 if (ExpectSymbol(aStop, true)) {
michael@0 5701 aHue = h;
michael@0 5702 aSaturation = s;
michael@0 5703 aLightness = l;
michael@0 5704 return true;
michael@0 5705 }
michael@0 5706
michael@0 5707 REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aStop);
michael@0 5708 return false;
michael@0 5709 }
michael@0 5710
michael@0 5711
michael@0 5712 bool
michael@0 5713 CSSParserImpl::ParseColorOpacity(uint8_t& aOpacity)
michael@0 5714 {
michael@0 5715 float floatOpacity;
michael@0 5716 if (!ParseColorOpacity(floatOpacity)) {
michael@0 5717 return false;
michael@0 5718 }
michael@0 5719
michael@0 5720 uint8_t value = nsStyleUtil::FloatToColorComponent(floatOpacity);
michael@0 5721 // Need to compare to something slightly larger
michael@0 5722 // than 0.5 due to floating point inaccuracies.
michael@0 5723 NS_ASSERTION(fabs(255.0f*mToken.mNumber - value) <= 0.51f,
michael@0 5724 "FloatToColorComponent did something weird");
michael@0 5725
michael@0 5726 aOpacity = value;
michael@0 5727 return true;
michael@0 5728 }
michael@0 5729
michael@0 5730 bool
michael@0 5731 CSSParserImpl::ParseColorOpacity(float& aOpacity)
michael@0 5732 {
michael@0 5733 if (!GetToken(true)) {
michael@0 5734 REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
michael@0 5735 return false;
michael@0 5736 }
michael@0 5737
michael@0 5738 if (mToken.mType != eCSSToken_Number) {
michael@0 5739 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
michael@0 5740 UngetToken();
michael@0 5741 return false;
michael@0 5742 }
michael@0 5743
michael@0 5744 if (!ExpectSymbol(')', true)) {
michael@0 5745 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
michael@0 5746 return false;
michael@0 5747 }
michael@0 5748
michael@0 5749 if (mToken.mNumber < 0.0f) {
michael@0 5750 mToken.mNumber = 0.0f;
michael@0 5751 } else if (mToken.mNumber > 1.0f) {
michael@0 5752 mToken.mNumber = 1.0f;
michael@0 5753 }
michael@0 5754
michael@0 5755 aOpacity = mToken.mNumber;
michael@0 5756 return true;
michael@0 5757 }
michael@0 5758
michael@0 5759 #ifdef MOZ_XUL
michael@0 5760 bool
michael@0 5761 CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs)
michael@0 5762 {
michael@0 5763 // The argument to a tree pseudo-element is a sequence of identifiers
michael@0 5764 // that are either space- or comma-separated. (Was the intent to
michael@0 5765 // allow only comma-separated? That's not what was done.)
michael@0 5766 nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass
michael@0 5767
michael@0 5768 while (!ExpectSymbol(')', true)) {
michael@0 5769 if (!GetToken(true)) {
michael@0 5770 return false;
michael@0 5771 }
michael@0 5772 if (eCSSToken_Ident == mToken.mType) {
michael@0 5773 fakeSelector.AddClass(mToken.mIdent);
michael@0 5774 }
michael@0 5775 else if (!mToken.IsSymbol(',')) {
michael@0 5776 UngetToken();
michael@0 5777 SkipUntil(')');
michael@0 5778 return false;
michael@0 5779 }
michael@0 5780 }
michael@0 5781 *aPseudoElementArgs = fakeSelector.mClassList;
michael@0 5782 fakeSelector.mClassList = nullptr;
michael@0 5783 return true;
michael@0 5784 }
michael@0 5785 #endif
michael@0 5786
michael@0 5787 //----------------------------------------------------------------------
michael@0 5788
michael@0 5789 bool
michael@0 5790 CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration,
michael@0 5791 uint32_t aFlags,
michael@0 5792 bool aMustCallValueAppended,
michael@0 5793 bool* aChanged,
michael@0 5794 nsCSSContextType aContext)
michael@0 5795 {
michael@0 5796 NS_PRECONDITION(aContext == eCSSContext_General ||
michael@0 5797 aContext == eCSSContext_Page,
michael@0 5798 "Must be page or general context");
michael@0 5799
michael@0 5800 bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
michael@0 5801
michael@0 5802 mTempData.AssertInitialState();
michael@0 5803
michael@0 5804 // Get property name
michael@0 5805 nsCSSToken* tk = &mToken;
michael@0 5806 nsAutoString propertyName;
michael@0 5807 for (;;) {
michael@0 5808 if (!GetToken(true)) {
michael@0 5809 if (checkForBraces) {
michael@0 5810 REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
michael@0 5811 }
michael@0 5812 return false;
michael@0 5813 }
michael@0 5814 if (eCSSToken_Ident == tk->mType) {
michael@0 5815 propertyName = tk->mIdent;
michael@0 5816 // grab the ident before the ExpectSymbol trashes the token
michael@0 5817 if (!ExpectSymbol(':', true)) {
michael@0 5818 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
michael@0 5819 REPORT_UNEXPECTED(PEDeclDropped);
michael@0 5820 OUTPUT_ERROR();
michael@0 5821 return false;
michael@0 5822 }
michael@0 5823 break;
michael@0 5824 }
michael@0 5825 if (tk->IsSymbol(';')) {
michael@0 5826 // dangling semicolons are skipped
michael@0 5827 continue;
michael@0 5828 }
michael@0 5829
michael@0 5830 if (!tk->IsSymbol('}')) {
michael@0 5831 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
michael@0 5832 REPORT_UNEXPECTED(PEDeclSkipped);
michael@0 5833 OUTPUT_ERROR();
michael@0 5834
michael@0 5835 if (eCSSToken_AtKeyword == tk->mType) {
michael@0 5836 SkipAtRule(checkForBraces);
michael@0 5837 return true; // Not a declaration, but don't skip until ';'
michael@0 5838 }
michael@0 5839 }
michael@0 5840 // Not a declaration...
michael@0 5841 UngetToken();
michael@0 5842 return false;
michael@0 5843 }
michael@0 5844
michael@0 5845 // Don't report property parse errors if we're inside a failing @supports
michael@0 5846 // rule.
michael@0 5847 nsAutoSuppressErrors suppressErrors(this, mInFailingSupportsRule);
michael@0 5848
michael@0 5849 // Information about a parsed non-custom property.
michael@0 5850 nsCSSProperty propID;
michael@0 5851
michael@0 5852 // Information about a parsed custom property.
michael@0 5853 CSSVariableDeclarations::Type variableType;
michael@0 5854 nsString variableValue;
michael@0 5855
michael@0 5856 // Check if the property name is a custom property.
michael@0 5857 bool customProperty = nsLayoutUtils::CSSVariablesEnabled() &&
michael@0 5858 nsCSSProps::IsCustomPropertyName(propertyName) &&
michael@0 5859 aContext == eCSSContext_General;
michael@0 5860
michael@0 5861 if (customProperty) {
michael@0 5862 if (!ParseVariableDeclaration(&variableType, variableValue)) {
michael@0 5863 REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
michael@0 5864 REPORT_UNEXPECTED(PEDeclDropped);
michael@0 5865 OUTPUT_ERROR();
michael@0 5866 return false;
michael@0 5867 }
michael@0 5868 } else {
michael@0 5869 // Map property name to its ID.
michael@0 5870 propID = LookupEnabledProperty(propertyName);
michael@0 5871 if (eCSSProperty_UNKNOWN == propID ||
michael@0 5872 (aContext == eCSSContext_Page &&
michael@0 5873 !nsCSSProps::PropHasFlags(propID,
michael@0 5874 CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property
michael@0 5875 if (!NonMozillaVendorIdentifier(propertyName)) {
michael@0 5876 REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName);
michael@0 5877 REPORT_UNEXPECTED(PEDeclDropped);
michael@0 5878 OUTPUT_ERROR();
michael@0 5879 }
michael@0 5880 return false;
michael@0 5881 }
michael@0 5882 // Then parse the property.
michael@0 5883 if (!ParseProperty(propID)) {
michael@0 5884 // XXX Much better to put stuff in the value parsers instead...
michael@0 5885 REPORT_UNEXPECTED_P(PEValueParsingError, propertyName);
michael@0 5886 REPORT_UNEXPECTED(PEDeclDropped);
michael@0 5887 OUTPUT_ERROR();
michael@0 5888 mTempData.ClearProperty(propID);
michael@0 5889 mTempData.AssertInitialState();
michael@0 5890 return false;
michael@0 5891 }
michael@0 5892 }
michael@0 5893
michael@0 5894 CLEAR_ERROR();
michael@0 5895
michael@0 5896 // Look for "!important".
michael@0 5897 PriorityParsingStatus status;
michael@0 5898 if ((aFlags & eParseDeclaration_AllowImportant) != 0) {
michael@0 5899 status = ParsePriority();
michael@0 5900 } else {
michael@0 5901 status = ePriority_None;
michael@0 5902 }
michael@0 5903
michael@0 5904 // Look for a semicolon or close brace.
michael@0 5905 if (status != ePriority_Error) {
michael@0 5906 if (!GetToken(true)) {
michael@0 5907 // EOF is always ok
michael@0 5908 } else if (mToken.IsSymbol(';')) {
michael@0 5909 // semicolon is always ok
michael@0 5910 } else if (mToken.IsSymbol('}')) {
michael@0 5911 // brace is ok if checkForBraces, but don't eat it
michael@0 5912 UngetToken();
michael@0 5913 if (!checkForBraces) {
michael@0 5914 status = ePriority_Error;
michael@0 5915 }
michael@0 5916 } else {
michael@0 5917 UngetToken();
michael@0 5918 status = ePriority_Error;
michael@0 5919 }
michael@0 5920 }
michael@0 5921
michael@0 5922 if (status == ePriority_Error) {
michael@0 5923 if (checkForBraces) {
michael@0 5924 REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
michael@0 5925 } else {
michael@0 5926 REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
michael@0 5927 }
michael@0 5928 REPORT_UNEXPECTED(PEDeclDropped);
michael@0 5929 OUTPUT_ERROR();
michael@0 5930 if (!customProperty) {
michael@0 5931 mTempData.ClearProperty(propID);
michael@0 5932 }
michael@0 5933 mTempData.AssertInitialState();
michael@0 5934 return false;
michael@0 5935 }
michael@0 5936
michael@0 5937 if (customProperty) {
michael@0 5938 MOZ_ASSERT(Substring(propertyName, 0,
michael@0 5939 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
michael@0 5940 // remove '--'
michael@0 5941 nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
michael@0 5942 aDeclaration->AddVariableDeclaration(varName, variableType, variableValue,
michael@0 5943 status == ePriority_Important, false);
michael@0 5944 } else {
michael@0 5945 *aChanged |= mData.TransferFromBlock(mTempData, propID,
michael@0 5946 status == ePriority_Important,
michael@0 5947 false, aMustCallValueAppended,
michael@0 5948 aDeclaration);
michael@0 5949 }
michael@0 5950
michael@0 5951 return true;
michael@0 5952 }
michael@0 5953
michael@0 5954 static const nsCSSProperty kBorderTopIDs[] = {
michael@0 5955 eCSSProperty_border_top_width,
michael@0 5956 eCSSProperty_border_top_style,
michael@0 5957 eCSSProperty_border_top_color
michael@0 5958 };
michael@0 5959 static const nsCSSProperty kBorderRightIDs[] = {
michael@0 5960 eCSSProperty_border_right_width_value,
michael@0 5961 eCSSProperty_border_right_style_value,
michael@0 5962 eCSSProperty_border_right_color_value,
michael@0 5963 eCSSProperty_border_right_width,
michael@0 5964 eCSSProperty_border_right_style,
michael@0 5965 eCSSProperty_border_right_color
michael@0 5966 };
michael@0 5967 static const nsCSSProperty kBorderBottomIDs[] = {
michael@0 5968 eCSSProperty_border_bottom_width,
michael@0 5969 eCSSProperty_border_bottom_style,
michael@0 5970 eCSSProperty_border_bottom_color
michael@0 5971 };
michael@0 5972 static const nsCSSProperty kBorderLeftIDs[] = {
michael@0 5973 eCSSProperty_border_left_width_value,
michael@0 5974 eCSSProperty_border_left_style_value,
michael@0 5975 eCSSProperty_border_left_color_value,
michael@0 5976 eCSSProperty_border_left_width,
michael@0 5977 eCSSProperty_border_left_style,
michael@0 5978 eCSSProperty_border_left_color
michael@0 5979 };
michael@0 5980 static const nsCSSProperty kBorderStartIDs[] = {
michael@0 5981 eCSSProperty_border_start_width_value,
michael@0 5982 eCSSProperty_border_start_style_value,
michael@0 5983 eCSSProperty_border_start_color_value,
michael@0 5984 eCSSProperty_border_start_width,
michael@0 5985 eCSSProperty_border_start_style,
michael@0 5986 eCSSProperty_border_start_color
michael@0 5987 };
michael@0 5988 static const nsCSSProperty kBorderEndIDs[] = {
michael@0 5989 eCSSProperty_border_end_width_value,
michael@0 5990 eCSSProperty_border_end_style_value,
michael@0 5991 eCSSProperty_border_end_color_value,
michael@0 5992 eCSSProperty_border_end_width,
michael@0 5993 eCSSProperty_border_end_style,
michael@0 5994 eCSSProperty_border_end_color
michael@0 5995 };
michael@0 5996 static const nsCSSProperty kColumnRuleIDs[] = {
michael@0 5997 eCSSProperty__moz_column_rule_width,
michael@0 5998 eCSSProperty__moz_column_rule_style,
michael@0 5999 eCSSProperty__moz_column_rule_color
michael@0 6000 };
michael@0 6001
michael@0 6002 bool
michael@0 6003 CSSParserImpl::ParseEnum(nsCSSValue& aValue,
michael@0 6004 const KTableValue aKeywordTable[])
michael@0 6005 {
michael@0 6006 nsSubstring* ident = NextIdent();
michael@0 6007 if (nullptr == ident) {
michael@0 6008 return false;
michael@0 6009 }
michael@0 6010 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
michael@0 6011 if (eCSSKeyword_UNKNOWN < keyword) {
michael@0 6012 int32_t value;
michael@0 6013 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
michael@0 6014 aValue.SetIntValue(value, eCSSUnit_Enumerated);
michael@0 6015 return true;
michael@0 6016 }
michael@0 6017 }
michael@0 6018
michael@0 6019 // Put the unknown identifier back and return
michael@0 6020 UngetToken();
michael@0 6021 return false;
michael@0 6022 }
michael@0 6023
michael@0 6024
michael@0 6025 struct UnitInfo {
michael@0 6026 char name[6]; // needs to be long enough for the longest unit, with
michael@0 6027 // terminating null.
michael@0 6028 uint32_t length;
michael@0 6029 nsCSSUnit unit;
michael@0 6030 int32_t type;
michael@0 6031 };
michael@0 6032
michael@0 6033 #define STR_WITH_LEN(_str) \
michael@0 6034 _str, sizeof(_str) - 1
michael@0 6035
michael@0 6036 const UnitInfo UnitData[] = {
michael@0 6037 { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
michael@0 6038 { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
michael@0 6039 { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
michael@0 6040 { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
michael@0 6041 { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
michael@0 6042 { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
michael@0 6043 { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
michael@0 6044 { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH },
michael@0 6045 { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
michael@0 6046 { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH },
michael@0 6047 { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH },
michael@0 6048 { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH },
michael@0 6049 { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH },
michael@0 6050 { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH },
michael@0 6051 { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
michael@0 6052 { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
michael@0 6053 { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
michael@0 6054 { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
michael@0 6055 { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE },
michael@0 6056 { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
michael@0 6057 { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
michael@0 6058 { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
michael@0 6059 { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
michael@0 6060 };
michael@0 6061
michael@0 6062 #undef STR_WITH_LEN
michael@0 6063
michael@0 6064 bool
michael@0 6065 CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
michael@0 6066 int32_t aVariantMask,
michael@0 6067 float aNumber,
michael@0 6068 const nsString& aUnit)
michael@0 6069 {
michael@0 6070 nsCSSUnit units;
michael@0 6071 int32_t type = 0;
michael@0 6072 if (!aUnit.IsEmpty()) {
michael@0 6073 uint32_t i;
michael@0 6074 for (i = 0; i < ArrayLength(UnitData); ++i) {
michael@0 6075 if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
michael@0 6076 UnitData[i].length)) {
michael@0 6077 units = UnitData[i].unit;
michael@0 6078 type = UnitData[i].type;
michael@0 6079 break;
michael@0 6080 }
michael@0 6081 }
michael@0 6082
michael@0 6083 if (i == ArrayLength(UnitData)) {
michael@0 6084 // Unknown unit
michael@0 6085 return false;
michael@0 6086 }
michael@0 6087
michael@0 6088 if (!mViewportUnitsEnabled &&
michael@0 6089 (eCSSUnit_ViewportWidth == units ||
michael@0 6090 eCSSUnit_ViewportHeight == units ||
michael@0 6091 eCSSUnit_ViewportMin == units ||
michael@0 6092 eCSSUnit_ViewportMax == units)) {
michael@0 6093 // Viewport units aren't allowed right now, probably because we're
michael@0 6094 // inside an @page declaration. Fail.
michael@0 6095 return false;
michael@0 6096 }
michael@0 6097 } else {
michael@0 6098 // Must be a zero number...
michael@0 6099 NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
michael@0 6100 if ((VARIANT_LENGTH & aVariantMask) != 0) {
michael@0 6101 units = eCSSUnit_Pixel;
michael@0 6102 type = VARIANT_LENGTH;
michael@0 6103 }
michael@0 6104 else if ((VARIANT_ANGLE & aVariantMask) != 0) {
michael@0 6105 NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE,
michael@0 6106 "must have allowed zero angle");
michael@0 6107 units = eCSSUnit_Degree;
michael@0 6108 type = VARIANT_ANGLE;
michael@0 6109 }
michael@0 6110 else {
michael@0 6111 NS_ERROR("Variant mask does not include dimension; why were we called?");
michael@0 6112 return false;
michael@0 6113 }
michael@0 6114 }
michael@0 6115 if ((type & aVariantMask) != 0) {
michael@0 6116 aValue.SetFloatValue(aNumber, units);
michael@0 6117 return true;
michael@0 6118 }
michael@0 6119 return false;
michael@0 6120 }
michael@0 6121
michael@0 6122 // Note that this does include VARIANT_CALC, which is numeric. This is
michael@0 6123 // because calc() parsing, as proposed, drops range restrictions inside
michael@0 6124 // the calc() expression and clamps the result of the calculation to the
michael@0 6125 // range.
michael@0 6126 #define VARIANT_ALL_NONNUMERIC \
michael@0 6127 VARIANT_KEYWORD | \
michael@0 6128 VARIANT_COLOR | \
michael@0 6129 VARIANT_URL | \
michael@0 6130 VARIANT_STRING | \
michael@0 6131 VARIANT_COUNTER | \
michael@0 6132 VARIANT_ATTR | \
michael@0 6133 VARIANT_IDENTIFIER | \
michael@0 6134 VARIANT_IDENTIFIER_NO_INHERIT | \
michael@0 6135 VARIANT_AUTO | \
michael@0 6136 VARIANT_INHERIT | \
michael@0 6137 VARIANT_NONE | \
michael@0 6138 VARIANT_NORMAL | \
michael@0 6139 VARIANT_SYSFONT | \
michael@0 6140 VARIANT_GRADIENT | \
michael@0 6141 VARIANT_TIMING_FUNCTION | \
michael@0 6142 VARIANT_ALL | \
michael@0 6143 VARIANT_CALC | \
michael@0 6144 VARIANT_OPENTYPE_SVG_KEYWORD
michael@0 6145
michael@0 6146 // Note that callers passing VARIANT_CALC in aVariantMask will get
michael@0 6147 // full-range parsing inside the calc() expression, and the code that
michael@0 6148 // computes the calc will be required to clamp the resulting value to an
michael@0 6149 // appropriate range.
michael@0 6150 bool
michael@0 6151 CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue,
michael@0 6152 int32_t aVariantMask,
michael@0 6153 const KTableValue aKeywordTable[])
michael@0 6154 {
michael@0 6155 // The variant mask must only contain non-numeric variants or the ones
michael@0 6156 // that we specifically handle.
michael@0 6157 NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
michael@0 6158 VARIANT_NUMBER |
michael@0 6159 VARIANT_LENGTH |
michael@0 6160 VARIANT_PERCENT |
michael@0 6161 VARIANT_INTEGER)) == 0,
michael@0 6162 "need to update code below to handle additional variants");
michael@0 6163
michael@0 6164 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
michael@0 6165 if (eCSSUnit_Number == aValue.GetUnit() ||
michael@0 6166 aValue.IsLengthUnit()){
michael@0 6167 if (aValue.GetFloatValue() < 0) {
michael@0 6168 UngetToken();
michael@0 6169 return false;
michael@0 6170 }
michael@0 6171 }
michael@0 6172 else if (aValue.GetUnit() == eCSSUnit_Percent) {
michael@0 6173 if (aValue.GetPercentValue() < 0) {
michael@0 6174 UngetToken();
michael@0 6175 return false;
michael@0 6176 }
michael@0 6177 } else if (aValue.GetUnit() == eCSSUnit_Integer) {
michael@0 6178 if (aValue.GetIntValue() < 0) {
michael@0 6179 UngetToken();
michael@0 6180 return false;
michael@0 6181 }
michael@0 6182 }
michael@0 6183 return true;
michael@0 6184 }
michael@0 6185 return false;
michael@0 6186 }
michael@0 6187
michael@0 6188 // Note that callers passing VARIANT_CALC in aVariantMask will get
michael@0 6189 // full-range parsing inside the calc() expression, and the code that
michael@0 6190 // computes the calc will be required to clamp the resulting value to an
michael@0 6191 // appropriate range.
michael@0 6192 bool
michael@0 6193 CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue,
michael@0 6194 int32_t aVariantMask,
michael@0 6195 const KTableValue aKeywordTable[])
michael@0 6196 {
michael@0 6197 // The variant mask must only contain non-numeric variants or the ones
michael@0 6198 // that we specifically handle.
michael@0 6199 NS_ABORT_IF_FALSE((aVariantMask & ~(VARIANT_ALL_NONNUMERIC |
michael@0 6200 VARIANT_NUMBER |
michael@0 6201 VARIANT_INTEGER)) == 0,
michael@0 6202 "need to update code below to handle additional variants");
michael@0 6203
michael@0 6204 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
michael@0 6205 if (aValue.GetUnit() == eCSSUnit_Integer) {
michael@0 6206 if (aValue.GetIntValue() < 1) {
michael@0 6207 UngetToken();
michael@0 6208 return false;
michael@0 6209 }
michael@0 6210 } else if (eCSSUnit_Number == aValue.GetUnit()) {
michael@0 6211 if (aValue.GetFloatValue() < 1.0f) {
michael@0 6212 UngetToken();
michael@0 6213 return false;
michael@0 6214 }
michael@0 6215 }
michael@0 6216 return true;
michael@0 6217 }
michael@0 6218 return false;
michael@0 6219 }
michael@0 6220
michael@0 6221 // Assigns to aValue iff it returns true.
michael@0 6222 bool
michael@0 6223 CSSParserImpl::ParseVariant(nsCSSValue& aValue,
michael@0 6224 int32_t aVariantMask,
michael@0 6225 const KTableValue aKeywordTable[])
michael@0 6226 {
michael@0 6227 NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
michael@0 6228 !(aVariantMask & VARIANT_NUMBER),
michael@0 6229 "can't distinguish colors from numbers");
michael@0 6230 NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) ||
michael@0 6231 !(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)),
michael@0 6232 "can't distinguish colors from lengths");
michael@0 6233 NS_ASSERTION(!(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)) ||
michael@0 6234 !(aVariantMask & VARIANT_NUMBER),
michael@0 6235 "can't distinguish lengths from numbers");
michael@0 6236 NS_ABORT_IF_FALSE(!(aVariantMask & VARIANT_IDENTIFIER) ||
michael@0 6237 !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT),
michael@0 6238 "must not set both VARIANT_IDENTIFIER and "
michael@0 6239 "VARIANT_IDENTIFIER_NO_INHERIT");
michael@0 6240
michael@0 6241 if (!GetToken(true)) {
michael@0 6242 return false;
michael@0 6243 }
michael@0 6244 nsCSSToken* tk = &mToken;
michael@0 6245 if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
michael@0 6246 (eCSSToken_Ident == tk->mType)) {
michael@0 6247 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
michael@0 6248 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
michael@0 6249 if ((aVariantMask & VARIANT_AUTO) != 0) {
michael@0 6250 if (eCSSKeyword_auto == keyword) {
michael@0 6251 aValue.SetAutoValue();
michael@0 6252 return true;
michael@0 6253 }
michael@0 6254 }
michael@0 6255 if ((aVariantMask & VARIANT_INHERIT) != 0) {
michael@0 6256 // XXX Should we check IsParsingCompoundProperty, or do all
michael@0 6257 // callers handle it? (Not all callers set it, though, since
michael@0 6258 // they want the quirks that are disabled by setting it.)
michael@0 6259
michael@0 6260 // IMPORTANT: If new keywords are added here,
michael@0 6261 // they probably need to be added in ParseCustomIdent as well.
michael@0 6262 if (eCSSKeyword_inherit == keyword) {
michael@0 6263 aValue.SetInheritValue();
michael@0 6264 return true;
michael@0 6265 }
michael@0 6266 else if (eCSSKeyword_initial == keyword) {
michael@0 6267 aValue.SetInitialValue();
michael@0 6268 return true;
michael@0 6269 }
michael@0 6270 else if (eCSSKeyword_unset == keyword &&
michael@0 6271 nsLayoutUtils::UnsetValueEnabled()) {
michael@0 6272 aValue.SetUnsetValue();
michael@0 6273 return true;
michael@0 6274 }
michael@0 6275 }
michael@0 6276 if ((aVariantMask & VARIANT_NONE) != 0) {
michael@0 6277 if (eCSSKeyword_none == keyword) {
michael@0 6278 aValue.SetNoneValue();
michael@0 6279 return true;
michael@0 6280 }
michael@0 6281 }
michael@0 6282 if ((aVariantMask & VARIANT_ALL) != 0) {
michael@0 6283 if (eCSSKeyword_all == keyword) {
michael@0 6284 aValue.SetAllValue();
michael@0 6285 return true;
michael@0 6286 }
michael@0 6287 }
michael@0 6288 if ((aVariantMask & VARIANT_NORMAL) != 0) {
michael@0 6289 if (eCSSKeyword_normal == keyword) {
michael@0 6290 aValue.SetNormalValue();
michael@0 6291 return true;
michael@0 6292 }
michael@0 6293 }
michael@0 6294 if ((aVariantMask & VARIANT_SYSFONT) != 0) {
michael@0 6295 if (eCSSKeyword__moz_use_system_font == keyword &&
michael@0 6296 !IsParsingCompoundProperty()) {
michael@0 6297 aValue.SetSystemFontValue();
michael@0 6298 return true;
michael@0 6299 }
michael@0 6300 }
michael@0 6301 if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) {
michael@0 6302 static bool sOpentypeSVGEnabled;
michael@0 6303 static bool sOpentypeSVGEnabledCached = false;
michael@0 6304 if (!sOpentypeSVGEnabledCached) {
michael@0 6305 sOpentypeSVGEnabledCached = true;
michael@0 6306 Preferences::AddBoolVarCache(&sOpentypeSVGEnabled,
michael@0 6307 "gfx.font_rendering.opentype_svg.enabled");
michael@0 6308 }
michael@0 6309 if (sOpentypeSVGEnabled) {
michael@0 6310 aVariantMask |= VARIANT_KEYWORD;
michael@0 6311 }
michael@0 6312 }
michael@0 6313 if ((aVariantMask & VARIANT_KEYWORD) != 0) {
michael@0 6314 int32_t value;
michael@0 6315 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
michael@0 6316 aValue.SetIntValue(value, eCSSUnit_Enumerated);
michael@0 6317 return true;
michael@0 6318 }
michael@0 6319 }
michael@0 6320 }
michael@0 6321 }
michael@0 6322 // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or
michael@0 6323 // VARIANT_ZERO_ANGLE.
michael@0 6324 if (((aVariantMask & VARIANT_NUMBER) != 0) &&
michael@0 6325 (eCSSToken_Number == tk->mType)) {
michael@0 6326 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
michael@0 6327 return true;
michael@0 6328 }
michael@0 6329 if (((aVariantMask & VARIANT_INTEGER) != 0) &&
michael@0 6330 (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
michael@0 6331 aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
michael@0 6332 return true;
michael@0 6333 }
michael@0 6334 if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
michael@0 6335 VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
michael@0 6336 eCSSToken_Dimension == tk->mType) ||
michael@0 6337 ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
michael@0 6338 eCSSToken_Number == tk->mType &&
michael@0 6339 tk->mNumber == 0.0f)) {
michael@0 6340 if (((aVariantMask & VARIANT_POSITIVE_DIMENSION) != 0 &&
michael@0 6341 tk->mNumber <= 0.0) ||
michael@0 6342 ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 &&
michael@0 6343 tk->mNumber < 0.0)) {
michael@0 6344 UngetToken();
michael@0 6345 return false;
michael@0 6346 }
michael@0 6347 if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
michael@0 6348 return true;
michael@0 6349 }
michael@0 6350 // Put the token back; we didn't parse it, so we shouldn't consume it
michael@0 6351 UngetToken();
michael@0 6352 return false;
michael@0 6353 }
michael@0 6354 if (((aVariantMask & VARIANT_PERCENT) != 0) &&
michael@0 6355 (eCSSToken_Percentage == tk->mType)) {
michael@0 6356 aValue.SetPercentValue(tk->mNumber);
michael@0 6357 return true;
michael@0 6358 }
michael@0 6359 if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px
michael@0 6360 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
michael@0 6361 (eCSSToken_Number == tk->mType)) {
michael@0 6362 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
michael@0 6363 return true;
michael@0 6364 }
michael@0 6365 }
michael@0 6366
michael@0 6367 if (IsSVGMode() && !IsParsingCompoundProperty()) {
michael@0 6368 // STANDARD: SVG Spec states that lengths and coordinates can be unitless
michael@0 6369 // in which case they default to user-units (1 px = 1 user unit)
michael@0 6370 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
michael@0 6371 (eCSSToken_Number == tk->mType)) {
michael@0 6372 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
michael@0 6373 return true;
michael@0 6374 }
michael@0 6375 }
michael@0 6376
michael@0 6377 if (((aVariantMask & VARIANT_URL) != 0) &&
michael@0 6378 eCSSToken_URL == tk->mType) {
michael@0 6379 SetValueToURL(aValue, tk->mIdent);
michael@0 6380 return true;
michael@0 6381 }
michael@0 6382 if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
michael@0 6383 eCSSToken_Function == tk->mType) {
michael@0 6384 // a generated gradient
michael@0 6385 nsDependentString tmp(tk->mIdent, 0);
michael@0 6386 bool isLegacy = false;
michael@0 6387 if (StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) {
michael@0 6388 tmp.Rebind(tmp, 5);
michael@0 6389 isLegacy = true;
michael@0 6390 }
michael@0 6391 bool isRepeating = false;
michael@0 6392 if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) {
michael@0 6393 tmp.Rebind(tmp, 10);
michael@0 6394 isRepeating = true;
michael@0 6395 }
michael@0 6396
michael@0 6397 if (tmp.LowerCaseEqualsLiteral("linear-gradient")) {
michael@0 6398 return ParseLinearGradient(aValue, isRepeating, isLegacy);
michael@0 6399 }
michael@0 6400 if (tmp.LowerCaseEqualsLiteral("radial-gradient")) {
michael@0 6401 return ParseRadialGradient(aValue, isRepeating, isLegacy);
michael@0 6402 }
michael@0 6403 }
michael@0 6404 if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
michael@0 6405 eCSSToken_Function == tk->mType &&
michael@0 6406 tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) {
michael@0 6407 return ParseImageRect(aValue);
michael@0 6408 }
michael@0 6409 if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
michael@0 6410 eCSSToken_Function == tk->mType &&
michael@0 6411 tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) {
michael@0 6412 return ParseElement(aValue);
michael@0 6413 }
michael@0 6414 if ((aVariantMask & VARIANT_COLOR) != 0) {
michael@0 6415 if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
michael@0 6416 (eCSSToken_ID == tk->mType) ||
michael@0 6417 (eCSSToken_Hash == tk->mType) ||
michael@0 6418 (eCSSToken_Ident == tk->mType) ||
michael@0 6419 ((eCSSToken_Function == tk->mType) &&
michael@0 6420 (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
michael@0 6421 tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
michael@0 6422 tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
michael@0 6423 tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
michael@0 6424 {
michael@0 6425 // Put token back so that parse color can get it
michael@0 6426 UngetToken();
michael@0 6427 if (ParseColor(aValue)) {
michael@0 6428 return true;
michael@0 6429 }
michael@0 6430 return false;
michael@0 6431 }
michael@0 6432 }
michael@0 6433 if (((aVariantMask & VARIANT_STRING) != 0) &&
michael@0 6434 (eCSSToken_String == tk->mType)) {
michael@0 6435 nsAutoString buffer;
michael@0 6436 buffer.Append(tk->mIdent);
michael@0 6437 aValue.SetStringValue(buffer, eCSSUnit_String);
michael@0 6438 return true;
michael@0 6439 }
michael@0 6440 if (((aVariantMask &
michael@0 6441 (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
michael@0 6442 (eCSSToken_Ident == tk->mType) &&
michael@0 6443 ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
michael@0 6444 !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
michael@0 6445 tk->mIdent.LowerCaseEqualsLiteral("initial") ||
michael@0 6446 (tk->mIdent.LowerCaseEqualsLiteral("unset") &&
michael@0 6447 nsLayoutUtils::UnsetValueEnabled())))) {
michael@0 6448 aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
michael@0 6449 return true;
michael@0 6450 }
michael@0 6451 if (((aVariantMask & VARIANT_COUNTER) != 0) &&
michael@0 6452 (eCSSToken_Function == tk->mType) &&
michael@0 6453 (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
michael@0 6454 tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
michael@0 6455 return ParseCounter(aValue);
michael@0 6456 }
michael@0 6457 if (((aVariantMask & VARIANT_ATTR) != 0) &&
michael@0 6458 (eCSSToken_Function == tk->mType) &&
michael@0 6459 tk->mIdent.LowerCaseEqualsLiteral("attr")) {
michael@0 6460 if (!ParseAttr(aValue)) {
michael@0 6461 SkipUntil(')');
michael@0 6462 return false;
michael@0 6463 }
michael@0 6464 return true;
michael@0 6465 }
michael@0 6466 if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
michael@0 6467 (eCSSToken_Function == tk->mType)) {
michael@0 6468 if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) {
michael@0 6469 if (!ParseTransitionTimingFunctionValues(aValue)) {
michael@0 6470 SkipUntil(')');
michael@0 6471 return false;
michael@0 6472 }
michael@0 6473 return true;
michael@0 6474 }
michael@0 6475 if (tk->mIdent.LowerCaseEqualsLiteral("steps")) {
michael@0 6476 if (!ParseTransitionStepTimingFunctionValues(aValue)) {
michael@0 6477 SkipUntil(')');
michael@0 6478 return false;
michael@0 6479 }
michael@0 6480 return true;
michael@0 6481 }
michael@0 6482 }
michael@0 6483 if ((aVariantMask & VARIANT_CALC) &&
michael@0 6484 (eCSSToken_Function == tk->mType) &&
michael@0 6485 (tk->mIdent.LowerCaseEqualsLiteral("calc") ||
michael@0 6486 tk->mIdent.LowerCaseEqualsLiteral("-moz-calc"))) {
michael@0 6487 // calc() currently allows only lengths and percents inside it.
michael@0 6488 return ParseCalc(aValue, aVariantMask & VARIANT_LP);
michael@0 6489 }
michael@0 6490
michael@0 6491 UngetToken();
michael@0 6492 return false;
michael@0 6493 }
michael@0 6494
michael@0 6495 bool
michael@0 6496 CSSParserImpl::ParseCustomIdent(nsCSSValue& aValue,
michael@0 6497 const nsAutoString& aIdentValue,
michael@0 6498 const nsCSSKeyword aExcludedKeywords[],
michael@0 6499 const nsCSSProps::KTableValue aPropertyKTable[])
michael@0 6500 {
michael@0 6501 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aIdentValue);
michael@0 6502 if (keyword == eCSSKeyword_UNKNOWN) {
michael@0 6503 // Fast path for identifiers that are not known CSS keywords:
michael@0 6504 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
michael@0 6505 return true;
michael@0 6506 }
michael@0 6507 if (keyword == eCSSKeyword_inherit ||
michael@0 6508 keyword == eCSSKeyword_initial ||
michael@0 6509 keyword == eCSSKeyword_unset ||
michael@0 6510 keyword == eCSSKeyword_default ||
michael@0 6511 (aPropertyKTable &&
michael@0 6512 nsCSSProps::FindIndexOfKeyword(keyword, aPropertyKTable) >= 0)) {
michael@0 6513 return false;
michael@0 6514 }
michael@0 6515 if (aExcludedKeywords) {
michael@0 6516 for (uint32_t i = 0;; i++) {
michael@0 6517 nsCSSKeyword excludedKeyword = aExcludedKeywords[i];
michael@0 6518 if (excludedKeyword == eCSSKeyword_UNKNOWN) {
michael@0 6519 break;
michael@0 6520 }
michael@0 6521 if (excludedKeyword == keyword) {
michael@0 6522 return false;
michael@0 6523 }
michael@0 6524 }
michael@0 6525 }
michael@0 6526 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
michael@0 6527 return true;
michael@0 6528 }
michael@0 6529
michael@0 6530 bool
michael@0 6531 CSSParserImpl::ParseCounter(nsCSSValue& aValue)
michael@0 6532 {
michael@0 6533 nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
michael@0 6534 eCSSUnit_Counter : eCSSUnit_Counters);
michael@0 6535
michael@0 6536 // A non-iterative for loop to break out when an error occurs.
michael@0 6537 for (;;) {
michael@0 6538 if (!GetToken(true)) {
michael@0 6539 break;
michael@0 6540 }
michael@0 6541 if (eCSSToken_Ident != mToken.mType) {
michael@0 6542 UngetToken();
michael@0 6543 break;
michael@0 6544 }
michael@0 6545
michael@0 6546 nsRefPtr<nsCSSValue::Array> val =
michael@0 6547 nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
michael@0 6548
michael@0 6549 val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident);
michael@0 6550
michael@0 6551 if (eCSSUnit_Counters == unit) {
michael@0 6552 // must have a comma and then a separator string
michael@0 6553 if (!ExpectSymbol(',', true) || !GetToken(true)) {
michael@0 6554 break;
michael@0 6555 }
michael@0 6556 if (eCSSToken_String != mToken.mType) {
michael@0 6557 UngetToken();
michael@0 6558 break;
michael@0 6559 }
michael@0 6560 val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
michael@0 6561 }
michael@0 6562
michael@0 6563 // get optional type
michael@0 6564 int32_t type = NS_STYLE_LIST_STYLE_DECIMAL;
michael@0 6565 if (ExpectSymbol(',', true)) {
michael@0 6566 if (!GetToken(true)) {
michael@0 6567 break;
michael@0 6568 }
michael@0 6569 nsCSSKeyword keyword;
michael@0 6570 if (eCSSToken_Ident != mToken.mType ||
michael@0 6571 (keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) ==
michael@0 6572 eCSSKeyword_UNKNOWN ||
michael@0 6573 !nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable,
michael@0 6574 type)) {
michael@0 6575 UngetToken();
michael@0 6576 break;
michael@0 6577 }
michael@0 6578 }
michael@0 6579
michael@0 6580 int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1;
michael@0 6581 val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
michael@0 6582
michael@0 6583 if (!ExpectSymbol(')', true)) {
michael@0 6584 break;
michael@0 6585 }
michael@0 6586
michael@0 6587 aValue.SetArrayValue(val, unit);
michael@0 6588 return true;
michael@0 6589 }
michael@0 6590
michael@0 6591 SkipUntil(')');
michael@0 6592 return false;
michael@0 6593 }
michael@0 6594
michael@0 6595 bool
michael@0 6596 CSSParserImpl::ParseAttr(nsCSSValue& aValue)
michael@0 6597 {
michael@0 6598 if (!GetToken(true)) {
michael@0 6599 return false;
michael@0 6600 }
michael@0 6601
michael@0 6602 nsAutoString attr;
michael@0 6603 if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
michael@0 6604 nsAutoString holdIdent(mToken.mIdent);
michael@0 6605 if (ExpectSymbol('|', false)) { // namespace
michael@0 6606 int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent);
michael@0 6607 if (nameSpaceID == kNameSpaceID_Unknown) {
michael@0 6608 return false;
michael@0 6609 }
michael@0 6610 attr.AppendInt(nameSpaceID, 10);
michael@0 6611 attr.Append(char16_t('|'));
michael@0 6612 if (! GetToken(false)) {
michael@0 6613 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
michael@0 6614 return false;
michael@0 6615 }
michael@0 6616 if (eCSSToken_Ident == mToken.mType) {
michael@0 6617 attr.Append(mToken.mIdent);
michael@0 6618 }
michael@0 6619 else {
michael@0 6620 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
michael@0 6621 UngetToken();
michael@0 6622 return false;
michael@0 6623 }
michael@0 6624 }
michael@0 6625 else { // no namespace
michael@0 6626 attr = holdIdent;
michael@0 6627 }
michael@0 6628 }
michael@0 6629 else if (mToken.IsSymbol('*')) { // namespace wildcard
michael@0 6630 // Wildcard namespace makes no sense here and is not allowed
michael@0 6631 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
michael@0 6632 UngetToken();
michael@0 6633 return false;
michael@0 6634 }
michael@0 6635 else if (mToken.IsSymbol('|')) { // explicit NO namespace
michael@0 6636 if (! GetToken(false)) {
michael@0 6637 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
michael@0 6638 return false;
michael@0 6639 }
michael@0 6640 if (eCSSToken_Ident == mToken.mType) {
michael@0 6641 attr.Append(mToken.mIdent);
michael@0 6642 }
michael@0 6643 else {
michael@0 6644 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
michael@0 6645 UngetToken();
michael@0 6646 return false;
michael@0 6647 }
michael@0 6648 }
michael@0 6649 else {
michael@0 6650 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
michael@0 6651 UngetToken();
michael@0 6652 return false;
michael@0 6653 }
michael@0 6654 if (!ExpectSymbol(')', true)) {
michael@0 6655 return false;
michael@0 6656 }
michael@0 6657 aValue.SetStringValue(attr, eCSSUnit_Attr);
michael@0 6658 return true;
michael@0 6659 }
michael@0 6660
michael@0 6661 bool
michael@0 6662 CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL)
michael@0 6663 {
michael@0 6664 if (!mSheetPrincipal) {
michael@0 6665 NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
michael@0 6666 "origin principal");
michael@0 6667 return false;
michael@0 6668 }
michael@0 6669
michael@0 6670 nsRefPtr<nsStringBuffer> buffer(nsCSSValue::BufferFromString(aURL));
michael@0 6671
michael@0 6672 // Note: urlVal retains its own reference to |buffer|.
michael@0 6673 mozilla::css::URLValue *urlVal =
michael@0 6674 new mozilla::css::URLValue(buffer, mBaseURI, mSheetURI, mSheetPrincipal);
michael@0 6675 aValue.SetURLValue(urlVal);
michael@0 6676 return true;
michael@0 6677 }
michael@0 6678
michael@0 6679 /**
michael@0 6680 * Parse the image-orientation property, which has the grammar:
michael@0 6681 * <angle> flip? | flip | from-image
michael@0 6682 */
michael@0 6683 bool
michael@0 6684 CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue)
michael@0 6685 {
michael@0 6686 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
michael@0 6687 // 'inherit', 'initial' and 'unset' must be alone
michael@0 6688 return true;
michael@0 6689 }
michael@0 6690
michael@0 6691 // Check for an angle with optional 'flip'.
michael@0 6692 nsCSSValue angle;
michael@0 6693 if (ParseVariant(angle, VARIANT_ANGLE, nullptr)) {
michael@0 6694 nsCSSValue flip;
michael@0 6695
michael@0 6696 if (ParseVariant(flip, VARIANT_KEYWORD, nsCSSProps::kImageOrientationFlipKTable)) {
michael@0 6697 nsRefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(2);
michael@0 6698 array->Item(0) = angle;
michael@0 6699 array->Item(1) = flip;
michael@0 6700 aValue.SetArrayValue(array, eCSSUnit_Array);
michael@0 6701 } else {
michael@0 6702 aValue = angle;
michael@0 6703 }
michael@0 6704
michael@0 6705 return true;
michael@0 6706 }
michael@0 6707
michael@0 6708 // The remaining possibilities (bare 'flip' and 'from-image') are both
michael@0 6709 // keywords, so we can handle them at the same time.
michael@0 6710 nsCSSValue keyword;
michael@0 6711 if (ParseVariant(keyword, VARIANT_KEYWORD, nsCSSProps::kImageOrientationKTable)) {
michael@0 6712 aValue = keyword;
michael@0 6713 return true;
michael@0 6714 }
michael@0 6715
michael@0 6716 // All possibilities failed.
michael@0 6717 return false;
michael@0 6718 }
michael@0 6719
michael@0 6720 /**
michael@0 6721 * Parse the arguments of -moz-image-rect() function.
michael@0 6722 * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
michael@0 6723 */
michael@0 6724 bool
michael@0 6725 CSSParserImpl::ParseImageRect(nsCSSValue& aImage)
michael@0 6726 {
michael@0 6727 // A non-iterative for loop to break out when an error occurs.
michael@0 6728 for (;;) {
michael@0 6729 nsCSSValue newFunction;
michael@0 6730 static const uint32_t kNumArgs = 5;
michael@0 6731 nsCSSValue::Array* func =
michael@0 6732 newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs);
michael@0 6733
michael@0 6734 // func->Item(0) is reserved for the function name.
michael@0 6735 nsCSSValue& url = func->Item(1);
michael@0 6736 nsCSSValue& top = func->Item(2);
michael@0 6737 nsCSSValue& right = func->Item(3);
michael@0 6738 nsCSSValue& bottom = func->Item(4);
michael@0 6739 nsCSSValue& left = func->Item(5);
michael@0 6740
michael@0 6741 nsAutoString urlString;
michael@0 6742 if (!ParseURLOrString(urlString) ||
michael@0 6743 !SetValueToURL(url, urlString) ||
michael@0 6744 !ExpectSymbol(',', true)) {
michael@0 6745 break;
michael@0 6746 }
michael@0 6747
michael@0 6748 static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT;
michael@0 6749 if (!ParseNonNegativeVariant(top, VARIANT_SIDE, nullptr) ||
michael@0 6750 !ExpectSymbol(',', true) ||
michael@0 6751 !ParseNonNegativeVariant(right, VARIANT_SIDE, nullptr) ||
michael@0 6752 !ExpectSymbol(',', true) ||
michael@0 6753 !ParseNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) ||
michael@0 6754 !ExpectSymbol(',', true) ||
michael@0 6755 !ParseNonNegativeVariant(left, VARIANT_SIDE, nullptr) ||
michael@0 6756 !ExpectSymbol(')', true))
michael@0 6757 break;
michael@0 6758
michael@0 6759 aImage = newFunction;
michael@0 6760 return true;
michael@0 6761 }
michael@0 6762
michael@0 6763 SkipUntil(')');
michael@0 6764 return false;
michael@0 6765 }
michael@0 6766
michael@0 6767 // <element>: -moz-element(# <element_id> )
michael@0 6768 bool
michael@0 6769 CSSParserImpl::ParseElement(nsCSSValue& aValue)
michael@0 6770 {
michael@0 6771 // A non-iterative for loop to break out when an error occurs.
michael@0 6772 for (;;) {
michael@0 6773 if (!GetToken(true))
michael@0 6774 break;
michael@0 6775
michael@0 6776 if (mToken.mType == eCSSToken_ID) {
michael@0 6777 aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element);
michael@0 6778 } else {
michael@0 6779 UngetToken();
michael@0 6780 break;
michael@0 6781 }
michael@0 6782
michael@0 6783 if (!ExpectSymbol(')', true))
michael@0 6784 break;
michael@0 6785
michael@0 6786 return true;
michael@0 6787 }
michael@0 6788
michael@0 6789 // If we detect a syntax error, we must match the opening parenthesis of the
michael@0 6790 // function with the closing parenthesis and skip all the tokens in between.
michael@0 6791 SkipUntil(')');
michael@0 6792 return false;
michael@0 6793 }
michael@0 6794
michael@0 6795 // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
michael@0 6796 bool
michael@0 6797 CSSParserImpl::ParseFlex()
michael@0 6798 {
michael@0 6799 // First check for inherit / initial / unset
michael@0 6800 nsCSSValue tmpVal;
michael@0 6801 if (ParseVariant(tmpVal, VARIANT_INHERIT, nullptr)) {
michael@0 6802 AppendValue(eCSSProperty_flex_grow, tmpVal);
michael@0 6803 AppendValue(eCSSProperty_flex_shrink, tmpVal);
michael@0 6804 AppendValue(eCSSProperty_flex_basis, tmpVal);
michael@0 6805 return true;
michael@0 6806 }
michael@0 6807
michael@0 6808 // Next, check for 'none' == '0 0 auto'
michael@0 6809 if (ParseVariant(tmpVal, VARIANT_NONE, nullptr)) {
michael@0 6810 AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number));
michael@0 6811 AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number));
michael@0 6812 AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto));
michael@0 6813 return true;
michael@0 6814 }
michael@0 6815
michael@0 6816 // OK, try parsing our value as individual per-subproperty components:
michael@0 6817 // [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
michael@0 6818
michael@0 6819 // Each subproperty has a default value that it takes when it's omitted in a
michael@0 6820 // "flex" shorthand value. These default values are *only* for the shorthand
michael@0 6821 // syntax -- they're distinct from the subproperties' own initial values. We
michael@0 6822 // start with each subproperty at its default, as if we had "flex: 1 1 0%".
michael@0 6823 nsCSSValue flexGrow(1.0f, eCSSUnit_Number);
michael@0 6824 nsCSSValue flexShrink(1.0f, eCSSUnit_Number);
michael@0 6825 nsCSSValue flexBasis(0.0f, eCSSUnit_Percent);
michael@0 6826
michael@0 6827 // OVERVIEW OF PARSING STRATEGY:
michael@0 6828 // =============================
michael@0 6829 // a) Parse the first component as either flex-basis or flex-grow.
michael@0 6830 // b) If it wasn't flex-grow, parse the _next_ component as flex-grow.
michael@0 6831 // c) Now we've just parsed flex-grow -- so try parsing the next thing as
michael@0 6832 // flex-shrink.
michael@0 6833 // d) Finally: If we didn't get flex-basis at the beginning, try to parse
michael@0 6834 // it now, at the end.
michael@0 6835 //
michael@0 6836 // More details in each section below.
michael@0 6837
michael@0 6838 uint32_t flexBasisVariantMask =
michael@0 6839 (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT));
michael@0 6840
michael@0 6841 // (a) Parse first component. It can be either be a 'flex-basis' value or a
michael@0 6842 // 'flex-grow' value, so we use the flex-basis-specific variant mask, along
michael@0 6843 // with VARIANT_NUMBER to accept 'flex-grow' values.
michael@0 6844 //
michael@0 6845 // NOTE: if we encounter unitless 0 here, we *must* interpret it as a
michael@0 6846 // 'flex-grow' value (a number), *not* as a 'flex-basis' value (a length).
michael@0 6847 // Conveniently, that's the behavior this combined variant-mask gives us --
michael@0 6848 // it'll treat unitless 0 as a number. The flexbox spec requires this:
michael@0 6849 // "a unitless zero that is not already preceded by two flex factors must be
michael@0 6850 // interpreted as a flex factor.
michael@0 6851 if (!ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER,
michael@0 6852 nsCSSProps::kWidthKTable)) {
michael@0 6853 // First component was not a valid flex-basis or flex-grow value. Fail.
michael@0 6854 return false;
michael@0 6855 }
michael@0 6856
michael@0 6857 // Record what we just parsed as either flex-basis or flex-grow:
michael@0 6858 bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number);
michael@0 6859 (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal;
michael@0 6860
michael@0 6861 // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow.
michael@0 6862 bool doneParsing = false;
michael@0 6863 if (wasFirstComponentFlexBasis) {
michael@0 6864 if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) {
michael@0 6865 flexGrow = tmpVal;
michael@0 6866 } else {
michael@0 6867 // Failed to parse anything after our flex-basis -- that's fine. We can
michael@0 6868 // skip the remaining parsing.
michael@0 6869 doneParsing = true;
michael@0 6870 }
michael@0 6871 }
michael@0 6872
michael@0 6873 if (!doneParsing) {
michael@0 6874 // (c) OK -- the last thing we parsed was flex-grow, so look for a
michael@0 6875 // flex-shrink in the next position.
michael@0 6876 if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) {
michael@0 6877 flexShrink = tmpVal;
michael@0 6878 }
michael@0 6879
michael@0 6880 // d) Finally: If we didn't get flex-basis at the beginning, try to parse
michael@0 6881 // it now, at the end.
michael@0 6882 //
michael@0 6883 // NOTE: If we encounter unitless 0 in this final position, we'll parse it
michael@0 6884 // as a 'flex-basis' value. That's OK, because we know it must have
michael@0 6885 // been "preceded by 2 flex factors" (justification below), which gets us
michael@0 6886 // out of the spec's requirement of otherwise having to treat unitless 0
michael@0 6887 // as a flex factor.
michael@0 6888 //
michael@0 6889 // JUSTIFICATION: How do we know that a unitless 0 here must have been
michael@0 6890 // preceded by 2 flex factors? Well, suppose we had a unitless 0 that
michael@0 6891 // was preceded by only 1 flex factor. Then, we would have already
michael@0 6892 // accepted this unitless 0 as the 'flex-shrink' value, up above (since
michael@0 6893 // it's a valid flex-shrink value), and we'd have moved on to the next
michael@0 6894 // token (if any). And of course, if we instead had a unitless 0 preceded
michael@0 6895 // by *no* flex factors (if it were the first token), we would've already
michael@0 6896 // parsed it in our very first call to ParseNonNegativeVariant(). So, any
michael@0 6897 // unitless 0 encountered here *must* have been preceded by 2 flex factors.
michael@0 6898 if (!wasFirstComponentFlexBasis &&
michael@0 6899 ParseNonNegativeVariant(tmpVal, flexBasisVariantMask,
michael@0 6900 nsCSSProps::kWidthKTable)) {
michael@0 6901 flexBasis = tmpVal;
michael@0 6902 }
michael@0 6903 }
michael@0 6904
michael@0 6905 AppendValue(eCSSProperty_flex_grow, flexGrow);
michael@0 6906 AppendValue(eCSSProperty_flex_shrink, flexShrink);
michael@0 6907 AppendValue(eCSSProperty_flex_basis, flexBasis);
michael@0 6908
michael@0 6909 return true;
michael@0 6910 }
michael@0 6911
michael@0 6912 // flex-flow: <flex-direction> || <flex-wrap>
michael@0 6913 bool
michael@0 6914 CSSParserImpl::ParseFlexFlow()
michael@0 6915 {
michael@0 6916 static const nsCSSProperty kFlexFlowSubprops[] = {
michael@0 6917 eCSSProperty_flex_direction,
michael@0 6918 eCSSProperty_flex_wrap
michael@0 6919 };
michael@0 6920 const size_t numProps = MOZ_ARRAY_LENGTH(kFlexFlowSubprops);
michael@0 6921 nsCSSValue values[numProps];
michael@0 6922
michael@0 6923 int32_t found = ParseChoice(values, kFlexFlowSubprops, numProps);
michael@0 6924
michael@0 6925 // Bail if we didn't successfully parse anything
michael@0 6926 if (found < 1) {
michael@0 6927 return false;
michael@0 6928 }
michael@0 6929
michael@0 6930 // If either property didn't get an explicit value, use its initial value.
michael@0 6931 if ((found & 1) == 0) {
michael@0 6932 values[0].SetIntValue(NS_STYLE_FLEX_DIRECTION_ROW, eCSSUnit_Enumerated);
michael@0 6933 }
michael@0 6934 if ((found & 2) == 0) {
michael@0 6935 values[1].SetIntValue(NS_STYLE_FLEX_WRAP_NOWRAP, eCSSUnit_Enumerated);
michael@0 6936 }
michael@0 6937
michael@0 6938 // Store these values and declare success!
michael@0 6939 for (size_t i = 0; i < numProps; i++) {
michael@0 6940 AppendValue(kFlexFlowSubprops[i], values[i]);
michael@0 6941 }
michael@0 6942 return true;
michael@0 6943 }
michael@0 6944
michael@0 6945 bool
michael@0 6946 CSSParserImpl::ParseGridAutoFlow()
michael@0 6947 {
michael@0 6948 nsCSSValue value;
michael@0 6949 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 6950 AppendValue(eCSSProperty_grid_auto_flow, value);
michael@0 6951 return true;
michael@0 6952 }
michael@0 6953
michael@0 6954 static const int32_t mask[] = {
michael@0 6955 NS_STYLE_GRID_AUTO_FLOW_COLUMN | NS_STYLE_GRID_AUTO_FLOW_ROW,
michael@0 6956 MASK_END_VALUE
michael@0 6957 };
michael@0 6958 if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) {
michael@0 6959 return false;
michael@0 6960 }
michael@0 6961 int32_t bitField = value.GetIntValue();
michael@0 6962
michael@0 6963 // Requires one of these
michael@0 6964 if (!(bitField & NS_STYLE_GRID_AUTO_FLOW_NONE ||
michael@0 6965 bitField & NS_STYLE_GRID_AUTO_FLOW_COLUMN ||
michael@0 6966 bitField & NS_STYLE_GRID_AUTO_FLOW_ROW)) {
michael@0 6967 return false;
michael@0 6968 }
michael@0 6969
michael@0 6970 // 'none' is only valid if it occurs alone:
michael@0 6971 if (bitField & NS_STYLE_GRID_AUTO_FLOW_NONE &&
michael@0 6972 bitField != NS_STYLE_GRID_AUTO_FLOW_NONE) {
michael@0 6973 return false;
michael@0 6974 }
michael@0 6975
michael@0 6976 AppendValue(eCSSProperty_grid_auto_flow, value);
michael@0 6977 return true;
michael@0 6978 }
michael@0 6979
michael@0 6980 CSSParseResult
michael@0 6981 CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue)
michael@0 6982 {
michael@0 6983 if (!ExpectSymbol('(', true)) {
michael@0 6984 return CSSParseResult::NotFound;
michael@0 6985 }
michael@0 6986 if (!GetToken(true) || mToken.IsSymbol(')')) {
michael@0 6987 return CSSParseResult::Ok;
michael@0 6988 }
michael@0 6989 // 'return' so far leaves aValue untouched, to represent an empty list.
michael@0 6990
michael@0 6991 nsCSSValueList* item;
michael@0 6992 if (aValue.GetUnit() == eCSSUnit_List) {
michael@0 6993 // Find the end of an existing list.
michael@0 6994 // The grid-template shorthand uses this, at most once for a given list.
michael@0 6995
michael@0 6996 // NOTE: we could avoid this traversal by somehow keeping around
michael@0 6997 // a pointer to the last item from the previous call.
michael@0 6998 // It's not yet clear if this is worth the additional code complexity.
michael@0 6999 item = aValue.GetListValue();
michael@0 7000 while (item->mNext) {
michael@0 7001 item = item->mNext;
michael@0 7002 }
michael@0 7003 item->mNext = new nsCSSValueList;
michael@0 7004 item = item->mNext;
michael@0 7005 } else {
michael@0 7006 MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "Unexpected unit");
michael@0 7007 item = aValue.SetListValue();
michael@0 7008 }
michael@0 7009 for (;;) {
michael@0 7010 if (!(eCSSToken_Ident == mToken.mType &&
michael@0 7011 ParseCustomIdent(item->mValue, mToken.mIdent))) {
michael@0 7012 UngetToken();
michael@0 7013 SkipUntil(')');
michael@0 7014 return CSSParseResult::Error;
michael@0 7015 }
michael@0 7016 if (!GetToken(true) || mToken.IsSymbol(')')) {
michael@0 7017 return CSSParseResult::Ok;
michael@0 7018 }
michael@0 7019 item->mNext = new nsCSSValueList;
michael@0 7020 item = item->mNext;
michael@0 7021 }
michael@0 7022 }
michael@0 7023
michael@0 7024 // Assuming the 'repeat(' function token has already been consumed,
michael@0 7025 // parse the rest of repeat(<positive-integer>, <line-names>+)
michael@0 7026 // Append to the linked list whose end is given by |aTailPtr|,
michael@0 7027 // and updated |aTailPtr| to point to the new end of the list.
michael@0 7028 bool
michael@0 7029 CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr)
michael@0 7030 {
michael@0 7031 if (!(GetToken(true) &&
michael@0 7032 mToken.mType == eCSSToken_Number &&
michael@0 7033 mToken.mIntegerValid &&
michael@0 7034 mToken.mInteger > 0)) {
michael@0 7035 SkipUntil(')');
michael@0 7036 return false;
michael@0 7037 }
michael@0 7038 int32_t repetitions = std::min(mToken.mInteger,
michael@0 7039 GRID_TEMPLATE_MAX_REPETITIONS);
michael@0 7040 if (!ExpectSymbol(',', true)) {
michael@0 7041 SkipUntil(')');
michael@0 7042 return false;
michael@0 7043 }
michael@0 7044
michael@0 7045 // Parse at least one <line-names>
michael@0 7046 nsCSSValueList* tail = *aTailPtr;
michael@0 7047 do {
michael@0 7048 tail->mNext = new nsCSSValueList;
michael@0 7049 tail = tail->mNext;
michael@0 7050 if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) {
michael@0 7051 SkipUntil(')');
michael@0 7052 return false;
michael@0 7053 }
michael@0 7054 } while (!ExpectSymbol(')', true));
michael@0 7055 nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext;
michael@0 7056 nsCSSValueList* lastRepeatedItem = tail;
michael@0 7057
michael@0 7058 // Our repeated items are already in the target list once,
michael@0 7059 // so they need to be repeated |repetitions - 1| more times.
michael@0 7060 MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
michael@0 7061 while (--repetitions) {
michael@0 7062 nsCSSValueList* repeatedItem = firstRepeatedItem;
michael@0 7063 for (;;) {
michael@0 7064 tail->mNext = new nsCSSValueList;
michael@0 7065 tail = tail->mNext;
michael@0 7066 tail->mValue = repeatedItem->mValue;
michael@0 7067 if (repeatedItem == lastRepeatedItem) {
michael@0 7068 break;
michael@0 7069 }
michael@0 7070 repeatedItem = repeatedItem->mNext;
michael@0 7071 }
michael@0 7072 }
michael@0 7073 *aTailPtr = tail;
michael@0 7074 return true;
michael@0 7075 }
michael@0 7076
michael@0 7077 // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>?
michael@0 7078 bool
michael@0 7079 CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
michael@0 7080 {
michael@0 7081 nsCSSValueList* item = aValue.SetListValue();
michael@0 7082 // This marker distinguishes the value from a <track-list>.
michael@0 7083 item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID,
michael@0 7084 eCSSUnit_Enumerated);
michael@0 7085 for (;;) {
michael@0 7086 // First try to parse repeat(<positive-integer>, <line-names>+)
michael@0 7087 if (!GetToken(true)) {
michael@0 7088 return true;
michael@0 7089 }
michael@0 7090 if (mToken.mType == eCSSToken_Function &&
michael@0 7091 mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
michael@0 7092 if (!ParseGridLineNameListRepeat(&item)) {
michael@0 7093 return false;
michael@0 7094 }
michael@0 7095 } else {
michael@0 7096 UngetToken();
michael@0 7097
michael@0 7098 // This was not a repeat() function. Try to parse <line-names>.
michael@0 7099 nsCSSValue lineNames;
michael@0 7100 CSSParseResult result = ParseGridLineNames(lineNames);
michael@0 7101 if (result == CSSParseResult::NotFound) {
michael@0 7102 return true;
michael@0 7103 }
michael@0 7104 if (result == CSSParseResult::Error) {
michael@0 7105 return false;
michael@0 7106 }
michael@0 7107 item->mNext = new nsCSSValueList;
michael@0 7108 item = item->mNext;
michael@0 7109 item->mValue = lineNames;
michael@0 7110 }
michael@0 7111 }
michael@0 7112 }
michael@0 7113
michael@0 7114 // Parse a <track-breadth>
michael@0 7115 bool
michael@0 7116 CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
michael@0 7117 {
michael@0 7118 if (ParseNonNegativeVariant(aValue,
michael@0 7119 VARIANT_LPCALC | VARIANT_KEYWORD,
michael@0 7120 nsCSSProps::kGridTrackBreadthKTable)) {
michael@0 7121 return true;
michael@0 7122 }
michael@0 7123
michael@0 7124 // Attempt to parse <flex> (a dimension with the "fr" unit)
michael@0 7125 if (!GetToken(true)) {
michael@0 7126 return false;
michael@0 7127 }
michael@0 7128 if (!(eCSSToken_Dimension == mToken.mType &&
michael@0 7129 mToken.mIdent.LowerCaseEqualsLiteral("fr") &&
michael@0 7130 mToken.mNumber >= 0)) {
michael@0 7131 UngetToken();
michael@0 7132 return false;
michael@0 7133 }
michael@0 7134 aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction);
michael@0 7135 return true;
michael@0 7136 }
michael@0 7137
michael@0 7138 // Parse a <track-size>
michael@0 7139 CSSParseResult
michael@0 7140 CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue)
michael@0 7141 {
michael@0 7142 // Attempt to parse 'auto' or a single <track-breadth>
michael@0 7143 if (ParseGridTrackBreadth(aValue) ||
michael@0 7144 ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
michael@0 7145 return CSSParseResult::Ok;
michael@0 7146 }
michael@0 7147
michael@0 7148 // Attempt to parse a minmax() function
michael@0 7149 if (!GetToken(true)) {
michael@0 7150 return CSSParseResult::NotFound;
michael@0 7151 }
michael@0 7152 if (!(eCSSToken_Function == mToken.mType &&
michael@0 7153 mToken.mIdent.LowerCaseEqualsLiteral("minmax"))) {
michael@0 7154 UngetToken();
michael@0 7155 return CSSParseResult::NotFound;
michael@0 7156 }
michael@0 7157 nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2);
michael@0 7158 if (ParseGridTrackBreadth(func->Item(1)) &&
michael@0 7159 ExpectSymbol(',', true) &&
michael@0 7160 ParseGridTrackBreadth(func->Item(2)) &&
michael@0 7161 ExpectSymbol(')', true)) {
michael@0 7162 return CSSParseResult::Ok;
michael@0 7163 }
michael@0 7164 SkipUntil(')');
michael@0 7165 return CSSParseResult::Error;
michael@0 7166 }
michael@0 7167
michael@0 7168 bool
michael@0 7169 CSSParserImpl::ParseGridAutoColumnsRows(nsCSSProperty aPropID)
michael@0 7170 {
michael@0 7171 nsCSSValue value;
michael@0 7172 if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
michael@0 7173 ParseGridTrackSize(value) == CSSParseResult::Ok) {
michael@0 7174 AppendValue(aPropID, value);
michael@0 7175 return true;
michael@0 7176 }
michael@0 7177 return false;
michael@0 7178 }
michael@0 7179
michael@0 7180 bool
michael@0 7181 CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
michael@0 7182 const nsCSSValue& aFirstLineNames)
michael@0 7183 {
michael@0 7184 nsCSSValueList* firstLineNamesItem = aValue.SetListValue();
michael@0 7185 firstLineNamesItem->mValue = aFirstLineNames;
michael@0 7186
michael@0 7187 // This function is trying to parse <track-list>, which is
michael@0 7188 // [ <line-names>? [ <track-size> | <repeat()> ] ]+ <line-names>?
michael@0 7189 // and we're already past the first "<line-names>?".
michael@0 7190 //
michael@0 7191 // Each iteration of the following loop attempts to parse either a
michael@0 7192 // repeat() or a <track-size> expression, and then an (optional)
michael@0 7193 // <line-names> expression.
michael@0 7194 //
michael@0 7195 // The only successful exit point from this loop is the ::NotFound
michael@0 7196 // case after ParseGridTrackSize(); i.e. we'll greedily parse
michael@0 7197 // repeat()/<track-size> until we can't find one.
michael@0 7198 nsCSSValueList* item = firstLineNamesItem;
michael@0 7199 for (;;) {
michael@0 7200 // First try to parse repeat()
michael@0 7201 if (!GetToken(true)) {
michael@0 7202 break;
michael@0 7203 }
michael@0 7204 if (mToken.mType == eCSSToken_Function &&
michael@0 7205 mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
michael@0 7206 if (!ParseGridTrackListRepeat(&item)) {
michael@0 7207 return false;
michael@0 7208 }
michael@0 7209 } else {
michael@0 7210 UngetToken();
michael@0 7211
michael@0 7212 // This was not a repeat() function. Try to parse <track-size>.
michael@0 7213 nsCSSValue trackSize;
michael@0 7214 CSSParseResult result = ParseGridTrackSize(trackSize);
michael@0 7215 if (result == CSSParseResult::Error) {
michael@0 7216 return false;
michael@0 7217 }
michael@0 7218 if (result == CSSParseResult::NotFound) {
michael@0 7219 // What we've parsed so far is a valid <track-list>
michael@0 7220 // (modulo the "at least one <track-size>" check below.)
michael@0 7221 // Stop here.
michael@0 7222 break;
michael@0 7223 }
michael@0 7224 item->mNext = new nsCSSValueList;
michael@0 7225 item = item->mNext;
michael@0 7226 item->mValue = trackSize;
michael@0 7227
michael@0 7228 item->mNext = new nsCSSValueList;
michael@0 7229 item = item->mNext;
michael@0 7230 }
michael@0 7231 if (ParseGridLineNames(item->mValue) == CSSParseResult::Error) {
michael@0 7232 return false;
michael@0 7233 }
michael@0 7234 }
michael@0 7235
michael@0 7236 // Require at least one <track-size>.
michael@0 7237 if (item == firstLineNamesItem) {
michael@0 7238 return false;
michael@0 7239 }
michael@0 7240
michael@0 7241 MOZ_ASSERT(aValue.GetListValue() &&
michael@0 7242 aValue.GetListValue()->mNext &&
michael@0 7243 aValue.GetListValue()->mNext->mNext,
michael@0 7244 "<track-list> should have a minimum length of 3");
michael@0 7245 return true;
michael@0 7246 }
michael@0 7247
michael@0 7248 // Takes ownership of |aSecond|
michael@0 7249 static void
michael@0 7250 ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond)
michael@0 7251 {
michael@0 7252 if (aSecond.GetUnit() == eCSSUnit_Null) {
michael@0 7253 // Nothing to do.
michael@0 7254 return;
michael@0 7255 }
michael@0 7256 if (aFirst.GetUnit() == eCSSUnit_Null) {
michael@0 7257 // Empty or omitted <line-names>. Replace it.
michael@0 7258 aFirst = aSecond;
michael@0 7259 return;
michael@0 7260 }
michael@0 7261
michael@0 7262 // Join the two <line-names> lists.
michael@0 7263 nsCSSValueList* source = aSecond.GetListValue();
michael@0 7264 nsCSSValueList* target = aFirst.GetListValue();
michael@0 7265 // Find the end:
michael@0 7266 while (target->mNext) {
michael@0 7267 target = target->mNext;
michael@0 7268 }
michael@0 7269 // Copy the first name. We can't take ownership of it
michael@0 7270 // as it'll be destroyed when |aSecond| goes out of scope.
michael@0 7271 target->mNext = new nsCSSValueList;
michael@0 7272 target = target->mNext;
michael@0 7273 target->mValue = source->mValue;
michael@0 7274 // Move the rest of the linked list.
michael@0 7275 target->mNext = source->mNext;
michael@0 7276 source->mNext = nullptr;
michael@0 7277 }
michael@0 7278
michael@0 7279 // Assuming the 'repeat(' function token has already been consumed,
michael@0 7280 // parse the rest of
michael@0 7281 // repeat( <positive-integer> ,
michael@0 7282 // [ <line-names>? <track-size> ]+ <line-names>? )
michael@0 7283 // Append to the linked list whose end is given by |aTailPtr|,
michael@0 7284 // and updated |aTailPtr| to point to the new end of the list.
michael@0 7285 bool
michael@0 7286 CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
michael@0 7287 {
michael@0 7288 if (!(GetToken(true) &&
michael@0 7289 mToken.mType == eCSSToken_Number &&
michael@0 7290 mToken.mIntegerValid &&
michael@0 7291 mToken.mInteger > 0)) {
michael@0 7292 SkipUntil(')');
michael@0 7293 return false;
michael@0 7294 }
michael@0 7295 int32_t repetitions = std::min(mToken.mInteger,
michael@0 7296 GRID_TEMPLATE_MAX_REPETITIONS);
michael@0 7297 if (!ExpectSymbol(',', true)) {
michael@0 7298 SkipUntil(')');
michael@0 7299 return false;
michael@0 7300 }
michael@0 7301
michael@0 7302 // Parse [ <line-names>? <track-size> ]+ <line-names>?
michael@0 7303 // but keep the first and last <line-names> separate
michael@0 7304 // because they'll need to be joined.
michael@0 7305 // http://dev.w3.org/csswg/css-grid/#repeat-notation
michael@0 7306 nsCSSValue firstLineNames;
michael@0 7307 nsCSSValue trackSize;
michael@0 7308 nsCSSValue lastLineNames;
michael@0 7309 // Optional
michael@0 7310 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) {
michael@0 7311 SkipUntil(')');
michael@0 7312 return false;
michael@0 7313 }
michael@0 7314 // Required
michael@0 7315 if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
michael@0 7316 SkipUntil(')');
michael@0 7317 return false;
michael@0 7318 }
michael@0 7319 // Use nsAutoPtr to free the list in case of early return.
michael@0 7320 nsAutoPtr<nsCSSValueList> firstTrackSizeItemAuto(new nsCSSValueList);
michael@0 7321 firstTrackSizeItemAuto->mValue = trackSize;
michael@0 7322
michael@0 7323 nsCSSValueList* item = firstTrackSizeItemAuto;
michael@0 7324 for (;;) {
michael@0 7325 // Optional
michael@0 7326 if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) {
michael@0 7327 SkipUntil(')');
michael@0 7328 return false;
michael@0 7329 }
michael@0 7330
michael@0 7331 if (ExpectSymbol(')', true)) {
michael@0 7332 break;
michael@0 7333 }
michael@0 7334
michael@0 7335 // Required
michael@0 7336 if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
michael@0 7337 SkipUntil(')');
michael@0 7338 return false;
michael@0 7339 }
michael@0 7340
michael@0 7341 item->mNext = new nsCSSValueList;
michael@0 7342 item = item->mNext;
michael@0 7343 item->mValue = lastLineNames;
michael@0 7344 // Do not append to this list at the next iteration.
michael@0 7345 lastLineNames.Reset();
michael@0 7346
michael@0 7347 item->mNext = new nsCSSValueList;
michael@0 7348 item = item->mNext;
michael@0 7349 item->mValue = trackSize;
michael@0 7350 }
michael@0 7351 nsCSSValueList* lastTrackSizeItem = item;
michael@0 7352
michael@0 7353 // [ <line-names>? <track-size> ]+ <line-names>? is now parsed into:
michael@0 7354 // * firstLineNames: the first <line-names>
michael@0 7355 // * a linked list of odd length >= 1, from firstTrackSizeItem
michael@0 7356 // (the first <track-size>) to lastTrackSizeItem (the last),
michael@0 7357 // with the <line-names> sublists in between
michael@0 7358 // * lastLineNames: the last <line-names>
michael@0 7359
michael@0 7360
michael@0 7361 // Join the last and first <line-names> (in that order.)
michael@0 7362 // For example, repeat(3, (a) 100px (b) 200px (c)) results in
michael@0 7363 // (a) 100px (b) 200px (c a) 100px (b) 200px (c a) 100px (b) 200px (c)
michael@0 7364 // This is (c a).
michael@0 7365 // Make deep copies: the originals will be moved.
michael@0 7366 nsCSSValue joinerLineNames;
michael@0 7367 {
michael@0 7368 nsCSSValueList* target = nullptr;
michael@0 7369 if (lastLineNames.GetUnit() != eCSSUnit_Null) {
michael@0 7370 target = joinerLineNames.SetListValue();
michael@0 7371 nsCSSValueList* source = lastLineNames.GetListValue();
michael@0 7372 for (;;) {
michael@0 7373 target->mValue = source->mValue;
michael@0 7374 source = source->mNext;
michael@0 7375 if (!source) {
michael@0 7376 break;
michael@0 7377 }
michael@0 7378 target->mNext = new nsCSSValueList;
michael@0 7379 target = target->mNext;
michael@0 7380 }
michael@0 7381 }
michael@0 7382
michael@0 7383 if (firstLineNames.GetUnit() != eCSSUnit_Null) {
michael@0 7384 if (target) {
michael@0 7385 target->mNext = new nsCSSValueList;
michael@0 7386 target = target->mNext;
michael@0 7387 } else {
michael@0 7388 target = joinerLineNames.SetListValue();
michael@0 7389 }
michael@0 7390 nsCSSValueList* source = firstLineNames.GetListValue();
michael@0 7391 for (;;) {
michael@0 7392 target->mValue = source->mValue;
michael@0 7393 source = source->mNext;
michael@0 7394 if (!source) {
michael@0 7395 break;
michael@0 7396 }
michael@0 7397 target->mNext = new nsCSSValueList;
michael@0 7398 target = target->mNext;
michael@0 7399 }
michael@0 7400 }
michael@0 7401 }
michael@0 7402
michael@0 7403 // Join our first <line-names> with the one before repeat().
michael@0 7404 // (a) repeat(1, (b) 20px) expands to (a b) 20px
michael@0 7405 nsCSSValueList* previousItemBeforeRepeat = *aTailPtr;
michael@0 7406 ConcatLineNames(previousItemBeforeRepeat->mValue, firstLineNames);
michael@0 7407
michael@0 7408 // Move our linked list
michael@0 7409 // (first to last <track-size>, with the <line-names> sublists in between).
michael@0 7410 // This is the first repetition.
michael@0 7411 NS_ASSERTION(previousItemBeforeRepeat->mNext == nullptr,
michael@0 7412 "Expected the end of a linked list");
michael@0 7413 previousItemBeforeRepeat->mNext = firstTrackSizeItemAuto.forget();
michael@0 7414 nsCSSValueList* firstTrackSizeItem = previousItemBeforeRepeat->mNext;
michael@0 7415 nsCSSValueList* tail = lastTrackSizeItem;
michael@0 7416
michael@0 7417 // Repeat |repetitions - 1| more times:
michael@0 7418 // * the joiner <line-names>
michael@0 7419 // * the linked list
michael@0 7420 // (first to last <track-size>, with the <line-names> sublists in between)
michael@0 7421 MOZ_ASSERT(repetitions > 0, "Expected positive repetitions");
michael@0 7422 while (--repetitions) {
michael@0 7423 tail->mNext = new nsCSSValueList;
michael@0 7424 tail = tail->mNext;
michael@0 7425 tail->mValue = joinerLineNames;
michael@0 7426
michael@0 7427 nsCSSValueList* repeatedItem = firstTrackSizeItem;
michael@0 7428 for (;;) {
michael@0 7429 tail->mNext = new nsCSSValueList;
michael@0 7430 tail = tail->mNext;
michael@0 7431 tail->mValue = repeatedItem->mValue;
michael@0 7432 if (repeatedItem == lastTrackSizeItem) {
michael@0 7433 break;
michael@0 7434 }
michael@0 7435 repeatedItem = repeatedItem->mNext;
michael@0 7436 }
michael@0 7437 }
michael@0 7438
michael@0 7439 // Finally, move our last <line-names>.
michael@0 7440 // Any <line-names> immediately after repeat() will append to it.
michael@0 7441 tail->mNext = new nsCSSValueList;
michael@0 7442 tail = tail->mNext;
michael@0 7443 tail->mValue = lastLineNames;
michael@0 7444
michael@0 7445 *aTailPtr = tail;
michael@0 7446 return true;
michael@0 7447 }
michael@0 7448
michael@0 7449 bool
michael@0 7450 CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSProperty aPropID)
michael@0 7451 {
michael@0 7452 nsCSSValue value;
michael@0 7453 if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
michael@0 7454 AppendValue(aPropID, value);
michael@0 7455 return true;
michael@0 7456 }
michael@0 7457
michael@0 7458 nsSubstring* ident = NextIdent();
michael@0 7459 if (ident) {
michael@0 7460 if (ident->LowerCaseEqualsLiteral("subgrid")) {
michael@0 7461 if (!ParseOptionalLineNameListAfterSubgrid(value)) {
michael@0 7462 return false;
michael@0 7463 }
michael@0 7464 AppendValue(aPropID, value);
michael@0 7465 return true;
michael@0 7466 }
michael@0 7467 UngetToken();
michael@0 7468 }
michael@0 7469
michael@0 7470 nsCSSValue firstLineNames;
michael@0 7471 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
michael@0 7472 !ParseGridTrackListWithFirstLineNames(value, firstLineNames)) {
michael@0 7473 return false;
michael@0 7474 }
michael@0 7475 AppendValue(aPropID, value);
michael@0 7476 return true;
michael@0 7477 }
michael@0 7478
michael@0 7479 bool
michael@0 7480 CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput,
michael@0 7481 css::GridTemplateAreasValue* aAreas,
michael@0 7482 nsDataHashtable<nsStringHashKey, uint32_t>& aAreaIndices)
michael@0 7483 {
michael@0 7484 aAreas->mTemplates.AppendElement(mToken.mIdent);
michael@0 7485
michael@0 7486 nsCSSGridTemplateAreaScanner scanner(aInput);
michael@0 7487 nsCSSGridTemplateAreaToken token;
michael@0 7488 css::GridNamedArea* currentArea = nullptr;
michael@0 7489 uint32_t row = aAreas->NRows();
michael@0 7490 uint32_t column;
michael@0 7491 for (column = 1; scanner.Next(token); column++) {
michael@0 7492 if (token.isTrash) {
michael@0 7493 return false;
michael@0 7494 }
michael@0 7495 if (currentArea) {
michael@0 7496 if (token.mName == currentArea->mName) {
michael@0 7497 if (currentArea->mRowStart == row) {
michael@0 7498 // Next column in the first row of this named area.
michael@0 7499 currentArea->mColumnEnd++;
michael@0 7500 }
michael@0 7501 continue;
michael@0 7502 }
michael@0 7503 // We're exiting |currentArea|, so currentArea is ending at |column|.
michael@0 7504 // Make sure that this is consistent with currentArea on previous rows:
michael@0 7505 if (currentArea->mColumnEnd != column) {
michael@0 7506 NS_ASSERTION(currentArea->mRowStart != row,
michael@0 7507 "Inconsistent column end for the first row of a named area.");
michael@0 7508 // Not a rectangle
michael@0 7509 return false;
michael@0 7510 }
michael@0 7511 currentArea = nullptr;
michael@0 7512 }
michael@0 7513 if (!token.mName.IsEmpty()) {
michael@0 7514 // Named cell that doesn't have a cell with the same name on its left.
michael@0 7515
michael@0 7516 // Check if this is the continuation of an existing named area:
michael@0 7517 uint32_t index;
michael@0 7518 if (aAreaIndices.Get(token.mName, &index)) {
michael@0 7519 MOZ_ASSERT(index < aAreas->mNamedAreas.Length(),
michael@0 7520 "Invalid aAreaIndices hash table");
michael@0 7521 currentArea = &aAreas->mNamedAreas[index];
michael@0 7522 if (currentArea->mColumnStart != column ||
michael@0 7523 currentArea->mRowEnd != row) {
michael@0 7524 // Existing named area, but not forming a rectangle
michael@0 7525 return false;
michael@0 7526 }
michael@0 7527 // Next row of an existing named area
michael@0 7528 currentArea->mRowEnd++;
michael@0 7529 } else {
michael@0 7530 // New named area
michael@0 7531 aAreaIndices.Put(token.mName, aAreas->mNamedAreas.Length());
michael@0 7532 currentArea = aAreas->mNamedAreas.AppendElement();
michael@0 7533 currentArea->mName = token.mName;
michael@0 7534 // For column or row N (starting at 1),
michael@0 7535 // the start line is N, the end line is N + 1
michael@0 7536 currentArea->mColumnStart = column;
michael@0 7537 currentArea->mColumnEnd = column + 1;
michael@0 7538 currentArea->mRowStart = row;
michael@0 7539 currentArea->mRowEnd = row + 1;
michael@0 7540 }
michael@0 7541 }
michael@0 7542 }
michael@0 7543 if (currentArea && currentArea->mColumnEnd != column) {
michael@0 7544 NS_ASSERTION(currentArea->mRowStart != row,
michael@0 7545 "Inconsistent column end for the first row of a named area.");
michael@0 7546 // Not a rectangle
michael@0 7547 return false;
michael@0 7548 }
michael@0 7549
michael@0 7550 // On the first row, set the number of columns
michael@0 7551 // that grid-template-areas contributes to the explicit grid.
michael@0 7552 // On other rows, check that the number of columns is consistent
michael@0 7553 // between rows.
michael@0 7554 if (row == 1) {
michael@0 7555 aAreas->mNColumns = column;
michael@0 7556 } else if (aAreas->mNColumns != column) {
michael@0 7557 return false;
michael@0 7558 }
michael@0 7559 return true;
michael@0 7560 }
michael@0 7561
michael@0 7562 bool
michael@0 7563 CSSParserImpl::ParseGridTemplateAreas()
michael@0 7564 {
michael@0 7565 nsCSSValue value;
michael@0 7566 if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
michael@0 7567 AppendValue(eCSSProperty_grid_template_areas, value);
michael@0 7568 return true;
michael@0 7569 }
michael@0 7570
michael@0 7571 nsRefPtr<css::GridTemplateAreasValue> areas =
michael@0 7572 new css::GridTemplateAreasValue();
michael@0 7573 nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
michael@0 7574 for (;;) {
michael@0 7575 if (!GetToken(true)) {
michael@0 7576 break;
michael@0 7577 }
michael@0 7578 if (eCSSToken_String != mToken.mType) {
michael@0 7579 UngetToken();
michael@0 7580 break;
michael@0 7581 }
michael@0 7582 if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
michael@0 7583 return false;
michael@0 7584 }
michael@0 7585 }
michael@0 7586
michael@0 7587 if (areas->NRows() == 0) {
michael@0 7588 return false;
michael@0 7589 }
michael@0 7590
michael@0 7591 AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
michael@0 7592 return true;
michael@0 7593 }
michael@0 7594
michael@0 7595 bool
michael@0 7596 CSSParserImpl::ParseGridTemplate()
michael@0 7597 {
michael@0 7598 // none |
michael@0 7599 // subgrid |
michael@0 7600 // <'grid-template-columns'> / <'grid-template-rows'> |
michael@0 7601 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
michael@0 7602 nsCSSValue value;
michael@0 7603 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 7604 AppendValue(eCSSProperty_grid_template_areas, value);
michael@0 7605 AppendValue(eCSSProperty_grid_template_columns, value);
michael@0 7606 AppendValue(eCSSProperty_grid_template_rows, value);
michael@0 7607 return true;
michael@0 7608 }
michael@0 7609
michael@0 7610 // TODO (bug 983175): add parsing for 'subgrid' by itself
michael@0 7611
michael@0 7612 // 'none' can appear either by itself,
michael@0 7613 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
michael@0 7614 if (ParseVariant(value, VARIANT_NONE, nullptr)) {
michael@0 7615 AppendValue(eCSSProperty_grid_template_columns, value);
michael@0 7616 if (ExpectSymbol('/', true)) {
michael@0 7617 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
michael@0 7618 }
michael@0 7619 AppendValue(eCSSProperty_grid_template_areas, value);
michael@0 7620 AppendValue(eCSSProperty_grid_template_rows, value);
michael@0 7621 return true;
michael@0 7622 }
michael@0 7623
michael@0 7624 // 'subgrid' can appear either by itself,
michael@0 7625 // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'>
michael@0 7626 nsSubstring* ident = NextIdent();
michael@0 7627 if (ident) {
michael@0 7628 if (ident->LowerCaseEqualsLiteral("subgrid")) {
michael@0 7629 if (!ParseOptionalLineNameListAfterSubgrid(value)) {
michael@0 7630 return false;
michael@0 7631 }
michael@0 7632 AppendValue(eCSSProperty_grid_template_columns, value);
michael@0 7633 if (ExpectSymbol('/', true)) {
michael@0 7634 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
michael@0 7635 }
michael@0 7636 if (value.GetListValue()->mNext) {
michael@0 7637 // Non-empty <line-name-list> after 'subgrid'.
michael@0 7638 // This is only valid as part of <'grid-template-columns'>,
michael@0 7639 // which must be followed by a slash.
michael@0 7640 return false;
michael@0 7641 }
michael@0 7642 // 'subgrid' by itself sets both grid-template-columns
michael@0 7643 // and grid-template-rows.
michael@0 7644 AppendValue(eCSSProperty_grid_template_rows, value);
michael@0 7645 value.SetNoneValue();
michael@0 7646 AppendValue(eCSSProperty_grid_template_areas, value);
michael@0 7647 return true;
michael@0 7648 }
michael@0 7649 UngetToken();
michael@0 7650 }
michael@0 7651
michael@0 7652 // [ <line-names>? ] here is ambiguous:
michael@0 7653 // it can be either the start of a <track-list>,
michael@0 7654 // or the start of [ <line-names>? <string> <track-size>? <line-names>? ]+
michael@0 7655 nsCSSValue firstLineNames;
michael@0 7656 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
michael@0 7657 !GetToken(true)) {
michael@0 7658 return false;
michael@0 7659 }
michael@0 7660 if (mToken.mType == eCSSToken_String) {
michael@0 7661 // [ <track-list> / ]? was omitted
michael@0 7662 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
michael@0 7663 value.SetNoneValue();
michael@0 7664 AppendValue(eCSSProperty_grid_template_columns, value);
michael@0 7665 return ParseGridTemplateAfterString(firstLineNames);
michael@0 7666 }
michael@0 7667 UngetToken();
michael@0 7668
michael@0 7669 if (!(ParseGridTrackListWithFirstLineNames(value, firstLineNames) &&
michael@0 7670 ExpectSymbol('/', true))) {
michael@0 7671 return false;
michael@0 7672 }
michael@0 7673 AppendValue(eCSSProperty_grid_template_columns, value);
michael@0 7674 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ true);
michael@0 7675 }
michael@0 7676
michael@0 7677 // Helper for parsing the 'grid-template' shorthand
michael@0 7678 //
michael@0 7679 // NOTE: This parses the portion after the slash, for *one* of the
michael@0 7680 // following types of expressions:
michael@0 7681 // - <'grid-template-columns'> / <'grid-template-rows'>
michael@0 7682 // - <track-list> / [ <line-names>? <string> <track-size>? <line-names>? ]+
michael@0 7683 //
michael@0 7684 // We don't know which type of expression we've got until we've parsed the
michael@0 7685 // second half, since the pre-slash part is ambiguous. The various return
michael@0 7686 // clauses below are labeled with the type of expression they're completing.
michael@0 7687 bool
michael@0 7688 CSSParserImpl::ParseGridTemplateAfterSlash(bool aColumnsIsTrackList)
michael@0 7689 {
michael@0 7690 nsCSSValue rowsValue;
michael@0 7691 if (ParseVariant(rowsValue, VARIANT_NONE, nullptr)) {
michael@0 7692 // <'grid-template-columns'> / <'grid-template-rows'>
michael@0 7693 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
michael@0 7694 nsCSSValue areasValue(eCSSUnit_None); // implied
michael@0 7695 AppendValue(eCSSProperty_grid_template_areas, areasValue);
michael@0 7696 return true;
michael@0 7697 }
michael@0 7698
michael@0 7699 nsSubstring* ident = NextIdent();
michael@0 7700 if (ident) {
michael@0 7701 if (ident->LowerCaseEqualsLiteral("subgrid")) {
michael@0 7702 if (!ParseOptionalLineNameListAfterSubgrid(rowsValue)) {
michael@0 7703 return false;
michael@0 7704 }
michael@0 7705 // <'grid-template-columns'> / <'grid-template-rows'>
michael@0 7706 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
michael@0 7707 nsCSSValue areasValue(eCSSUnit_None); // implied
michael@0 7708 AppendValue(eCSSProperty_grid_template_areas, areasValue);
michael@0 7709 return true;
michael@0 7710 }
michael@0 7711 UngetToken();
michael@0 7712 }
michael@0 7713
michael@0 7714 nsCSSValue firstLineNames;
michael@0 7715 if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error ||
michael@0 7716 !GetToken(true)) {
michael@0 7717 return false;
michael@0 7718 }
michael@0 7719 if (aColumnsIsTrackList && mToken.mType == eCSSToken_String) {
michael@0 7720 // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+
michael@0 7721 return ParseGridTemplateAfterString(firstLineNames);
michael@0 7722 }
michael@0 7723 UngetToken();
michael@0 7724
michael@0 7725 if (!ParseGridTrackListWithFirstLineNames(rowsValue, firstLineNames)) {
michael@0 7726 return false;
michael@0 7727 }
michael@0 7728
michael@0 7729 // <'grid-template-columns'> / <'grid-template-rows'>
michael@0 7730 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
michael@0 7731 nsCSSValue areasValue(eCSSUnit_None); // implied
michael@0 7732 AppendValue(eCSSProperty_grid_template_areas, areasValue);
michael@0 7733 return true;
michael@0 7734 }
michael@0 7735
michael@0 7736 // Helper for parsing the 'grid-template' shorthand:
michael@0 7737 // Parse [ <line-names>? <string> <track-size>? <line-names>? ]+
michael@0 7738 // with a <line-names>? already consumed, stored in |aFirstLineNames|,
michael@0 7739 // and the current token a <string>
michael@0 7740 bool
michael@0 7741 CSSParserImpl::ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames)
michael@0 7742 {
michael@0 7743 MOZ_ASSERT(mToken.mType == eCSSToken_String,
michael@0 7744 "ParseGridTemplateAfterString called with a non-string token");
michael@0 7745
michael@0 7746 nsCSSValue rowsValue;
michael@0 7747 nsRefPtr<css::GridTemplateAreasValue> areas =
michael@0 7748 new css::GridTemplateAreasValue();
michael@0 7749 nsDataHashtable<nsStringHashKey, uint32_t> areaIndices;
michael@0 7750 nsCSSValueList* rowsItem = rowsValue.SetListValue();
michael@0 7751 rowsItem->mValue = aFirstLineNames;
michael@0 7752
michael@0 7753 for (;;) {
michael@0 7754 if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) {
michael@0 7755 return false;
michael@0 7756 }
michael@0 7757
michael@0 7758 rowsItem->mNext = new nsCSSValueList;
michael@0 7759 rowsItem = rowsItem->mNext;
michael@0 7760 CSSParseResult result = ParseGridTrackSize(rowsItem->mValue);
michael@0 7761 if (result == CSSParseResult::Error) {
michael@0 7762 return false;
michael@0 7763 }
michael@0 7764 if (result == CSSParseResult::NotFound) {
michael@0 7765 rowsItem->mValue.SetAutoValue();
michael@0 7766 }
michael@0 7767
michael@0 7768 rowsItem->mNext = new nsCSSValueList;
michael@0 7769 rowsItem = rowsItem->mNext;
michael@0 7770 result = ParseGridLineNames(rowsItem->mValue);
michael@0 7771 if (result == CSSParseResult::Error) {
michael@0 7772 return false;
michael@0 7773 }
michael@0 7774 if (result == CSSParseResult::Ok) {
michael@0 7775 // Append to the same list as the previous call to ParseGridLineNames.
michael@0 7776 result = ParseGridLineNames(rowsItem->mValue);
michael@0 7777 if (result == CSSParseResult::Error) {
michael@0 7778 return false;
michael@0 7779 }
michael@0 7780 if (result == CSSParseResult::Ok) {
michael@0 7781 // Parsed <line-name> twice.
michael@0 7782 // The property value can not end here, we expect a string next.
michael@0 7783 if (!GetToken(true)) {
michael@0 7784 return false;
michael@0 7785 }
michael@0 7786 if (eCSSToken_String != mToken.mType) {
michael@0 7787 UngetToken();
michael@0 7788 return false;
michael@0 7789 }
michael@0 7790 continue;
michael@0 7791 }
michael@0 7792 }
michael@0 7793
michael@0 7794 // Did not find a <line-names>.
michael@0 7795 // Next, we expect either a string or the end of the property value.
michael@0 7796 if (!GetToken(true)) {
michael@0 7797 break;
michael@0 7798 }
michael@0 7799 if (eCSSToken_String != mToken.mType) {
michael@0 7800 UngetToken();
michael@0 7801 break;
michael@0 7802 }
michael@0 7803 }
michael@0 7804
michael@0 7805 AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas));
michael@0 7806 AppendValue(eCSSProperty_grid_template_rows, rowsValue);
michael@0 7807 return true;
michael@0 7808 }
michael@0 7809
michael@0 7810 // <'grid-template'> |
michael@0 7811 // [ <'grid-auto-flow'> [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]? ]
michael@0 7812 bool
michael@0 7813 CSSParserImpl::ParseGrid()
michael@0 7814 {
michael@0 7815 nsCSSValue value;
michael@0 7816 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 7817 for (const nsCSSProperty* subprops =
michael@0 7818 nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid);
michael@0 7819 *subprops != eCSSProperty_UNKNOWN; ++subprops) {
michael@0 7820 AppendValue(*subprops, value);
michael@0 7821 }
michael@0 7822 return true;
michael@0 7823 }
michael@0 7824
michael@0 7825 // 'none' at the beginning could be a <'grid-auto-flow'>
michael@0 7826 // (which also covers 'none' by itself)
michael@0 7827 // or a <'grid-template-columns'> (as part of <'grid-template'>)
michael@0 7828 if (ParseVariant(value, VARIANT_NONE, nullptr)) {
michael@0 7829 if (ExpectSymbol('/', true)) {
michael@0 7830 AppendValue(eCSSProperty_grid_template_columns, value);
michael@0 7831
michael@0 7832 // Set grid-auto-* subproperties to their initial values.
michael@0 7833 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated);
michael@0 7834 AppendValue(eCSSProperty_grid_auto_flow, value);
michael@0 7835 value.SetAutoValue();
michael@0 7836 AppendValue(eCSSProperty_grid_auto_columns, value);
michael@0 7837 AppendValue(eCSSProperty_grid_auto_rows, value);
michael@0 7838
michael@0 7839 return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false);
michael@0 7840 }
michael@0 7841 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated);
michael@0 7842 AppendValue(eCSSProperty_grid_auto_flow, value);
michael@0 7843 return ParseGridShorthandAutoProps();
michael@0 7844 }
michael@0 7845
michael@0 7846 // An empty value is always invalid.
michael@0 7847 if (!GetToken(true)) {
michael@0 7848 return false;
michael@0 7849 }
michael@0 7850
michael@0 7851 // If the value starts with a 'dense', 'column' or 'row' keyword,
michael@0 7852 // it can only start with a <'grid-auto-flow'>
michael@0 7853 if (mToken.mType == eCSSToken_Ident) {
michael@0 7854 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
michael@0 7855 if (keyword == eCSSKeyword_dense ||
michael@0 7856 keyword == eCSSKeyword_column ||
michael@0 7857 keyword == eCSSKeyword_row) {
michael@0 7858 UngetToken();
michael@0 7859 return ParseGridAutoFlow() && ParseGridShorthandAutoProps();
michael@0 7860 }
michael@0 7861 }
michael@0 7862 UngetToken();
michael@0 7863
michael@0 7864 // Set other subproperties to their initial values
michael@0 7865 // and parse <'grid-template'>.
michael@0 7866 value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_NONE, eCSSUnit_Enumerated);
michael@0 7867 AppendValue(eCSSProperty_grid_auto_flow, value);
michael@0 7868 value.SetAutoValue();
michael@0 7869 AppendValue(eCSSProperty_grid_auto_columns, value);
michael@0 7870 AppendValue(eCSSProperty_grid_auto_rows, value);
michael@0 7871 return ParseGridTemplate();
michael@0 7872 }
michael@0 7873
michael@0 7874 // Parse [ <'grid-auto-columns'> [ / <'grid-auto-rows'> ]? ]?
michael@0 7875 // for the 'grid' shorthand.
michael@0 7876 // Assumes that <'grid-auto-flow'> was already parsed by the caller.
michael@0 7877 bool
michael@0 7878 CSSParserImpl::ParseGridShorthandAutoProps()
michael@0 7879 {
michael@0 7880 nsCSSValue autoColumnsValue;
michael@0 7881 nsCSSValue autoRowsValue;
michael@0 7882 CSSParseResult result = ParseGridTrackSize(autoColumnsValue);
michael@0 7883 if (result == CSSParseResult::Error) {
michael@0 7884 return false;
michael@0 7885 }
michael@0 7886 if (result == CSSParseResult::NotFound) {
michael@0 7887 autoColumnsValue.SetAutoValue();
michael@0 7888 autoRowsValue.SetAutoValue();
michael@0 7889 } else {
michael@0 7890 if (!ExpectSymbol('/', true)) {
michael@0 7891 autoRowsValue.SetAutoValue();
michael@0 7892 } else if (ParseGridTrackSize(autoRowsValue) != CSSParseResult::Ok) {
michael@0 7893 return false;
michael@0 7894 }
michael@0 7895 }
michael@0 7896 AppendValue(eCSSProperty_grid_auto_columns, autoColumnsValue);
michael@0 7897 AppendValue(eCSSProperty_grid_auto_rows, autoRowsValue);
michael@0 7898 nsCSSValue templateValue(eCSSUnit_None); // Initial values
michael@0 7899 AppendValue(eCSSProperty_grid_template_areas, templateValue);
michael@0 7900 AppendValue(eCSSProperty_grid_template_columns, templateValue);
michael@0 7901 AppendValue(eCSSProperty_grid_template_rows, templateValue);
michael@0 7902 return true;
michael@0 7903 }
michael@0 7904
michael@0 7905 // Parse a <grid-line>.
michael@0 7906 // If successful, set aValue to eCSSUnit_Auto,
michael@0 7907 // or a eCSSUnit_List containing, in that order:
michael@0 7908 //
michael@0 7909 // * An optional eCSSUnit_Enumerated marking a "span" keyword.
michael@0 7910 // * An optional eCSSUnit_Integer
michael@0 7911 // * An optional eCSSUnit_Ident
michael@0 7912 //
michael@0 7913 // At least one of eCSSUnit_Integer or eCSSUnit_Ident is present.
michael@0 7914 bool
michael@0 7915 CSSParserImpl::ParseGridLine(nsCSSValue& aValue)
michael@0 7916 {
michael@0 7917 // <grid-line> =
michael@0 7918 // auto |
michael@0 7919 // <custom-ident> |
michael@0 7920 // [ <integer> && <custom-ident>? ] |
michael@0 7921 // [ span && [ <integer> || <custom-ident> ] ]
michael@0 7922 //
michael@0 7923 // Syntactically, this simplifies to:
michael@0 7924 //
michael@0 7925 // <grid-line> =
michael@0 7926 // auto |
michael@0 7927 // [ span? && [ <integer> || <custom-ident> ] ]
michael@0 7928
michael@0 7929 if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
michael@0 7930 return true;
michael@0 7931 }
michael@0 7932
michael@0 7933 static const nsCSSKeyword kGridLineKeywords[] = {
michael@0 7934 eCSSKeyword_span,
michael@0 7935 eCSSKeyword_UNKNOWN // End-of-array marker
michael@0 7936 };
michael@0 7937 bool hasSpan = false;
michael@0 7938 bool hasInteger = false;
michael@0 7939 bool hasIdent = false;
michael@0 7940 int32_t integer;
michael@0 7941 nsCSSValue ident;
michael@0 7942
michael@0 7943 if (!GetToken(true)) {
michael@0 7944 return false;
michael@0 7945 }
michael@0 7946 if (mToken.mType == eCSSToken_Ident &&
michael@0 7947 mToken.mIdent.LowerCaseEqualsLiteral("span")) {
michael@0 7948 hasSpan = true;
michael@0 7949 if (!GetToken(true)) {
michael@0 7950 return false;
michael@0 7951 }
michael@0 7952 }
michael@0 7953
michael@0 7954 do {
michael@0 7955 if (!hasIdent &&
michael@0 7956 mToken.mType == eCSSToken_Ident &&
michael@0 7957 ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) {
michael@0 7958 hasIdent = true;
michael@0 7959 } else if (!hasInteger &&
michael@0 7960 mToken.mType == eCSSToken_Number &&
michael@0 7961 mToken.mIntegerValid &&
michael@0 7962 mToken.mInteger != 0) {
michael@0 7963 hasInteger = true;
michael@0 7964 integer = mToken.mInteger;
michael@0 7965 } else {
michael@0 7966 UngetToken();
michael@0 7967 break;
michael@0 7968 }
michael@0 7969 } while (!(hasInteger && hasIdent) && GetToken(true));
michael@0 7970
michael@0 7971 // Require at least one of <integer> or <custom-ident>
michael@0 7972 if (!(hasInteger || hasIdent)) {
michael@0 7973 return false;
michael@0 7974 }
michael@0 7975
michael@0 7976 if (!hasSpan && GetToken(true)) {
michael@0 7977 if (mToken.mType == eCSSToken_Ident &&
michael@0 7978 mToken.mIdent.LowerCaseEqualsLiteral("span")) {
michael@0 7979 hasSpan = true;
michael@0 7980 } else {
michael@0 7981 UngetToken();
michael@0 7982 }
michael@0 7983 }
michael@0 7984
michael@0 7985 nsCSSValueList* item = aValue.SetListValue();
michael@0 7986 if (hasSpan) {
michael@0 7987 // Given "span", a negative <integer> is invalid.
michael@0 7988 if (hasInteger && integer < 0) {
michael@0 7989 return false;
michael@0 7990 }
michael@0 7991 // '1' here is a dummy value.
michael@0 7992 // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword.
michael@0 7993 item->mValue.SetIntValue(1, eCSSUnit_Enumerated);
michael@0 7994 item->mNext = new nsCSSValueList;
michael@0 7995 item = item->mNext;
michael@0 7996 }
michael@0 7997 if (hasInteger) {
michael@0 7998 item->mValue.SetIntValue(integer, eCSSUnit_Integer);
michael@0 7999 if (hasIdent) {
michael@0 8000 item->mNext = new nsCSSValueList;
michael@0 8001 item = item->mNext;
michael@0 8002 }
michael@0 8003 }
michael@0 8004 if (hasIdent) {
michael@0 8005 item->mValue = ident;
michael@0 8006 }
michael@0 8007 return true;
michael@0 8008 }
michael@0 8009
michael@0 8010 bool
michael@0 8011 CSSParserImpl::ParseGridAutoPosition()
michael@0 8012 {
michael@0 8013 nsCSSValue value;
michael@0 8014 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 8015 AppendValue(eCSSProperty_grid_auto_position, value);
michael@0 8016 return true;
michael@0 8017 }
michael@0 8018 nsCSSValue columnStartValue;
michael@0 8019 nsCSSValue rowStartValue;
michael@0 8020 if (ParseGridLine(columnStartValue) &&
michael@0 8021 ExpectSymbol('/', true) &&
michael@0 8022 ParseGridLine(rowStartValue)) {
michael@0 8023 value.SetPairValue(columnStartValue, rowStartValue);
michael@0 8024 AppendValue(eCSSProperty_grid_auto_position, value);
michael@0 8025 return true;
michael@0 8026 }
michael@0 8027 return false;
michael@0 8028 }
michael@0 8029
michael@0 8030 bool
michael@0 8031 CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSProperty aPropID)
michael@0 8032 {
michael@0 8033 nsCSSValue value;
michael@0 8034 if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
michael@0 8035 ParseGridLine(value)) {
michael@0 8036 AppendValue(aPropID, value);
michael@0 8037 return true;
michael@0 8038 }
michael@0 8039 return false;
michael@0 8040 }
michael@0 8041
michael@0 8042 // If |aFallback| is a List containing a single Ident, set |aValue| to that.
michael@0 8043 // Otherwise, set |aValue| to Auto.
michael@0 8044 // Used with |aFallback| from ParseGridLine()
michael@0 8045 static void
michael@0 8046 HandleGridLineFallback(const nsCSSValue& aFallback, nsCSSValue& aValue)
michael@0 8047 {
michael@0 8048 if (aFallback.GetUnit() == eCSSUnit_List &&
michael@0 8049 aFallback.GetListValue()->mValue.GetUnit() == eCSSUnit_Ident &&
michael@0 8050 !aFallback.GetListValue()->mNext) {
michael@0 8051 aValue = aFallback;
michael@0 8052 } else {
michael@0 8053 aValue.SetAutoValue();
michael@0 8054 }
michael@0 8055 }
michael@0 8056
michael@0 8057 bool
michael@0 8058 CSSParserImpl::ParseGridColumnRow(nsCSSProperty aStartPropID,
michael@0 8059 nsCSSProperty aEndPropID)
michael@0 8060 {
michael@0 8061 nsCSSValue value;
michael@0 8062 nsCSSValue secondValue;
michael@0 8063 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 8064 AppendValue(aStartPropID, value);
michael@0 8065 AppendValue(aEndPropID, value);
michael@0 8066 return true;
michael@0 8067 }
michael@0 8068
michael@0 8069 if (!ParseGridLine(value)) {
michael@0 8070 return false;
michael@0 8071 }
michael@0 8072 if (GetToken(true)) {
michael@0 8073 if (mToken.IsSymbol('/')) {
michael@0 8074 if (ParseGridLine(secondValue)) {
michael@0 8075 AppendValue(aStartPropID, value);
michael@0 8076 AppendValue(aEndPropID, secondValue);
michael@0 8077 return true;
michael@0 8078 } else {
michael@0 8079 return false;
michael@0 8080 }
michael@0 8081 }
michael@0 8082 UngetToken();
michael@0 8083 }
michael@0 8084
michael@0 8085 // A single <custom-ident> is repeated to both properties,
michael@0 8086 // anything else sets the grid-{column,row}-end property to 'auto'.
michael@0 8087 HandleGridLineFallback(value, secondValue);
michael@0 8088
michael@0 8089 AppendValue(aStartPropID, value);
michael@0 8090 AppendValue(aEndPropID, secondValue);
michael@0 8091 return true;
michael@0 8092 }
michael@0 8093
michael@0 8094 bool
michael@0 8095 CSSParserImpl::ParseGridArea()
michael@0 8096 {
michael@0 8097 nsCSSValue values[4];
michael@0 8098 if (ParseVariant(values[0], VARIANT_INHERIT, nullptr)) {
michael@0 8099 AppendValue(eCSSProperty_grid_row_start, values[0]);
michael@0 8100 AppendValue(eCSSProperty_grid_column_start, values[0]);
michael@0 8101 AppendValue(eCSSProperty_grid_row_end, values[0]);
michael@0 8102 AppendValue(eCSSProperty_grid_column_end, values[0]);
michael@0 8103 return true;
michael@0 8104 }
michael@0 8105
michael@0 8106 int32_t i = 0;
michael@0 8107 for (;;) {
michael@0 8108 if (!ParseGridLine(values[i])) {
michael@0 8109 return false;
michael@0 8110 }
michael@0 8111 if (++i == 4 || !GetToken(true)) {
michael@0 8112 break;
michael@0 8113 }
michael@0 8114 if (!mToken.IsSymbol('/')) {
michael@0 8115 UngetToken();
michael@0 8116 break;
michael@0 8117 }
michael@0 8118 }
michael@0 8119
michael@0 8120 MOZ_ASSERT(i >= 1, "should have parsed at least one grid-line (or returned)");
michael@0 8121 if (i < 2) {
michael@0 8122 HandleGridLineFallback(values[0], values[1]);
michael@0 8123 }
michael@0 8124 if (i < 3) {
michael@0 8125 HandleGridLineFallback(values[0], values[2]);
michael@0 8126 }
michael@0 8127 if (i < 4) {
michael@0 8128 HandleGridLineFallback(values[1], values[3]);
michael@0 8129 }
michael@0 8130
michael@0 8131 AppendValue(eCSSProperty_grid_row_start, values[0]);
michael@0 8132 AppendValue(eCSSProperty_grid_column_start, values[1]);
michael@0 8133 AppendValue(eCSSProperty_grid_row_end, values[2]);
michael@0 8134 AppendValue(eCSSProperty_grid_column_end, values[3]);
michael@0 8135 return true;
michael@0 8136 }
michael@0 8137
michael@0 8138 // <color-stop> : <color> [ <percentage> | <length> ]?
michael@0 8139 bool
michael@0 8140 CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
michael@0 8141 {
michael@0 8142 nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
michael@0 8143 if (!ParseVariant(stop->mColor, VARIANT_COLOR, nullptr)) {
michael@0 8144 return false;
michael@0 8145 }
michael@0 8146
michael@0 8147 // Stop positions do not have to fall between the starting-point and
michael@0 8148 // ending-point, so we don't use ParseNonNegativeVariant.
michael@0 8149 if (!ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr)) {
michael@0 8150 stop->mLocation.SetNoneValue();
michael@0 8151 }
michael@0 8152 return true;
michael@0 8153 }
michael@0 8154
michael@0 8155 // <gradient>
michael@0 8156 // : linear-gradient( <linear-gradient-line>? <color-stops> ')'
michael@0 8157 // | radial-gradient( <radial-gradient-line>? <color-stops> ')'
michael@0 8158 //
michael@0 8159 // <linear-gradient-line> : [ to [left | right] || [top | bottom] ] ,
michael@0 8160 // | <legacy-gradient-line>
michael@0 8161 // <radial-gradient-line> : [ <shape> || <size> ] [ at <position> ]? ,
michael@0 8162 // | [ at <position> ] ,
michael@0 8163 // | <legacy-gradient-line>? <legacy-shape-size>?
michael@0 8164 // <shape> : circle | ellipse
michael@0 8165 // <size> : closest-side | closest-corner | farthest-side | farthest-corner
michael@0 8166 // | <length> | [<length> | <percentage>]{2}
michael@0 8167 //
michael@0 8168 // <legacy-gradient-line> : [ <position> || <angle>] ,
michael@0 8169 //
michael@0 8170 // <legacy-shape-size> : [ <shape> || <legacy-size> ] ,
michael@0 8171 // <legacy-size> : closest-side | closest-corner | farthest-side
michael@0 8172 // | farthest-corner | contain | cover
michael@0 8173 //
michael@0 8174 // <color-stops> : <color-stop> , <color-stop> [, <color-stop>]*
michael@0 8175 bool
michael@0 8176 CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating,
michael@0 8177 bool aIsLegacy)
michael@0 8178 {
michael@0 8179 nsRefPtr<nsCSSValueGradient> cssGradient
michael@0 8180 = new nsCSSValueGradient(false, aIsRepeating);
michael@0 8181
michael@0 8182 if (!GetToken(true)) {
michael@0 8183 return false;
michael@0 8184 }
michael@0 8185
michael@0 8186 if (mToken.mType == eCSSToken_Ident &&
michael@0 8187 mToken.mIdent.LowerCaseEqualsLiteral("to")) {
michael@0 8188
michael@0 8189 // "to" syntax doesn't allow explicit "center"
michael@0 8190 if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) {
michael@0 8191 SkipUntil(')');
michael@0 8192 return false;
michael@0 8193 }
michael@0 8194
michael@0 8195 // [ to [left | right] || [top | bottom] ] ,
michael@0 8196 const nsCSSValue& xValue = cssGradient->mBgPos.mXValue;
michael@0 8197 const nsCSSValue& yValue = cssGradient->mBgPos.mYValue;
michael@0 8198 if (xValue.GetUnit() != eCSSUnit_Enumerated ||
michael@0 8199 !(xValue.GetIntValue() & (NS_STYLE_BG_POSITION_LEFT |
michael@0 8200 NS_STYLE_BG_POSITION_CENTER |
michael@0 8201 NS_STYLE_BG_POSITION_RIGHT)) ||
michael@0 8202 yValue.GetUnit() != eCSSUnit_Enumerated ||
michael@0 8203 !(yValue.GetIntValue() & (NS_STYLE_BG_POSITION_TOP |
michael@0 8204 NS_STYLE_BG_POSITION_CENTER |
michael@0 8205 NS_STYLE_BG_POSITION_BOTTOM))) {
michael@0 8206 SkipUntil(')');
michael@0 8207 return false;
michael@0 8208 }
michael@0 8209
michael@0 8210 if (!ExpectSymbol(',', true)) {
michael@0 8211 SkipUntil(')');
michael@0 8212 return false;
michael@0 8213 }
michael@0 8214
michael@0 8215 return ParseGradientColorStops(cssGradient, aValue);
michael@0 8216 }
michael@0 8217
michael@0 8218 if (!aIsLegacy) {
michael@0 8219 UngetToken();
michael@0 8220
michael@0 8221 // <angle> ,
michael@0 8222 if (ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) &&
michael@0 8223 !ExpectSymbol(',', true)) {
michael@0 8224 SkipUntil(')');
michael@0 8225 return false;
michael@0 8226 }
michael@0 8227
michael@0 8228 return ParseGradientColorStops(cssGradient, aValue);
michael@0 8229 }
michael@0 8230
michael@0 8231 nsCSSTokenType ty = mToken.mType;
michael@0 8232 nsString id = mToken.mIdent;
michael@0 8233 UngetToken();
michael@0 8234
michael@0 8235 // <legacy-gradient-line>
michael@0 8236 bool haveGradientLine = IsLegacyGradientLine(ty, id);
michael@0 8237 if (haveGradientLine) {
michael@0 8238 cssGradient->mIsLegacySyntax = true;
michael@0 8239 bool haveAngle =
michael@0 8240 ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
michael@0 8241
michael@0 8242 // if we got an angle, we might now have a comma, ending the gradient-line
michael@0 8243 if (!haveAngle || !ExpectSymbol(',', true)) {
michael@0 8244 if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
michael@0 8245 SkipUntil(')');
michael@0 8246 return false;
michael@0 8247 }
michael@0 8248
michael@0 8249 if (!ExpectSymbol(',', true) &&
michael@0 8250 // if we didn't already get an angle, we might have one now,
michael@0 8251 // otherwise it's an error
michael@0 8252 (haveAngle ||
michael@0 8253 !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) ||
michael@0 8254 // now we better have a comma
michael@0 8255 !ExpectSymbol(',', true))) {
michael@0 8256 SkipUntil(')');
michael@0 8257 return false;
michael@0 8258 }
michael@0 8259 }
michael@0 8260 }
michael@0 8261
michael@0 8262 return ParseGradientColorStops(cssGradient, aValue);
michael@0 8263 }
michael@0 8264
michael@0 8265 bool
michael@0 8266 CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating,
michael@0 8267 bool aIsLegacy)
michael@0 8268 {
michael@0 8269 nsRefPtr<nsCSSValueGradient> cssGradient
michael@0 8270 = new nsCSSValueGradient(true, aIsRepeating);
michael@0 8271
michael@0 8272 // [ <shape> || <size> ]
michael@0 8273 bool haveShape =
michael@0 8274 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
michael@0 8275 nsCSSProps::kRadialGradientShapeKTable);
michael@0 8276
michael@0 8277 bool haveSize = ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
michael@0 8278 aIsLegacy ?
michael@0 8279 nsCSSProps::kRadialGradientLegacySizeKTable :
michael@0 8280 nsCSSProps::kRadialGradientSizeKTable);
michael@0 8281 if (haveSize) {
michael@0 8282 if (!haveShape) {
michael@0 8283 // <size> <shape>
michael@0 8284 haveShape = ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
michael@0 8285 nsCSSProps::kRadialGradientShapeKTable);
michael@0 8286 }
michael@0 8287 } else if (!aIsLegacy) {
michael@0 8288 // Save RadialShape before parsing RadiusX because RadialShape and
michael@0 8289 // RadiusX share the storage.
michael@0 8290 int32_t shape =
michael@0 8291 cssGradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated ?
michael@0 8292 cssGradient->GetRadialShape().GetIntValue() : -1;
michael@0 8293 // <length> | [<length> | <percentage>]{2}
michael@0 8294 cssGradient->mIsExplicitSize = true;
michael@0 8295 haveSize =
michael@0 8296 ParseNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP, nullptr);
michael@0 8297 if (!haveSize) {
michael@0 8298 // It was not an explicit size after all.
michael@0 8299 // Note that ParseNonNegativeVariant may have put something
michael@0 8300 // invalid into our storage, but only in the case where it was
michael@0 8301 // rejected only for being negative. Since this means the token
michael@0 8302 // was a length or a percentage, we know it's not valid syntax
michael@0 8303 // (which must be a comma, the 'at' keyword, or a color), so we
michael@0 8304 // know this value will be dropped. This means it doesn't matter
michael@0 8305 // that we have something invalid in our storage.
michael@0 8306 cssGradient->mIsExplicitSize = false;
michael@0 8307 } else {
michael@0 8308 // vertical extent is optional
michael@0 8309 bool haveYSize =
michael@0 8310 ParseNonNegativeVariant(cssGradient->GetRadiusY(), VARIANT_LP, nullptr);
michael@0 8311 if (!haveShape) {
michael@0 8312 nsCSSValue shapeValue;
michael@0 8313 haveShape = ParseVariant(shapeValue, VARIANT_KEYWORD,
michael@0 8314 nsCSSProps::kRadialGradientShapeKTable);
michael@0 8315 if (haveShape) {
michael@0 8316 shape = shapeValue.GetIntValue();
michael@0 8317 }
michael@0 8318 }
michael@0 8319 if (haveYSize
michael@0 8320 ? shape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR
michael@0 8321 : cssGradient->GetRadiusX().GetUnit() == eCSSUnit_Percent ||
michael@0 8322 shape == NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL) {
michael@0 8323 SkipUntil(')');
michael@0 8324 return false;
michael@0 8325 }
michael@0 8326 }
michael@0 8327 }
michael@0 8328
michael@0 8329 if ((haveShape || haveSize) && ExpectSymbol(',', true)) {
michael@0 8330 // [ <shape> || <size> ] ,
michael@0 8331 return ParseGradientColorStops(cssGradient, aValue);
michael@0 8332 }
michael@0 8333
michael@0 8334 if (!GetToken(true)) {
michael@0 8335 return false;
michael@0 8336 }
michael@0 8337
michael@0 8338 if (!aIsLegacy) {
michael@0 8339 if (mToken.mType == eCSSToken_Ident &&
michael@0 8340 mToken.mIdent.LowerCaseEqualsLiteral("at")) {
michael@0 8341 // [ <shape> || <size> ]? at <position> ,
michael@0 8342 if (!ParseBoxPositionValues(cssGradient->mBgPos, false) ||
michael@0 8343 !ExpectSymbol(',', true)) {
michael@0 8344 SkipUntil(')');
michael@0 8345 return false;
michael@0 8346 }
michael@0 8347
michael@0 8348 return ParseGradientColorStops(cssGradient, aValue);
michael@0 8349 }
michael@0 8350
michael@0 8351 // <color-stops> only
michael@0 8352 UngetToken();
michael@0 8353 return ParseGradientColorStops(cssGradient, aValue);
michael@0 8354 }
michael@0 8355 MOZ_ASSERT(!cssGradient->mIsExplicitSize);
michael@0 8356
michael@0 8357 nsCSSTokenType ty = mToken.mType;
michael@0 8358 nsString id = mToken.mIdent;
michael@0 8359 UngetToken();
michael@0 8360
michael@0 8361 // <legacy-gradient-line>
michael@0 8362 bool haveGradientLine = false;
michael@0 8363 // if we already encountered a shape or size,
michael@0 8364 // we can not have a gradient-line in legacy syntax
michael@0 8365 if (!haveShape && !haveSize) {
michael@0 8366 haveGradientLine = IsLegacyGradientLine(ty, id);
michael@0 8367 }
michael@0 8368 if (haveGradientLine) {
michael@0 8369 bool haveAngle =
michael@0 8370 ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr);
michael@0 8371
michael@0 8372 // if we got an angle, we might now have a comma, ending the gradient-line
michael@0 8373 if (!haveAngle || !ExpectSymbol(',', true)) {
michael@0 8374 if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) {
michael@0 8375 SkipUntil(')');
michael@0 8376 return false;
michael@0 8377 }
michael@0 8378
michael@0 8379 if (!ExpectSymbol(',', true) &&
michael@0 8380 // if we didn't already get an angle, we might have one now,
michael@0 8381 // otherwise it's an error
michael@0 8382 (haveAngle ||
michael@0 8383 !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) ||
michael@0 8384 // now we better have a comma
michael@0 8385 !ExpectSymbol(',', true))) {
michael@0 8386 SkipUntil(')');
michael@0 8387 return false;
michael@0 8388 }
michael@0 8389 }
michael@0 8390
michael@0 8391 if (cssGradient->mAngle.GetUnit() != eCSSUnit_None) {
michael@0 8392 cssGradient->mIsLegacySyntax = true;
michael@0 8393 }
michael@0 8394 }
michael@0 8395
michael@0 8396 // radial gradients might have a shape and size here for legacy syntax
michael@0 8397 if (!haveShape && !haveSize) {
michael@0 8398 haveShape =
michael@0 8399 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
michael@0 8400 nsCSSProps::kRadialGradientShapeKTable);
michael@0 8401 haveSize =
michael@0 8402 ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD,
michael@0 8403 nsCSSProps::kRadialGradientLegacySizeKTable);
michael@0 8404
michael@0 8405 // could be in either order
michael@0 8406 if (!haveShape) {
michael@0 8407 haveShape =
michael@0 8408 ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD,
michael@0 8409 nsCSSProps::kRadialGradientShapeKTable);
michael@0 8410 }
michael@0 8411 }
michael@0 8412
michael@0 8413 if ((haveShape || haveSize) && !ExpectSymbol(',', true)) {
michael@0 8414 SkipUntil(')');
michael@0 8415 return false;
michael@0 8416 }
michael@0 8417
michael@0 8418 return ParseGradientColorStops(cssGradient, aValue);
michael@0 8419 }
michael@0 8420
michael@0 8421 bool
michael@0 8422 CSSParserImpl::IsLegacyGradientLine(const nsCSSTokenType& aType,
michael@0 8423 const nsString& aId)
michael@0 8424 {
michael@0 8425 // N.B. ParseBoxPositionValues is not guaranteed to put back
michael@0 8426 // everything it scanned if it fails, so we must only call it
michael@0 8427 // if there is no alternative to consuming a <box-position>.
michael@0 8428 // ParseVariant, as used here, will either succeed and consume
michael@0 8429 // a single token, or fail and consume none, so we can be more
michael@0 8430 // cavalier about calling it.
michael@0 8431
michael@0 8432 bool haveGradientLine = false;
michael@0 8433 switch (aType) {
michael@0 8434 case eCSSToken_Percentage:
michael@0 8435 case eCSSToken_Number:
michael@0 8436 case eCSSToken_Dimension:
michael@0 8437 haveGradientLine = true;
michael@0 8438 break;
michael@0 8439
michael@0 8440 case eCSSToken_Function:
michael@0 8441 if (aId.LowerCaseEqualsLiteral("calc") ||
michael@0 8442 aId.LowerCaseEqualsLiteral("-moz-calc")) {
michael@0 8443 haveGradientLine = true;
michael@0 8444 break;
michael@0 8445 }
michael@0 8446 // fall through
michael@0 8447 case eCSSToken_ID:
michael@0 8448 case eCSSToken_Hash:
michael@0 8449 // this is a color
michael@0 8450 break;
michael@0 8451
michael@0 8452 case eCSSToken_Ident: {
michael@0 8453 // This is only a gradient line if it's a box position keyword.
michael@0 8454 nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(aId);
michael@0 8455 int32_t junk;
michael@0 8456 if (kw != eCSSKeyword_UNKNOWN &&
michael@0 8457 nsCSSProps::FindKeyword(kw, nsCSSProps::kBackgroundPositionKTable,
michael@0 8458 junk)) {
michael@0 8459 haveGradientLine = true;
michael@0 8460 }
michael@0 8461 break;
michael@0 8462 }
michael@0 8463
michael@0 8464 default:
michael@0 8465 // error
michael@0 8466 break;
michael@0 8467 }
michael@0 8468
michael@0 8469 return haveGradientLine;
michael@0 8470 }
michael@0 8471
michael@0 8472 bool
michael@0 8473 CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient,
michael@0 8474 nsCSSValue& aValue)
michael@0 8475 {
michael@0 8476 // At least two color stops are required
michael@0 8477 if (!ParseColorStop(aGradient) ||
michael@0 8478 !ExpectSymbol(',', true) ||
michael@0 8479 !ParseColorStop(aGradient)) {
michael@0 8480 SkipUntil(')');
michael@0 8481 return false;
michael@0 8482 }
michael@0 8483
michael@0 8484 // Additional color stops
michael@0 8485 while (ExpectSymbol(',', true)) {
michael@0 8486 if (!ParseColorStop(aGradient)) {
michael@0 8487 SkipUntil(')');
michael@0 8488 return false;
michael@0 8489 }
michael@0 8490 }
michael@0 8491
michael@0 8492 if (!ExpectSymbol(')', true)) {
michael@0 8493 SkipUntil(')');
michael@0 8494 return false;
michael@0 8495 }
michael@0 8496
michael@0 8497 aValue.SetGradientValue(aGradient);
michael@0 8498 return true;
michael@0 8499 }
michael@0 8500
michael@0 8501 int32_t
michael@0 8502 CSSParserImpl::ParseChoice(nsCSSValue aValues[],
michael@0 8503 const nsCSSProperty aPropIDs[], int32_t aNumIDs)
michael@0 8504 {
michael@0 8505 int32_t found = 0;
michael@0 8506 nsAutoParseCompoundProperty compound(this);
michael@0 8507
michael@0 8508 int32_t loop;
michael@0 8509 for (loop = 0; loop < aNumIDs; loop++) {
michael@0 8510 // Try each property parser in order
michael@0 8511 int32_t hadFound = found;
michael@0 8512 int32_t index;
michael@0 8513 for (index = 0; index < aNumIDs; index++) {
michael@0 8514 int32_t bit = 1 << index;
michael@0 8515 if ((found & bit) == 0) {
michael@0 8516 if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) {
michael@0 8517 found |= bit;
michael@0 8518 // It's more efficient to break since it will reset |hadFound|
michael@0 8519 // to |found|. Furthermore, ParseListStyle depends on our going
michael@0 8520 // through the properties in order for each value..
michael@0 8521 break;
michael@0 8522 }
michael@0 8523 }
michael@0 8524 }
michael@0 8525 if (found == hadFound) { // found nothing new
michael@0 8526 break;
michael@0 8527 }
michael@0 8528 }
michael@0 8529 if (0 < found) {
michael@0 8530 if (1 == found) { // only first property
michael@0 8531 if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
michael@0 8532 for (loop = 1; loop < aNumIDs; loop++) {
michael@0 8533 aValues[loop].SetInheritValue();
michael@0 8534 }
michael@0 8535 found = ((1 << aNumIDs) - 1);
michael@0 8536 }
michael@0 8537 else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
michael@0 8538 for (loop = 1; loop < aNumIDs; loop++) {
michael@0 8539 aValues[loop].SetInitialValue();
michael@0 8540 }
michael@0 8541 found = ((1 << aNumIDs) - 1);
michael@0 8542 }
michael@0 8543 else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset
michael@0 8544 for (loop = 1; loop < aNumIDs; loop++) {
michael@0 8545 aValues[loop].SetUnsetValue();
michael@0 8546 }
michael@0 8547 found = ((1 << aNumIDs) - 1);
michael@0 8548 }
michael@0 8549 }
michael@0 8550 else { // more than one value, verify no inherits, initials or unsets
michael@0 8551 for (loop = 0; loop < aNumIDs; loop++) {
michael@0 8552 if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
michael@0 8553 found = -1;
michael@0 8554 break;
michael@0 8555 }
michael@0 8556 else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
michael@0 8557 found = -1;
michael@0 8558 break;
michael@0 8559 }
michael@0 8560 else if (eCSSUnit_Unset == aValues[loop].GetUnit()) {
michael@0 8561 found = -1;
michael@0 8562 break;
michael@0 8563 }
michael@0 8564 }
michael@0 8565 }
michael@0 8566 }
michael@0 8567 return found;
michael@0 8568 }
michael@0 8569
michael@0 8570 void
michael@0 8571 CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue)
michael@0 8572 {
michael@0 8573 mTempData.AddLonghandProperty(aPropID, aValue);
michael@0 8574 }
michael@0 8575
michael@0 8576 /**
michael@0 8577 * Parse a "box" property. Box properties have 1 to 4 values. When less
michael@0 8578 * than 4 values are provided a standard mapping is used to replicate
michael@0 8579 * existing values.
michael@0 8580 */
michael@0 8581 bool
michael@0 8582 CSSParserImpl::ParseBoxProperties(const nsCSSProperty aPropIDs[])
michael@0 8583 {
michael@0 8584 // Get up to four values for the property
michael@0 8585 int32_t count = 0;
michael@0 8586 nsCSSRect result;
michael@0 8587 NS_FOR_CSS_SIDES (index) {
michael@0 8588 if (! ParseSingleValueProperty(result.*(nsCSSRect::sides[index]),
michael@0 8589 aPropIDs[index])) {
michael@0 8590 break;
michael@0 8591 }
michael@0 8592 count++;
michael@0 8593 }
michael@0 8594 if (count == 0) {
michael@0 8595 return false;
michael@0 8596 }
michael@0 8597
michael@0 8598 if (1 < count) { // verify no more than single inherit, initial or unset
michael@0 8599 NS_FOR_CSS_SIDES (index) {
michael@0 8600 nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
michael@0 8601 if (eCSSUnit_Inherit == unit ||
michael@0 8602 eCSSUnit_Initial == unit ||
michael@0 8603 eCSSUnit_Unset == unit) {
michael@0 8604 return false;
michael@0 8605 }
michael@0 8606 }
michael@0 8607 }
michael@0 8608
michael@0 8609 // Provide missing values by replicating some of the values found
michael@0 8610 switch (count) {
michael@0 8611 case 1: // Make right == top
michael@0 8612 result.mRight = result.mTop;
michael@0 8613 case 2: // Make bottom == top
michael@0 8614 result.mBottom = result.mTop;
michael@0 8615 case 3: // Make left == right
michael@0 8616 result.mLeft = result.mRight;
michael@0 8617 }
michael@0 8618
michael@0 8619 NS_FOR_CSS_SIDES (index) {
michael@0 8620 AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index]));
michael@0 8621 }
michael@0 8622 return true;
michael@0 8623 }
michael@0 8624
michael@0 8625 // Similar to ParseBoxProperties, except there is only one property
michael@0 8626 // with the result as its value, not four. Requires values be nonnegative.
michael@0 8627 bool
michael@0 8628 CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask,
michael@0 8629 /** outparam */ nsCSSValue& aValue)
michael@0 8630 {
michael@0 8631 nsCSSRect& result = aValue.SetRectValue();
michael@0 8632
michael@0 8633 int32_t count = 0;
michael@0 8634 NS_FOR_CSS_SIDES (index) {
michael@0 8635 if (!ParseNonNegativeVariant(result.*(nsCSSRect::sides[index]),
michael@0 8636 aVariantMask, nullptr)) {
michael@0 8637 break;
michael@0 8638 }
michael@0 8639 count++;
michael@0 8640 }
michael@0 8641
michael@0 8642 if (count == 0) {
michael@0 8643 return false;
michael@0 8644 }
michael@0 8645
michael@0 8646 // Provide missing values by replicating some of the values found
michael@0 8647 switch (count) {
michael@0 8648 case 1: // Make right == top
michael@0 8649 result.mRight = result.mTop;
michael@0 8650 case 2: // Make bottom == top
michael@0 8651 result.mBottom = result.mTop;
michael@0 8652 case 3: // Make left == right
michael@0 8653 result.mLeft = result.mRight;
michael@0 8654 }
michael@0 8655
michael@0 8656 return true;
michael@0 8657 }
michael@0 8658
michael@0 8659 bool
michael@0 8660 CSSParserImpl::ParseDirectionalBoxProperty(nsCSSProperty aProperty,
michael@0 8661 int32_t aSourceType)
michael@0 8662 {
michael@0 8663 const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 8664 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
michael@0 8665 "not box property with physical vs. logical cascading");
michael@0 8666 nsCSSValue value;
michael@0 8667 if (!ParseSingleValueProperty(value, subprops[0])) {
michael@0 8668 return false;
michael@0 8669 }
michael@0 8670
michael@0 8671 AppendValue(subprops[0], value);
michael@0 8672 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
michael@0 8673 AppendValue(subprops[1], typeVal);
michael@0 8674 AppendValue(subprops[2], typeVal);
michael@0 8675 return true;
michael@0 8676 }
michael@0 8677
michael@0 8678 bool
michael@0 8679 CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID)
michael@0 8680 {
michael@0 8681 nsCSSValue dimenX, dimenY;
michael@0 8682 // required first value
michael@0 8683 if (! ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr))
michael@0 8684 return false;
michael@0 8685
michael@0 8686 // optional second value (forbidden if first value is inherit/initial/unset)
michael@0 8687 if (dimenX.GetUnit() != eCSSUnit_Inherit &&
michael@0 8688 dimenX.GetUnit() != eCSSUnit_Initial &&
michael@0 8689 dimenX.GetUnit() != eCSSUnit_Unset) {
michael@0 8690 ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr);
michael@0 8691 }
michael@0 8692
michael@0 8693 if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) {
michael@0 8694 AppendValue(aPropID, dimenX);
michael@0 8695 } else {
michael@0 8696 nsCSSValue value;
michael@0 8697 value.SetPairValue(dimenX, dimenY);
michael@0 8698 AppendValue(aPropID, value);
michael@0 8699 }
michael@0 8700 return true;
michael@0 8701 }
michael@0 8702
michael@0 8703 bool
michael@0 8704 CSSParserImpl::ParseBoxCornerRadii(const nsCSSProperty aPropIDs[])
michael@0 8705 {
michael@0 8706 // Rectangles are used as scratch storage.
michael@0 8707 // top => top-left, right => top-right,
michael@0 8708 // bottom => bottom-right, left => bottom-left.
michael@0 8709 nsCSSRect dimenX, dimenY;
michael@0 8710 int32_t countX = 0, countY = 0;
michael@0 8711
michael@0 8712 NS_FOR_CSS_SIDES (side) {
michael@0 8713 if (! ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side],
michael@0 8714 (side > 0 ? 0 : VARIANT_INHERIT) |
michael@0 8715 VARIANT_LP | VARIANT_CALC,
michael@0 8716 nullptr))
michael@0 8717 break;
michael@0 8718 countX++;
michael@0 8719 }
michael@0 8720 if (countX == 0)
michael@0 8721 return false;
michael@0 8722
michael@0 8723 if (ExpectSymbol('/', true)) {
michael@0 8724 NS_FOR_CSS_SIDES (side) {
michael@0 8725 if (! ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side],
michael@0 8726 VARIANT_LP | VARIANT_CALC, nullptr))
michael@0 8727 break;
michael@0 8728 countY++;
michael@0 8729 }
michael@0 8730 if (countY == 0)
michael@0 8731 return false;
michael@0 8732 }
michael@0 8733
michael@0 8734 // if 'initial', 'inherit' or 'unset' was used, it must be the only value
michael@0 8735 if (countX > 1 || countY > 0) {
michael@0 8736 nsCSSUnit unit = dimenX.mTop.GetUnit();
michael@0 8737 if (eCSSUnit_Inherit == unit ||
michael@0 8738 eCSSUnit_Initial == unit ||
michael@0 8739 eCSSUnit_Unset == unit)
michael@0 8740 return false;
michael@0 8741 }
michael@0 8742
michael@0 8743 // if we have no Y-values, use the X-values
michael@0 8744 if (countY == 0) {
michael@0 8745 dimenY = dimenX;
michael@0 8746 countY = countX;
michael@0 8747 }
michael@0 8748
michael@0 8749 // Provide missing values by replicating some of the values found
michael@0 8750 switch (countX) {
michael@0 8751 case 1: dimenX.mRight = dimenX.mTop; // top-right same as top-left, and
michael@0 8752 case 2: dimenX.mBottom = dimenX.mTop; // bottom-right same as top-left, and
michael@0 8753 case 3: dimenX.mLeft = dimenX.mRight; // bottom-left same as top-right
michael@0 8754 }
michael@0 8755
michael@0 8756 switch (countY) {
michael@0 8757 case 1: dimenY.mRight = dimenY.mTop; // top-right same as top-left, and
michael@0 8758 case 2: dimenY.mBottom = dimenY.mTop; // bottom-right same as top-left, and
michael@0 8759 case 3: dimenY.mLeft = dimenY.mRight; // bottom-left same as top-right
michael@0 8760 }
michael@0 8761
michael@0 8762 NS_FOR_CSS_SIDES(side) {
michael@0 8763 nsCSSValue& x = dimenX.*nsCSSRect::sides[side];
michael@0 8764 nsCSSValue& y = dimenY.*nsCSSRect::sides[side];
michael@0 8765
michael@0 8766 if (x == y) {
michael@0 8767 AppendValue(aPropIDs[side], x);
michael@0 8768 } else {
michael@0 8769 nsCSSValue pair;
michael@0 8770 pair.SetPairValue(x, y);
michael@0 8771 AppendValue(aPropIDs[side], pair);
michael@0 8772 }
michael@0 8773 }
michael@0 8774 return true;
michael@0 8775 }
michael@0 8776
michael@0 8777 // These must be in CSS order (top,right,bottom,left) for indexing to work
michael@0 8778 static const nsCSSProperty kBorderStyleIDs[] = {
michael@0 8779 eCSSProperty_border_top_style,
michael@0 8780 eCSSProperty_border_right_style_value,
michael@0 8781 eCSSProperty_border_bottom_style,
michael@0 8782 eCSSProperty_border_left_style_value
michael@0 8783 };
michael@0 8784 static const nsCSSProperty kBorderWidthIDs[] = {
michael@0 8785 eCSSProperty_border_top_width,
michael@0 8786 eCSSProperty_border_right_width_value,
michael@0 8787 eCSSProperty_border_bottom_width,
michael@0 8788 eCSSProperty_border_left_width_value
michael@0 8789 };
michael@0 8790 static const nsCSSProperty kBorderColorIDs[] = {
michael@0 8791 eCSSProperty_border_top_color,
michael@0 8792 eCSSProperty_border_right_color_value,
michael@0 8793 eCSSProperty_border_bottom_color,
michael@0 8794 eCSSProperty_border_left_color_value
michael@0 8795 };
michael@0 8796 static const nsCSSProperty kBorderRadiusIDs[] = {
michael@0 8797 eCSSProperty_border_top_left_radius,
michael@0 8798 eCSSProperty_border_top_right_radius,
michael@0 8799 eCSSProperty_border_bottom_right_radius,
michael@0 8800 eCSSProperty_border_bottom_left_radius
michael@0 8801 };
michael@0 8802 static const nsCSSProperty kOutlineRadiusIDs[] = {
michael@0 8803 eCSSProperty__moz_outline_radius_topLeft,
michael@0 8804 eCSSProperty__moz_outline_radius_topRight,
michael@0 8805 eCSSProperty__moz_outline_radius_bottomRight,
michael@0 8806 eCSSProperty__moz_outline_radius_bottomLeft
michael@0 8807 };
michael@0 8808
michael@0 8809 void
michael@0 8810 CSSParserImpl::SaveInputState(CSSParserInputState& aState)
michael@0 8811 {
michael@0 8812 aState.mToken = mToken;
michael@0 8813 aState.mHavePushBack = mHavePushBack;
michael@0 8814 mScanner->SavePosition(aState.mPosition);
michael@0 8815 }
michael@0 8816
michael@0 8817 void
michael@0 8818 CSSParserImpl::RestoreSavedInputState(const CSSParserInputState& aState)
michael@0 8819 {
michael@0 8820 mToken = aState.mToken;
michael@0 8821 mHavePushBack = aState.mHavePushBack;
michael@0 8822 mScanner->RestoreSavedPosition(aState.mPosition);
michael@0 8823 }
michael@0 8824
michael@0 8825 bool
michael@0 8826 CSSParserImpl::ParseProperty(nsCSSProperty aPropID)
michael@0 8827 {
michael@0 8828 // Can't use AutoRestore<bool> because it's a bitfield.
michael@0 8829 NS_ABORT_IF_FALSE(!mHashlessColorQuirk,
michael@0 8830 "hashless color quirk should not be set");
michael@0 8831 NS_ABORT_IF_FALSE(!mUnitlessLengthQuirk,
michael@0 8832 "unitless length quirk should not be set");
michael@0 8833
michael@0 8834 if (mNavQuirkMode) {
michael@0 8835 mHashlessColorQuirk =
michael@0 8836 nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_HASHLESS_COLOR_QUIRK);
michael@0 8837 mUnitlessLengthQuirk =
michael@0 8838 nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_UNITLESS_LENGTH_QUIRK);
michael@0 8839 }
michael@0 8840
michael@0 8841 // Save the current input state so that we can restore it later if we
michael@0 8842 // have to re-parse the property value as a variable-reference-containing
michael@0 8843 // token stream.
michael@0 8844 CSSParserInputState stateBeforeProperty;
michael@0 8845 SaveInputState(stateBeforeProperty);
michael@0 8846 mScanner->ClearSeenVariableReference();
michael@0 8847
michael@0 8848 NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
michael@0 8849 bool allowVariables = true;
michael@0 8850 bool result;
michael@0 8851 switch (nsCSSProps::PropertyParseType(aPropID)) {
michael@0 8852 case CSS_PROPERTY_PARSE_INACCESSIBLE: {
michael@0 8853 // The user can't use these
michael@0 8854 REPORT_UNEXPECTED(PEInaccessibleProperty2);
michael@0 8855 allowVariables = false;
michael@0 8856 result = false;
michael@0 8857 break;
michael@0 8858 }
michael@0 8859 case CSS_PROPERTY_PARSE_FUNCTION: {
michael@0 8860 result = ParsePropertyByFunction(aPropID);
michael@0 8861 break;
michael@0 8862 }
michael@0 8863 case CSS_PROPERTY_PARSE_VALUE: {
michael@0 8864 result = false;
michael@0 8865 nsCSSValue value;
michael@0 8866 if (ParseSingleValueProperty(value, aPropID)) {
michael@0 8867 AppendValue(aPropID, value);
michael@0 8868 result = true;
michael@0 8869 }
michael@0 8870 // XXX Report errors?
michael@0 8871 break;
michael@0 8872 }
michael@0 8873 case CSS_PROPERTY_PARSE_VALUE_LIST: {
michael@0 8874 result = ParseValueList(aPropID);
michael@0 8875 break;
michael@0 8876 }
michael@0 8877 default: {
michael@0 8878 result = false;
michael@0 8879 allowVariables = false;
michael@0 8880 NS_ABORT_IF_FALSE(false,
michael@0 8881 "Property's flags field in nsCSSPropList.h is missing "
michael@0 8882 "one of the CSS_PROPERTY_PARSE_* constants");
michael@0 8883 break;
michael@0 8884 }
michael@0 8885 }
michael@0 8886
michael@0 8887 if (result) {
michael@0 8888 // We need to call ExpectEndProperty() to decide whether to reparse
michael@0 8889 // with variables. This is needed because the property parsing may
michael@0 8890 // have stopped upon finding a variable (e.g., 'margin: 1px var(a)')
michael@0 8891 // in a way that future variable substitutions will be valid, or
michael@0 8892 // because it parsed everything that's possible but we still want to
michael@0 8893 // act as though the property contains variables even though we know
michael@0 8894 // the substitution will never work (e.g., for 'margin: 1px 2px 3px
michael@0 8895 // 4px 5px var(a)').
michael@0 8896 //
michael@0 8897 // It would be nice to find a better solution here
michael@0 8898 // (and for the SkipUntilOneOf below), though, that doesn't depend
michael@0 8899 // on using what we don't accept for doing parsing correctly.
michael@0 8900 if (!ExpectEndProperty()) {
michael@0 8901 result = false;
michael@0 8902 }
michael@0 8903 }
michael@0 8904
michael@0 8905 bool seenVariable = mScanner->SeenVariableReference() ||
michael@0 8906 (stateBeforeProperty.mHavePushBack &&
michael@0 8907 stateBeforeProperty.mToken.mType == eCSSToken_Function &&
michael@0 8908 stateBeforeProperty.mToken.mIdent.LowerCaseEqualsLiteral("var"));
michael@0 8909 bool parseAsTokenStream;
michael@0 8910
michael@0 8911 if (!result && allowVariables) {
michael@0 8912 parseAsTokenStream = true;
michael@0 8913 if (!seenVariable) {
michael@0 8914 // We might have stopped parsing the property before its end and before
michael@0 8915 // finding a variable reference. Keep checking until the end of the
michael@0 8916 // property.
michael@0 8917 CSSParserInputState stateAtError;
michael@0 8918 SaveInputState(stateAtError);
michael@0 8919
michael@0 8920 const char16_t stopChars[] = { ';', '!', '}', ')', 0 };
michael@0 8921 SkipUntilOneOf(stopChars);
michael@0 8922 UngetToken();
michael@0 8923 parseAsTokenStream = mScanner->SeenVariableReference();
michael@0 8924
michael@0 8925 if (!parseAsTokenStream) {
michael@0 8926 // If we parsed to the end of the propery and didn't find any variable
michael@0 8927 // references, then the real position we want to report the error at
michael@0 8928 // is |stateAtError|.
michael@0 8929 RestoreSavedInputState(stateAtError);
michael@0 8930 }
michael@0 8931 }
michael@0 8932 } else {
michael@0 8933 parseAsTokenStream = false;
michael@0 8934 }
michael@0 8935
michael@0 8936 if (parseAsTokenStream) {
michael@0 8937 // Go back to the start of the property value and parse it to make sure
michael@0 8938 // its variable references are syntactically valid and is otherwise
michael@0 8939 // balanced.
michael@0 8940 RestoreSavedInputState(stateBeforeProperty);
michael@0 8941
michael@0 8942 if (!mInSupportsCondition) {
michael@0 8943 mScanner->StartRecording();
michael@0 8944 }
michael@0 8945
michael@0 8946 CSSVariableDeclarations::Type type;
michael@0 8947 bool dropBackslash;
michael@0 8948 nsString impliedCharacters;
michael@0 8949 nsCSSValue value;
michael@0 8950 if (ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
michael@0 8951 nullptr, nullptr)) {
michael@0 8952 MOZ_ASSERT(type == CSSVariableDeclarations::eTokenStream,
michael@0 8953 "a non-custom property reparsed since it contained variable "
michael@0 8954 "references should not have been 'initial' or 'inherit'");
michael@0 8955
michael@0 8956 nsString propertyValue;
michael@0 8957
michael@0 8958 if (!mInSupportsCondition) {
michael@0 8959 // If we are in an @supports condition, we don't need to store the
michael@0 8960 // actual token stream on the nsCSSValue.
michael@0 8961 mScanner->StopRecording(propertyValue);
michael@0 8962 if (dropBackslash) {
michael@0 8963 MOZ_ASSERT(!propertyValue.IsEmpty() &&
michael@0 8964 propertyValue[propertyValue.Length() - 1] == '\\');
michael@0 8965 propertyValue.Truncate(propertyValue.Length() - 1);
michael@0 8966 }
michael@0 8967 propertyValue.Append(impliedCharacters);
michael@0 8968 }
michael@0 8969
michael@0 8970 if (mHavePushBack) {
michael@0 8971 // If we came to the end of a property value that had a variable
michael@0 8972 // reference and a token was pushed back, then it would have been
michael@0 8973 // ended by '!', ')', ';', ']' or '}'. We should remove it from the
michael@0 8974 // recorded property value.
michael@0 8975 MOZ_ASSERT(mToken.IsSymbol('!') ||
michael@0 8976 mToken.IsSymbol(')') ||
michael@0 8977 mToken.IsSymbol(';') ||
michael@0 8978 mToken.IsSymbol(']') ||
michael@0 8979 mToken.IsSymbol('}'));
michael@0 8980 if (!mInSupportsCondition) {
michael@0 8981 MOZ_ASSERT(!propertyValue.IsEmpty());
michael@0 8982 MOZ_ASSERT(propertyValue[propertyValue.Length() - 1] ==
michael@0 8983 mToken.mSymbol);
michael@0 8984 propertyValue.Truncate(propertyValue.Length() - 1);
michael@0 8985 }
michael@0 8986 }
michael@0 8987
michael@0 8988 if (!mInSupportsCondition) {
michael@0 8989 if (nsCSSProps::IsShorthand(aPropID)) {
michael@0 8990 // If this is a shorthand property, we store the token stream on each
michael@0 8991 // of its corresponding longhand properties.
michael@0 8992 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
michael@0 8993 nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
michael@0 8994 tokenStream->mPropertyID = *p;
michael@0 8995 tokenStream->mShorthandPropertyID = aPropID;
michael@0 8996 tokenStream->mTokenStream = propertyValue;
michael@0 8997 tokenStream->mBaseURI = mBaseURI;
michael@0 8998 tokenStream->mSheetURI = mSheetURI;
michael@0 8999 tokenStream->mSheetPrincipal = mSheetPrincipal;
michael@0 9000 tokenStream->mSheet = mSheet;
michael@0 9001 tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
michael@0 9002 tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
michael@0 9003 value.SetTokenStreamValue(tokenStream);
michael@0 9004 AppendValue(*p, value);
michael@0 9005 }
michael@0 9006 } else {
michael@0 9007 nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
michael@0 9008 tokenStream->mPropertyID = aPropID;
michael@0 9009 tokenStream->mTokenStream = propertyValue;
michael@0 9010 tokenStream->mBaseURI = mBaseURI;
michael@0 9011 tokenStream->mSheetURI = mSheetURI;
michael@0 9012 tokenStream->mSheetPrincipal = mSheetPrincipal;
michael@0 9013 tokenStream->mSheet = mSheet;
michael@0 9014 tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
michael@0 9015 tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
michael@0 9016 value.SetTokenStreamValue(tokenStream);
michael@0 9017 AppendValue(aPropID, value);
michael@0 9018 }
michael@0 9019 }
michael@0 9020 result = true;
michael@0 9021 } else {
michael@0 9022 if (!mInSupportsCondition) {
michael@0 9023 mScanner->StopRecording();
michael@0 9024 }
michael@0 9025 }
michael@0 9026 }
michael@0 9027
michael@0 9028 if (mNavQuirkMode) {
michael@0 9029 mHashlessColorQuirk = false;
michael@0 9030 mUnitlessLengthQuirk = false;
michael@0 9031 }
michael@0 9032
michael@0 9033 return result;
michael@0 9034 }
michael@0 9035
michael@0 9036 bool
michael@0 9037 CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID)
michael@0 9038 {
michael@0 9039 switch (aPropID) { // handle shorthand or multiple properties
michael@0 9040 case eCSSProperty_background:
michael@0 9041 return ParseBackground();
michael@0 9042 case eCSSProperty_background_repeat:
michael@0 9043 return ParseBackgroundRepeat();
michael@0 9044 case eCSSProperty_background_position:
michael@0 9045 return ParseBackgroundPosition();
michael@0 9046 case eCSSProperty_background_size:
michael@0 9047 return ParseBackgroundSize();
michael@0 9048 case eCSSProperty_border:
michael@0 9049 return ParseBorderSide(kBorderTopIDs, true);
michael@0 9050 case eCSSProperty_border_color:
michael@0 9051 return ParseBorderColor();
michael@0 9052 case eCSSProperty_border_spacing:
michael@0 9053 return ParseBorderSpacing();
michael@0 9054 case eCSSProperty_border_style:
michael@0 9055 return ParseBorderStyle();
michael@0 9056 case eCSSProperty_border_bottom:
michael@0 9057 return ParseBorderSide(kBorderBottomIDs, false);
michael@0 9058 case eCSSProperty_border_end:
michael@0 9059 return ParseDirectionalBorderSide(kBorderEndIDs,
michael@0 9060 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9061 case eCSSProperty_border_left:
michael@0 9062 return ParseDirectionalBorderSide(kBorderLeftIDs,
michael@0 9063 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9064 case eCSSProperty_border_right:
michael@0 9065 return ParseDirectionalBorderSide(kBorderRightIDs,
michael@0 9066 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9067 case eCSSProperty_border_start:
michael@0 9068 return ParseDirectionalBorderSide(kBorderStartIDs,
michael@0 9069 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9070 case eCSSProperty_border_top:
michael@0 9071 return ParseBorderSide(kBorderTopIDs, false);
michael@0 9072 case eCSSProperty_border_bottom_colors:
michael@0 9073 case eCSSProperty_border_left_colors:
michael@0 9074 case eCSSProperty_border_right_colors:
michael@0 9075 case eCSSProperty_border_top_colors:
michael@0 9076 return ParseBorderColors(aPropID);
michael@0 9077 case eCSSProperty_border_image_slice:
michael@0 9078 return ParseBorderImageSlice(true, nullptr);
michael@0 9079 case eCSSProperty_border_image_width:
michael@0 9080 return ParseBorderImageWidth(true);
michael@0 9081 case eCSSProperty_border_image_outset:
michael@0 9082 return ParseBorderImageOutset(true);
michael@0 9083 case eCSSProperty_border_image_repeat:
michael@0 9084 return ParseBorderImageRepeat(true);
michael@0 9085 case eCSSProperty_border_image:
michael@0 9086 return ParseBorderImage();
michael@0 9087 case eCSSProperty_border_width:
michael@0 9088 return ParseBorderWidth();
michael@0 9089 case eCSSProperty_border_end_color:
michael@0 9090 return ParseDirectionalBoxProperty(eCSSProperty_border_end_color,
michael@0 9091 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9092 case eCSSProperty_border_left_color:
michael@0 9093 return ParseDirectionalBoxProperty(eCSSProperty_border_left_color,
michael@0 9094 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9095 case eCSSProperty_border_right_color:
michael@0 9096 return ParseDirectionalBoxProperty(eCSSProperty_border_right_color,
michael@0 9097 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9098 case eCSSProperty_border_start_color:
michael@0 9099 return ParseDirectionalBoxProperty(eCSSProperty_border_start_color,
michael@0 9100 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9101 case eCSSProperty_border_end_width:
michael@0 9102 return ParseDirectionalBoxProperty(eCSSProperty_border_end_width,
michael@0 9103 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9104 case eCSSProperty_border_left_width:
michael@0 9105 return ParseDirectionalBoxProperty(eCSSProperty_border_left_width,
michael@0 9106 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9107 case eCSSProperty_border_right_width:
michael@0 9108 return ParseDirectionalBoxProperty(eCSSProperty_border_right_width,
michael@0 9109 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9110 case eCSSProperty_border_start_width:
michael@0 9111 return ParseDirectionalBoxProperty(eCSSProperty_border_start_width,
michael@0 9112 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9113 case eCSSProperty_border_end_style:
michael@0 9114 return ParseDirectionalBoxProperty(eCSSProperty_border_end_style,
michael@0 9115 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9116 case eCSSProperty_border_left_style:
michael@0 9117 return ParseDirectionalBoxProperty(eCSSProperty_border_left_style,
michael@0 9118 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9119 case eCSSProperty_border_right_style:
michael@0 9120 return ParseDirectionalBoxProperty(eCSSProperty_border_right_style,
michael@0 9121 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9122 case eCSSProperty_border_start_style:
michael@0 9123 return ParseDirectionalBoxProperty(eCSSProperty_border_start_style,
michael@0 9124 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9125 case eCSSProperty_border_radius:
michael@0 9126 return ParseBoxCornerRadii(kBorderRadiusIDs);
michael@0 9127 case eCSSProperty__moz_outline_radius:
michael@0 9128 return ParseBoxCornerRadii(kOutlineRadiusIDs);
michael@0 9129
michael@0 9130 case eCSSProperty_border_top_left_radius:
michael@0 9131 case eCSSProperty_border_top_right_radius:
michael@0 9132 case eCSSProperty_border_bottom_right_radius:
michael@0 9133 case eCSSProperty_border_bottom_left_radius:
michael@0 9134 case eCSSProperty__moz_outline_radius_topLeft:
michael@0 9135 case eCSSProperty__moz_outline_radius_topRight:
michael@0 9136 case eCSSProperty__moz_outline_radius_bottomRight:
michael@0 9137 case eCSSProperty__moz_outline_radius_bottomLeft:
michael@0 9138 return ParseBoxCornerRadius(aPropID);
michael@0 9139
michael@0 9140 case eCSSProperty_box_shadow:
michael@0 9141 case eCSSProperty_text_shadow:
michael@0 9142 return ParseShadowList(aPropID);
michael@0 9143
michael@0 9144 case eCSSProperty_clip:
michael@0 9145 return ParseRect(eCSSProperty_clip);
michael@0 9146 case eCSSProperty__moz_columns:
michael@0 9147 return ParseColumns();
michael@0 9148 case eCSSProperty__moz_column_rule:
michael@0 9149 return ParseBorderSide(kColumnRuleIDs, false);
michael@0 9150 case eCSSProperty_content:
michael@0 9151 return ParseContent();
michael@0 9152 case eCSSProperty_counter_increment:
michael@0 9153 case eCSSProperty_counter_reset:
michael@0 9154 return ParseCounterData(aPropID);
michael@0 9155 case eCSSProperty_cursor:
michael@0 9156 return ParseCursor();
michael@0 9157 case eCSSProperty_filter:
michael@0 9158 return ParseFilter();
michael@0 9159 case eCSSProperty_flex:
michael@0 9160 return ParseFlex();
michael@0 9161 case eCSSProperty_flex_flow:
michael@0 9162 return ParseFlexFlow();
michael@0 9163 case eCSSProperty_font:
michael@0 9164 return ParseFont();
michael@0 9165 case eCSSProperty_grid_auto_flow:
michael@0 9166 return ParseGridAutoFlow();
michael@0 9167 case eCSSProperty_grid_auto_columns:
michael@0 9168 case eCSSProperty_grid_auto_rows:
michael@0 9169 return ParseGridAutoColumnsRows(aPropID);
michael@0 9170 case eCSSProperty_grid_template_areas:
michael@0 9171 return ParseGridTemplateAreas();
michael@0 9172 case eCSSProperty_grid_template_columns:
michael@0 9173 case eCSSProperty_grid_template_rows:
michael@0 9174 return ParseGridTemplateColumnsRows(aPropID);
michael@0 9175 case eCSSProperty_grid_template:
michael@0 9176 return ParseGridTemplate();
michael@0 9177 case eCSSProperty_grid:
michael@0 9178 return ParseGrid();
michael@0 9179 case eCSSProperty_grid_auto_position:
michael@0 9180 return ParseGridAutoPosition();
michael@0 9181 case eCSSProperty_grid_column_start:
michael@0 9182 case eCSSProperty_grid_column_end:
michael@0 9183 case eCSSProperty_grid_row_start:
michael@0 9184 case eCSSProperty_grid_row_end:
michael@0 9185 return ParseGridColumnRowStartEnd(aPropID);
michael@0 9186 case eCSSProperty_grid_column:
michael@0 9187 return ParseGridColumnRow(eCSSProperty_grid_column_start,
michael@0 9188 eCSSProperty_grid_column_end);
michael@0 9189 case eCSSProperty_grid_row:
michael@0 9190 return ParseGridColumnRow(eCSSProperty_grid_row_start,
michael@0 9191 eCSSProperty_grid_row_end);
michael@0 9192 case eCSSProperty_grid_area:
michael@0 9193 return ParseGridArea();
michael@0 9194 case eCSSProperty_image_region:
michael@0 9195 return ParseRect(eCSSProperty_image_region);
michael@0 9196 case eCSSProperty_list_style:
michael@0 9197 return ParseListStyle();
michael@0 9198 case eCSSProperty_margin:
michael@0 9199 return ParseMargin();
michael@0 9200 case eCSSProperty_margin_end:
michael@0 9201 return ParseDirectionalBoxProperty(eCSSProperty_margin_end,
michael@0 9202 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9203 case eCSSProperty_margin_left:
michael@0 9204 return ParseDirectionalBoxProperty(eCSSProperty_margin_left,
michael@0 9205 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9206 case eCSSProperty_margin_right:
michael@0 9207 return ParseDirectionalBoxProperty(eCSSProperty_margin_right,
michael@0 9208 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9209 case eCSSProperty_margin_start:
michael@0 9210 return ParseDirectionalBoxProperty(eCSSProperty_margin_start,
michael@0 9211 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9212 case eCSSProperty_outline:
michael@0 9213 return ParseOutline();
michael@0 9214 case eCSSProperty_overflow:
michael@0 9215 return ParseOverflow();
michael@0 9216 case eCSSProperty_padding:
michael@0 9217 return ParsePadding();
michael@0 9218 case eCSSProperty_padding_end:
michael@0 9219 return ParseDirectionalBoxProperty(eCSSProperty_padding_end,
michael@0 9220 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9221 case eCSSProperty_padding_left:
michael@0 9222 return ParseDirectionalBoxProperty(eCSSProperty_padding_left,
michael@0 9223 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9224 case eCSSProperty_padding_right:
michael@0 9225 return ParseDirectionalBoxProperty(eCSSProperty_padding_right,
michael@0 9226 NS_BOXPROP_SOURCE_PHYSICAL);
michael@0 9227 case eCSSProperty_padding_start:
michael@0 9228 return ParseDirectionalBoxProperty(eCSSProperty_padding_start,
michael@0 9229 NS_BOXPROP_SOURCE_LOGICAL);
michael@0 9230 case eCSSProperty_quotes:
michael@0 9231 return ParseQuotes();
michael@0 9232 case eCSSProperty_size:
michael@0 9233 return ParseSize();
michael@0 9234 case eCSSProperty_text_decoration:
michael@0 9235 return ParseTextDecoration();
michael@0 9236 case eCSSProperty_will_change:
michael@0 9237 return ParseWillChange();
michael@0 9238 case eCSSProperty_transform:
michael@0 9239 return ParseTransform(false);
michael@0 9240 case eCSSProperty__moz_transform:
michael@0 9241 return ParseTransform(true);
michael@0 9242 case eCSSProperty_transform_origin:
michael@0 9243 return ParseTransformOrigin(false);
michael@0 9244 case eCSSProperty_perspective_origin:
michael@0 9245 return ParseTransformOrigin(true);
michael@0 9246 case eCSSProperty_transition:
michael@0 9247 return ParseTransition();
michael@0 9248 case eCSSProperty_animation:
michael@0 9249 return ParseAnimation();
michael@0 9250 case eCSSProperty_transition_property:
michael@0 9251 return ParseTransitionProperty();
michael@0 9252 case eCSSProperty_fill:
michael@0 9253 case eCSSProperty_stroke:
michael@0 9254 return ParsePaint(aPropID);
michael@0 9255 case eCSSProperty_stroke_dasharray:
michael@0 9256 return ParseDasharray();
michael@0 9257 case eCSSProperty_marker:
michael@0 9258 return ParseMarker();
michael@0 9259 case eCSSProperty_paint_order:
michael@0 9260 return ParsePaintOrder();
michael@0 9261 case eCSSProperty_all:
michael@0 9262 return ParseAll();
michael@0 9263 default:
michael@0 9264 NS_ABORT_IF_FALSE(false, "should not be called");
michael@0 9265 return false;
michael@0 9266 }
michael@0 9267 }
michael@0 9268
michael@0 9269 // Bits used in determining which background position info we have
michael@0 9270 #define BG_CENTER NS_STYLE_BG_POSITION_CENTER
michael@0 9271 #define BG_TOP NS_STYLE_BG_POSITION_TOP
michael@0 9272 #define BG_BOTTOM NS_STYLE_BG_POSITION_BOTTOM
michael@0 9273 #define BG_LEFT NS_STYLE_BG_POSITION_LEFT
michael@0 9274 #define BG_RIGHT NS_STYLE_BG_POSITION_RIGHT
michael@0 9275 #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM)
michael@0 9276 #define BG_TB (BG_TOP | BG_BOTTOM)
michael@0 9277 #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT)
michael@0 9278 #define BG_LR (BG_LEFT | BG_RIGHT)
michael@0 9279
michael@0 9280 bool
michael@0 9281 CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
michael@0 9282 nsCSSProperty aPropID)
michael@0 9283 {
michael@0 9284 if (aPropID == eCSSPropertyExtra_x_none_value) {
michael@0 9285 return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nullptr);
michael@0 9286 }
michael@0 9287
michael@0 9288 if (aPropID == eCSSPropertyExtra_x_auto_value) {
michael@0 9289 return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nullptr);
michael@0 9290 }
michael@0 9291
michael@0 9292 if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) {
michael@0 9293 NS_ABORT_IF_FALSE(false, "not a single value property");
michael@0 9294 return false;
michael@0 9295 }
michael@0 9296
michael@0 9297 if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) {
michael@0 9298 switch (aPropID) {
michael@0 9299 case eCSSProperty_font_family:
michael@0 9300 return ParseFamily(aValue);
michael@0 9301 case eCSSProperty_font_synthesis:
michael@0 9302 return ParseFontSynthesis(aValue);
michael@0 9303 case eCSSProperty_font_variant_alternates:
michael@0 9304 return ParseFontVariantAlternates(aValue);
michael@0 9305 case eCSSProperty_font_variant_east_asian:
michael@0 9306 return ParseFontVariantEastAsian(aValue);
michael@0 9307 case eCSSProperty_font_variant_ligatures:
michael@0 9308 return ParseFontVariantLigatures(aValue);
michael@0 9309 case eCSSProperty_font_variant_numeric:
michael@0 9310 return ParseFontVariantNumeric(aValue);
michael@0 9311 case eCSSProperty_font_feature_settings:
michael@0 9312 return ParseFontFeatureSettings(aValue);
michael@0 9313 case eCSSProperty_font_weight:
michael@0 9314 return ParseFontWeight(aValue);
michael@0 9315 case eCSSProperty_image_orientation:
michael@0 9316 return ParseImageOrientation(aValue);
michael@0 9317 case eCSSProperty_marks:
michael@0 9318 return ParseMarks(aValue);
michael@0 9319 case eCSSProperty_text_align:
michael@0 9320 return ParseTextAlign(aValue);
michael@0 9321 case eCSSProperty_text_align_last:
michael@0 9322 return ParseTextAlignLast(aValue);
michael@0 9323 case eCSSProperty_text_decoration_line:
michael@0 9324 return ParseTextDecorationLine(aValue);
michael@0 9325 case eCSSProperty_text_combine_upright:
michael@0 9326 return ParseTextCombineUpright(aValue);
michael@0 9327 case eCSSProperty_text_overflow:
michael@0 9328 return ParseTextOverflow(aValue);
michael@0 9329 case eCSSProperty_touch_action:
michael@0 9330 return ParseTouchAction(aValue);
michael@0 9331 default:
michael@0 9332 NS_ABORT_IF_FALSE(false, "should not reach here");
michael@0 9333 return false;
michael@0 9334 }
michael@0 9335 }
michael@0 9336
michael@0 9337 uint32_t variant = nsCSSProps::ParserVariant(aPropID);
michael@0 9338 if (variant == 0) {
michael@0 9339 NS_ABORT_IF_FALSE(false, "not a single value property");
michael@0 9340 return false;
michael@0 9341 }
michael@0 9342
michael@0 9343 // We only allow 'script-level' when unsafe rules are enabled, because
michael@0 9344 // otherwise it could interfere with rulenode optimizations if used in
michael@0 9345 // a non-MathML-enabled document. We also only allow math-display when
michael@0 9346 // unsafe rules are enabled.
michael@0 9347 if (!mUnsafeRulesEnabled &&
michael@0 9348 (aPropID == eCSSProperty_script_level ||
michael@0 9349 aPropID == eCSSProperty_math_display))
michael@0 9350 return false;
michael@0 9351
michael@0 9352 const KTableValue *kwtable = nsCSSProps::kKeywordTableTable[aPropID];
michael@0 9353 switch (nsCSSProps::ValueRestrictions(aPropID)) {
michael@0 9354 default:
michael@0 9355 NS_ABORT_IF_FALSE(false, "should not be reached");
michael@0 9356 case 0:
michael@0 9357 return ParseVariant(aValue, variant, kwtable);
michael@0 9358 case CSS_PROPERTY_VALUE_NONNEGATIVE:
michael@0 9359 return ParseNonNegativeVariant(aValue, variant, kwtable);
michael@0 9360 case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
michael@0 9361 return ParseOneOrLargerVariant(aValue, variant, kwtable);
michael@0 9362 }
michael@0 9363 }
michael@0 9364
michael@0 9365 // nsFont::EnumerateFamilies callback for ParseFontDescriptorValue
michael@0 9366 struct MOZ_STACK_CLASS ExtractFirstFamilyData {
michael@0 9367 nsAutoString mFamilyName;
michael@0 9368 bool mGood;
michael@0 9369 ExtractFirstFamilyData() : mFamilyName(), mGood(false) {}
michael@0 9370 };
michael@0 9371
michael@0 9372 static bool
michael@0 9373 ExtractFirstFamily(const nsString& aFamily,
michael@0 9374 bool aGeneric,
michael@0 9375 void* aData)
michael@0 9376 {
michael@0 9377 ExtractFirstFamilyData* realData = (ExtractFirstFamilyData*) aData;
michael@0 9378 if (aGeneric || realData->mFamilyName.Length() > 0) {
michael@0 9379 realData->mGood = false;
michael@0 9380 return false;
michael@0 9381 }
michael@0 9382 realData->mFamilyName.Assign(aFamily);
michael@0 9383 realData->mGood = true;
michael@0 9384 return true;
michael@0 9385 }
michael@0 9386
michael@0 9387 // font-descriptor: descriptor ':' value ';'
michael@0 9388 // caller has advanced mToken to point at the descriptor
michael@0 9389 bool
michael@0 9390 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
michael@0 9391 nsCSSValue& aValue)
michael@0 9392 {
michael@0 9393 switch (aDescID) {
michael@0 9394 // These four are similar to the properties of the same name,
michael@0 9395 // possibly with more restrictions on the values they can take.
michael@0 9396 case eCSSFontDesc_Family: {
michael@0 9397 if (!ParseFamily(aValue) ||
michael@0 9398 aValue.GetUnit() != eCSSUnit_Families)
michael@0 9399 return false;
michael@0 9400
michael@0 9401 // the style parameters to the nsFont constructor are ignored,
michael@0 9402 // because it's only being used to call EnumerateFamilies
michael@0 9403 nsAutoString valueStr;
michael@0 9404 aValue.GetStringValue(valueStr);
michael@0 9405 nsFont font(valueStr, 0, 0, 0, 0, 0, 0);
michael@0 9406 ExtractFirstFamilyData dat;
michael@0 9407
michael@0 9408 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
michael@0 9409 if (!dat.mGood)
michael@0 9410 return false;
michael@0 9411
michael@0 9412 aValue.SetStringValue(dat.mFamilyName, eCSSUnit_String);
michael@0 9413 return true;
michael@0 9414 }
michael@0 9415
michael@0 9416 case eCSSFontDesc_Style:
michael@0 9417 // property is VARIANT_HMK|VARIANT_SYSFONT
michael@0 9418 return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
michael@0 9419 nsCSSProps::kFontStyleKTable);
michael@0 9420
michael@0 9421 case eCSSFontDesc_Weight:
michael@0 9422 return (ParseFontWeight(aValue) &&
michael@0 9423 aValue.GetUnit() != eCSSUnit_Inherit &&
michael@0 9424 aValue.GetUnit() != eCSSUnit_Initial &&
michael@0 9425 aValue.GetUnit() != eCSSUnit_Unset &&
michael@0 9426 (aValue.GetUnit() != eCSSUnit_Enumerated ||
michael@0 9427 (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
michael@0 9428 aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
michael@0 9429
michael@0 9430 case eCSSFontDesc_Stretch:
michael@0 9431 // property is VARIANT_HK|VARIANT_SYSFONT
michael@0 9432 return ParseVariant(aValue, VARIANT_KEYWORD,
michael@0 9433 nsCSSProps::kFontStretchKTable);
michael@0 9434
michael@0 9435 // These two are unique to @font-face and have their own special grammar.
michael@0 9436 case eCSSFontDesc_Src:
michael@0 9437 return ParseFontSrc(aValue);
michael@0 9438
michael@0 9439 case eCSSFontDesc_UnicodeRange:
michael@0 9440 return ParseFontRanges(aValue);
michael@0 9441
michael@0 9442 case eCSSFontDesc_FontFeatureSettings:
michael@0 9443 return ParseFontFeatureSettings(aValue);
michael@0 9444
michael@0 9445 case eCSSFontDesc_FontLanguageOverride:
michael@0 9446 return ParseVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, nullptr);
michael@0 9447
michael@0 9448 case eCSSFontDesc_UNKNOWN:
michael@0 9449 case eCSSFontDesc_COUNT:
michael@0 9450 NS_NOTREACHED("bad nsCSSFontDesc code");
michael@0 9451 }
michael@0 9452 // explicitly do NOT have a default case to let the compiler
michael@0 9453 // help find missing descriptors
michael@0 9454 return false;
michael@0 9455 }
michael@0 9456
michael@0 9457 void
michael@0 9458 CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties)
michael@0 9459 {
michael@0 9460 nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
michael@0 9461 for (const nsCSSProperty *prop = aSourceProperties;
michael@0 9462 *prop != eCSSProperty_UNKNOWN; ++prop) {
michael@0 9463 AppendValue(*prop, physical);
michael@0 9464 }
michael@0 9465 }
michael@0 9466
michael@0 9467 static nsCSSValue
michael@0 9468 BoxPositionMaskToCSSValue(int32_t aMask, bool isX)
michael@0 9469 {
michael@0 9470 int32_t val = NS_STYLE_BG_POSITION_CENTER;
michael@0 9471 if (isX) {
michael@0 9472 if (aMask & BG_LEFT) {
michael@0 9473 val = NS_STYLE_BG_POSITION_LEFT;
michael@0 9474 }
michael@0 9475 else if (aMask & BG_RIGHT) {
michael@0 9476 val = NS_STYLE_BG_POSITION_RIGHT;
michael@0 9477 }
michael@0 9478 }
michael@0 9479 else {
michael@0 9480 if (aMask & BG_TOP) {
michael@0 9481 val = NS_STYLE_BG_POSITION_TOP;
michael@0 9482 }
michael@0 9483 else if (aMask & BG_BOTTOM) {
michael@0 9484 val = NS_STYLE_BG_POSITION_BOTTOM;
michael@0 9485 }
michael@0 9486 }
michael@0 9487
michael@0 9488 return nsCSSValue(val, eCSSUnit_Enumerated);
michael@0 9489 }
michael@0 9490
michael@0 9491 bool
michael@0 9492 CSSParserImpl::ParseBackground()
michael@0 9493 {
michael@0 9494 nsAutoParseCompoundProperty compound(this);
michael@0 9495
michael@0 9496 // background-color can only be set once, so it's not a list.
michael@0 9497 nsCSSValue color;
michael@0 9498
michael@0 9499 // Check first for inherit/initial/unset.
michael@0 9500 if (ParseVariant(color, VARIANT_INHERIT, nullptr)) {
michael@0 9501 // must be alone
michael@0 9502 for (const nsCSSProperty* subprops =
michael@0 9503 nsCSSProps::SubpropertyEntryFor(eCSSProperty_background);
michael@0 9504 *subprops != eCSSProperty_UNKNOWN; ++subprops) {
michael@0 9505 AppendValue(*subprops, color);
michael@0 9506 }
michael@0 9507 return true;
michael@0 9508 }
michael@0 9509
michael@0 9510 nsCSSValue image, repeat, attachment, clip, origin, position, size;
michael@0 9511 BackgroundParseState state(color, image.SetListValue(),
michael@0 9512 repeat.SetPairListValue(),
michael@0 9513 attachment.SetListValue(), clip.SetListValue(),
michael@0 9514 origin.SetListValue(), position.SetListValue(),
michael@0 9515 size.SetPairListValue());
michael@0 9516
michael@0 9517 for (;;) {
michael@0 9518 if (!ParseBackgroundItem(state)) {
michael@0 9519 return false;
michael@0 9520 }
michael@0 9521 // If we saw a color, this must be the last item.
michael@0 9522 if (color.GetUnit() != eCSSUnit_Null) {
michael@0 9523 break;
michael@0 9524 }
michael@0 9525 // If there's a comma, expect another item.
michael@0 9526 if (!ExpectSymbol(',', true)) {
michael@0 9527 break;
michael@0 9528 }
michael@0 9529 // Chain another entry on all the lists.
michael@0 9530 state.mImage->mNext = new nsCSSValueList;
michael@0 9531 state.mImage = state.mImage->mNext;
michael@0 9532 state.mRepeat->mNext = new nsCSSValuePairList;
michael@0 9533 state.mRepeat = state.mRepeat->mNext;
michael@0 9534 state.mAttachment->mNext = new nsCSSValueList;
michael@0 9535 state.mAttachment = state.mAttachment->mNext;
michael@0 9536 state.mClip->mNext = new nsCSSValueList;
michael@0 9537 state.mClip = state.mClip->mNext;
michael@0 9538 state.mOrigin->mNext = new nsCSSValueList;
michael@0 9539 state.mOrigin = state.mOrigin->mNext;
michael@0 9540 state.mPosition->mNext = new nsCSSValueList;
michael@0 9541 state.mPosition = state.mPosition->mNext;
michael@0 9542 state.mSize->mNext = new nsCSSValuePairList;
michael@0 9543 state.mSize = state.mSize->mNext;
michael@0 9544 }
michael@0 9545
michael@0 9546 // If we get to this point without seeing a color, provide a default.
michael@0 9547 if (color.GetUnit() == eCSSUnit_Null) {
michael@0 9548 color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor);
michael@0 9549 }
michael@0 9550
michael@0 9551 AppendValue(eCSSProperty_background_image, image);
michael@0 9552 AppendValue(eCSSProperty_background_repeat, repeat);
michael@0 9553 AppendValue(eCSSProperty_background_attachment, attachment);
michael@0 9554 AppendValue(eCSSProperty_background_clip, clip);
michael@0 9555 AppendValue(eCSSProperty_background_origin, origin);
michael@0 9556 AppendValue(eCSSProperty_background_position, position);
michael@0 9557 AppendValue(eCSSProperty_background_size, size);
michael@0 9558 AppendValue(eCSSProperty_background_color, color);
michael@0 9559 return true;
michael@0 9560 }
michael@0 9561
michael@0 9562 // Parse one item of the background shorthand property.
michael@0 9563 bool
michael@0 9564 CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState)
michael@0 9565
michael@0 9566 {
michael@0 9567 // Fill in the values that the shorthand will set if we don't find
michael@0 9568 // other values.
michael@0 9569 aState.mImage->mValue.SetNoneValue();
michael@0 9570 aState.mRepeat->mXValue.SetIntValue(NS_STYLE_BG_REPEAT_REPEAT,
michael@0 9571 eCSSUnit_Enumerated);
michael@0 9572 aState.mRepeat->mYValue.Reset();
michael@0 9573 aState.mAttachment->mValue.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL,
michael@0 9574 eCSSUnit_Enumerated);
michael@0 9575 aState.mClip->mValue.SetIntValue(NS_STYLE_BG_CLIP_BORDER,
michael@0 9576 eCSSUnit_Enumerated);
michael@0 9577 aState.mOrigin->mValue.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING,
michael@0 9578 eCSSUnit_Enumerated);
michael@0 9579 nsRefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4);
michael@0 9580 aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array);
michael@0 9581 positionArr->Item(1).SetPercentValue(0.0f);
michael@0 9582 positionArr->Item(3).SetPercentValue(0.0f);
michael@0 9583 aState.mSize->mXValue.SetAutoValue();
michael@0 9584 aState.mSize->mYValue.SetAutoValue();
michael@0 9585
michael@0 9586 bool haveColor = false,
michael@0 9587 haveImage = false,
michael@0 9588 haveRepeat = false,
michael@0 9589 haveAttach = false,
michael@0 9590 havePositionAndSize = false,
michael@0 9591 haveOrigin = false,
michael@0 9592 haveSomething = false;
michael@0 9593
michael@0 9594 while (GetToken(true)) {
michael@0 9595 nsCSSTokenType tt = mToken.mType;
michael@0 9596 UngetToken(); // ...but we'll still cheat and use mToken
michael@0 9597 if (tt == eCSSToken_Symbol) {
michael@0 9598 // ExpectEndProperty only looks for symbols, and nothing else will
michael@0 9599 // show up as one.
michael@0 9600 break;
michael@0 9601 }
michael@0 9602
michael@0 9603 if (tt == eCSSToken_Ident) {
michael@0 9604 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
michael@0 9605 int32_t dummy;
michael@0 9606 if (keyword == eCSSKeyword_inherit ||
michael@0 9607 keyword == eCSSKeyword_initial ||
michael@0 9608 keyword == eCSSKeyword_unset) {
michael@0 9609 return false;
michael@0 9610 } else if (keyword == eCSSKeyword_none) {
michael@0 9611 if (haveImage)
michael@0 9612 return false;
michael@0 9613 haveImage = true;
michael@0 9614 if (!ParseSingleValueProperty(aState.mImage->mValue,
michael@0 9615 eCSSProperty_background_image)) {
michael@0 9616 NS_NOTREACHED("should be able to parse");
michael@0 9617 return false;
michael@0 9618 }
michael@0 9619 } else if (nsCSSProps::FindKeyword(keyword,
michael@0 9620 nsCSSProps::kBackgroundAttachmentKTable, dummy)) {
michael@0 9621 if (haveAttach)
michael@0 9622 return false;
michael@0 9623 haveAttach = true;
michael@0 9624 if (!ParseSingleValueProperty(aState.mAttachment->mValue,
michael@0 9625 eCSSProperty_background_attachment)) {
michael@0 9626 NS_NOTREACHED("should be able to parse");
michael@0 9627 return false;
michael@0 9628 }
michael@0 9629 } else if (nsCSSProps::FindKeyword(keyword,
michael@0 9630 nsCSSProps::kBackgroundRepeatKTable, dummy)) {
michael@0 9631 if (haveRepeat)
michael@0 9632 return false;
michael@0 9633 haveRepeat = true;
michael@0 9634 nsCSSValuePair scratch;
michael@0 9635 if (!ParseBackgroundRepeatValues(scratch)) {
michael@0 9636 NS_NOTREACHED("should be able to parse");
michael@0 9637 return false;
michael@0 9638 }
michael@0 9639 aState.mRepeat->mXValue = scratch.mXValue;
michael@0 9640 aState.mRepeat->mYValue = scratch.mYValue;
michael@0 9641 } else if (nsCSSProps::FindKeyword(keyword,
michael@0 9642 nsCSSProps::kBackgroundPositionKTable, dummy)) {
michael@0 9643 if (havePositionAndSize)
michael@0 9644 return false;
michael@0 9645 havePositionAndSize = true;
michael@0 9646 if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) {
michael@0 9647 return false;
michael@0 9648 }
michael@0 9649 if (ExpectSymbol('/', true)) {
michael@0 9650 nsCSSValuePair scratch;
michael@0 9651 if (!ParseBackgroundSizeValues(scratch)) {
michael@0 9652 return false;
michael@0 9653 }
michael@0 9654 aState.mSize->mXValue = scratch.mXValue;
michael@0 9655 aState.mSize->mYValue = scratch.mYValue;
michael@0 9656 }
michael@0 9657 } else if (nsCSSProps::FindKeyword(keyword,
michael@0 9658 nsCSSProps::kBackgroundOriginKTable, dummy)) {
michael@0 9659 if (haveOrigin)
michael@0 9660 return false;
michael@0 9661 haveOrigin = true;
michael@0 9662 if (!ParseSingleValueProperty(aState.mOrigin->mValue,
michael@0 9663 eCSSProperty_background_origin)) {
michael@0 9664 NS_NOTREACHED("should be able to parse");
michael@0 9665 return false;
michael@0 9666 }
michael@0 9667
michael@0 9668 // The spec allows a second box value (for background-clip),
michael@0 9669 // immediately following the first one (for background-origin).
michael@0 9670
michael@0 9671 // 'background-clip' and 'background-origin' use the same keyword table
michael@0 9672 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
michael@0 9673 eCSSProperty_background_origin] ==
michael@0 9674 nsCSSProps::kBackgroundOriginKTable);
michael@0 9675 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
michael@0 9676 eCSSProperty_background_clip] ==
michael@0 9677 nsCSSProps::kBackgroundOriginKTable);
michael@0 9678 static_assert(NS_STYLE_BG_CLIP_BORDER ==
michael@0 9679 NS_STYLE_BG_ORIGIN_BORDER &&
michael@0 9680 NS_STYLE_BG_CLIP_PADDING ==
michael@0 9681 NS_STYLE_BG_ORIGIN_PADDING &&
michael@0 9682 NS_STYLE_BG_CLIP_CONTENT ==
michael@0 9683 NS_STYLE_BG_ORIGIN_CONTENT,
michael@0 9684 "bg-clip and bg-origin style constants must agree");
michael@0 9685
michael@0 9686 if (!ParseSingleValueProperty(aState.mClip->mValue,
michael@0 9687 eCSSProperty_background_clip)) {
michael@0 9688 // When exactly one <box> value is set, it is used for both
michael@0 9689 // 'background-origin' and 'background-clip'.
michael@0 9690 // See assertions above showing these values are compatible.
michael@0 9691 aState.mClip->mValue = aState.mOrigin->mValue;
michael@0 9692 }
michael@0 9693 } else {
michael@0 9694 if (haveColor)
michael@0 9695 return false;
michael@0 9696 haveColor = true;
michael@0 9697 if (!ParseSingleValueProperty(aState.mColor,
michael@0 9698 eCSSProperty_background_color)) {
michael@0 9699 return false;
michael@0 9700 }
michael@0 9701 }
michael@0 9702 } else if (tt == eCSSToken_URL ||
michael@0 9703 (tt == eCSSToken_Function &&
michael@0 9704 (mToken.mIdent.LowerCaseEqualsLiteral("linear-gradient") ||
michael@0 9705 mToken.mIdent.LowerCaseEqualsLiteral("radial-gradient") ||
michael@0 9706 mToken.mIdent.LowerCaseEqualsLiteral("repeating-linear-gradient") ||
michael@0 9707 mToken.mIdent.LowerCaseEqualsLiteral("repeating-radial-gradient") ||
michael@0 9708 mToken.mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient") ||
michael@0 9709 mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient") ||
michael@0 9710 mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") ||
michael@0 9711 mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
michael@0 9712 mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect") ||
michael@0 9713 mToken.mIdent.LowerCaseEqualsLiteral("-moz-element")))) {
michael@0 9714 if (haveImage)
michael@0 9715 return false;
michael@0 9716 haveImage = true;
michael@0 9717 if (!ParseSingleValueProperty(aState.mImage->mValue,
michael@0 9718 eCSSProperty_background_image)) {
michael@0 9719 return false;
michael@0 9720 }
michael@0 9721 } else if (tt == eCSSToken_Dimension ||
michael@0 9722 tt == eCSSToken_Number ||
michael@0 9723 tt == eCSSToken_Percentage ||
michael@0 9724 (tt == eCSSToken_Function &&
michael@0 9725 (mToken.mIdent.LowerCaseEqualsLiteral("calc") ||
michael@0 9726 mToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")))) {
michael@0 9727 if (havePositionAndSize)
michael@0 9728 return false;
michael@0 9729 havePositionAndSize = true;
michael@0 9730 if (!ParseBackgroundPositionValues(aState.mPosition->mValue, false)) {
michael@0 9731 return false;
michael@0 9732 }
michael@0 9733 if (ExpectSymbol('/', true)) {
michael@0 9734 nsCSSValuePair scratch;
michael@0 9735 if (!ParseBackgroundSizeValues(scratch)) {
michael@0 9736 return false;
michael@0 9737 }
michael@0 9738 aState.mSize->mXValue = scratch.mXValue;
michael@0 9739 aState.mSize->mYValue = scratch.mYValue;
michael@0 9740 }
michael@0 9741 } else {
michael@0 9742 if (haveColor)
michael@0 9743 return false;
michael@0 9744 haveColor = true;
michael@0 9745 // Note: This parses 'inherit', 'initial' and 'unset', but
michael@0 9746 // we've already checked for them, so it's ok.
michael@0 9747 if (!ParseSingleValueProperty(aState.mColor,
michael@0 9748 eCSSProperty_background_color)) {
michael@0 9749 return false;
michael@0 9750 }
michael@0 9751 }
michael@0 9752 haveSomething = true;
michael@0 9753 }
michael@0 9754
michael@0 9755 return haveSomething;
michael@0 9756 }
michael@0 9757
michael@0 9758 // This function is very similar to ParseBackgroundPosition and
michael@0 9759 // ParseBackgroundSize.
michael@0 9760 bool
michael@0 9761 CSSParserImpl::ParseValueList(nsCSSProperty aPropID)
michael@0 9762 {
michael@0 9763 // aPropID is a single value prop-id
michael@0 9764 nsCSSValue value;
michael@0 9765 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
michael@0 9766 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 9767 nsCSSValueList* item = value.SetListValue();
michael@0 9768 for (;;) {
michael@0 9769 if (!ParseSingleValueProperty(item->mValue, aPropID)) {
michael@0 9770 return false;
michael@0 9771 }
michael@0 9772 if (!ExpectSymbol(',', true)) {
michael@0 9773 break;
michael@0 9774 }
michael@0 9775 item->mNext = new nsCSSValueList;
michael@0 9776 item = item->mNext;
michael@0 9777 }
michael@0 9778 }
michael@0 9779 AppendValue(aPropID, value);
michael@0 9780 return true;
michael@0 9781 }
michael@0 9782
michael@0 9783 bool
michael@0 9784 CSSParserImpl::ParseBackgroundRepeat()
michael@0 9785 {
michael@0 9786 nsCSSValue value;
michael@0 9787 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
michael@0 9788 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 9789 nsCSSValuePair valuePair;
michael@0 9790 if (!ParseBackgroundRepeatValues(valuePair)) {
michael@0 9791 return false;
michael@0 9792 }
michael@0 9793 nsCSSValuePairList* item = value.SetPairListValue();
michael@0 9794 for (;;) {
michael@0 9795 item->mXValue = valuePair.mXValue;
michael@0 9796 item->mYValue = valuePair.mYValue;
michael@0 9797 if (!ExpectSymbol(',', true)) {
michael@0 9798 break;
michael@0 9799 }
michael@0 9800 if (!ParseBackgroundRepeatValues(valuePair)) {
michael@0 9801 return false;
michael@0 9802 }
michael@0 9803 item->mNext = new nsCSSValuePairList;
michael@0 9804 item = item->mNext;
michael@0 9805 }
michael@0 9806 }
michael@0 9807
michael@0 9808 AppendValue(eCSSProperty_background_repeat, value);
michael@0 9809 return true;
michael@0 9810 }
michael@0 9811
michael@0 9812 bool
michael@0 9813 CSSParserImpl::ParseBackgroundRepeatValues(nsCSSValuePair& aValue)
michael@0 9814 {
michael@0 9815 nsCSSValue& xValue = aValue.mXValue;
michael@0 9816 nsCSSValue& yValue = aValue.mYValue;
michael@0 9817
michael@0 9818 if (ParseEnum(xValue, nsCSSProps::kBackgroundRepeatKTable)) {
michael@0 9819 int32_t value = xValue.GetIntValue();
michael@0 9820 // For single values set yValue as eCSSUnit_Null.
michael@0 9821 if (value == NS_STYLE_BG_REPEAT_REPEAT_X ||
michael@0 9822 value == NS_STYLE_BG_REPEAT_REPEAT_Y ||
michael@0 9823 !ParseEnum(yValue, nsCSSProps::kBackgroundRepeatPartKTable)) {
michael@0 9824 // the caller will fail cases like "repeat-x no-repeat"
michael@0 9825 // by expecting a list separator or an end property.
michael@0 9826 yValue.Reset();
michael@0 9827 }
michael@0 9828 return true;
michael@0 9829 }
michael@0 9830
michael@0 9831 return false;
michael@0 9832 }
michael@0 9833
michael@0 9834 // This function is very similar to ParseBackgroundList and ParseBackgroundSize.
michael@0 9835 bool
michael@0 9836 CSSParserImpl::ParseBackgroundPosition()
michael@0 9837 {
michael@0 9838 nsCSSValue value;
michael@0 9839 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
michael@0 9840 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 9841 nsCSSValue itemValue;
michael@0 9842 if (!ParseBackgroundPositionValues(itemValue, false)) {
michael@0 9843 return false;
michael@0 9844 }
michael@0 9845 nsCSSValueList* item = value.SetListValue();
michael@0 9846 for (;;) {
michael@0 9847 item->mValue = itemValue;
michael@0 9848 if (!ExpectSymbol(',', true)) {
michael@0 9849 break;
michael@0 9850 }
michael@0 9851 if (!ParseBackgroundPositionValues(itemValue, false)) {
michael@0 9852 return false;
michael@0 9853 }
michael@0 9854 item->mNext = new nsCSSValueList;
michael@0 9855 item = item->mNext;
michael@0 9856 }
michael@0 9857 }
michael@0 9858 AppendValue(eCSSProperty_background_position, value);
michael@0 9859 return true;
michael@0 9860 }
michael@0 9861
michael@0 9862 /**
michael@0 9863 * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used
michael@0 9864 * for parsing the CSS 2.1 background-position syntax (which has at
michael@0 9865 * most two values). (Compare to the css3-background syntax which
michael@0 9866 * takes up to four values.) Some current CSS specifications that
michael@0 9867 * use background-position-like syntax still use this old syntax.
michael@0 9868 **
michael@0 9869 * Parses two values that correspond to positions in a box. These can be
michael@0 9870 * values corresponding to percentages of the box, raw offsets, or keywords
michael@0 9871 * like "top," "left center," etc.
michael@0 9872 *
michael@0 9873 * @param aOut The nsCSSValuePair in which to place the result.
michael@0 9874 * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are
michael@0 9875 * legal values
michael@0 9876 * @param aAllowExplicitCenter If true, 'center' is a legal value
michael@0 9877 * @return Whether or not the operation succeeded.
michael@0 9878 */
michael@0 9879 bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
michael@0 9880 bool aAcceptsInherit,
michael@0 9881 bool aAllowExplicitCenter)
michael@0 9882 {
michael@0 9883 // First try a percentage or a length value
michael@0 9884 nsCSSValue &xValue = aOut.mXValue,
michael@0 9885 &yValue = aOut.mYValue;
michael@0 9886 int32_t variantMask =
michael@0 9887 (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC;
michael@0 9888 if (ParseVariant(xValue, variantMask, nullptr)) {
michael@0 9889 if (eCSSUnit_Inherit == xValue.GetUnit() ||
michael@0 9890 eCSSUnit_Initial == xValue.GetUnit() ||
michael@0 9891 eCSSUnit_Unset == xValue.GetUnit()) { // both are inherit, initial or unset
michael@0 9892 yValue = xValue;
michael@0 9893 return true;
michael@0 9894 }
michael@0 9895 // We have one percentage/length/calc. Get the optional second
michael@0 9896 // percentage/length/calc/keyword.
michael@0 9897 if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
michael@0 9898 // We have two numbers
michael@0 9899 return true;
michael@0 9900 }
michael@0 9901
michael@0 9902 if (ParseEnum(yValue, nsCSSProps::kBackgroundPositionKTable)) {
michael@0 9903 int32_t yVal = yValue.GetIntValue();
michael@0 9904 if (!(yVal & BG_CTB)) {
michael@0 9905 // The second keyword can only be 'center', 'top', or 'bottom'
michael@0 9906 return false;
michael@0 9907 }
michael@0 9908 yValue = BoxPositionMaskToCSSValue(yVal, false);
michael@0 9909 return true;
michael@0 9910 }
michael@0 9911
michael@0 9912 // If only one percentage or length value is given, it sets the
michael@0 9913 // horizontal position only, and the vertical position will be 50%.
michael@0 9914 yValue.SetPercentValue(0.5f);
michael@0 9915 return true;
michael@0 9916 }
michael@0 9917
michael@0 9918 // Now try keywords. We do this manually to allow for the first
michael@0 9919 // appearance of "center" to apply to the either the x or y
michael@0 9920 // position (it's ambiguous so we have to disambiguate). Each
michael@0 9921 // allowed keyword value is assigned it's own bit. We don't allow
michael@0 9922 // any duplicate keywords other than center. We try to get two
michael@0 9923 // keywords but it's okay if there is only one.
michael@0 9924 int32_t mask = 0;
michael@0 9925 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
michael@0 9926 int32_t bit = xValue.GetIntValue();
michael@0 9927 mask |= bit;
michael@0 9928 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
michael@0 9929 bit = xValue.GetIntValue();
michael@0 9930 if (mask & (bit & ~BG_CENTER)) {
michael@0 9931 // Only the 'center' keyword can be duplicated.
michael@0 9932 return false;
michael@0 9933 }
michael@0 9934 mask |= bit;
michael@0 9935 }
michael@0 9936 else {
michael@0 9937 // Only one keyword. See if we have a length, percentage, or calc.
michael@0 9938 if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
michael@0 9939 if (!(mask & BG_CLR)) {
michael@0 9940 // The first keyword can only be 'center', 'left', or 'right'
michael@0 9941 return false;
michael@0 9942 }
michael@0 9943
michael@0 9944 xValue = BoxPositionMaskToCSSValue(mask, true);
michael@0 9945 return true;
michael@0 9946 }
michael@0 9947 }
michael@0 9948 }
michael@0 9949
michael@0 9950 // Check for bad input. Bad input consists of no matching keywords,
michael@0 9951 // or pairs of x keywords or pairs of y keywords.
michael@0 9952 if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
michael@0 9953 (mask == (BG_LEFT | BG_RIGHT)) ||
michael@0 9954 (!aAllowExplicitCenter && (mask & BG_CENTER))) {
michael@0 9955 return false;
michael@0 9956 }
michael@0 9957
michael@0 9958 // Create style values
michael@0 9959 xValue = BoxPositionMaskToCSSValue(mask, true);
michael@0 9960 yValue = BoxPositionMaskToCSSValue(mask, false);
michael@0 9961 return true;
michael@0 9962 }
michael@0 9963
michael@0 9964 bool CSSParserImpl::ParseBackgroundPositionValues(nsCSSValue& aOut,
michael@0 9965 bool aAcceptsInherit)
michael@0 9966 {
michael@0 9967 // css3-background allows positions to be defined as offsets
michael@0 9968 // from an edge. There can be 2 keywords and 2 offsets given. These
michael@0 9969 // four 'values' are stored in an array in the following order:
michael@0 9970 // [keyword offset keyword offset]. If a keyword or offset isn't
michael@0 9971 // parsed the value of the corresponding array element is set
michael@0 9972 // to eCSSUnit_Null by a call to nsCSSValue::Reset().
michael@0 9973 if (aAcceptsInherit && ParseVariant(aOut, VARIANT_INHERIT, nullptr)) {
michael@0 9974 return true;
michael@0 9975 }
michael@0 9976
michael@0 9977 nsRefPtr<nsCSSValue::Array> value = nsCSSValue::Array::Create(4);
michael@0 9978 aOut.SetArrayValue(value, eCSSUnit_Array);
michael@0 9979
michael@0 9980 // The following clarifies organisation of the array.
michael@0 9981 nsCSSValue &xEdge = value->Item(0),
michael@0 9982 &xOffset = value->Item(1),
michael@0 9983 &yEdge = value->Item(2),
michael@0 9984 &yOffset = value->Item(3);
michael@0 9985
michael@0 9986 // Parse all the values into the array.
michael@0 9987 uint32_t valueCount = 0;
michael@0 9988 for (int32_t i = 0; i < 4; i++) {
michael@0 9989 if (!ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD,
michael@0 9990 nsCSSProps::kBackgroundPositionKTable)) {
michael@0 9991 break;
michael@0 9992 }
michael@0 9993 ++valueCount;
michael@0 9994 }
michael@0 9995
michael@0 9996 switch (valueCount) {
michael@0 9997 case 4:
michael@0 9998 // "If three or four values are given, then each <percentage> or <length>
michael@0 9999 // represents an offset and must be preceded by a keyword, which specifies
michael@0 10000 // from which edge the offset is given."
michael@0 10001 if (eCSSUnit_Enumerated != xEdge.GetUnit() ||
michael@0 10002 BG_CENTER == xEdge.GetIntValue() ||
michael@0 10003 eCSSUnit_Enumerated == xOffset.GetUnit() ||
michael@0 10004 eCSSUnit_Enumerated != yEdge.GetUnit() ||
michael@0 10005 BG_CENTER == yEdge.GetIntValue() ||
michael@0 10006 eCSSUnit_Enumerated == yOffset.GetUnit()) {
michael@0 10007 return false;
michael@0 10008 }
michael@0 10009 break;
michael@0 10010 case 3:
michael@0 10011 // "If three or four values are given, then each <percentage> or<length>
michael@0 10012 // represents an offset and must be preceded by a keyword, which specifies
michael@0 10013 // from which edge the offset is given." ... "If three values are given,
michael@0 10014 // the missing offset is assumed to be zero."
michael@0 10015 if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) {
michael@0 10016 // keyword offset keyword
michael@0 10017 // Second value is non-keyword, thus first value must be a non-center
michael@0 10018 // keyword.
michael@0 10019 if (eCSSUnit_Enumerated != value->Item(0).GetUnit() ||
michael@0 10020 BG_CENTER == value->Item(0).GetIntValue()) {
michael@0 10021 return false;
michael@0 10022 }
michael@0 10023
michael@0 10024 // Remaining value must be a keyword.
michael@0 10025 if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
michael@0 10026 return false;
michael@0 10027 }
michael@0 10028
michael@0 10029 yOffset.Reset(); // Everything else is in the correct position.
michael@0 10030 } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) {
michael@0 10031 // keyword keyword offset
michael@0 10032 // Third value is non-keyword, thus second value must be non-center
michael@0 10033 // keyword.
michael@0 10034 if (BG_CENTER == value->Item(1).GetIntValue()) {
michael@0 10035 return false;
michael@0 10036 }
michael@0 10037
michael@0 10038 // Remaining value must be a keyword.
michael@0 10039 if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) {
michael@0 10040 return false;
michael@0 10041 }
michael@0 10042
michael@0 10043 // Move the values to the correct position in the array.
michael@0 10044 value->Item(3) = value->Item(2); // yOffset
michael@0 10045 value->Item(2) = value->Item(1); // yEdge
michael@0 10046 value->Item(1).Reset(); // xOffset
michael@0 10047 } else {
michael@0 10048 return false;
michael@0 10049 }
michael@0 10050 break;
michael@0 10051 case 2:
michael@0 10052 // "If two values are given and at least one value is not a keyword, then
michael@0 10053 // the first value represents the horizontal position (or offset) and the
michael@0 10054 // second represents the vertical position (or offset)"
michael@0 10055 if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
michael@0 10056 if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
michael@0 10057 // keyword keyword
michael@0 10058 value->Item(2) = value->Item(1); // move yEdge to correct position
michael@0 10059 xOffset.Reset();
michael@0 10060 yOffset.Reset();
michael@0 10061 } else {
michael@0 10062 // keyword offset
michael@0 10063 // First value must represent horizontal position.
michael@0 10064 if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) {
michael@0 10065 return false;
michael@0 10066 }
michael@0 10067 value->Item(3) = value->Item(1); // move yOffset to correct position
michael@0 10068 xOffset.Reset();
michael@0 10069 yEdge.Reset();
michael@0 10070 }
michael@0 10071 } else {
michael@0 10072 if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) {
michael@0 10073 // offset keyword
michael@0 10074 // Second value must represent vertical position.
michael@0 10075 if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) {
michael@0 10076 return false;
michael@0 10077 }
michael@0 10078 value->Item(2) = value->Item(1); // move yEdge to correct position
michael@0 10079 value->Item(1) = value->Item(0); // move xOffset to correct position
michael@0 10080 xEdge.Reset();
michael@0 10081 yOffset.Reset();
michael@0 10082 } else {
michael@0 10083 // offset offset
michael@0 10084 value->Item(3) = value->Item(1); // move yOffset to correct position
michael@0 10085 value->Item(1) = value->Item(0); // move xOffset to correct position
michael@0 10086 xEdge.Reset();
michael@0 10087 yEdge.Reset();
michael@0 10088 }
michael@0 10089 }
michael@0 10090 break;
michael@0 10091 case 1:
michael@0 10092 // "If only one value is specified, the second value is assumed to be
michael@0 10093 // center."
michael@0 10094 if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) {
michael@0 10095 xOffset.Reset();
michael@0 10096 } else {
michael@0 10097 value->Item(1) = value->Item(0); // move xOffset to correct position
michael@0 10098 xEdge.Reset();
michael@0 10099 }
michael@0 10100 yEdge.SetIntValue(NS_STYLE_BG_POSITION_CENTER, eCSSUnit_Enumerated);
michael@0 10101 yOffset.Reset();
michael@0 10102 break;
michael@0 10103 default:
michael@0 10104 return false;
michael@0 10105 }
michael@0 10106
michael@0 10107 // For compatibility with CSS2.1 code the edges can be unspecified.
michael@0 10108 // Unspecified edges are recorded as nullptr.
michael@0 10109 NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() ||
michael@0 10110 eCSSUnit_Null == xEdge.GetUnit()) &&
michael@0 10111 (eCSSUnit_Enumerated == yEdge.GetUnit() ||
michael@0 10112 eCSSUnit_Null == yEdge.GetUnit()) &&
michael@0 10113 eCSSUnit_Enumerated != xOffset.GetUnit() &&
michael@0 10114 eCSSUnit_Enumerated != yOffset.GetUnit(),
michael@0 10115 "Unexpected units");
michael@0 10116
michael@0 10117 // Keywords in first and second pairs can not both be vertical or
michael@0 10118 // horizontal keywords. (eg. left right, bottom top). Additionally,
michael@0 10119 // non-center keyword can not be duplicated (eg. left left).
michael@0 10120 int32_t xEdgeEnum =
michael@0 10121 xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0;
michael@0 10122 int32_t yEdgeEnum =
michael@0 10123 yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0;
michael@0 10124 if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) ||
michael@0 10125 (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) ||
michael@0 10126 (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) {
michael@0 10127 return false;
michael@0 10128 }
michael@0 10129
michael@0 10130 // The values could be in an order that is different than expected.
michael@0 10131 // eg. x contains vertical information, y contains horizontal information.
michael@0 10132 // Swap if incorrect order.
michael@0 10133 if (xEdgeEnum & (BG_TOP | BG_BOTTOM) ||
michael@0 10134 yEdgeEnum & (BG_LEFT | BG_RIGHT)) {
michael@0 10135 nsCSSValue swapEdge = xEdge;
michael@0 10136 nsCSSValue swapOffset = xOffset;
michael@0 10137 xEdge = yEdge;
michael@0 10138 xOffset = yOffset;
michael@0 10139 yEdge = swapEdge;
michael@0 10140 yOffset = swapOffset;
michael@0 10141 }
michael@0 10142
michael@0 10143 return true;
michael@0 10144 }
michael@0 10145
michael@0 10146 // This function is very similar to ParseBackgroundList and
michael@0 10147 // ParseBackgroundPosition.
michael@0 10148 bool
michael@0 10149 CSSParserImpl::ParseBackgroundSize()
michael@0 10150 {
michael@0 10151 nsCSSValue value;
michael@0 10152 // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
michael@0 10153 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 10154 nsCSSValuePair valuePair;
michael@0 10155 if (!ParseBackgroundSizeValues(valuePair)) {
michael@0 10156 return false;
michael@0 10157 }
michael@0 10158 nsCSSValuePairList* item = value.SetPairListValue();
michael@0 10159 for (;;) {
michael@0 10160 item->mXValue = valuePair.mXValue;
michael@0 10161 item->mYValue = valuePair.mYValue;
michael@0 10162 if (!ExpectSymbol(',', true)) {
michael@0 10163 break;
michael@0 10164 }
michael@0 10165 if (!ParseBackgroundSizeValues(valuePair)) {
michael@0 10166 return false;
michael@0 10167 }
michael@0 10168 item->mNext = new nsCSSValuePairList;
michael@0 10169 item = item->mNext;
michael@0 10170 }
michael@0 10171 }
michael@0 10172 AppendValue(eCSSProperty_background_size, value);
michael@0 10173 return true;
michael@0 10174 }
michael@0 10175
michael@0 10176 /**
michael@0 10177 * Parses two values that correspond to lengths for the background-size
michael@0 10178 * property. These can be one or two lengths (or the 'auto' keyword) or
michael@0 10179 * percentages corresponding to the element's dimensions or the single keywords
michael@0 10180 * 'contain' or 'cover'. 'initial', 'inherit' and 'unset' must be handled by
michael@0 10181 * the caller if desired.
michael@0 10182 *
michael@0 10183 * @param aOut The nsCSSValuePair in which to place the result.
michael@0 10184 * @return Whether or not the operation succeeded.
michael@0 10185 */
michael@0 10186 #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC)
michael@0 10187 bool CSSParserImpl::ParseBackgroundSizeValues(nsCSSValuePair &aOut)
michael@0 10188 {
michael@0 10189 // First try a percentage or a length value
michael@0 10190 nsCSSValue &xValue = aOut.mXValue,
michael@0 10191 &yValue = aOut.mYValue;
michael@0 10192 if (ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr)) {
michael@0 10193 // We have one percentage/length/calc/auto. Get the optional second
michael@0 10194 // percentage/length/calc/keyword.
michael@0 10195 if (ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr)) {
michael@0 10196 // We have a second percentage/length/calc/auto.
michael@0 10197 return true;
michael@0 10198 }
michael@0 10199
michael@0 10200 // If only one percentage or length value is given, it sets the
michael@0 10201 // horizontal size only, and the vertical size will be as if by 'auto'.
michael@0 10202 yValue.SetAutoValue();
michael@0 10203 return true;
michael@0 10204 }
michael@0 10205
michael@0 10206 // Now address 'contain' and 'cover'.
michael@0 10207 if (!ParseEnum(xValue, nsCSSProps::kBackgroundSizeKTable))
michael@0 10208 return false;
michael@0 10209 yValue.Reset();
michael@0 10210 return true;
michael@0 10211 }
michael@0 10212 #undef BG_SIZE_VARIANT
michael@0 10213
michael@0 10214 bool
michael@0 10215 CSSParserImpl::ParseBorderColor()
michael@0 10216 {
michael@0 10217 static const nsCSSProperty kBorderColorSources[] = {
michael@0 10218 eCSSProperty_border_left_color_ltr_source,
michael@0 10219 eCSSProperty_border_left_color_rtl_source,
michael@0 10220 eCSSProperty_border_right_color_ltr_source,
michael@0 10221 eCSSProperty_border_right_color_rtl_source,
michael@0 10222 eCSSProperty_UNKNOWN
michael@0 10223 };
michael@0 10224
michael@0 10225 // do this now, in case 4 values weren't specified
michael@0 10226 InitBoxPropsAsPhysical(kBorderColorSources);
michael@0 10227 return ParseBoxProperties(kBorderColorIDs);
michael@0 10228 }
michael@0 10229
michael@0 10230 void
michael@0 10231 CSSParserImpl::SetBorderImageInitialValues()
michael@0 10232 {
michael@0 10233 // border-image-source: none
michael@0 10234 nsCSSValue source;
michael@0 10235 source.SetNoneValue();
michael@0 10236 AppendValue(eCSSProperty_border_image_source, source);
michael@0 10237
michael@0 10238 // border-image-slice: 100%
michael@0 10239 nsCSSValue sliceBoxValue;
michael@0 10240 nsCSSRect& sliceBox = sliceBoxValue.SetRectValue();
michael@0 10241 sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent));
michael@0 10242 nsCSSValue slice;
michael@0 10243 nsCSSValueList* sliceList = slice.SetListValue();
michael@0 10244 sliceList->mValue = sliceBoxValue;
michael@0 10245 AppendValue(eCSSProperty_border_image_slice, slice);
michael@0 10246
michael@0 10247 // border-image-width: 1
michael@0 10248 nsCSSValue width;
michael@0 10249 nsCSSRect& widthBox = width.SetRectValue();
michael@0 10250 widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number));
michael@0 10251 AppendValue(eCSSProperty_border_image_width, width);
michael@0 10252
michael@0 10253 // border-image-outset: 0
michael@0 10254 nsCSSValue outset;
michael@0 10255 nsCSSRect& outsetBox = outset.SetRectValue();
michael@0 10256 outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number));
michael@0 10257 AppendValue(eCSSProperty_border_image_outset, outset);
michael@0 10258
michael@0 10259 // border-image-repeat: repeat
michael@0 10260 nsCSSValue repeat;
michael@0 10261 nsCSSValuePair repeatPair;
michael@0 10262 repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
michael@0 10263 eCSSUnit_Enumerated));
michael@0 10264 repeat.SetPairValue(&repeatPair);
michael@0 10265 AppendValue(eCSSProperty_border_image_repeat, repeat);
michael@0 10266 }
michael@0 10267
michael@0 10268 bool
michael@0 10269 CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
michael@0 10270 bool* aConsumedTokens)
michael@0 10271 {
michael@0 10272 // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
michael@0 10273 nsCSSValue value;
michael@0 10274
michael@0 10275 if (aConsumedTokens) {
michael@0 10276 *aConsumedTokens = true;
michael@0 10277 }
michael@0 10278
michael@0 10279 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 10280 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
michael@0 10281 // are done.
michael@0 10282 AppendValue(eCSSProperty_border_image_slice, value);
michael@0 10283 return true;
michael@0 10284 }
michael@0 10285
michael@0 10286 // Try parsing "fill" value.
michael@0 10287 nsCSSValue imageSliceFillValue;
michael@0 10288 bool hasFill = ParseEnum(imageSliceFillValue,
michael@0 10289 nsCSSProps::kBorderImageSliceKTable);
michael@0 10290
michael@0 10291 // Parse the box dimensions.
michael@0 10292 nsCSSValue imageSliceBoxValue;
michael@0 10293 if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue)) {
michael@0 10294 if (!hasFill && aConsumedTokens) {
michael@0 10295 *aConsumedTokens = false;
michael@0 10296 }
michael@0 10297
michael@0 10298 return false;
michael@0 10299 }
michael@0 10300
michael@0 10301 // Try parsing "fill" keyword again if the first time failed because keyword
michael@0 10302 // and slice dimensions can be in any order.
michael@0 10303 if (!hasFill) {
michael@0 10304 hasFill = ParseEnum(imageSliceFillValue,
michael@0 10305 nsCSSProps::kBorderImageSliceKTable);
michael@0 10306 }
michael@0 10307
michael@0 10308 nsCSSValueList* borderImageSlice = value.SetListValue();
michael@0 10309 // Put the box value into the list.
michael@0 10310 borderImageSlice->mValue = imageSliceBoxValue;
michael@0 10311
michael@0 10312 if (hasFill) {
michael@0 10313 // Put the "fill" value into the list.
michael@0 10314 borderImageSlice->mNext = new nsCSSValueList;
michael@0 10315 borderImageSlice->mNext->mValue = imageSliceFillValue;
michael@0 10316 }
michael@0 10317
michael@0 10318 AppendValue(eCSSProperty_border_image_slice, value);
michael@0 10319 return true;
michael@0 10320 }
michael@0 10321
michael@0 10322 bool
michael@0 10323 CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
michael@0 10324 {
michael@0 10325 // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
michael@0 10326 nsCSSValue value;
michael@0 10327
michael@0 10328 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 10329 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
michael@0 10330 // are done.
michael@0 10331 AppendValue(eCSSProperty_border_image_width, value);
michael@0 10332 return true;
michael@0 10333 }
michael@0 10334
michael@0 10335 // Parse the box dimensions.
michael@0 10336 if (!ParseGroupedBoxProperty(VARIANT_ALPN, value)) {
michael@0 10337 return false;
michael@0 10338 }
michael@0 10339
michael@0 10340 AppendValue(eCSSProperty_border_image_width, value);
michael@0 10341 return true;
michael@0 10342 }
michael@0 10343
michael@0 10344 bool
michael@0 10345 CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
michael@0 10346 {
michael@0 10347 // border-image-outset: initial | [<length>|<number>]{1,4}
michael@0 10348 nsCSSValue value;
michael@0 10349
michael@0 10350 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 10351 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
michael@0 10352 // are done.
michael@0 10353 AppendValue(eCSSProperty_border_image_outset, value);
michael@0 10354 return true;
michael@0 10355 }
michael@0 10356
michael@0 10357 // Parse the box dimensions.
michael@0 10358 if (!ParseGroupedBoxProperty(VARIANT_LN, value)) {
michael@0 10359 return false;
michael@0 10360 }
michael@0 10361
michael@0 10362 AppendValue(eCSSProperty_border_image_outset, value);
michael@0 10363 return true;
michael@0 10364 }
michael@0 10365
michael@0 10366 bool
michael@0 10367 CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit)
michael@0 10368 {
michael@0 10369 nsCSSValue value;
michael@0 10370 if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 10371 // Keywords "inherit", "initial" and "unset" can not be mixed, so we
michael@0 10372 // are done.
michael@0 10373 AppendValue(eCSSProperty_border_image_repeat, value);
michael@0 10374 return true;
michael@0 10375 }
michael@0 10376
michael@0 10377 nsCSSValuePair result;
michael@0 10378 if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) {
michael@0 10379 return false;
michael@0 10380 }
michael@0 10381
michael@0 10382 // optional second keyword, defaults to first
michael@0 10383 if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) {
michael@0 10384 result.mYValue = result.mXValue;
michael@0 10385 }
michael@0 10386
michael@0 10387 value.SetPairValue(&result);
michael@0 10388 AppendValue(eCSSProperty_border_image_repeat, value);
michael@0 10389 return true;
michael@0 10390 }
michael@0 10391
michael@0 10392 bool
michael@0 10393 CSSParserImpl::ParseBorderImage()
michael@0 10394 {
michael@0 10395 nsAutoParseCompoundProperty compound(this);
michael@0 10396
michael@0 10397 // border-image: inherit | initial |
michael@0 10398 // <border-image-source> ||
michael@0 10399 // <border-image-slice>
michael@0 10400 // [ / <border-image-width> |
michael@0 10401 // / <border-image-width>? / <border-image-outset> ]? ||
michael@0 10402 // <border-image-repeat>
michael@0 10403
michael@0 10404 nsCSSValue value;
michael@0 10405 if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 10406 AppendValue(eCSSProperty_border_image_source, value);
michael@0 10407 AppendValue(eCSSProperty_border_image_slice, value);
michael@0 10408 AppendValue(eCSSProperty_border_image_width, value);
michael@0 10409 AppendValue(eCSSProperty_border_image_outset, value);
michael@0 10410 AppendValue(eCSSProperty_border_image_repeat, value);
michael@0 10411 // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done.
michael@0 10412 return true;
michael@0 10413 }
michael@0 10414
michael@0 10415 // No empty property.
michael@0 10416 if (CheckEndProperty()) {
michael@0 10417 return false;
michael@0 10418 }
michael@0 10419
michael@0 10420 // Shorthand properties are required to set everything they can.
michael@0 10421 SetBorderImageInitialValues();
michael@0 10422
michael@0 10423 bool foundSource = false;
michael@0 10424 bool foundSliceWidthOutset = false;
michael@0 10425 bool foundRepeat = false;
michael@0 10426
michael@0 10427 // This loop is used to handle the parsing of border-image properties which
michael@0 10428 // can appear in any order.
michael@0 10429 nsCSSValue imageSourceValue;
michael@0 10430 while (!CheckEndProperty()) {
michael@0 10431 // <border-image-source>
michael@0 10432 if (!foundSource && ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) {
michael@0 10433 AppendValue(eCSSProperty_border_image_source, imageSourceValue);
michael@0 10434 foundSource = true;
michael@0 10435 continue;
michael@0 10436 }
michael@0 10437
michael@0 10438 // <border-image-slice>
michael@0 10439 // ParseBorderImageSlice is weird. It may consume tokens and then return
michael@0 10440 // false, because it parses a property with two required components that
michael@0 10441 // can appear in either order. Since the tokens that were consumed cannot
michael@0 10442 // parse as anything else we care about, this isn't a problem.
michael@0 10443 if (!foundSliceWidthOutset) {
michael@0 10444 bool sliceConsumedTokens = false;
michael@0 10445 if (ParseBorderImageSlice(false, &sliceConsumedTokens)) {
michael@0 10446 foundSliceWidthOutset = true;
michael@0 10447
michael@0 10448 // [ / <border-image-width>?
michael@0 10449 if (ExpectSymbol('/', true)) {
michael@0 10450 bool foundBorderImageWidth = ParseBorderImageWidth(false);
michael@0 10451
michael@0 10452 // [ / <border-image-outset>
michael@0 10453 if (ExpectSymbol('/', true)) {
michael@0 10454 if (!ParseBorderImageOutset(false)) {
michael@0 10455 return false;
michael@0 10456 }
michael@0 10457 } else if (!foundBorderImageWidth) {
michael@0 10458 // If this part has an trailing slash, the whole declaration is
michael@0 10459 // invalid.
michael@0 10460 return false;
michael@0 10461 }
michael@0 10462 }
michael@0 10463
michael@0 10464 continue;
michael@0 10465 } else {
michael@0 10466 // If we consumed some tokens for <border-image-slice> but did not
michael@0 10467 // successfully parse it, we have an error.
michael@0 10468 if (sliceConsumedTokens) {
michael@0 10469 return false;
michael@0 10470 }
michael@0 10471 }
michael@0 10472 }
michael@0 10473
michael@0 10474 // <border-image-repeat>
michael@0 10475 if (!foundRepeat && ParseBorderImageRepeat(false)) {
michael@0 10476 foundRepeat = true;
michael@0 10477 continue;
michael@0 10478 }
michael@0 10479
michael@0 10480 return false;
michael@0 10481 }
michael@0 10482
michael@0 10483 return true;
michael@0 10484 }
michael@0 10485
michael@0 10486 bool
michael@0 10487 CSSParserImpl::ParseBorderSpacing()
michael@0 10488 {
michael@0 10489 nsCSSValue xValue, yValue;
michael@0 10490 if (!ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr)) {
michael@0 10491 return false;
michael@0 10492 }
michael@0 10493
michael@0 10494 // If we have one length, get the optional second length.
michael@0 10495 // set the second value equal to the first.
michael@0 10496 if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) {
michael@0 10497 ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC, nullptr);
michael@0 10498 }
michael@0 10499
michael@0 10500 if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) {
michael@0 10501 AppendValue(eCSSProperty_border_spacing, xValue);
michael@0 10502 } else {
michael@0 10503 nsCSSValue pair;
michael@0 10504 pair.SetPairValue(xValue, yValue);
michael@0 10505 AppendValue(eCSSProperty_border_spacing, pair);
michael@0 10506 }
michael@0 10507 return true;
michael@0 10508 }
michael@0 10509
michael@0 10510 bool
michael@0 10511 CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[],
michael@0 10512 bool aSetAllSides)
michael@0 10513 {
michael@0 10514 const int32_t numProps = 3;
michael@0 10515 nsCSSValue values[numProps];
michael@0 10516
michael@0 10517 int32_t found = ParseChoice(values, aPropIDs, numProps);
michael@0 10518 if (found < 1) {
michael@0 10519 return false;
michael@0 10520 }
michael@0 10521
michael@0 10522 if ((found & 1) == 0) { // Provide default border-width
michael@0 10523 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
michael@0 10524 }
michael@0 10525 if ((found & 2) == 0) { // Provide default border-style
michael@0 10526 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
michael@0 10527 }
michael@0 10528 if ((found & 4) == 0) { // text color will be used
michael@0 10529 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
michael@0 10530 }
michael@0 10531
michael@0 10532 if (aSetAllSides) {
michael@0 10533 static const nsCSSProperty kBorderSources[] = {
michael@0 10534 eCSSProperty_border_left_color_ltr_source,
michael@0 10535 eCSSProperty_border_left_color_rtl_source,
michael@0 10536 eCSSProperty_border_right_color_ltr_source,
michael@0 10537 eCSSProperty_border_right_color_rtl_source,
michael@0 10538 eCSSProperty_border_left_style_ltr_source,
michael@0 10539 eCSSProperty_border_left_style_rtl_source,
michael@0 10540 eCSSProperty_border_right_style_ltr_source,
michael@0 10541 eCSSProperty_border_right_style_rtl_source,
michael@0 10542 eCSSProperty_border_left_width_ltr_source,
michael@0 10543 eCSSProperty_border_left_width_rtl_source,
michael@0 10544 eCSSProperty_border_right_width_ltr_source,
michael@0 10545 eCSSProperty_border_right_width_rtl_source,
michael@0 10546 eCSSProperty_UNKNOWN
michael@0 10547 };
michael@0 10548
michael@0 10549 InitBoxPropsAsPhysical(kBorderSources);
michael@0 10550
michael@0 10551 // Parsing "border" shorthand; set all four sides to the same thing
michael@0 10552 for (int32_t index = 0; index < 4; index++) {
michael@0 10553 NS_ASSERTION(numProps == 3, "This code needs updating");
michael@0 10554 AppendValue(kBorderWidthIDs[index], values[0]);
michael@0 10555 AppendValue(kBorderStyleIDs[index], values[1]);
michael@0 10556 AppendValue(kBorderColorIDs[index], values[2]);
michael@0 10557 }
michael@0 10558
michael@0 10559 static const nsCSSProperty kBorderColorsProps[] = {
michael@0 10560 eCSSProperty_border_top_colors,
michael@0 10561 eCSSProperty_border_right_colors,
michael@0 10562 eCSSProperty_border_bottom_colors,
michael@0 10563 eCSSProperty_border_left_colors
michael@0 10564 };
michael@0 10565
michael@0 10566 // Set the other properties that the border shorthand sets to their
michael@0 10567 // initial values.
michael@0 10568 nsCSSValue extraValue;
michael@0 10569 switch (values[0].GetUnit()) {
michael@0 10570 case eCSSUnit_Inherit:
michael@0 10571 case eCSSUnit_Initial:
michael@0 10572 case eCSSUnit_Unset:
michael@0 10573 extraValue = values[0];
michael@0 10574 // Set value of border-image properties to initial/inherit/unset
michael@0 10575 AppendValue(eCSSProperty_border_image_source, extraValue);
michael@0 10576 AppendValue(eCSSProperty_border_image_slice, extraValue);
michael@0 10577 AppendValue(eCSSProperty_border_image_width, extraValue);
michael@0 10578 AppendValue(eCSSProperty_border_image_outset, extraValue);
michael@0 10579 AppendValue(eCSSProperty_border_image_repeat, extraValue);
michael@0 10580 break;
michael@0 10581 default:
michael@0 10582 extraValue.SetNoneValue();
michael@0 10583 SetBorderImageInitialValues();
michael@0 10584 break;
michael@0 10585 }
michael@0 10586 NS_FOR_CSS_SIDES(side) {
michael@0 10587 AppendValue(kBorderColorsProps[side], extraValue);
michael@0 10588 }
michael@0 10589 }
michael@0 10590 else {
michael@0 10591 // Just set our one side
michael@0 10592 for (int32_t index = 0; index < numProps; index++) {
michael@0 10593 AppendValue(aPropIDs[index], values[index]);
michael@0 10594 }
michael@0 10595 }
michael@0 10596 return true;
michael@0 10597 }
michael@0 10598
michael@0 10599 bool
michael@0 10600 CSSParserImpl::ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
michael@0 10601 int32_t aSourceType)
michael@0 10602 {
michael@0 10603 const int32_t numProps = 3;
michael@0 10604 nsCSSValue values[numProps];
michael@0 10605
michael@0 10606 int32_t found = ParseChoice(values, aPropIDs, numProps);
michael@0 10607 if (found < 1) {
michael@0 10608 return false;
michael@0 10609 }
michael@0 10610
michael@0 10611 if ((found & 1) == 0) { // Provide default border-width
michael@0 10612 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
michael@0 10613 }
michael@0 10614 if ((found & 2) == 0) { // Provide default border-style
michael@0 10615 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
michael@0 10616 }
michael@0 10617 if ((found & 4) == 0) { // text color will be used
michael@0 10618 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
michael@0 10619 }
michael@0 10620 for (int32_t index = 0; index < numProps; index++) {
michael@0 10621 const nsCSSProperty* subprops =
michael@0 10622 nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]);
michael@0 10623 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
michael@0 10624 "not box property with physical vs. logical cascading");
michael@0 10625 AppendValue(subprops[0], values[index]);
michael@0 10626 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
michael@0 10627 AppendValue(subprops[1], typeVal);
michael@0 10628 AppendValue(subprops[2], typeVal);
michael@0 10629 }
michael@0 10630 return true;
michael@0 10631 }
michael@0 10632
michael@0 10633 bool
michael@0 10634 CSSParserImpl::ParseBorderStyle()
michael@0 10635 {
michael@0 10636 static const nsCSSProperty kBorderStyleSources[] = {
michael@0 10637 eCSSProperty_border_left_style_ltr_source,
michael@0 10638 eCSSProperty_border_left_style_rtl_source,
michael@0 10639 eCSSProperty_border_right_style_ltr_source,
michael@0 10640 eCSSProperty_border_right_style_rtl_source,
michael@0 10641 eCSSProperty_UNKNOWN
michael@0 10642 };
michael@0 10643
michael@0 10644 // do this now, in case 4 values weren't specified
michael@0 10645 InitBoxPropsAsPhysical(kBorderStyleSources);
michael@0 10646 return ParseBoxProperties(kBorderStyleIDs);
michael@0 10647 }
michael@0 10648
michael@0 10649 bool
michael@0 10650 CSSParserImpl::ParseBorderWidth()
michael@0 10651 {
michael@0 10652 static const nsCSSProperty kBorderWidthSources[] = {
michael@0 10653 eCSSProperty_border_left_width_ltr_source,
michael@0 10654 eCSSProperty_border_left_width_rtl_source,
michael@0 10655 eCSSProperty_border_right_width_ltr_source,
michael@0 10656 eCSSProperty_border_right_width_rtl_source,
michael@0 10657 eCSSProperty_UNKNOWN
michael@0 10658 };
michael@0 10659
michael@0 10660 // do this now, in case 4 values weren't specified
michael@0 10661 InitBoxPropsAsPhysical(kBorderWidthSources);
michael@0 10662 return ParseBoxProperties(kBorderWidthIDs);
michael@0 10663 }
michael@0 10664
michael@0 10665 bool
michael@0 10666 CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty)
michael@0 10667 {
michael@0 10668 nsCSSValue value;
michael@0 10669 // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
michael@0 10670 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
michael@0 10671 nsCSSValueList *cur = value.SetListValue();
michael@0 10672 for (;;) {
michael@0 10673 if (!ParseVariant(cur->mValue, VARIANT_COLOR | VARIANT_KEYWORD,
michael@0 10674 nsCSSProps::kBorderColorKTable)) {
michael@0 10675 return false;
michael@0 10676 }
michael@0 10677 if (CheckEndProperty()) {
michael@0 10678 break;
michael@0 10679 }
michael@0 10680 cur->mNext = new nsCSSValueList;
michael@0 10681 cur = cur->mNext;
michael@0 10682 }
michael@0 10683 }
michael@0 10684 AppendValue(aProperty, value);
michael@0 10685 return true;
michael@0 10686 }
michael@0 10687
michael@0 10688 // Parse the top level of a calc() expression.
michael@0 10689 bool
michael@0 10690 CSSParserImpl::ParseCalc(nsCSSValue &aValue, int32_t aVariantMask)
michael@0 10691 {
michael@0 10692 // Parsing calc expressions requires, in a number of cases, looking
michael@0 10693 // for a token that is *either* a value of the property or a number.
michael@0 10694 // This can be done without lookahead when we assume that the property
michael@0 10695 // values cannot themselves be numbers.
michael@0 10696 NS_ASSERTION(!(aVariantMask & VARIANT_NUMBER), "unexpected variant mask");
michael@0 10697 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
michael@0 10698
michael@0 10699 bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk;
michael@0 10700 mUnitlessLengthQuirk = false;
michael@0 10701
michael@0 10702 // One-iteration loop so we can break to the error-handling case.
michael@0 10703 do {
michael@0 10704 // The toplevel of a calc() is always an nsCSSValue::Array of length 1.
michael@0 10705 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
michael@0 10706
michael@0 10707 if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask))
michael@0 10708 break;
michael@0 10709
michael@0 10710 if (!ExpectSymbol(')', true))
michael@0 10711 break;
michael@0 10712
michael@0 10713 aValue.SetArrayValue(arr, eCSSUnit_Calc);
michael@0 10714 mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
michael@0 10715 return true;
michael@0 10716 } while (false);
michael@0 10717
michael@0 10718 SkipUntil(')');
michael@0 10719 mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
michael@0 10720 return false;
michael@0 10721 }
michael@0 10722
michael@0 10723 // We optimize away the <value-expression> production given that
michael@0 10724 // ParseVariant consumes initial whitespace and we call
michael@0 10725 // ExpectSymbol(')') with true for aSkipWS.
michael@0 10726 // * If aVariantMask is VARIANT_NUMBER, this function parses the
michael@0 10727 // <number-additive-expression> production.
michael@0 10728 // * If aVariantMask does not contain VARIANT_NUMBER, this function
michael@0 10729 // parses the <value-additive-expression> production.
michael@0 10730 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
michael@0 10731 // whichever one of the productions matches ***and modifies
michael@0 10732 // aVariantMask*** to reflect which one it has parsed by either
michael@0 10733 // removing VARIANT_NUMBER or removing all other bits.
michael@0 10734 // It does so iteratively, but builds the correct recursive
michael@0 10735 // data structure.
michael@0 10736 bool
michael@0 10737 CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue,
michael@0 10738 int32_t& aVariantMask)
michael@0 10739 {
michael@0 10740 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
michael@0 10741 nsCSSValue *storage = &aValue;
michael@0 10742 for (;;) {
michael@0 10743 bool haveWS;
michael@0 10744 if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS))
michael@0 10745 return false;
michael@0 10746
michael@0 10747 if (!haveWS || !GetToken(false))
michael@0 10748 return true;
michael@0 10749 nsCSSUnit unit;
michael@0 10750 if (mToken.IsSymbol('+')) {
michael@0 10751 unit = eCSSUnit_Calc_Plus;
michael@0 10752 } else if (mToken.IsSymbol('-')) {
michael@0 10753 unit = eCSSUnit_Calc_Minus;
michael@0 10754 } else {
michael@0 10755 UngetToken();
michael@0 10756 return true;
michael@0 10757 }
michael@0 10758 if (!RequireWhitespace())
michael@0 10759 return false;
michael@0 10760
michael@0 10761 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
michael@0 10762 arr->Item(0) = aValue;
michael@0 10763 storage = &arr->Item(1);
michael@0 10764 aValue.SetArrayValue(arr, unit);
michael@0 10765 }
michael@0 10766 }
michael@0 10767
michael@0 10768 struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
michael@0 10769 public mozilla::css::CSSValueInputCalcOps
michael@0 10770 {
michael@0 10771 result_type ComputeLeafValue(const nsCSSValue& aValue)
michael@0 10772 {
michael@0 10773 NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
michael@0 10774 return aValue.GetFloatValue();
michael@0 10775 }
michael@0 10776
michael@0 10777 float ComputeNumber(const nsCSSValue& aValue)
michael@0 10778 {
michael@0 10779 return mozilla::css::ComputeCalc(aValue, *this);
michael@0 10780 }
michael@0 10781 };
michael@0 10782
michael@0 10783 // * If aVariantMask is VARIANT_NUMBER, this function parses the
michael@0 10784 // <number-multiplicative-expression> production.
michael@0 10785 // * If aVariantMask does not contain VARIANT_NUMBER, this function
michael@0 10786 // parses the <value-multiplicative-expression> production.
michael@0 10787 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
michael@0 10788 // whichever one of the productions matches ***and modifies
michael@0 10789 // aVariantMask*** to reflect which one it has parsed by either
michael@0 10790 // removing VARIANT_NUMBER or removing all other bits.
michael@0 10791 // It does so iteratively, but builds the correct recursive data
michael@0 10792 // structure.
michael@0 10793 // This function always consumes *trailing* whitespace when it returns
michael@0 10794 // true; whether there was any such whitespace is returned in the
michael@0 10795 // aHadFinalWS parameter.
michael@0 10796 bool
michael@0 10797 CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
michael@0 10798 int32_t& aVariantMask,
michael@0 10799 bool *aHadFinalWS)
michael@0 10800 {
michael@0 10801 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
michael@0 10802 bool gotValue = false; // already got the part with the unit
michael@0 10803 bool afterDivision = false;
michael@0 10804
michael@0 10805 nsCSSValue *storage = &aValue;
michael@0 10806 for (;;) {
michael@0 10807 int32_t variantMask;
michael@0 10808 if (afterDivision || gotValue) {
michael@0 10809 variantMask = VARIANT_NUMBER;
michael@0 10810 } else {
michael@0 10811 variantMask = aVariantMask | VARIANT_NUMBER;
michael@0 10812 }
michael@0 10813 if (!ParseCalcTerm(*storage, variantMask))
michael@0 10814 return false;
michael@0 10815 NS_ABORT_IF_FALSE(variantMask != 0,
michael@0 10816 "ParseCalcTerm did not set variantMask appropriately");
michael@0 10817 NS_ABORT_IF_FALSE(!(variantMask & VARIANT_NUMBER) ||
michael@0 10818 !(variantMask & ~int32_t(VARIANT_NUMBER)),
michael@0 10819 "ParseCalcTerm did not set variantMask appropriately");
michael@0 10820
michael@0 10821 if (variantMask & VARIANT_NUMBER) {
michael@0 10822 // Simplify the value immediately so we can check for division by
michael@0 10823 // zero.
michael@0 10824 ReduceNumberCalcOps ops;
michael@0 10825 float number = mozilla::css::ComputeCalc(*storage, ops);
michael@0 10826 if (number == 0.0 && afterDivision)
michael@0 10827 return false;
michael@0 10828 storage->SetFloatValue(number, eCSSUnit_Number);
michael@0 10829 } else {
michael@0 10830 gotValue = true;
michael@0 10831
michael@0 10832 if (storage != &aValue) {
michael@0 10833 // Simplify any numbers in the Times_L position (which are
michael@0 10834 // not simplified by the check above).
michael@0 10835 NS_ABORT_IF_FALSE(storage == &aValue.GetArrayValue()->Item(1),
michael@0 10836 "unexpected relationship to current storage");
michael@0 10837 nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
michael@0 10838 ReduceNumberCalcOps ops;
michael@0 10839 float number = mozilla::css::ComputeCalc(leftValue, ops);
michael@0 10840 leftValue.SetFloatValue(number, eCSSUnit_Number);
michael@0 10841 }
michael@0 10842 }
michael@0 10843
michael@0 10844 bool hadWS = RequireWhitespace();
michael@0 10845 if (!GetToken(false)) {
michael@0 10846 *aHadFinalWS = hadWS;
michael@0 10847 break;
michael@0 10848 }
michael@0 10849 nsCSSUnit unit;
michael@0 10850 if (mToken.IsSymbol('*')) {
michael@0 10851 unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
michael@0 10852 afterDivision = false;
michael@0 10853 } else if (mToken.IsSymbol('/')) {
michael@0 10854 unit = eCSSUnit_Calc_Divided;
michael@0 10855 afterDivision = true;
michael@0 10856 } else {
michael@0 10857 UngetToken();
michael@0 10858 *aHadFinalWS = hadWS;
michael@0 10859 break;
michael@0 10860 }
michael@0 10861
michael@0 10862 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(2);
michael@0 10863 arr->Item(0) = aValue;
michael@0 10864 storage = &arr->Item(1);
michael@0 10865 aValue.SetArrayValue(arr, unit);
michael@0 10866 }
michael@0 10867
michael@0 10868 // Adjust aVariantMask (see comments above function) to reflect which
michael@0 10869 // option we took.
michael@0 10870 if (aVariantMask & VARIANT_NUMBER) {
michael@0 10871 if (gotValue) {
michael@0 10872 aVariantMask &= ~int32_t(VARIANT_NUMBER);
michael@0 10873 } else {
michael@0 10874 aVariantMask = VARIANT_NUMBER;
michael@0 10875 }
michael@0 10876 } else {
michael@0 10877 if (!gotValue) {
michael@0 10878 // We had to find a value, but we didn't.
michael@0 10879 return false;
michael@0 10880 }
michael@0 10881 }
michael@0 10882
michael@0 10883 return true;
michael@0 10884 }
michael@0 10885
michael@0 10886 // * If aVariantMask is VARIANT_NUMBER, this function parses the
michael@0 10887 // <number-term> production.
michael@0 10888 // * If aVariantMask does not contain VARIANT_NUMBER, this function
michael@0 10889 // parses the <value-term> production.
michael@0 10890 // * Otherwise (VARIANT_NUMBER and other bits) this function parses
michael@0 10891 // whichever one of the productions matches ***and modifies
michael@0 10892 // aVariantMask*** to reflect which one it has parsed by either
michael@0 10893 // removing VARIANT_NUMBER or removing all other bits.
michael@0 10894 bool
michael@0 10895 CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask)
michael@0 10896 {
michael@0 10897 NS_ABORT_IF_FALSE(aVariantMask != 0, "unexpected variant mask");
michael@0 10898 if (!GetToken(true))
michael@0 10899 return false;
michael@0 10900 // Either an additive expression in parentheses...
michael@0 10901 if (mToken.IsSymbol('(')) {
michael@0 10902 if (!ParseCalcAdditiveExpression(aValue, aVariantMask) ||
michael@0 10903 !ExpectSymbol(')', true)) {
michael@0 10904 SkipUntil(')');
michael@0 10905 return false;
michael@0 10906 }
michael@0 10907 return true;
michael@0 10908 }
michael@0 10909 // ... or just a value
michael@0 10910 UngetToken();
michael@0 10911 // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
michael@0 10912 // always gets picked up
michael@0 10913 if (!ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr)) {
michael@0 10914 return false;
michael@0 10915 }
michael@0 10916 // ...and do the VARIANT_NUMBER check ourselves.
michael@0 10917 if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
michael@0 10918 return false;
michael@0 10919 }
michael@0 10920 // If we did the value parsing, we need to adjust aVariantMask to
michael@0 10921 // reflect which option we took (see above).
michael@0 10922 if (aVariantMask & VARIANT_NUMBER) {
michael@0 10923 if (aValue.GetUnit() == eCSSUnit_Number) {
michael@0 10924 aVariantMask = VARIANT_NUMBER;
michael@0 10925 } else {
michael@0 10926 aVariantMask &= ~int32_t(VARIANT_NUMBER);
michael@0 10927 }
michael@0 10928 }
michael@0 10929 return true;
michael@0 10930 }
michael@0 10931
michael@0 10932 // This function consumes all consecutive whitespace and returns whether
michael@0 10933 // there was any.
michael@0 10934 bool
michael@0 10935 CSSParserImpl::RequireWhitespace()
michael@0 10936 {
michael@0 10937 if (!GetToken(false))
michael@0 10938 return false;
michael@0 10939 if (mToken.mType != eCSSToken_Whitespace) {
michael@0 10940 UngetToken();
michael@0 10941 return false;
michael@0 10942 }
michael@0 10943 // Skip any additional whitespace tokens.
michael@0 10944 if (GetToken(true)) {
michael@0 10945 UngetToken();
michael@0 10946 }
michael@0 10947 return true;
michael@0 10948 }
michael@0 10949
michael@0 10950 bool
michael@0 10951 CSSParserImpl::ParseRect(nsCSSProperty aPropID)
michael@0 10952 {
michael@0 10953 nsCSSValue val;
michael@0 10954 if (ParseVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) {
michael@0 10955 AppendValue(aPropID, val);
michael@0 10956 return true;
michael@0 10957 }
michael@0 10958
michael@0 10959 if (! GetToken(true)) {
michael@0 10960 return false;
michael@0 10961 }
michael@0 10962
michael@0 10963 if (mToken.mType == eCSSToken_Function &&
michael@0 10964 mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
michael@0 10965 nsCSSRect& rect = val.SetRectValue();
michael@0 10966 bool useCommas;
michael@0 10967 NS_FOR_CSS_SIDES(side) {
michael@0 10968 if (! ParseVariant(rect.*(nsCSSRect::sides[side]),
michael@0 10969 VARIANT_AL, nullptr)) {
michael@0 10970 return false;
michael@0 10971 }
michael@0 10972 if (side == 0) {
michael@0 10973 useCommas = ExpectSymbol(',', true);
michael@0 10974 } else if (useCommas && side < 3) {
michael@0 10975 // Skip optional commas between elements, but only if the first
michael@0 10976 // separator was a comma.
michael@0 10977 if (!ExpectSymbol(',', true)) {
michael@0 10978 return false;
michael@0 10979 }
michael@0 10980 }
michael@0 10981 }
michael@0 10982 if (!ExpectSymbol(')', true)) {
michael@0 10983 return false;
michael@0 10984 }
michael@0 10985 } else {
michael@0 10986 UngetToken();
michael@0 10987 return false;
michael@0 10988 }
michael@0 10989
michael@0 10990 AppendValue(aPropID, val);
michael@0 10991 return true;
michael@0 10992 }
michael@0 10993
michael@0 10994 bool
michael@0 10995 CSSParserImpl::ParseColumns()
michael@0 10996 {
michael@0 10997 // We use a similar "fake value" hack to ParseListStyle, because
michael@0 10998 // "auto" is acceptable for both column-count and column-width.
michael@0 10999 // If the fake "auto" value is found, and one of the real values isn't,
michael@0 11000 // that means the fake auto value is meant for the real value we didn't
michael@0 11001 // find.
michael@0 11002 static const nsCSSProperty columnIDs[] = {
michael@0 11003 eCSSPropertyExtra_x_auto_value,
michael@0 11004 eCSSProperty__moz_column_count,
michael@0 11005 eCSSProperty__moz_column_width
michael@0 11006 };
michael@0 11007 const int32_t numProps = MOZ_ARRAY_LENGTH(columnIDs);
michael@0 11008
michael@0 11009 nsCSSValue values[numProps];
michael@0 11010 int32_t found = ParseChoice(values, columnIDs, numProps);
michael@0 11011 if (found < 1) {
michael@0 11012 return false;
michael@0 11013 }
michael@0 11014 if ((found & (1|2|4)) == (1|2|4) &&
michael@0 11015 values[0].GetUnit() == eCSSUnit_Auto) {
michael@0 11016 // We filled all 3 values, which is invalid
michael@0 11017 return false;
michael@0 11018 }
michael@0 11019
michael@0 11020 if ((found & 2) == 0) {
michael@0 11021 // Provide auto column-count
michael@0 11022 values[1].SetAutoValue();
michael@0 11023 }
michael@0 11024 if ((found & 4) == 0) {
michael@0 11025 // Provide auto column-width
michael@0 11026 values[2].SetAutoValue();
michael@0 11027 }
michael@0 11028
michael@0 11029 // Start at index 1 to skip the fake auto value.
michael@0 11030 for (int32_t index = 1; index < numProps; index++) {
michael@0 11031 AppendValue(columnIDs[index], values[index]);
michael@0 11032 }
michael@0 11033 return true;
michael@0 11034 }
michael@0 11035
michael@0 11036 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
michael@0 11037 VARIANT_KEYWORD)
michael@0 11038 bool
michael@0 11039 CSSParserImpl::ParseContent()
michael@0 11040 {
michael@0 11041 // We need to divide the 'content' keywords into two classes for
michael@0 11042 // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable.
michael@0 11043 static const KTableValue kContentListKWs[] = {
michael@0 11044 eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE,
michael@0 11045 eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE,
michael@0 11046 eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE,
michael@0 11047 eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE,
michael@0 11048 eCSSKeyword_UNKNOWN,-1
michael@0 11049 };
michael@0 11050
michael@0 11051 static const KTableValue kContentSolitaryKWs[] = {
michael@0 11052 eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT,
michael@0 11053 eCSSKeyword_UNKNOWN,-1
michael@0 11054 };
michael@0 11055
michael@0 11056 // Verify that these two lists add up to the size of
michael@0 11057 // nsCSSProps::kContentKTable.
michael@0 11058 NS_ABORT_IF_FALSE(nsCSSProps::kContentKTable[
michael@0 11059 ArrayLength(kContentListKWs) +
michael@0 11060 ArrayLength(kContentSolitaryKWs) - 4] ==
michael@0 11061 eCSSKeyword_UNKNOWN &&
michael@0 11062 nsCSSProps::kContentKTable[
michael@0 11063 ArrayLength(kContentListKWs) +
michael@0 11064 ArrayLength(kContentSolitaryKWs) - 3] == -1,
michael@0 11065 "content keyword tables out of sync");
michael@0 11066
michael@0 11067 nsCSSValue value;
michael@0 11068 // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must
michael@0 11069 // be alone
michael@0 11070 if (!ParseVariant(value, VARIANT_HMK | VARIANT_NONE,
michael@0 11071 kContentSolitaryKWs)) {
michael@0 11072 nsCSSValueList* cur = value.SetListValue();
michael@0 11073 for (;;) {
michael@0 11074 if (!ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs)) {
michael@0 11075 return false;
michael@0 11076 }
michael@0 11077 if (CheckEndProperty()) {
michael@0 11078 break;
michael@0 11079 }
michael@0 11080 cur->mNext = new nsCSSValueList;
michael@0 11081 cur = cur->mNext;
michael@0 11082 }
michael@0 11083 }
michael@0 11084 AppendValue(eCSSProperty_content, value);
michael@0 11085 return true;
michael@0 11086 }
michael@0 11087
michael@0 11088 bool
michael@0 11089 CSSParserImpl::ParseCounterData(nsCSSProperty aPropID)
michael@0 11090 {
michael@0 11091 static const nsCSSKeyword kCounterDataKTable[] = {
michael@0 11092 eCSSKeyword_none,
michael@0 11093 eCSSKeyword_UNKNOWN
michael@0 11094 };
michael@0 11095 nsCSSValue value;
michael@0 11096 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
michael@0 11097 if (!GetToken(true)) {
michael@0 11098 return false;
michael@0 11099 }
michael@0 11100 if (mToken.mType != eCSSToken_Ident) {
michael@0 11101 UngetToken();
michael@0 11102 return false;
michael@0 11103 }
michael@0 11104
michael@0 11105 nsCSSValuePairList *cur = value.SetPairListValue();
michael@0 11106 for (;;) {
michael@0 11107 if (!ParseCustomIdent(cur->mXValue, mToken.mIdent, kCounterDataKTable)) {
michael@0 11108 return false;
michael@0 11109 }
michael@0 11110 if (!GetToken(true)) {
michael@0 11111 break;
michael@0 11112 }
michael@0 11113 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
michael@0 11114 cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
michael@0 11115 } else {
michael@0 11116 UngetToken();
michael@0 11117 }
michael@0 11118 if (!GetToken(true)) {
michael@0 11119 break;
michael@0 11120 }
michael@0 11121 if (mToken.mType != eCSSToken_Ident) {
michael@0 11122 UngetToken();
michael@0 11123 break;
michael@0 11124 }
michael@0 11125 cur->mNext = new nsCSSValuePairList;
michael@0 11126 cur = cur->mNext;
michael@0 11127 }
michael@0 11128 }
michael@0 11129 AppendValue(aPropID, value);
michael@0 11130 return true;
michael@0 11131 }
michael@0 11132
michael@0 11133 bool
michael@0 11134 CSSParserImpl::ParseCursor()
michael@0 11135 {
michael@0 11136 nsCSSValue value;
michael@0 11137 // 'inherit', 'initial' and 'unset' must be alone
michael@0 11138 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 11139 nsCSSValueList* cur = value.SetListValue();
michael@0 11140 for (;;) {
michael@0 11141 if (!ParseVariant(cur->mValue, VARIANT_UK, nsCSSProps::kCursorKTable)) {
michael@0 11142 return false;
michael@0 11143 }
michael@0 11144 if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last
michael@0 11145 break;
michael@0 11146 }
michael@0 11147
michael@0 11148 // We have a URL, so make a value array with three values.
michael@0 11149 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
michael@0 11150 val->Item(0) = cur->mValue;
michael@0 11151
michael@0 11152 // Parse optional x and y position of cursor hotspot (css3-ui).
michael@0 11153 if (ParseVariant(val->Item(1), VARIANT_NUMBER, nullptr)) {
michael@0 11154 // If we have one number, we must have two.
michael@0 11155 if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nullptr)) {
michael@0 11156 return false;
michael@0 11157 }
michael@0 11158 }
michael@0 11159 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
michael@0 11160
michael@0 11161 if (!ExpectSymbol(',', true)) { // url must not be last
michael@0 11162 return false;
michael@0 11163 }
michael@0 11164 cur->mNext = new nsCSSValueList;
michael@0 11165 cur = cur->mNext;
michael@0 11166 }
michael@0 11167 }
michael@0 11168 AppendValue(eCSSProperty_cursor, value);
michael@0 11169 return true;
michael@0 11170 }
michael@0 11171
michael@0 11172
michael@0 11173 bool
michael@0 11174 CSSParserImpl::ParseFont()
michael@0 11175 {
michael@0 11176 static const nsCSSProperty fontIDs[] = {
michael@0 11177 eCSSProperty_font_style,
michael@0 11178 eCSSProperty_font_variant,
michael@0 11179 eCSSProperty_font_weight
michael@0 11180 };
michael@0 11181
michael@0 11182 // font-variant-alternates enabled ==> layout.css.font-features.enabled is true
michael@0 11183 bool featuresEnabled =
michael@0 11184 nsCSSProps::IsEnabled(eCSSProperty_font_variant_alternates);
michael@0 11185 nsCSSValue family;
michael@0 11186 if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
michael@0 11187 if (eCSSUnit_Inherit == family.GetUnit() ||
michael@0 11188 eCSSUnit_Initial == family.GetUnit() ||
michael@0 11189 eCSSUnit_Unset == family.GetUnit()) {
michael@0 11190 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
michael@0 11191 AppendValue(eCSSProperty_font_family, family);
michael@0 11192 AppendValue(eCSSProperty_font_style, family);
michael@0 11193 AppendValue(eCSSProperty_font_variant, family);
michael@0 11194 AppendValue(eCSSProperty_font_weight, family);
michael@0 11195 AppendValue(eCSSProperty_font_size, family);
michael@0 11196 AppendValue(eCSSProperty_line_height, family);
michael@0 11197 AppendValue(eCSSProperty_font_stretch, family);
michael@0 11198 AppendValue(eCSSProperty_font_size_adjust, family);
michael@0 11199 AppendValue(eCSSProperty_font_feature_settings, family);
michael@0 11200 AppendValue(eCSSProperty_font_language_override, family);
michael@0 11201 if (featuresEnabled) {
michael@0 11202 AppendValue(eCSSProperty_font_kerning, family);
michael@0 11203 AppendValue(eCSSProperty_font_synthesis, family);
michael@0 11204 AppendValue(eCSSProperty_font_variant_alternates, family);
michael@0 11205 AppendValue(eCSSProperty_font_variant_caps, family);
michael@0 11206 AppendValue(eCSSProperty_font_variant_east_asian, family);
michael@0 11207 AppendValue(eCSSProperty_font_variant_ligatures, family);
michael@0 11208 AppendValue(eCSSProperty_font_variant_numeric, family);
michael@0 11209 AppendValue(eCSSProperty_font_variant_position, family);
michael@0 11210 }
michael@0 11211 }
michael@0 11212 else {
michael@0 11213 AppendValue(eCSSProperty__x_system_font, family);
michael@0 11214 nsCSSValue systemFont(eCSSUnit_System_Font);
michael@0 11215 AppendValue(eCSSProperty_font_family, systemFont);
michael@0 11216 AppendValue(eCSSProperty_font_style, systemFont);
michael@0 11217 AppendValue(eCSSProperty_font_variant, systemFont);
michael@0 11218 AppendValue(eCSSProperty_font_weight, systemFont);
michael@0 11219 AppendValue(eCSSProperty_font_size, systemFont);
michael@0 11220 AppendValue(eCSSProperty_line_height, systemFont);
michael@0 11221 AppendValue(eCSSProperty_font_stretch, systemFont);
michael@0 11222 AppendValue(eCSSProperty_font_size_adjust, systemFont);
michael@0 11223 AppendValue(eCSSProperty_font_feature_settings, systemFont);
michael@0 11224 AppendValue(eCSSProperty_font_language_override, systemFont);
michael@0 11225 if (featuresEnabled) {
michael@0 11226 AppendValue(eCSSProperty_font_kerning, systemFont);
michael@0 11227 AppendValue(eCSSProperty_font_synthesis, systemFont);
michael@0 11228 AppendValue(eCSSProperty_font_variant_alternates, systemFont);
michael@0 11229 AppendValue(eCSSProperty_font_variant_caps, systemFont);
michael@0 11230 AppendValue(eCSSProperty_font_variant_east_asian, systemFont);
michael@0 11231 AppendValue(eCSSProperty_font_variant_ligatures, systemFont);
michael@0 11232 AppendValue(eCSSProperty_font_variant_numeric, systemFont);
michael@0 11233 AppendValue(eCSSProperty_font_variant_position, systemFont);
michael@0 11234 }
michael@0 11235 }
michael@0 11236 return true;
michael@0 11237 }
michael@0 11238
michael@0 11239 // Get optional font-style, font-variant and font-weight (in any order)
michael@0 11240 const int32_t numProps = 3;
michael@0 11241 nsCSSValue values[numProps];
michael@0 11242 int32_t found = ParseChoice(values, fontIDs, numProps);
michael@0 11243 if (found < 0 ||
michael@0 11244 eCSSUnit_Inherit == values[0].GetUnit() ||
michael@0 11245 eCSSUnit_Initial == values[0].GetUnit() ||
michael@0 11246 eCSSUnit_Unset == values[0].GetUnit()) { // illegal data
michael@0 11247 return false;
michael@0 11248 }
michael@0 11249 if ((found & 1) == 0) {
michael@0 11250 // Provide default font-style
michael@0 11251 values[0].SetIntValue(NS_FONT_STYLE_NORMAL, eCSSUnit_Enumerated);
michael@0 11252 }
michael@0 11253 if ((found & 2) == 0) {
michael@0 11254 // Provide default font-variant
michael@0 11255 values[1].SetIntValue(NS_FONT_VARIANT_NORMAL, eCSSUnit_Enumerated);
michael@0 11256 }
michael@0 11257 if ((found & 4) == 0) {
michael@0 11258 // Provide default font-weight
michael@0 11259 values[2].SetIntValue(NS_FONT_WEIGHT_NORMAL, eCSSUnit_Enumerated);
michael@0 11260 }
michael@0 11261
michael@0 11262 // Get mandatory font-size
michael@0 11263 nsCSSValue size;
michael@0 11264 if (! ParseNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP,
michael@0 11265 nsCSSProps::kFontSizeKTable)) {
michael@0 11266 return false;
michael@0 11267 }
michael@0 11268
michael@0 11269 // Get optional "/" line-height
michael@0 11270 nsCSSValue lineHeight;
michael@0 11271 if (ExpectSymbol('/', true)) {
michael@0 11272 if (! ParseNonNegativeVariant(lineHeight,
michael@0 11273 VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
michael@0 11274 nullptr)) {
michael@0 11275 return false;
michael@0 11276 }
michael@0 11277 }
michael@0 11278 else {
michael@0 11279 lineHeight.SetNormalValue();
michael@0 11280 }
michael@0 11281
michael@0 11282 // Get final mandatory font-family
michael@0 11283 nsAutoParseCompoundProperty compound(this);
michael@0 11284 if (ParseFamily(family)) {
michael@0 11285 if (eCSSUnit_Inherit != family.GetUnit() &&
michael@0 11286 eCSSUnit_Initial != family.GetUnit() &&
michael@0 11287 eCSSUnit_Unset != family.GetUnit()) {
michael@0 11288 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
michael@0 11289 AppendValue(eCSSProperty_font_family, family);
michael@0 11290 AppendValue(eCSSProperty_font_style, values[0]);
michael@0 11291 AppendValue(eCSSProperty_font_variant, values[1]);
michael@0 11292 AppendValue(eCSSProperty_font_weight, values[2]);
michael@0 11293 AppendValue(eCSSProperty_font_size, size);
michael@0 11294 AppendValue(eCSSProperty_line_height, lineHeight);
michael@0 11295 AppendValue(eCSSProperty_font_stretch,
michael@0 11296 nsCSSValue(NS_FONT_STRETCH_NORMAL, eCSSUnit_Enumerated));
michael@0 11297 AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
michael@0 11298 AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal));
michael@0 11299 AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal));
michael@0 11300 if (featuresEnabled) {
michael@0 11301 AppendValue(eCSSProperty_font_kerning,
michael@0 11302 nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated));
michael@0 11303 AppendValue(eCSSProperty_font_synthesis,
michael@0 11304 nsCSSValue(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE,
michael@0 11305 eCSSUnit_Enumerated));
michael@0 11306 AppendValue(eCSSProperty_font_variant_alternates,
michael@0 11307 nsCSSValue(eCSSUnit_Normal));
michael@0 11308 AppendValue(eCSSProperty_font_variant_caps, nsCSSValue(eCSSUnit_Normal));
michael@0 11309 AppendValue(eCSSProperty_font_variant_east_asian,
michael@0 11310 nsCSSValue(eCSSUnit_Normal));
michael@0 11311 AppendValue(eCSSProperty_font_variant_ligatures,
michael@0 11312 nsCSSValue(eCSSUnit_Normal));
michael@0 11313 AppendValue(eCSSProperty_font_variant_numeric,
michael@0 11314 nsCSSValue(eCSSUnit_Normal));
michael@0 11315 AppendValue(eCSSProperty_font_variant_position,
michael@0 11316 nsCSSValue(eCSSUnit_Normal));
michael@0 11317 }
michael@0 11318 return true;
michael@0 11319 }
michael@0 11320 }
michael@0 11321 return false;
michael@0 11322 }
michael@0 11323
michael@0 11324 bool
michael@0 11325 CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue)
michael@0 11326 {
michael@0 11327 if (!ParseVariant(aValue, VARIANT_HK | VARIANT_NONE,
michael@0 11328 nsCSSProps::kFontSynthesisKTable)) {
michael@0 11329 return false;
michael@0 11330 }
michael@0 11331
michael@0 11332 // first value 'none' ==> done
michael@0 11333 if (eCSSUnit_None == aValue.GetUnit() ||
michael@0 11334 eCSSUnit_Initial == aValue.GetUnit() ||
michael@0 11335 eCSSUnit_Inherit == aValue.GetUnit() ||
michael@0 11336 eCSSUnit_Unset == aValue.GetUnit())
michael@0 11337 {
michael@0 11338 return true;
michael@0 11339 }
michael@0 11340
michael@0 11341 // look for a second value
michael@0 11342 int32_t intValue = aValue.GetIntValue();
michael@0 11343 nsCSSValue nextValue;
michael@0 11344
michael@0 11345 if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) {
michael@0 11346 int32_t nextIntValue = nextValue.GetIntValue();
michael@0 11347 if (nextIntValue & intValue) {
michael@0 11348 return false;
michael@0 11349 }
michael@0 11350 aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
michael@0 11351 }
michael@0 11352
michael@0 11353 return true;
michael@0 11354 }
michael@0 11355
michael@0 11356 // font-variant-alternates allows for a combination of multiple
michael@0 11357 // simple enumerated values and functional values. Functional values have
michael@0 11358 // parameter lists with one or more idents which are later resolved
michael@0 11359 // based on values defined in @font-feature-value rules.
michael@0 11360 //
michael@0 11361 // font-variant-alternates: swash(flowing), historical-forms, styleset(alt-g, alt-m);
michael@0 11362 //
michael@0 11363 // So for this the nsCSSValue is set to a pair value, with one
michael@0 11364 // value for a bitmask of both simple and functional property values
michael@0 11365 // and another value containing a ValuePairList with lists of idents
michael@0 11366 // for each functional property value.
michael@0 11367 //
michael@0 11368 // pairValue
michael@0 11369 // o intValue
michael@0 11370 // NS_FONT_VARIANT_ALTERNATES_SWASH |
michael@0 11371 // NS_FONT_VARIANT_ALTERNATES_STYLESET
michael@0 11372 // o valuePairList, each element with
michael@0 11373 // - intValue - indicates which alternate
michael@0 11374 // - string or valueList of strings
michael@0 11375 //
michael@0 11376 // Note: when only 'historical-forms' is specified, there are no
michael@0 11377 // functional values to store, in which case the valuePairList is a
michael@0 11378 // single element dummy list. In all other cases, the length of the
michael@0 11379 // list will match the number of functional values.
michael@0 11380
michael@0 11381 #define MAX_ALLOWED_FEATURES 512
michael@0 11382
michael@0 11383 bool
michael@0 11384 CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature,
michael@0 11385 nsCSSValue& aValue)
michael@0 11386 {
michael@0 11387 if (!GetToken(true)) {
michael@0 11388 return false;
michael@0 11389 }
michael@0 11390
michael@0 11391 bool isIdent = (mToken.mType == eCSSToken_Ident);
michael@0 11392 if (mToken.mType != eCSSToken_Function && !isIdent) {
michael@0 11393 UngetToken();
michael@0 11394 return false;
michael@0 11395 }
michael@0 11396
michael@0 11397 // ident ==> simple enumerated prop val (e.g. historical-forms)
michael@0 11398 // function ==> e.g. swash(flowing) styleset(alt-g, alt-m)
michael@0 11399
michael@0 11400 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
michael@0 11401 if (!(eCSSKeyword_UNKNOWN < keyword &&
michael@0 11402 nsCSSProps::FindKeyword(keyword,
michael@0 11403 (isIdent ?
michael@0 11404 nsCSSProps::kFontVariantAlternatesKTable :
michael@0 11405 nsCSSProps::kFontVariantAlternatesFuncsKTable),
michael@0 11406 aWhichFeature)))
michael@0 11407 {
michael@0 11408 // failed, pop token
michael@0 11409 UngetToken();
michael@0 11410 return false;
michael@0 11411 }
michael@0 11412
michael@0 11413 if (isIdent) {
michael@0 11414 aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated);
michael@0 11415 return true;
michael@0 11416 }
michael@0 11417
michael@0 11418 uint16_t maxElems = 1;
michael@0 11419 if (keyword == eCSSKeyword_styleset ||
michael@0 11420 keyword == eCSSKeyword_character_variant) {
michael@0 11421 maxElems = MAX_ALLOWED_FEATURES;
michael@0 11422 }
michael@0 11423 return ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER,
michael@0 11424 1, maxElems, aValue);
michael@0 11425 }
michael@0 11426
michael@0 11427 bool
michael@0 11428 CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue)
michael@0 11429 {
michael@0 11430 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
michael@0 11431 return true;
michael@0 11432 }
michael@0 11433
michael@0 11434 // iterate through parameters
michael@0 11435 nsCSSValue listValue;
michael@0 11436 int32_t feature, featureFlags = 0;
michael@0 11437
michael@0 11438 // if no functional values, this may be a list with a single, unused element
michael@0 11439 listValue.SetListValue();
michael@0 11440
michael@0 11441 nsCSSValueList* list = nullptr;
michael@0 11442 nsCSSValue value;
michael@0 11443 while (ParseSingleAlternate(feature, value)) {
michael@0 11444
michael@0 11445 // check to make sure value not already set
michael@0 11446 if (feature == 0 ||
michael@0 11447 feature & featureFlags) {
michael@0 11448 return false;
michael@0 11449 }
michael@0 11450
michael@0 11451 featureFlags |= feature;
michael@0 11452
michael@0 11453 // if function, need to add to the list of functions
michael@0 11454 if (value.GetUnit() == eCSSUnit_Function) {
michael@0 11455 if (!list) {
michael@0 11456 list = listValue.GetListValue();
michael@0 11457 } else {
michael@0 11458 list->mNext = new nsCSSValueList;
michael@0 11459 list = list->mNext;
michael@0 11460 }
michael@0 11461 list->mValue = value;
michael@0 11462 }
michael@0 11463 }
michael@0 11464
michael@0 11465 if (featureFlags == 0) {
michael@0 11466 // ParseSingleAlternate failed the first time through the loop.
michael@0 11467 return false;
michael@0 11468 }
michael@0 11469
michael@0 11470 nsCSSValue featureValue;
michael@0 11471 featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated);
michael@0 11472 aValue.SetPairValue(featureValue, listValue);
michael@0 11473
michael@0 11474 return true;
michael@0 11475 }
michael@0 11476
michael@0 11477 // aMasks - array of masks for mutually-exclusive property values,
michael@0 11478 // e.g. proportial-nums, tabular-nums
michael@0 11479
michael@0 11480 bool
michael@0 11481 CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue,
michael@0 11482 const KTableValue aKeywordTable[],
michael@0 11483 const int32_t aMasks[])
michael@0 11484 {
michael@0 11485 // Parse at least one keyword
michael@0 11486 if (!ParseEnum(aValue, aKeywordTable)) {
michael@0 11487 return false;
michael@0 11488 }
michael@0 11489
michael@0 11490 // look for more values
michael@0 11491 nsCSSValue nextValue;
michael@0 11492 int32_t mergedValue = aValue.GetIntValue();
michael@0 11493
michael@0 11494 while (ParseEnum(nextValue, aKeywordTable))
michael@0 11495 {
michael@0 11496 int32_t nextIntValue = nextValue.GetIntValue();
michael@0 11497
michael@0 11498 // check to make sure value not already set
michael@0 11499 if (nextIntValue & mergedValue) {
michael@0 11500 return false;
michael@0 11501 }
michael@0 11502
michael@0 11503 const int32_t *m = aMasks;
michael@0 11504 int32_t c = 0;
michael@0 11505
michael@0 11506 while (*m != MASK_END_VALUE) {
michael@0 11507 if (*m & nextIntValue) {
michael@0 11508 c = mergedValue & *m;
michael@0 11509 break;
michael@0 11510 }
michael@0 11511 m++;
michael@0 11512 }
michael@0 11513
michael@0 11514 if (c) {
michael@0 11515 return false;
michael@0 11516 }
michael@0 11517
michael@0 11518 mergedValue |= nextIntValue;
michael@0 11519 }
michael@0 11520
michael@0 11521 aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated);
michael@0 11522
michael@0 11523 return true;
michael@0 11524 }
michael@0 11525
michael@0 11526 static const int32_t maskEastAsian[] = {
michael@0 11527 NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK,
michael@0 11528 NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK,
michael@0 11529 MASK_END_VALUE
michael@0 11530 };
michael@0 11531
michael@0 11532 bool
michael@0 11533 CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue)
michael@0 11534 {
michael@0 11535 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
michael@0 11536 return true;
michael@0 11537 }
michael@0 11538
michael@0 11539 NS_ASSERTION(maskEastAsian[ArrayLength(maskEastAsian) - 1] ==
michael@0 11540 MASK_END_VALUE,
michael@0 11541 "incorrectly terminated array");
michael@0 11542
michael@0 11543 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable,
michael@0 11544 maskEastAsian);
michael@0 11545 }
michael@0 11546
michael@0 11547 static const int32_t maskLigatures[] = {
michael@0 11548 NS_FONT_VARIANT_LIGATURES_COMMON_MASK,
michael@0 11549 NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK,
michael@0 11550 NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK,
michael@0 11551 NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK,
michael@0 11552 MASK_END_VALUE
michael@0 11553 };
michael@0 11554
michael@0 11555 bool
michael@0 11556 CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue)
michael@0 11557 {
michael@0 11558 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
michael@0 11559 return true;
michael@0 11560 }
michael@0 11561
michael@0 11562 NS_ASSERTION(maskLigatures[ArrayLength(maskLigatures) - 1] ==
michael@0 11563 MASK_END_VALUE,
michael@0 11564 "incorrectly terminated array");
michael@0 11565
michael@0 11566 bool parsed =
michael@0 11567 ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable,
michael@0 11568 maskLigatures);
michael@0 11569
michael@0 11570 // if none value included, no other values are possible
michael@0 11571 if (parsed && eCSSUnit_Enumerated == aValue.GetUnit()) {
michael@0 11572 int32_t val = aValue.GetIntValue();
michael@0 11573 if ((val & NS_FONT_VARIANT_LIGATURES_NONE) &&
michael@0 11574 (val & ~int32_t(NS_FONT_VARIANT_LIGATURES_NONE))) {
michael@0 11575 parsed = false;
michael@0 11576 }
michael@0 11577 }
michael@0 11578
michael@0 11579 return parsed;
michael@0 11580 }
michael@0 11581
michael@0 11582 static const int32_t maskNumeric[] = {
michael@0 11583 NS_FONT_VARIANT_NUMERIC_FIGURE_MASK,
michael@0 11584 NS_FONT_VARIANT_NUMERIC_SPACING_MASK,
michael@0 11585 NS_FONT_VARIANT_NUMERIC_FRACTION_MASK,
michael@0 11586 MASK_END_VALUE
michael@0 11587 };
michael@0 11588
michael@0 11589 bool
michael@0 11590 CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue)
michael@0 11591 {
michael@0 11592 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
michael@0 11593 return true;
michael@0 11594 }
michael@0 11595
michael@0 11596 NS_ASSERTION(maskNumeric[ArrayLength(maskNumeric) - 1] ==
michael@0 11597 MASK_END_VALUE,
michael@0 11598 "incorrectly terminated array");
michael@0 11599
michael@0 11600 return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable,
michael@0 11601 maskNumeric);
michael@0 11602 }
michael@0 11603
michael@0 11604 bool
michael@0 11605 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
michael@0 11606 {
michael@0 11607 if (ParseVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT,
michael@0 11608 nsCSSProps::kFontWeightKTable)) {
michael@0 11609 if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
michael@0 11610 int32_t intValue = aValue.GetIntValue();
michael@0 11611 if ((100 <= intValue) &&
michael@0 11612 (intValue <= 900) &&
michael@0 11613 (0 == (intValue % 100))) {
michael@0 11614 return true;
michael@0 11615 } else {
michael@0 11616 UngetToken();
michael@0 11617 return false;
michael@0 11618 }
michael@0 11619 }
michael@0 11620 return true;
michael@0 11621 }
michael@0 11622 return false;
michael@0 11623 }
michael@0 11624
michael@0 11625 bool
michael@0 11626 CSSParserImpl::ParseOneFamily(nsAString& aFamily, bool& aOneKeyword)
michael@0 11627 {
michael@0 11628 if (!GetToken(true))
michael@0 11629 return false;
michael@0 11630
michael@0 11631 nsCSSToken* tk = &mToken;
michael@0 11632
michael@0 11633 aOneKeyword = false;
michael@0 11634 if (eCSSToken_Ident == tk->mType) {
michael@0 11635 aOneKeyword = true;
michael@0 11636 aFamily.Append(tk->mIdent);
michael@0 11637 for (;;) {
michael@0 11638 if (!GetToken(false))
michael@0 11639 break;
michael@0 11640
michael@0 11641 if (eCSSToken_Ident == tk->mType) {
michael@0 11642 aOneKeyword = false;
michael@0 11643 // We had at least another keyword before.
michael@0 11644 // "If a sequence of identifiers is given as a font family name,
michael@0 11645 // the computed value is the name converted to a string by joining
michael@0 11646 // all the identifiers in the sequence by single spaces."
michael@0 11647 // -- CSS 2.1, section 15.3
michael@0 11648 // Whitespace tokens do not actually matter,
michael@0 11649 // identifier tokens can be separated by comments.
michael@0 11650 aFamily.Append(char16_t(' '));
michael@0 11651 aFamily.Append(tk->mIdent);
michael@0 11652 } else if (eCSSToken_Whitespace != tk->mType) {
michael@0 11653 UngetToken();
michael@0 11654 break;
michael@0 11655 }
michael@0 11656 }
michael@0 11657 return true;
michael@0 11658
michael@0 11659 } else if (eCSSToken_String == tk->mType) {
michael@0 11660 aFamily.Append(tk->mSymbol); // replace the quotes
michael@0 11661 aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
michael@0 11662 aFamily.Append(tk->mSymbol);
michael@0 11663 return true;
michael@0 11664
michael@0 11665 } else {
michael@0 11666 UngetToken();
michael@0 11667 return false;
michael@0 11668 }
michael@0 11669 }
michael@0 11670
michael@0 11671 bool
michael@0 11672 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
michael@0 11673 {
michael@0 11674 nsAutoString family;
michael@0 11675 bool single;
michael@0 11676
michael@0 11677 // keywords only have meaning in the first position
michael@0 11678 if (!ParseOneFamily(family, single))
michael@0 11679 return false;
michael@0 11680
michael@0 11681 // check for keywords, but only when keywords appear by themselves
michael@0 11682 // i.e. not in compounds such as font-family: default blah;
michael@0 11683 if (single) {
michael@0 11684 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
michael@0 11685 if (keyword == eCSSKeyword_inherit) {
michael@0 11686 aValue.SetInheritValue();
michael@0 11687 return true;
michael@0 11688 }
michael@0 11689 // 605231 - don't parse unquoted 'default' reserved keyword
michael@0 11690 if (keyword == eCSSKeyword_default) {
michael@0 11691 return false;
michael@0 11692 }
michael@0 11693 if (keyword == eCSSKeyword_initial) {
michael@0 11694 aValue.SetInitialValue();
michael@0 11695 return true;
michael@0 11696 }
michael@0 11697 if (keyword == eCSSKeyword_unset &&
michael@0 11698 nsLayoutUtils::UnsetValueEnabled()) {
michael@0 11699 aValue.SetUnsetValue();
michael@0 11700 return true;
michael@0 11701 }
michael@0 11702 if (keyword == eCSSKeyword__moz_use_system_font &&
michael@0 11703 !IsParsingCompoundProperty()) {
michael@0 11704 aValue.SetSystemFontValue();
michael@0 11705 return true;
michael@0 11706 }
michael@0 11707 }
michael@0 11708
michael@0 11709 for (;;) {
michael@0 11710 if (!ExpectSymbol(',', true))
michael@0 11711 break;
michael@0 11712
michael@0 11713 family.Append(char16_t(','));
michael@0 11714
michael@0 11715 nsAutoString nextFamily;
michael@0 11716 if (!ParseOneFamily(nextFamily, single))
michael@0 11717 return false;
michael@0 11718
michael@0 11719 // at this point unquoted keywords are not allowed
michael@0 11720 // as font family names but can appear within names
michael@0 11721 if (single) {
michael@0 11722 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily);
michael@0 11723 switch (keyword) {
michael@0 11724 case eCSSKeyword_inherit:
michael@0 11725 case eCSSKeyword_initial:
michael@0 11726 case eCSSKeyword_default:
michael@0 11727 case eCSSKeyword__moz_use_system_font:
michael@0 11728 return false;
michael@0 11729 case eCSSKeyword_unset:
michael@0 11730 if (nsLayoutUtils::UnsetValueEnabled()) {
michael@0 11731 return false;
michael@0 11732 }
michael@0 11733 // fall through
michael@0 11734 default:
michael@0 11735 break;
michael@0 11736 }
michael@0 11737 }
michael@0 11738
michael@0 11739 family.Append(nextFamily);
michael@0 11740 }
michael@0 11741
michael@0 11742 if (family.IsEmpty()) {
michael@0 11743 return false;
michael@0 11744 }
michael@0 11745 aValue.SetStringValue(family, eCSSUnit_Families);
michael@0 11746 return true;
michael@0 11747 }
michael@0 11748
michael@0 11749 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
michael@0 11750 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
michael@0 11751 // local-src: 'local(' ( string | ident ) ')'
michael@0 11752
michael@0 11753 bool
michael@0 11754 CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
michael@0 11755 {
michael@0 11756 // could we maybe turn nsCSSValue::Array into InfallibleTArray<nsCSSValue>?
michael@0 11757 InfallibleTArray<nsCSSValue> values;
michael@0 11758 nsCSSValue cur;
michael@0 11759 for (;;) {
michael@0 11760 if (!GetToken(true))
michael@0 11761 break;
michael@0 11762
michael@0 11763 if (mToken.mType == eCSSToken_URL) {
michael@0 11764 SetValueToURL(cur, mToken.mIdent);
michael@0 11765 values.AppendElement(cur);
michael@0 11766 if (!ParseFontSrcFormat(values))
michael@0 11767 return false;
michael@0 11768
michael@0 11769 } else if (mToken.mType == eCSSToken_Function &&
michael@0 11770 mToken.mIdent.LowerCaseEqualsLiteral("local")) {
michael@0 11771 // css3-fonts does not specify a formal grammar for local().
michael@0 11772 // The text permits both unquoted identifiers and quoted
michael@0 11773 // strings. We resolve this ambiguity in the spec by
michael@0 11774 // assuming that the appropriate production is a single
michael@0 11775 // <family-name>, possibly surrounded by whitespace.
michael@0 11776
michael@0 11777 nsAutoString family;
michael@0 11778 bool single;
michael@0 11779 if (!ParseOneFamily(family, single)) {
michael@0 11780 SkipUntil(')');
michael@0 11781 return false;
michael@0 11782 }
michael@0 11783 if (!ExpectSymbol(')', true)) {
michael@0 11784 SkipUntil(')');
michael@0 11785 return false;
michael@0 11786 }
michael@0 11787
michael@0 11788 // XXX: Getting closer...
michael@0 11789 // the style parameters to the nsFont constructor are ignored,
michael@0 11790 // because it's only being used to call EnumerateFamilies
michael@0 11791 nsFont font(family, 0, 0, 0, 0, 0, 0);
michael@0 11792 ExtractFirstFamilyData dat;
michael@0 11793
michael@0 11794 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
michael@0 11795 if (!dat.mGood)
michael@0 11796 return false;
michael@0 11797
michael@0 11798 cur.SetStringValue(dat.mFamilyName, eCSSUnit_Local_Font);
michael@0 11799 values.AppendElement(cur);
michael@0 11800 } else {
michael@0 11801 // We don't know what to do with this token; unget it and error out
michael@0 11802 UngetToken();
michael@0 11803 return false;
michael@0 11804 }
michael@0 11805
michael@0 11806 if (!ExpectSymbol(',', true))
michael@0 11807 break;
michael@0 11808 }
michael@0 11809
michael@0 11810 if (values.Length() == 0)
michael@0 11811 return false;
michael@0 11812
michael@0 11813 nsRefPtr<nsCSSValue::Array> srcVals
michael@0 11814 = nsCSSValue::Array::Create(values.Length());
michael@0 11815
michael@0 11816 uint32_t i;
michael@0 11817 for (i = 0; i < values.Length(); i++)
michael@0 11818 srcVals->Item(i) = values[i];
michael@0 11819 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
michael@0 11820 return true;
michael@0 11821 }
michael@0 11822
michael@0 11823 bool
michael@0 11824 CSSParserImpl::ParseFontSrcFormat(InfallibleTArray<nsCSSValue> & values)
michael@0 11825 {
michael@0 11826 if (!GetToken(true))
michael@0 11827 return true; // EOF harmless here
michael@0 11828 if (mToken.mType != eCSSToken_Function ||
michael@0 11829 !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
michael@0 11830 UngetToken();
michael@0 11831 return true;
michael@0 11832 }
michael@0 11833
michael@0 11834 do {
michael@0 11835 if (!GetToken(true))
michael@0 11836 return false; // EOF - no need for SkipUntil
michael@0 11837
michael@0 11838 if (mToken.mType != eCSSToken_String) {
michael@0 11839 UngetToken();
michael@0 11840 SkipUntil(')');
michael@0 11841 return false;
michael@0 11842 }
michael@0 11843
michael@0 11844 nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
michael@0 11845 values.AppendElement(cur);
michael@0 11846 } while (ExpectSymbol(',', true));
michael@0 11847
michael@0 11848 if (!ExpectSymbol(')', true)) {
michael@0 11849 SkipUntil(')');
michael@0 11850 return false;
michael@0 11851 }
michael@0 11852
michael@0 11853 return true;
michael@0 11854 }
michael@0 11855
michael@0 11856 // font-ranges: urange ( ',' urange )*
michael@0 11857 bool
michael@0 11858 CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
michael@0 11859 {
michael@0 11860 InfallibleTArray<uint32_t> ranges;
michael@0 11861 for (;;) {
michael@0 11862 if (!GetToken(true))
michael@0 11863 break;
michael@0 11864
michael@0 11865 if (mToken.mType != eCSSToken_URange) {
michael@0 11866 UngetToken();
michael@0 11867 break;
michael@0 11868 }
michael@0 11869
michael@0 11870 // An invalid range token is a parsing error, causing the entire
michael@0 11871 // descriptor to be ignored.
michael@0 11872 if (!mToken.mIntegerValid)
michael@0 11873 return false;
michael@0 11874
michael@0 11875 uint32_t low = mToken.mInteger;
michael@0 11876 uint32_t high = mToken.mInteger2;
michael@0 11877
michael@0 11878 // A range that descends, or a range that is entirely outside the
michael@0 11879 // current range of Unicode (U+0-10FFFF) is ignored, but does not
michael@0 11880 // invalidate the descriptor. A range that straddles the high end
michael@0 11881 // is clipped.
michael@0 11882 if (low <= 0x10FFFF && low <= high) {
michael@0 11883 if (high > 0x10FFFF)
michael@0 11884 high = 0x10FFFF;
michael@0 11885
michael@0 11886 ranges.AppendElement(low);
michael@0 11887 ranges.AppendElement(high);
michael@0 11888 }
michael@0 11889 if (!ExpectSymbol(',', true))
michael@0 11890 break;
michael@0 11891 }
michael@0 11892
michael@0 11893 if (ranges.Length() == 0)
michael@0 11894 return false;
michael@0 11895
michael@0 11896 nsRefPtr<nsCSSValue::Array> srcVals
michael@0 11897 = nsCSSValue::Array::Create(ranges.Length());
michael@0 11898
michael@0 11899 for (uint32_t i = 0; i < ranges.Length(); i++)
michael@0 11900 srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer);
michael@0 11901 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
michael@0 11902 return true;
michael@0 11903 }
michael@0 11904
michael@0 11905 // font-feature-settings: normal | <feature-tag-value> [, <feature-tag-value>]*
michael@0 11906 // <feature-tag-value> = <string> [ <integer> | on | off ]?
michael@0 11907
michael@0 11908 // minimum - "tagx", "tagy", "tagz"
michael@0 11909 // edge error case - "tagx" on 1, "tagx" "tagy", "tagx" -1, "tagx" big
michael@0 11910
michael@0 11911 // pair value is always x = string, y = int
michael@0 11912
michael@0 11913 // font feature tags must be four ASCII characters
michael@0 11914 #define FEATURE_TAG_LENGTH 4
michael@0 11915
michael@0 11916 static bool
michael@0 11917 ValidFontFeatureTag(const nsString& aTag)
michael@0 11918 {
michael@0 11919 if (aTag.Length() != FEATURE_TAG_LENGTH) {
michael@0 11920 return false;
michael@0 11921 }
michael@0 11922 uint32_t i;
michael@0 11923 for (i = 0; i < FEATURE_TAG_LENGTH; i++) {
michael@0 11924 uint32_t ch = aTag[i];
michael@0 11925 if (ch < 0x20 || ch > 0x7e) {
michael@0 11926 return false;
michael@0 11927 }
michael@0 11928 }
michael@0 11929 return true;
michael@0 11930 }
michael@0 11931
michael@0 11932 bool
michael@0 11933 CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue)
michael@0 11934 {
michael@0 11935 if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) {
michael@0 11936 return true;
michael@0 11937 }
michael@0 11938
michael@0 11939 nsCSSValuePairList *cur = aValue.SetPairListValue();
michael@0 11940 for (;;) {
michael@0 11941 // feature tag
michael@0 11942 if (!GetToken(true)) {
michael@0 11943 return false;
michael@0 11944 }
michael@0 11945
michael@0 11946 if (mToken.mType != eCSSToken_String ||
michael@0 11947 !ValidFontFeatureTag(mToken.mIdent)) {
michael@0 11948 UngetToken();
michael@0 11949 return false;
michael@0 11950 }
michael@0 11951 cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
michael@0 11952
michael@0 11953 if (!GetToken(true)) {
michael@0 11954 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
michael@0 11955 break;
michael@0 11956 }
michael@0 11957
michael@0 11958 // optional value or on/off keyword
michael@0 11959 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid &&
michael@0 11960 mToken.mInteger >= 0) {
michael@0 11961 cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
michael@0 11962 } else if (mToken.mType == eCSSToken_Ident &&
michael@0 11963 mToken.mIdent.LowerCaseEqualsLiteral("on")) {
michael@0 11964 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
michael@0 11965 } else if (mToken.mType == eCSSToken_Ident &&
michael@0 11966 mToken.mIdent.LowerCaseEqualsLiteral("off")) {
michael@0 11967 cur->mYValue.SetIntValue(0, eCSSUnit_Integer);
michael@0 11968 } else {
michael@0 11969 // something other than value/on/off, set default value
michael@0 11970 cur->mYValue.SetIntValue(1, eCSSUnit_Integer);
michael@0 11971 UngetToken();
michael@0 11972 }
michael@0 11973
michael@0 11974 if (!ExpectSymbol(',', true)) {
michael@0 11975 break;
michael@0 11976 }
michael@0 11977
michael@0 11978 cur->mNext = new nsCSSValuePairList;
michael@0 11979 cur = cur->mNext;
michael@0 11980 }
michael@0 11981
michael@0 11982 return true;
michael@0 11983 }
michael@0 11984
michael@0 11985 bool
michael@0 11986 CSSParserImpl::ParseListStyle()
michael@0 11987 {
michael@0 11988 // 'list-style' can accept 'none' for two different subproperties,
michael@0 11989 // 'list-style-type' and 'list-style-position'. In order to accept
michael@0 11990 // 'none' as the value of either but still allow another value for
michael@0 11991 // either, we need to ensure that the first 'none' we find gets
michael@0 11992 // allocated to a dummy property instead.
michael@0 11993 static const nsCSSProperty listStyleIDs[] = {
michael@0 11994 eCSSPropertyExtra_x_none_value,
michael@0 11995 eCSSProperty_list_style_type,
michael@0 11996 eCSSProperty_list_style_position,
michael@0 11997 eCSSProperty_list_style_image
michael@0 11998 };
michael@0 11999
michael@0 12000 nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)];
michael@0 12001 int32_t found =
michael@0 12002 ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs));
michael@0 12003 if (found < 1) {
michael@0 12004 return false;
michael@0 12005 }
michael@0 12006
michael@0 12007 if ((found & (1|2|8)) == (1|2|8)) {
michael@0 12008 if (values[0].GetUnit() == eCSSUnit_None) {
michael@0 12009 // We found a 'none' plus another value for both of
michael@0 12010 // 'list-style-type' and 'list-style-image'. This is a parse
michael@0 12011 // error, since the 'none' has to count for at least one of them.
michael@0 12012 return false;
michael@0 12013 } else {
michael@0 12014 NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] &&
michael@0 12015 values[0] == values[2] && values[0] == values[3],
michael@0 12016 "should be a special value");
michael@0 12017 }
michael@0 12018 }
michael@0 12019
michael@0 12020 // Provide default values
michael@0 12021 if ((found & 2) == 0) {
michael@0 12022 if (found & 1) {
michael@0 12023 values[1].SetIntValue(NS_STYLE_LIST_STYLE_NONE, eCSSUnit_Enumerated);
michael@0 12024 } else {
michael@0 12025 values[1].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
michael@0 12026 }
michael@0 12027 }
michael@0 12028 if ((found & 4) == 0) {
michael@0 12029 values[2].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE,
michael@0 12030 eCSSUnit_Enumerated);
michael@0 12031 }
michael@0 12032 if ((found & 8) == 0) {
michael@0 12033 values[3].SetNoneValue();
michael@0 12034 }
michael@0 12035
michael@0 12036 // Start at 1 to avoid appending fake value.
michael@0 12037 for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) {
michael@0 12038 AppendValue(listStyleIDs[index], values[index]);
michael@0 12039 }
michael@0 12040 return true;
michael@0 12041 }
michael@0 12042
michael@0 12043 bool
michael@0 12044 CSSParserImpl::ParseMargin()
michael@0 12045 {
michael@0 12046 static const nsCSSProperty kMarginSideIDs[] = {
michael@0 12047 eCSSProperty_margin_top,
michael@0 12048 eCSSProperty_margin_right_value,
michael@0 12049 eCSSProperty_margin_bottom,
michael@0 12050 eCSSProperty_margin_left_value
michael@0 12051 };
michael@0 12052 static const nsCSSProperty kMarginSources[] = {
michael@0 12053 eCSSProperty_margin_left_ltr_source,
michael@0 12054 eCSSProperty_margin_left_rtl_source,
michael@0 12055 eCSSProperty_margin_right_ltr_source,
michael@0 12056 eCSSProperty_margin_right_rtl_source,
michael@0 12057 eCSSProperty_UNKNOWN
michael@0 12058 };
michael@0 12059
michael@0 12060 // do this now, in case 4 values weren't specified
michael@0 12061 InitBoxPropsAsPhysical(kMarginSources);
michael@0 12062 return ParseBoxProperties(kMarginSideIDs);
michael@0 12063 }
michael@0 12064
michael@0 12065 bool
michael@0 12066 CSSParserImpl::ParseMarks(nsCSSValue& aValue)
michael@0 12067 {
michael@0 12068 if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPageMarksKTable)) {
michael@0 12069 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
michael@0 12070 if (NS_STYLE_PAGE_MARKS_NONE != aValue.GetIntValue() &&
michael@0 12071 false == CheckEndProperty()) {
michael@0 12072 nsCSSValue second;
michael@0 12073 if (ParseEnum(second, nsCSSProps::kPageMarksKTable)) {
michael@0 12074 // 'none' keyword in conjuction with others is not allowed
michael@0 12075 if (NS_STYLE_PAGE_MARKS_NONE != second.GetIntValue()) {
michael@0 12076 aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(),
michael@0 12077 eCSSUnit_Enumerated);
michael@0 12078 return true;
michael@0 12079 }
michael@0 12080 }
michael@0 12081 return false;
michael@0 12082 }
michael@0 12083 }
michael@0 12084 return true;
michael@0 12085 }
michael@0 12086 return false;
michael@0 12087 }
michael@0 12088
michael@0 12089 bool
michael@0 12090 CSSParserImpl::ParseOutline()
michael@0 12091 {
michael@0 12092 const int32_t numProps = 3;
michael@0 12093 static const nsCSSProperty kOutlineIDs[] = {
michael@0 12094 eCSSProperty_outline_color,
michael@0 12095 eCSSProperty_outline_style,
michael@0 12096 eCSSProperty_outline_width
michael@0 12097 };
michael@0 12098
michael@0 12099 nsCSSValue values[numProps];
michael@0 12100 int32_t found = ParseChoice(values, kOutlineIDs, numProps);
michael@0 12101 if (found < 1) {
michael@0 12102 return false;
michael@0 12103 }
michael@0 12104
michael@0 12105 // Provide default values
michael@0 12106 if ((found & 1) == 0) { // Provide default outline-color
michael@0 12107 values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
michael@0 12108 }
michael@0 12109 if ((found & 2) == 0) { // Provide default outline-style
michael@0 12110 values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
michael@0 12111 }
michael@0 12112 if ((found & 4) == 0) { // Provide default outline-width
michael@0 12113 values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
michael@0 12114 }
michael@0 12115
michael@0 12116 int32_t index;
michael@0 12117 for (index = 0; index < numProps; index++) {
michael@0 12118 AppendValue(kOutlineIDs[index], values[index]);
michael@0 12119 }
michael@0 12120 return true;
michael@0 12121 }
michael@0 12122
michael@0 12123 bool
michael@0 12124 CSSParserImpl::ParseOverflow()
michael@0 12125 {
michael@0 12126 nsCSSValue overflow;
michael@0 12127 if (!ParseVariant(overflow, VARIANT_HK, nsCSSProps::kOverflowKTable)) {
michael@0 12128 return false;
michael@0 12129 }
michael@0 12130
michael@0 12131 nsCSSValue overflowX(overflow);
michael@0 12132 nsCSSValue overflowY(overflow);
michael@0 12133 if (eCSSUnit_Enumerated == overflow.GetUnit())
michael@0 12134 switch(overflow.GetIntValue()) {
michael@0 12135 case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
michael@0 12136 overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
michael@0 12137 overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
michael@0 12138 break;
michael@0 12139 case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
michael@0 12140 overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
michael@0 12141 overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
michael@0 12142 break;
michael@0 12143 }
michael@0 12144 AppendValue(eCSSProperty_overflow_x, overflowX);
michael@0 12145 AppendValue(eCSSProperty_overflow_y, overflowY);
michael@0 12146 return true;
michael@0 12147 }
michael@0 12148
michael@0 12149 bool
michael@0 12150 CSSParserImpl::ParsePadding()
michael@0 12151 {
michael@0 12152 static const nsCSSProperty kPaddingSideIDs[] = {
michael@0 12153 eCSSProperty_padding_top,
michael@0 12154 eCSSProperty_padding_right_value,
michael@0 12155 eCSSProperty_padding_bottom,
michael@0 12156 eCSSProperty_padding_left_value
michael@0 12157 };
michael@0 12158 static const nsCSSProperty kPaddingSources[] = {
michael@0 12159 eCSSProperty_padding_left_ltr_source,
michael@0 12160 eCSSProperty_padding_left_rtl_source,
michael@0 12161 eCSSProperty_padding_right_ltr_source,
michael@0 12162 eCSSProperty_padding_right_rtl_source,
michael@0 12163 eCSSProperty_UNKNOWN
michael@0 12164 };
michael@0 12165
michael@0 12166 // do this now, in case 4 values weren't specified
michael@0 12167 InitBoxPropsAsPhysical(kPaddingSources);
michael@0 12168 return ParseBoxProperties(kPaddingSideIDs);
michael@0 12169 }
michael@0 12170
michael@0 12171 bool
michael@0 12172 CSSParserImpl::ParseQuotes()
michael@0 12173 {
michael@0 12174 nsCSSValue value;
michael@0 12175 if (!ParseVariant(value, VARIANT_HOS, nullptr)) {
michael@0 12176 return false;
michael@0 12177 }
michael@0 12178 if (value.GetUnit() == eCSSUnit_String) {
michael@0 12179 nsCSSValue open = value;
michael@0 12180 nsCSSValuePairList* quotes = value.SetPairListValue();
michael@0 12181 for (;;) {
michael@0 12182 quotes->mXValue = open;
michael@0 12183 // get mandatory close
michael@0 12184 if (!ParseVariant(quotes->mYValue, VARIANT_STRING, nullptr)) {
michael@0 12185 return false;
michael@0 12186 }
michael@0 12187 // look for another open
michael@0 12188 if (!ParseVariant(open, VARIANT_STRING, nullptr)) {
michael@0 12189 break;
michael@0 12190 }
michael@0 12191 quotes->mNext = new nsCSSValuePairList;
michael@0 12192 quotes = quotes->mNext;
michael@0 12193 }
michael@0 12194 }
michael@0 12195 AppendValue(eCSSProperty_quotes, value);
michael@0 12196 return true;
michael@0 12197 }
michael@0 12198
michael@0 12199 bool
michael@0 12200 CSSParserImpl::ParseSize()
michael@0 12201 {
michael@0 12202 nsCSSValue width, height;
michael@0 12203 if (!ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
michael@0 12204 return false;
michael@0 12205 }
michael@0 12206 if (width.IsLengthUnit()) {
michael@0 12207 ParseVariant(height, VARIANT_LENGTH, nullptr);
michael@0 12208 }
michael@0 12209
michael@0 12210 if (width == height || height.GetUnit() == eCSSUnit_Null) {
michael@0 12211 AppendValue(eCSSProperty_size, width);
michael@0 12212 } else {
michael@0 12213 nsCSSValue pair;
michael@0 12214 pair.SetPairValue(width, height);
michael@0 12215 AppendValue(eCSSProperty_size, pair);
michael@0 12216 }
michael@0 12217 return true;
michael@0 12218 }
michael@0 12219
michael@0 12220 bool
michael@0 12221 CSSParserImpl::ParseTextDecoration()
michael@0 12222 {
michael@0 12223 enum {
michael@0 12224 eDecorationNone = NS_STYLE_TEXT_DECORATION_LINE_NONE,
michael@0 12225 eDecorationUnderline = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
michael@0 12226 eDecorationOverline = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE,
michael@0 12227 eDecorationLineThrough = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
michael@0 12228 eDecorationBlink = NS_STYLE_TEXT_DECORATION_LINE_BLINK,
michael@0 12229 eDecorationPrefAnchors = NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS
michael@0 12230 };
michael@0 12231 static_assert((eDecorationNone ^ eDecorationUnderline ^
michael@0 12232 eDecorationOverline ^ eDecorationLineThrough ^
michael@0 12233 eDecorationBlink ^ eDecorationPrefAnchors) ==
michael@0 12234 (eDecorationNone | eDecorationUnderline |
michael@0 12235 eDecorationOverline | eDecorationLineThrough |
michael@0 12236 eDecorationBlink | eDecorationPrefAnchors),
michael@0 12237 "text decoration constants need to be bitmasks");
michael@0 12238
michael@0 12239 static const KTableValue kTextDecorationKTable[] = {
michael@0 12240 eCSSKeyword_none, eDecorationNone,
michael@0 12241 eCSSKeyword_underline, eDecorationUnderline,
michael@0 12242 eCSSKeyword_overline, eDecorationOverline,
michael@0 12243 eCSSKeyword_line_through, eDecorationLineThrough,
michael@0 12244 eCSSKeyword_blink, eDecorationBlink,
michael@0 12245 eCSSKeyword__moz_anchor_decoration, eDecorationPrefAnchors,
michael@0 12246 eCSSKeyword_UNKNOWN,-1
michael@0 12247 };
michael@0 12248
michael@0 12249 nsCSSValue value;
michael@0 12250 if (!ParseVariant(value, VARIANT_HK, kTextDecorationKTable)) {
michael@0 12251 return false;
michael@0 12252 }
michael@0 12253
michael@0 12254 nsCSSValue line, style, color;
michael@0 12255 switch (value.GetUnit()) {
michael@0 12256 case eCSSUnit_Enumerated: {
michael@0 12257 // We shouldn't accept decoration line style and color via
michael@0 12258 // text-decoration.
michael@0 12259 color.SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR,
michael@0 12260 eCSSUnit_Enumerated);
michael@0 12261 style.SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID,
michael@0 12262 eCSSUnit_Enumerated);
michael@0 12263
michael@0 12264 int32_t intValue = value.GetIntValue();
michael@0 12265 if (intValue == eDecorationNone) {
michael@0 12266 line.SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE,
michael@0 12267 eCSSUnit_Enumerated);
michael@0 12268 break;
michael@0 12269 }
michael@0 12270
michael@0 12271 // look for more keywords
michael@0 12272 nsCSSValue keyword;
michael@0 12273 int32_t index;
michael@0 12274 for (index = 0; index < 3; index++) {
michael@0 12275 if (!ParseEnum(keyword, kTextDecorationKTable)) {
michael@0 12276 break;
michael@0 12277 }
michael@0 12278 int32_t newValue = keyword.GetIntValue();
michael@0 12279 if (newValue == eDecorationNone || newValue & intValue) {
michael@0 12280 // 'none' keyword in conjuction with others is not allowed, and
michael@0 12281 // duplicate keyword is not allowed.
michael@0 12282 return false;
michael@0 12283 }
michael@0 12284 intValue |= newValue;
michael@0 12285 }
michael@0 12286
michael@0 12287 line.SetIntValue(intValue, eCSSUnit_Enumerated);
michael@0 12288 break;
michael@0 12289 }
michael@0 12290 default:
michael@0 12291 line = color = style = value;
michael@0 12292 break;
michael@0 12293 }
michael@0 12294
michael@0 12295 AppendValue(eCSSProperty_text_decoration_line, line);
michael@0 12296 AppendValue(eCSSProperty_text_decoration_color, color);
michael@0 12297 AppendValue(eCSSProperty_text_decoration_style, style);
michael@0 12298
michael@0 12299 return true;
michael@0 12300 }
michael@0 12301
michael@0 12302 bool
michael@0 12303 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableValue aTable[])
michael@0 12304 {
michael@0 12305 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
michael@0 12306 // 'inherit', 'initial' and 'unset' must be alone
michael@0 12307 return true;
michael@0 12308 }
michael@0 12309
michael@0 12310 nsCSSValue left;
michael@0 12311 if (!ParseVariant(left, VARIANT_KEYWORD, aTable)) {
michael@0 12312 return false;
michael@0 12313 }
michael@0 12314
michael@0 12315 if (!nsLayoutUtils::IsTextAlignTrueValueEnabled()) {
michael@0 12316 aValue = left;
michael@0 12317 return true;
michael@0 12318 }
michael@0 12319
michael@0 12320 nsCSSValue right;
michael@0 12321 if (ParseVariant(right, VARIANT_KEYWORD, aTable)) {
michael@0 12322 // 'true' must be combined with some other value than 'true'.
michael@0 12323 if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE &&
michael@0 12324 right.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
michael@0 12325 return false;
michael@0 12326 }
michael@0 12327 aValue.SetPairValue(left, right);
michael@0 12328 } else {
michael@0 12329 // Single value 'true' is not allowed.
michael@0 12330 if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
michael@0 12331 return false;
michael@0 12332 }
michael@0 12333 aValue = left;
michael@0 12334 }
michael@0 12335 return true;
michael@0 12336 }
michael@0 12337
michael@0 12338 bool
michael@0 12339 CSSParserImpl::ParseTextAlign(nsCSSValue& aValue)
michael@0 12340 {
michael@0 12341 return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable);
michael@0 12342 }
michael@0 12343
michael@0 12344 bool
michael@0 12345 CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue)
michael@0 12346 {
michael@0 12347 return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable);
michael@0 12348 }
michael@0 12349
michael@0 12350 bool
michael@0 12351 CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
michael@0 12352 {
michael@0 12353 if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTextDecorationLineKTable)) {
michael@0 12354 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
michael@0 12355 int32_t intValue = aValue.GetIntValue();
michael@0 12356 if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
michael@0 12357 // look for more keywords
michael@0 12358 nsCSSValue keyword;
michael@0 12359 int32_t index;
michael@0 12360 for (index = 0; index < 3; index++) {
michael@0 12361 if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) {
michael@0 12362 int32_t newValue = keyword.GetIntValue();
michael@0 12363 if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE ||
michael@0 12364 newValue & intValue) {
michael@0 12365 // 'none' keyword in conjuction with others is not allowed, and
michael@0 12366 // duplicate keyword is not allowed.
michael@0 12367 return false;
michael@0 12368 }
michael@0 12369 intValue |= newValue;
michael@0 12370 }
michael@0 12371 else {
michael@0 12372 break;
michael@0 12373 }
michael@0 12374 }
michael@0 12375 aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
michael@0 12376 }
michael@0 12377 }
michael@0 12378 return true;
michael@0 12379 }
michael@0 12380 return false;
michael@0 12381 }
michael@0 12382
michael@0 12383 bool
michael@0 12384 CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
michael@0 12385 {
michael@0 12386 if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
michael@0 12387 // 'inherit', 'initial' and 'unset' must be alone
michael@0 12388 return true;
michael@0 12389 }
michael@0 12390
michael@0 12391 nsCSSValue left;
michael@0 12392 if (!ParseVariant(left, VARIANT_KEYWORD | VARIANT_STRING,
michael@0 12393 nsCSSProps::kTextOverflowKTable))
michael@0 12394 return false;
michael@0 12395
michael@0 12396 nsCSSValue right;
michael@0 12397 if (ParseVariant(right, VARIANT_KEYWORD | VARIANT_STRING,
michael@0 12398 nsCSSProps::kTextOverflowKTable))
michael@0 12399 aValue.SetPairValue(left, right);
michael@0 12400 else {
michael@0 12401 aValue = left;
michael@0 12402 }
michael@0 12403 return true;
michael@0 12404 }
michael@0 12405
michael@0 12406 bool
michael@0 12407 CSSParserImpl::ParseTouchAction(nsCSSValue& aValue)
michael@0 12408 {
michael@0 12409 // Avaliable values of property touch-action:
michael@0 12410 // auto | none | [pan-x || pan-y] | manipulation
michael@0 12411
michael@0 12412 if (!ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTouchActionKTable)) {
michael@0 12413 return false;
michael@0 12414 }
michael@0 12415
michael@0 12416 // Auto and None keywords aren't allowed in conjunction with others.
michael@0 12417 // Also inherit, initial and unset values are available.
michael@0 12418 if (eCSSUnit_Enumerated != aValue.GetUnit()) {
michael@0 12419 return true;
michael@0 12420 }
michael@0 12421
michael@0 12422 int32_t intValue = aValue.GetIntValue();
michael@0 12423 nsCSSValue nextValue;
michael@0 12424 if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) {
michael@0 12425 int32_t nextIntValue = nextValue.GetIntValue();
michael@0 12426
michael@0 12427 // duplicates aren't allowed.
michael@0 12428 if (nextIntValue & intValue) {
michael@0 12429 return false;
michael@0 12430 }
michael@0 12431
michael@0 12432 // Auto and None and Manipulation is not allowed in conjunction with others.
michael@0 12433 if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE |
michael@0 12434 NS_STYLE_TOUCH_ACTION_AUTO |
michael@0 12435 NS_STYLE_TOUCH_ACTION_MANIPULATION)) {
michael@0 12436 return false;
michael@0 12437 }
michael@0 12438
michael@0 12439 aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated);
michael@0 12440 }
michael@0 12441
michael@0 12442 return true;
michael@0 12443 }
michael@0 12444
michael@0 12445 bool
michael@0 12446 CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue)
michael@0 12447 {
michael@0 12448 if (!ParseVariant(aValue, VARIANT_HK,
michael@0 12449 nsCSSProps::kTextCombineUprightKTable)) {
michael@0 12450 return false;
michael@0 12451 }
michael@0 12452
michael@0 12453 // if 'digits', need to check for an explicit number [2, 3, 4]
michael@0 12454 if (eCSSUnit_Enumerated == aValue.GetUnit() &&
michael@0 12455 aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) {
michael@0 12456 if (!GetToken(true)) {
michael@0 12457 return true;
michael@0 12458 }
michael@0 12459 if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) {
michael@0 12460 switch (mToken.mInteger) {
michael@0 12461 case 2: // already set, nothing to do
michael@0 12462 break;
michael@0 12463 case 3:
michael@0 12464 aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3,
michael@0 12465 eCSSUnit_Enumerated);
michael@0 12466 break;
michael@0 12467 case 4:
michael@0 12468 aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4,
michael@0 12469 eCSSUnit_Enumerated);
michael@0 12470 break;
michael@0 12471 default:
michael@0 12472 // invalid digits value
michael@0 12473 return false;
michael@0 12474 }
michael@0 12475 } else {
michael@0 12476 UngetToken();
michael@0 12477 }
michael@0 12478 }
michael@0 12479 return true;
michael@0 12480 }
michael@0 12481
michael@0 12482 ///////////////////////////////////////////////////////
michael@0 12483 // transform Parsing Implementation
michael@0 12484
michael@0 12485 /* Reads a function list of arguments and consumes the closing parenthesis.
michael@0 12486 * Do not call this function directly; it's meant to be called from
michael@0 12487 * ParseFunction.
michael@0 12488 */
michael@0 12489 bool
michael@0 12490 CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[],
michael@0 12491 int32_t aVariantMaskAll,
michael@0 12492 uint16_t aMinElems,
michael@0 12493 uint16_t aMaxElems,
michael@0 12494 InfallibleTArray<nsCSSValue> &aOutput)
michael@0 12495 {
michael@0 12496 NS_ASSERTION((aVariantMask && !aVariantMaskAll) ||
michael@0 12497 (!aVariantMask && aVariantMaskAll),
michael@0 12498 "only one of the two variant mask parameters can be set");
michael@0 12499
michael@0 12500 for (uint16_t index = 0; index < aMaxElems; ++index) {
michael@0 12501 nsCSSValue newValue;
michael@0 12502 int32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index];
michael@0 12503 if (!ParseVariant(newValue, m, nullptr)) {
michael@0 12504 break;
michael@0 12505 }
michael@0 12506
michael@0 12507 aOutput.AppendElement(newValue);
michael@0 12508
michael@0 12509 if (ExpectSymbol(',', true)) {
michael@0 12510 // Move on to the next argument if we see a comma.
michael@0 12511 continue;
michael@0 12512 }
michael@0 12513
michael@0 12514 if (ExpectSymbol(')', true)) {
michael@0 12515 // Make sure we've read enough symbols if we see a closing parenthesis.
michael@0 12516 return (index + 1) >= aMinElems;
michael@0 12517 }
michael@0 12518
michael@0 12519 // Only a comma or a closing parenthesis is valid after an argument.
michael@0 12520 break;
michael@0 12521 }
michael@0 12522
michael@0 12523 // If we're here, we've hit an error without seeing a closing parenthesis or
michael@0 12524 // we've read too many elements without seeing a closing parenthesis.
michael@0 12525 SkipUntil(')');
michael@0 12526 return false;
michael@0 12527 }
michael@0 12528
michael@0 12529 /* Parses a function [ input of the form (a [, b]*) ] and stores it
michael@0 12530 * as an nsCSSValue that holds a function of the form
michael@0 12531 * function-name arg1 arg2 ... argN
michael@0 12532 *
michael@0 12533 * On error, the return value is false.
michael@0 12534 *
michael@0 12535 * @param aFunction The name of the function that we're reading.
michael@0 12536 * @param aAllowedTypes An array of values corresponding to the legal
michael@0 12537 * types for each element in the function. The zeroth element in the
michael@0 12538 * array corresponds to the first function parameter, etc. The length
michael@0 12539 * of this array _must_ be greater than or equal to aMaxElems or the
michael@0 12540 * behavior is undefined. If not null, aAllowTypesAll must be 0.
michael@0 12541 * @param aAllowedTypesAll If set, every element tested for these types
michael@0 12542 * @param aMinElems Minimum number of elements to read. Reading fewer than
michael@0 12543 * this many elements will result in the function failing.
michael@0 12544 * @param aMaxElems Maximum number of elements to read. Reading more than
michael@0 12545 * this many elements will result in the function failing.
michael@0 12546 * @param aValue (out) The value that was parsed.
michael@0 12547 */
michael@0 12548 bool
michael@0 12549 CSSParserImpl::ParseFunction(nsCSSKeyword aFunction,
michael@0 12550 const int32_t aAllowedTypes[],
michael@0 12551 int32_t aAllowedTypesAll,
michael@0 12552 uint16_t aMinElems, uint16_t aMaxElems,
michael@0 12553 nsCSSValue &aValue)
michael@0 12554 {
michael@0 12555 NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) ||
michael@0 12556 (!aAllowedTypes && aAllowedTypesAll),
michael@0 12557 "only one of the two allowed type parameter can be set");
michael@0 12558 typedef InfallibleTArray<nsCSSValue>::size_type arrlen_t;
michael@0 12559
michael@0 12560 /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
michael@0 12561 * elements stored in the the nsCSSValue::Array.
michael@0 12562 */
michael@0 12563 static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
michael@0 12564
michael@0 12565 /* Read in a list of values as an array, failing if we can't or if
michael@0 12566 * it's out of bounds.
michael@0 12567 *
michael@0 12568 * We reserve 16 entries in the foundValues array in order to avoid
michael@0 12569 * having to resize the array dynamically when parsing some well-formed
michael@0 12570 * functions. The number 16 is coming from the number of arguments that
michael@0 12571 * matrix3d() accepts.
michael@0 12572 */
michael@0 12573 AutoInfallibleTArray<nsCSSValue, 16> foundValues;
michael@0 12574 if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems,
michael@0 12575 aMaxElems, foundValues)) {
michael@0 12576 return false;
michael@0 12577 }
michael@0 12578
michael@0 12579 /*
michael@0 12580 * In case the user has given us more than 2^16 - 2 arguments,
michael@0 12581 * we'll truncate them at 2^16 - 2 arguments.
michael@0 12582 */
michael@0 12583 uint16_t numArgs = std::min(foundValues.Length(), MAX_ALLOWED_ELEMS);
michael@0 12584 nsRefPtr<nsCSSValue::Array> convertedArray =
michael@0 12585 aValue.InitFunction(aFunction, numArgs);
michael@0 12586
michael@0 12587 /* Copy things over. */
michael@0 12588 for (uint16_t index = 0; index < numArgs; ++index)
michael@0 12589 convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
michael@0 12590
michael@0 12591 /* Return it! */
michael@0 12592 return true;
michael@0 12593 }
michael@0 12594
michael@0 12595 /**
michael@0 12596 * Given a token, determines the minimum and maximum number of function
michael@0 12597 * parameters to read, along with the mask that should be used to read
michael@0 12598 * those function parameters. If the token isn't a transform function,
michael@0 12599 * returns an error.
michael@0 12600 *
michael@0 12601 * @param aToken The token identifying the function.
michael@0 12602 * @param aMinElems [out] The minimum number of elements to read.
michael@0 12603 * @param aMaxElems [out] The maximum number of elements to read
michael@0 12604 * @param aVariantMask [out] The variant mask to use during parsing
michael@0 12605 * @return Whether the information was loaded successfully.
michael@0 12606 */
michael@0 12607 static bool GetFunctionParseInformation(nsCSSKeyword aToken,
michael@0 12608 bool aIsPrefixed,
michael@0 12609 uint16_t &aMinElems,
michael@0 12610 uint16_t &aMaxElems,
michael@0 12611 const int32_t *& aVariantMask)
michael@0 12612 {
michael@0 12613 /* These types represent the common variant masks that will be used to
michael@0 12614 * parse out the individual functions. The order in the enumeration
michael@0 12615 * must match the order in which the masks are declared.
michael@0 12616 */
michael@0 12617 enum { eLengthPercentCalc,
michael@0 12618 eLengthCalc,
michael@0 12619 eTwoLengthPercentCalcs,
michael@0 12620 eTwoLengthPercentCalcsOneLengthCalc,
michael@0 12621 eAngle,
michael@0 12622 eTwoAngles,
michael@0 12623 eNumber,
michael@0 12624 ePositiveLength,
michael@0 12625 eTwoNumbers,
michael@0 12626 eThreeNumbers,
michael@0 12627 eThreeNumbersOneAngle,
michael@0 12628 eMatrix,
michael@0 12629 eMatrixPrefixed,
michael@0 12630 eMatrix3d,
michael@0 12631 eMatrix3dPrefixed,
michael@0 12632 eNumVariantMasks };
michael@0 12633 static const int32_t kMaxElemsPerFunction = 16;
michael@0 12634 static const int32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
michael@0 12635 {VARIANT_LPCALC},
michael@0 12636 {VARIANT_LENGTH|VARIANT_CALC},
michael@0 12637 {VARIANT_LPCALC, VARIANT_LPCALC},
michael@0 12638 {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LENGTH|VARIANT_CALC},
michael@0 12639 {VARIANT_ANGLE_OR_ZERO},
michael@0 12640 {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO},
michael@0 12641 {VARIANT_NUMBER},
michael@0 12642 {VARIANT_LENGTH|VARIANT_POSITIVE_DIMENSION},
michael@0 12643 {VARIANT_NUMBER, VARIANT_NUMBER},
michael@0 12644 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
michael@0 12645 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO},
michael@0 12646 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
michael@0 12647 VARIANT_NUMBER, VARIANT_NUMBER},
michael@0 12648 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
michael@0 12649 VARIANT_LPNCALC, VARIANT_LPNCALC},
michael@0 12650 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
michael@0 12651 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
michael@0 12652 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
michael@0 12653 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER},
michael@0 12654 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
michael@0 12655 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
michael@0 12656 VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
michael@0 12657 VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}};
michael@0 12658
michael@0 12659 #ifdef DEBUG
michael@0 12660 static const uint8_t kVariantMaskLengths[eNumVariantMasks] =
michael@0 12661 {1, 1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 6, 6, 16, 16};
michael@0 12662 #endif
michael@0 12663
michael@0 12664 int32_t variantIndex = eNumVariantMasks;
michael@0 12665
michael@0 12666 switch (aToken) {
michael@0 12667 case eCSSKeyword_translatex:
michael@0 12668 case eCSSKeyword_translatey:
michael@0 12669 /* Exactly one length or percent. */
michael@0 12670 variantIndex = eLengthPercentCalc;
michael@0 12671 aMinElems = 1U;
michael@0 12672 aMaxElems = 1U;
michael@0 12673 break;
michael@0 12674 case eCSSKeyword_translatez:
michael@0 12675 /* Exactly one length */
michael@0 12676 variantIndex = eLengthCalc;
michael@0 12677 aMinElems = 1U;
michael@0 12678 aMaxElems = 1U;
michael@0 12679 break;
michael@0 12680 case eCSSKeyword_translate3d:
michael@0 12681 /* Exactly two lengthds or percents and a number */
michael@0 12682 variantIndex = eTwoLengthPercentCalcsOneLengthCalc;
michael@0 12683 aMinElems = 3U;
michael@0 12684 aMaxElems = 3U;
michael@0 12685 break;
michael@0 12686 case eCSSKeyword_scalez:
michael@0 12687 case eCSSKeyword_scalex:
michael@0 12688 case eCSSKeyword_scaley:
michael@0 12689 /* Exactly one scale factor. */
michael@0 12690 variantIndex = eNumber;
michael@0 12691 aMinElems = 1U;
michael@0 12692 aMaxElems = 1U;
michael@0 12693 break;
michael@0 12694 case eCSSKeyword_scale3d:
michael@0 12695 /* Exactly three scale factors. */
michael@0 12696 variantIndex = eThreeNumbers;
michael@0 12697 aMinElems = 3U;
michael@0 12698 aMaxElems = 3U;
michael@0 12699 break;
michael@0 12700 case eCSSKeyword_rotatex:
michael@0 12701 case eCSSKeyword_rotatey:
michael@0 12702 case eCSSKeyword_rotate:
michael@0 12703 case eCSSKeyword_rotatez:
michael@0 12704 /* Exactly one angle. */
michael@0 12705 variantIndex = eAngle;
michael@0 12706 aMinElems = 1U;
michael@0 12707 aMaxElems = 1U;
michael@0 12708 break;
michael@0 12709 case eCSSKeyword_rotate3d:
michael@0 12710 variantIndex = eThreeNumbersOneAngle;
michael@0 12711 aMinElems = 4U;
michael@0 12712 aMaxElems = 4U;
michael@0 12713 break;
michael@0 12714 case eCSSKeyword_translate:
michael@0 12715 /* One or two lengths or percents. */
michael@0 12716 variantIndex = eTwoLengthPercentCalcs;
michael@0 12717 aMinElems = 1U;
michael@0 12718 aMaxElems = 2U;
michael@0 12719 break;
michael@0 12720 case eCSSKeyword_skew:
michael@0 12721 /* Exactly one or two angles. */
michael@0 12722 variantIndex = eTwoAngles;
michael@0 12723 aMinElems = 1U;
michael@0 12724 aMaxElems = 2U;
michael@0 12725 break;
michael@0 12726 case eCSSKeyword_scale:
michael@0 12727 /* One or two scale factors. */
michael@0 12728 variantIndex = eTwoNumbers;
michael@0 12729 aMinElems = 1U;
michael@0 12730 aMaxElems = 2U;
michael@0 12731 break;
michael@0 12732 case eCSSKeyword_skewx:
michael@0 12733 /* Exactly one angle. */
michael@0 12734 variantIndex = eAngle;
michael@0 12735 aMinElems = 1U;
michael@0 12736 aMaxElems = 1U;
michael@0 12737 break;
michael@0 12738 case eCSSKeyword_skewy:
michael@0 12739 /* Exactly one angle. */
michael@0 12740 variantIndex = eAngle;
michael@0 12741 aMinElems = 1U;
michael@0 12742 aMaxElems = 1U;
michael@0 12743 break;
michael@0 12744 case eCSSKeyword_matrix:
michael@0 12745 /* Six values, all numbers. */
michael@0 12746 variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix;
michael@0 12747 aMinElems = 6U;
michael@0 12748 aMaxElems = 6U;
michael@0 12749 break;
michael@0 12750 case eCSSKeyword_matrix3d:
michael@0 12751 /* 16 matrix values, all numbers */
michael@0 12752 variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d;
michael@0 12753 aMinElems = 16U;
michael@0 12754 aMaxElems = 16U;
michael@0 12755 break;
michael@0 12756 case eCSSKeyword_perspective:
michael@0 12757 /* Exactly one scale number. */
michael@0 12758 variantIndex = ePositiveLength;
michael@0 12759 aMinElems = 1U;
michael@0 12760 aMaxElems = 1U;
michael@0 12761 break;
michael@0 12762 default:
michael@0 12763 /* Oh dear, we didn't match. Report an error. */
michael@0 12764 return false;
michael@0 12765 }
michael@0 12766
michael@0 12767 NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
michael@0 12768 NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
michael@0 12769 NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
michael@0 12770 NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
michael@0 12771 NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
michael@0 12772 #ifdef DEBUG
michael@0 12773 NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
michael@0 12774 "Invalid aMaxElems for this variant mask.");
michael@0 12775 #endif
michael@0 12776
michael@0 12777 // Convert the index into a mask.
michael@0 12778 aVariantMask = kVariantMasks[variantIndex];
michael@0 12779
michael@0 12780 return true;
michael@0 12781 }
michael@0 12782
michael@0 12783 bool CSSParserImpl::ParseWillChange()
michael@0 12784 {
michael@0 12785 nsCSSValue listValue;
michael@0 12786 nsCSSValueList* currentListValue = listValue.SetListValue();
michael@0 12787 bool first = true;
michael@0 12788 for (;;) {
michael@0 12789 const uint32_t variantMask = VARIANT_IDENTIFIER |
michael@0 12790 VARIANT_INHERIT |
michael@0 12791 VARIANT_NONE |
michael@0 12792 VARIANT_ALL |
michael@0 12793 VARIANT_AUTO;
michael@0 12794 nsCSSValue value;
michael@0 12795 if (!ParseVariant(value, variantMask, nullptr)) {
michael@0 12796 return false;
michael@0 12797 }
michael@0 12798
michael@0 12799 if (value.GetUnit() == eCSSUnit_None ||
michael@0 12800 value.GetUnit() == eCSSUnit_All)
michael@0 12801 {
michael@0 12802 return false;
michael@0 12803 }
michael@0 12804
michael@0 12805 if (value.GetUnit() != eCSSUnit_Ident) {
michael@0 12806 if (first) {
michael@0 12807 AppendValue(eCSSProperty_will_change, value);
michael@0 12808 return true;
michael@0 12809 } else {
michael@0 12810 return false;
michael@0 12811 }
michael@0 12812 }
michael@0 12813
michael@0 12814 nsString str;
michael@0 12815 value.GetStringValue(str);
michael@0 12816 if (str.LowerCaseEqualsLiteral("default")) {
michael@0 12817 return false;
michael@0 12818 }
michael@0 12819
michael@0 12820 currentListValue->mValue = value;
michael@0 12821
michael@0 12822 if (!ExpectSymbol(',', true)) {
michael@0 12823 break;
michael@0 12824 }
michael@0 12825 currentListValue->mNext = new nsCSSValueList;
michael@0 12826 currentListValue = currentListValue->mNext;
michael@0 12827 first = false;
michael@0 12828 }
michael@0 12829
michael@0 12830 AppendValue(eCSSProperty_will_change, listValue);
michael@0 12831 return true;
michael@0 12832 }
michael@0 12833
michael@0 12834 /* Reads a single transform function from the tokenizer stream, reporting an
michael@0 12835 * error if something goes wrong.
michael@0 12836 */
michael@0 12837 bool
michael@0 12838 CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue)
michael@0 12839 {
michael@0 12840 if (!GetToken(true))
michael@0 12841 return false;
michael@0 12842
michael@0 12843 if (mToken.mType != eCSSToken_Function) {
michael@0 12844 UngetToken();
michael@0 12845 return false;
michael@0 12846 }
michael@0 12847
michael@0 12848 const int32_t* variantMask;
michael@0 12849 uint16_t minElems, maxElems;
michael@0 12850 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
michael@0 12851
michael@0 12852 if (!GetFunctionParseInformation(keyword, aIsPrefixed,
michael@0 12853 minElems, maxElems, variantMask))
michael@0 12854 return false;
michael@0 12855
michael@0 12856 return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue);
michael@0 12857 }
michael@0 12858
michael@0 12859 /* Parses a transform property list by continuously reading in properties
michael@0 12860 * and constructing a matrix from it.
michael@0 12861 */
michael@0 12862 bool CSSParserImpl::ParseTransform(bool aIsPrefixed)
michael@0 12863 {
michael@0 12864 nsCSSValue value;
michael@0 12865 // 'inherit', 'initial', 'unset' and 'none' must be alone
michael@0 12866 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
michael@0 12867 nsCSSValueSharedList* list = new nsCSSValueSharedList;
michael@0 12868 value.SetSharedListValue(list);
michael@0 12869 list->mHead = new nsCSSValueList;
michael@0 12870 nsCSSValueList* cur = list->mHead;
michael@0 12871 for (;;) {
michael@0 12872 if (!ParseSingleTransform(aIsPrefixed, cur->mValue)) {
michael@0 12873 return false;
michael@0 12874 }
michael@0 12875 if (CheckEndProperty()) {
michael@0 12876 break;
michael@0 12877 }
michael@0 12878 cur->mNext = new nsCSSValueList;
michael@0 12879 cur = cur->mNext;
michael@0 12880 }
michael@0 12881 }
michael@0 12882 AppendValue(eCSSProperty_transform, value);
michael@0 12883 return true;
michael@0 12884 }
michael@0 12885
michael@0 12886 bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
michael@0 12887 {
michael@0 12888 nsCSSValuePair position;
michael@0 12889 if (!ParseBoxPositionValues(position, true))
michael@0 12890 return false;
michael@0 12891
michael@0 12892 nsCSSProperty prop = eCSSProperty_transform_origin;
michael@0 12893 if (aPerspective) {
michael@0 12894 prop = eCSSProperty_perspective_origin;
michael@0 12895 }
michael@0 12896
michael@0 12897 // Unlike many other uses of pairs, this position should always be stored
michael@0 12898 // as a pair, even if the values are the same, so it always serializes as
michael@0 12899 // a pair, and to keep the computation code simple.
michael@0 12900 if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
michael@0 12901 position.mXValue.GetUnit() == eCSSUnit_Initial ||
michael@0 12902 position.mXValue.GetUnit() == eCSSUnit_Unset) {
michael@0 12903 NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
michael@0 12904 "inherit/initial/unset only half?");
michael@0 12905 AppendValue(prop, position.mXValue);
michael@0 12906 } else {
michael@0 12907 nsCSSValue value;
michael@0 12908 if (aPerspective) {
michael@0 12909 value.SetPairValue(position.mXValue, position.mYValue);
michael@0 12910 } else {
michael@0 12911 nsCSSValue depth;
michael@0 12912 if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr)) {
michael@0 12913 depth.SetFloatValue(0.0f, eCSSUnit_Pixel);
michael@0 12914 }
michael@0 12915 value.SetTripletValue(position.mXValue, position.mYValue, depth);
michael@0 12916 }
michael@0 12917
michael@0 12918 AppendValue(prop, value);
michael@0 12919 }
michael@0 12920 return true;
michael@0 12921 }
michael@0 12922
michael@0 12923 /**
michael@0 12924 * Reads a drop-shadow value. At the moment the Filter Effects specification
michael@0 12925 * just expects one shadow item. Should this ever change to a list of shadow
michael@0 12926 * items, use ParseShadowList instead.
michael@0 12927 */
michael@0 12928 bool
michael@0 12929 CSSParserImpl::ParseDropShadow(nsCSSValue* aValue)
michael@0 12930 {
michael@0 12931 // Use nsCSSValueList to reuse the shadow resolving code in
michael@0 12932 // nsRuleNode and nsComputedDOMStyle.
michael@0 12933 nsCSSValue shadow;
michael@0 12934 nsCSSValueList* cur = shadow.SetListValue();
michael@0 12935 if (!ParseShadowItem(cur->mValue, false))
michael@0 12936 return false;
michael@0 12937
michael@0 12938 if (!ExpectSymbol(')', true))
michael@0 12939 return false;
michael@0 12940
michael@0 12941 nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1);
michael@0 12942
michael@0 12943 // Copy things over.
michael@0 12944 dropShadow->Item(1) = shadow;
michael@0 12945
michael@0 12946 return true;
michael@0 12947 }
michael@0 12948
michael@0 12949 /**
michael@0 12950 * Reads a single url or filter function from the tokenizer stream, reporting an
michael@0 12951 * error if something goes wrong.
michael@0 12952 */
michael@0 12953 bool
michael@0 12954 CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue)
michael@0 12955 {
michael@0 12956 if (ParseVariant(*aValue, VARIANT_URL, nullptr)) {
michael@0 12957 return true;
michael@0 12958 }
michael@0 12959
michael@0 12960 if (!nsLayoutUtils::CSSFiltersEnabled()) {
michael@0 12961 // With CSS Filters disabled, we should only accept an SVG reference filter.
michael@0 12962 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
michael@0 12963 return false;
michael@0 12964 }
michael@0 12965
michael@0 12966 if (!GetToken(true)) {
michael@0 12967 REPORT_UNEXPECTED_EOF(PEFilterEOF);
michael@0 12968 return false;
michael@0 12969 }
michael@0 12970
michael@0 12971 if (mToken.mType != eCSSToken_Function) {
michael@0 12972 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
michael@0 12973 return false;
michael@0 12974 }
michael@0 12975
michael@0 12976 nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent);
michael@0 12977 // Parse drop-shadow independently of the other filter functions
michael@0 12978 // because of its more complex characteristics.
michael@0 12979 if (functionName == eCSSKeyword_drop_shadow) {
michael@0 12980 if (ParseDropShadow(aValue)) {
michael@0 12981 return true;
michael@0 12982 } else {
michael@0 12983 // Unrecognized filter function.
michael@0 12984 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
michael@0 12985 SkipUntil(')');
michael@0 12986 return false;
michael@0 12987 }
michael@0 12988 }
michael@0 12989
michael@0 12990 // Set up the parsing rules based on the filter function.
michael@0 12991 int32_t variantMask = VARIANT_PN;
michael@0 12992 bool rejectNegativeArgument = true;
michael@0 12993 bool clampArgumentToOne = false;
michael@0 12994 switch (functionName) {
michael@0 12995 case eCSSKeyword_blur:
michael@0 12996 variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION;
michael@0 12997 // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths.
michael@0 12998 rejectNegativeArgument = false;
michael@0 12999 break;
michael@0 13000 case eCSSKeyword_brightness:
michael@0 13001 case eCSSKeyword_contrast:
michael@0 13002 case eCSSKeyword_saturate:
michael@0 13003 break;
michael@0 13004 case eCSSKeyword_grayscale:
michael@0 13005 case eCSSKeyword_invert:
michael@0 13006 case eCSSKeyword_sepia:
michael@0 13007 case eCSSKeyword_opacity:
michael@0 13008 clampArgumentToOne = true;
michael@0 13009 break;
michael@0 13010 case eCSSKeyword_hue_rotate:
michael@0 13011 variantMask = VARIANT_ANGLE;
michael@0 13012 rejectNegativeArgument = false;
michael@0 13013 break;
michael@0 13014 default:
michael@0 13015 // Unrecognized filter function.
michael@0 13016 REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction);
michael@0 13017 SkipUntil(')');
michael@0 13018 return false;
michael@0 13019 }
michael@0 13020
michael@0 13021 // Parse the function.
michael@0 13022 uint16_t minElems = 1U;
michael@0 13023 uint16_t maxElems = 1U;
michael@0 13024 uint32_t allVariants = 0;
michael@0 13025 if (!ParseFunction(functionName, &variantMask, allVariants,
michael@0 13026 minElems, maxElems, *aValue)) {
michael@0 13027 REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError);
michael@0 13028 return false;
michael@0 13029 }
michael@0 13030
michael@0 13031 // Get the first and only argument to the filter function.
michael@0 13032 NS_ABORT_IF_FALSE(aValue->GetUnit() == eCSSUnit_Function,
michael@0 13033 "expected a filter function");
michael@0 13034 NS_ABORT_IF_FALSE(aValue->UnitHasArrayValue(),
michael@0 13035 "filter function should be an array");
michael@0 13036 NS_ABORT_IF_FALSE(aValue->GetArrayValue()->Count() == 2,
michael@0 13037 "filter function should have exactly one argument");
michael@0 13038 nsCSSValue& arg = aValue->GetArrayValue()->Item(1);
michael@0 13039
michael@0 13040 if (rejectNegativeArgument &&
michael@0 13041 ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) ||
michael@0 13042 (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) {
michael@0 13043 REPORT_UNEXPECTED(PEExpectedNonnegativeNP);
michael@0 13044 return false;
michael@0 13045 }
michael@0 13046
michael@0 13047 if (clampArgumentToOne) {
michael@0 13048 if (arg.GetUnit() == eCSSUnit_Number &&
michael@0 13049 arg.GetFloatValue() > 1.0f) {
michael@0 13050 arg.SetFloatValue(1.0f, arg.GetUnit());
michael@0 13051 } else if (arg.GetUnit() == eCSSUnit_Percent &&
michael@0 13052 arg.GetPercentValue() > 1.0f) {
michael@0 13053 arg.SetPercentValue(1.0f);
michael@0 13054 }
michael@0 13055 }
michael@0 13056
michael@0 13057 return true;
michael@0 13058 }
michael@0 13059
michael@0 13060 /**
michael@0 13061 * Parses a filter property value by continuously reading in urls and/or filter
michael@0 13062 * functions and constructing a list.
michael@0 13063 *
michael@0 13064 * When CSS Filters are enabled, the filter property accepts one or more SVG
michael@0 13065 * reference filters and/or CSS filter functions.
michael@0 13066 * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%);
michael@0 13067 *
michael@0 13068 * When CSS Filters are disabled, the filter property only accepts one SVG
michael@0 13069 * reference filter.
michael@0 13070 * e.g. filter: url(#my-filter);
michael@0 13071 */
michael@0 13072 bool
michael@0 13073 CSSParserImpl::ParseFilter()
michael@0 13074 {
michael@0 13075 nsCSSValue value;
michael@0 13076 // 'inherit', 'initial', 'unset' and 'none' must be alone
michael@0 13077 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
michael@0 13078 nsCSSValueList* cur = value.SetListValue();
michael@0 13079 while (cur) {
michael@0 13080 if (!ParseSingleFilter(&cur->mValue)) {
michael@0 13081 return false;
michael@0 13082 }
michael@0 13083 if (CheckEndProperty()) {
michael@0 13084 break;
michael@0 13085 }
michael@0 13086 if (!nsLayoutUtils::CSSFiltersEnabled()) {
michael@0 13087 // With CSS Filters disabled, we should only accept one SVG reference
michael@0 13088 // filter.
michael@0 13089 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
michael@0 13090 return false;
michael@0 13091 }
michael@0 13092 cur->mNext = new nsCSSValueList;
michael@0 13093 cur = cur->mNext;
michael@0 13094 }
michael@0 13095 }
michael@0 13096 AppendValue(eCSSProperty_filter, value);
michael@0 13097 return true;
michael@0 13098 }
michael@0 13099
michael@0 13100 bool
michael@0 13101 CSSParserImpl::ParseTransitionProperty()
michael@0 13102 {
michael@0 13103 nsCSSValue value;
michael@0 13104 // 'inherit', 'initial', 'unset' and 'none' must be alone
michael@0 13105 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
michael@0 13106 // Accept a list of arbitrary identifiers. They should be
michael@0 13107 // CSS properties, but we want to accept any so that we
michael@0 13108 // accept properties that we don't know about yet, e.g.
michael@0 13109 // transition-property: invalid-property, left, opacity;
michael@0 13110 nsCSSValueList* cur = value.SetListValue();
michael@0 13111 for (;;) {
michael@0 13112 if (!ParseVariant(cur->mValue, VARIANT_IDENTIFIER | VARIANT_ALL, nullptr)) {
michael@0 13113 return false;
michael@0 13114 }
michael@0 13115 if (cur->mValue.GetUnit() == eCSSUnit_Ident) {
michael@0 13116 nsDependentString str(cur->mValue.GetStringBufferValue());
michael@0 13117 // Exclude 'none', 'inherit', 'initial' and 'unset' according to the
michael@0 13118 // same rules as for 'counter-reset' in CSS 2.1.
michael@0 13119 if (str.LowerCaseEqualsLiteral("none") ||
michael@0 13120 str.LowerCaseEqualsLiteral("inherit") ||
michael@0 13121 str.LowerCaseEqualsLiteral("initial") ||
michael@0 13122 (str.LowerCaseEqualsLiteral("unset") &&
michael@0 13123 nsLayoutUtils::UnsetValueEnabled())) {
michael@0 13124 return false;
michael@0 13125 }
michael@0 13126 }
michael@0 13127 if (!ExpectSymbol(',', true)) {
michael@0 13128 break;
michael@0 13129 }
michael@0 13130 cur->mNext = new nsCSSValueList;
michael@0 13131 cur = cur->mNext;
michael@0 13132 }
michael@0 13133 }
michael@0 13134 AppendValue(eCSSProperty_transition_property, value);
michael@0 13135 return true;
michael@0 13136 }
michael@0 13137
michael@0 13138 bool
michael@0 13139 CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue)
michael@0 13140 {
michael@0 13141 NS_ASSERTION(!mHavePushBack &&
michael@0 13142 mToken.mType == eCSSToken_Function &&
michael@0 13143 mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"),
michael@0 13144 "unexpected initial state");
michael@0 13145
michael@0 13146 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(4);
michael@0 13147
michael@0 13148 float x1, x2, y1, y2;
michael@0 13149 if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) ||
michael@0 13150 !ParseTransitionTimingFunctionValueComponent(y1, ',', false) ||
michael@0 13151 !ParseTransitionTimingFunctionValueComponent(x2, ',', true) ||
michael@0 13152 !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) {
michael@0 13153 return false;
michael@0 13154 }
michael@0 13155
michael@0 13156 val->Item(0).SetFloatValue(x1, eCSSUnit_Number);
michael@0 13157 val->Item(1).SetFloatValue(y1, eCSSUnit_Number);
michael@0 13158 val->Item(2).SetFloatValue(x2, eCSSUnit_Number);
michael@0 13159 val->Item(3).SetFloatValue(y2, eCSSUnit_Number);
michael@0 13160
michael@0 13161 aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier);
michael@0 13162
michael@0 13163 return true;
michael@0 13164 }
michael@0 13165
michael@0 13166 bool
michael@0 13167 CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent,
michael@0 13168 char aStop,
michael@0 13169 bool aCheckRange)
michael@0 13170 {
michael@0 13171 if (!GetToken(true)) {
michael@0 13172 return false;
michael@0 13173 }
michael@0 13174 nsCSSToken* tk = &mToken;
michael@0 13175 if (tk->mType == eCSSToken_Number) {
michael@0 13176 float num = tk->mNumber;
michael@0 13177 if (aCheckRange && (num < 0.0 || num > 1.0)) {
michael@0 13178 return false;
michael@0 13179 }
michael@0 13180 aComponent = num;
michael@0 13181 if (ExpectSymbol(aStop, true)) {
michael@0 13182 return true;
michael@0 13183 }
michael@0 13184 }
michael@0 13185 return false;
michael@0 13186 }
michael@0 13187
michael@0 13188 bool
michael@0 13189 CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
michael@0 13190 {
michael@0 13191 NS_ASSERTION(!mHavePushBack &&
michael@0 13192 mToken.mType == eCSSToken_Function &&
michael@0 13193 mToken.mIdent.LowerCaseEqualsLiteral("steps"),
michael@0 13194 "unexpected initial state");
michael@0 13195
michael@0 13196 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
michael@0 13197
michael@0 13198 if (!ParseOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, nullptr)) {
michael@0 13199 return false;
michael@0 13200 }
michael@0 13201
michael@0 13202 int32_t type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
michael@0 13203 if (ExpectSymbol(',', true)) {
michael@0 13204 if (!GetToken(true)) {
michael@0 13205 return false;
michael@0 13206 }
michael@0 13207 type = -1;
michael@0 13208 if (mToken.mType == eCSSToken_Ident) {
michael@0 13209 if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
michael@0 13210 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;
michael@0 13211 } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) {
michael@0 13212 type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
michael@0 13213 }
michael@0 13214 }
michael@0 13215 if (type == -1) {
michael@0 13216 UngetToken();
michael@0 13217 return false;
michael@0 13218 }
michael@0 13219 }
michael@0 13220 val->Item(1).SetIntValue(type, eCSSUnit_Enumerated);
michael@0 13221
michael@0 13222 if (!ExpectSymbol(')', true)) {
michael@0 13223 return false;
michael@0 13224 }
michael@0 13225
michael@0 13226 aValue.SetArrayValue(val, eCSSUnit_Steps);
michael@0 13227 return true;
michael@0 13228 }
michael@0 13229
michael@0 13230 static nsCSSValueList*
michael@0 13231 AppendValueToList(nsCSSValue& aContainer,
michael@0 13232 nsCSSValueList* aTail,
michael@0 13233 const nsCSSValue& aValue)
michael@0 13234 {
michael@0 13235 nsCSSValueList* entry;
michael@0 13236 if (aContainer.GetUnit() == eCSSUnit_Null) {
michael@0 13237 NS_ABORT_IF_FALSE(!aTail, "should not have an entry");
michael@0 13238 entry = aContainer.SetListValue();
michael@0 13239 } else {
michael@0 13240 NS_ABORT_IF_FALSE(!aTail->mNext, "should not have a next entry");
michael@0 13241 NS_ABORT_IF_FALSE(aContainer.GetUnit() == eCSSUnit_List, "not a list");
michael@0 13242 entry = new nsCSSValueList;
michael@0 13243 aTail->mNext = entry;
michael@0 13244 }
michael@0 13245 entry->mValue = aValue;
michael@0 13246 return entry;
michael@0 13247 }
michael@0 13248
michael@0 13249 CSSParserImpl::ParseAnimationOrTransitionShorthandResult
michael@0 13250 CSSParserImpl::ParseAnimationOrTransitionShorthand(
michael@0 13251 const nsCSSProperty* aProperties,
michael@0 13252 const nsCSSValue* aInitialValues,
michael@0 13253 nsCSSValue* aValues,
michael@0 13254 size_t aNumProperties)
michael@0 13255 {
michael@0 13256 nsCSSValue tempValue;
michael@0 13257 // first see if 'inherit', 'initial' or 'unset' is specified. If one is,
michael@0 13258 // it can be the only thing specified, so don't attempt to parse any
michael@0 13259 // additional properties
michael@0 13260 if (ParseVariant(tempValue, VARIANT_INHERIT, nullptr)) {
michael@0 13261 for (uint32_t i = 0; i < aNumProperties; ++i) {
michael@0 13262 AppendValue(aProperties[i], tempValue);
michael@0 13263 }
michael@0 13264 return eParseAnimationOrTransitionShorthand_Inherit;
michael@0 13265 }
michael@0 13266
michael@0 13267 static const size_t maxNumProperties = 7;
michael@0 13268 NS_ABORT_IF_FALSE(aNumProperties <= maxNumProperties,
michael@0 13269 "can't handle this many properties");
michael@0 13270 nsCSSValueList *cur[maxNumProperties];
michael@0 13271 bool parsedProperty[maxNumProperties];
michael@0 13272
michael@0 13273 for (size_t i = 0; i < aNumProperties; ++i) {
michael@0 13274 cur[i] = nullptr;
michael@0 13275 }
michael@0 13276 bool atEOP = false; // at end of property?
michael@0 13277 for (;;) { // loop over comma-separated transitions or animations
michael@0 13278 // whether a particular subproperty was specified for this
michael@0 13279 // transition or animation
michael@0 13280 bool haveAnyProperty = false;
michael@0 13281 for (size_t i = 0; i < aNumProperties; ++i) {
michael@0 13282 parsedProperty[i] = false;
michael@0 13283 }
michael@0 13284 for (;;) { // loop over values within a transition or animation
michael@0 13285 bool foundProperty = false;
michael@0 13286 // check to see if we're at the end of one full transition or
michael@0 13287 // animation definition (either because we hit a comma or because
michael@0 13288 // we hit the end of the property definition)
michael@0 13289 if (ExpectSymbol(',', true))
michael@0 13290 break;
michael@0 13291 if (CheckEndProperty()) {
michael@0 13292 atEOP = true;
michael@0 13293 break;
michael@0 13294 }
michael@0 13295
michael@0 13296 // else, try to parse the next transition or animation sub-property
michael@0 13297 for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) {
michael@0 13298 if (!parsedProperty[i]) {
michael@0 13299 // if we haven't found this property yet, try to parse it
michael@0 13300 if (ParseSingleValueProperty(tempValue, aProperties[i])) {
michael@0 13301 parsedProperty[i] = true;
michael@0 13302 cur[i] = AppendValueToList(aValues[i], cur[i], tempValue);
michael@0 13303 foundProperty = true;
michael@0 13304 haveAnyProperty = true;
michael@0 13305 break; // out of inner loop; continue looking for next sub-property
michael@0 13306 }
michael@0 13307 }
michael@0 13308 }
michael@0 13309 if (!foundProperty) {
michael@0 13310 // We're not at a ',' or at the end of the property, but we couldn't
michael@0 13311 // parse any of the sub-properties, so the declaration is invalid.
michael@0 13312 return eParseAnimationOrTransitionShorthand_Error;
michael@0 13313 }
michael@0 13314 }
michael@0 13315
michael@0 13316 if (!haveAnyProperty) {
michael@0 13317 // Got an empty item.
michael@0 13318 return eParseAnimationOrTransitionShorthand_Error;
michael@0 13319 }
michael@0 13320
michael@0 13321 // We hit the end of the property or the end of one transition
michael@0 13322 // or animation definition, add its components to the list.
michael@0 13323 for (uint32_t i = 0; i < aNumProperties; ++i) {
michael@0 13324 // If all of the subproperties were not explicitly specified, fill
michael@0 13325 // in the missing ones with initial values.
michael@0 13326 if (!parsedProperty[i]) {
michael@0 13327 cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]);
michael@0 13328 }
michael@0 13329 }
michael@0 13330
michael@0 13331 if (atEOP)
michael@0 13332 break;
michael@0 13333 // else we just hit a ',' so continue parsing the next compound transition
michael@0 13334 }
michael@0 13335
michael@0 13336 return eParseAnimationOrTransitionShorthand_Values;
michael@0 13337 }
michael@0 13338
michael@0 13339 bool
michael@0 13340 CSSParserImpl::ParseTransition()
michael@0 13341 {
michael@0 13342 static const nsCSSProperty kTransitionProperties[] = {
michael@0 13343 eCSSProperty_transition_duration,
michael@0 13344 eCSSProperty_transition_timing_function,
michael@0 13345 // Must check 'transition-delay' after 'transition-duration', since
michael@0 13346 // that's our assumption about what the spec means for the shorthand
michael@0 13347 // syntax (the first time given is the duration, and the second
michael@0 13348 // given is the delay).
michael@0 13349 eCSSProperty_transition_delay,
michael@0 13350 // Must check 'transition-property' after
michael@0 13351 // 'transition-timing-function' since 'transition-property' accepts
michael@0 13352 // any keyword.
michael@0 13353 eCSSProperty_transition_property
michael@0 13354 };
michael@0 13355 static const uint32_t numProps = MOZ_ARRAY_LENGTH(kTransitionProperties);
michael@0 13356 // this is a shorthand property that accepts -property, -delay,
michael@0 13357 // -duration, and -timing-function with some components missing.
michael@0 13358 // there can be multiple transitions, separated with commas
michael@0 13359
michael@0 13360 nsCSSValue initialValues[numProps];
michael@0 13361 initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
michael@0 13362 initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
michael@0 13363 eCSSUnit_Enumerated);
michael@0 13364 initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
michael@0 13365 initialValues[3].SetAllValue();
michael@0 13366
michael@0 13367 nsCSSValue values[numProps];
michael@0 13368
michael@0 13369 ParseAnimationOrTransitionShorthandResult spres =
michael@0 13370 ParseAnimationOrTransitionShorthand(kTransitionProperties,
michael@0 13371 initialValues, values, numProps);
michael@0 13372 if (spres != eParseAnimationOrTransitionShorthand_Values) {
michael@0 13373 return spres != eParseAnimationOrTransitionShorthand_Error;
michael@0 13374 }
michael@0 13375
michael@0 13376 // Make two checks on the list for 'transition-property':
michael@0 13377 // + If there is more than one item, then none of the items can be
michael@0 13378 // 'none'.
michael@0 13379 // + None of the items can be 'inherit', 'initial' or 'unset'.
michael@0 13380 {
michael@0 13381 NS_ABORT_IF_FALSE(kTransitionProperties[3] ==
michael@0 13382 eCSSProperty_transition_property,
michael@0 13383 "array index mismatch");
michael@0 13384 nsCSSValueList *l = values[3].GetListValue();
michael@0 13385 bool multipleItems = !!l->mNext;
michael@0 13386 do {
michael@0 13387 const nsCSSValue& val = l->mValue;
michael@0 13388 if (val.GetUnit() == eCSSUnit_None) {
michael@0 13389 if (multipleItems) {
michael@0 13390 // This is a syntax error.
michael@0 13391 return false;
michael@0 13392 }
michael@0 13393
michael@0 13394 // Unbox a solitary 'none'.
michael@0 13395 values[3].SetNoneValue();
michael@0 13396 break;
michael@0 13397 }
michael@0 13398 if (val.GetUnit() == eCSSUnit_Ident) {
michael@0 13399 nsDependentString str(val.GetStringBufferValue());
michael@0 13400 if (str.EqualsLiteral("inherit") ||
michael@0 13401 str.EqualsLiteral("initial") ||
michael@0 13402 (str.EqualsLiteral("unset") &&
michael@0 13403 nsLayoutUtils::UnsetValueEnabled())) {
michael@0 13404 return false;
michael@0 13405 }
michael@0 13406 }
michael@0 13407 } while ((l = l->mNext));
michael@0 13408 }
michael@0 13409
michael@0 13410 // Save all parsed transition sub-properties in mTempData
michael@0 13411 for (uint32_t i = 0; i < numProps; ++i) {
michael@0 13412 AppendValue(kTransitionProperties[i], values[i]);
michael@0 13413 }
michael@0 13414 return true;
michael@0 13415 }
michael@0 13416
michael@0 13417 bool
michael@0 13418 CSSParserImpl::ParseAnimation()
michael@0 13419 {
michael@0 13420 static const nsCSSProperty kAnimationProperties[] = {
michael@0 13421 eCSSProperty_animation_duration,
michael@0 13422 eCSSProperty_animation_timing_function,
michael@0 13423 // Must check 'animation-delay' after 'animation-duration', since
michael@0 13424 // that's our assumption about what the spec means for the shorthand
michael@0 13425 // syntax (the first time given is the duration, and the second
michael@0 13426 // given is the delay).
michael@0 13427 eCSSProperty_animation_delay,
michael@0 13428 eCSSProperty_animation_direction,
michael@0 13429 eCSSProperty_animation_fill_mode,
michael@0 13430 eCSSProperty_animation_iteration_count,
michael@0 13431 // Must check 'animation-name' after 'animation-timing-function',
michael@0 13432 // 'animation-direction', 'animation-fill-mode',
michael@0 13433 // 'animation-iteration-count', and 'animation-play-state' since
michael@0 13434 // 'animation-name' accepts any keyword.
michael@0 13435 eCSSProperty_animation_name
michael@0 13436 };
michael@0 13437 static const uint32_t numProps = MOZ_ARRAY_LENGTH(kAnimationProperties);
michael@0 13438 // this is a shorthand property that accepts -property, -delay,
michael@0 13439 // -duration, and -timing-function with some components missing.
michael@0 13440 // there can be multiple animations, separated with commas
michael@0 13441
michael@0 13442 nsCSSValue initialValues[numProps];
michael@0 13443 initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds);
michael@0 13444 initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE,
michael@0 13445 eCSSUnit_Enumerated);
michael@0 13446 initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds);
michael@0 13447 initialValues[3].SetIntValue(NS_STYLE_ANIMATION_DIRECTION_NORMAL, eCSSUnit_Enumerated);
michael@0 13448 initialValues[4].SetIntValue(NS_STYLE_ANIMATION_FILL_MODE_NONE, eCSSUnit_Enumerated);
michael@0 13449 initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number);
michael@0 13450 initialValues[6].SetNoneValue();
michael@0 13451
michael@0 13452 nsCSSValue values[numProps];
michael@0 13453
michael@0 13454 ParseAnimationOrTransitionShorthandResult spres =
michael@0 13455 ParseAnimationOrTransitionShorthand(kAnimationProperties,
michael@0 13456 initialValues, values, numProps);
michael@0 13457 if (spres != eParseAnimationOrTransitionShorthand_Values) {
michael@0 13458 return spres != eParseAnimationOrTransitionShorthand_Error;
michael@0 13459 }
michael@0 13460
michael@0 13461 // Save all parsed animation sub-properties in mTempData
michael@0 13462 for (uint32_t i = 0; i < numProps; ++i) {
michael@0 13463 AppendValue(kAnimationProperties[i], values[i]);
michael@0 13464 }
michael@0 13465 return true;
michael@0 13466 }
michael@0 13467
michael@0 13468 bool
michael@0 13469 CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow)
michael@0 13470 {
michael@0 13471 // A shadow list item is an array, with entries in this sequence:
michael@0 13472 enum {
michael@0 13473 IndexX,
michael@0 13474 IndexY,
michael@0 13475 IndexRadius,
michael@0 13476 IndexSpread, // only for box-shadow
michael@0 13477 IndexColor,
michael@0 13478 IndexInset // only for box-shadow
michael@0 13479 };
michael@0 13480
michael@0 13481 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6);
michael@0 13482
michael@0 13483 if (aIsBoxShadow) {
michael@0 13484 // Optional inset keyword (ignore errors)
michael@0 13485 ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
michael@0 13486 nsCSSProps::kBoxShadowTypeKTable);
michael@0 13487 }
michael@0 13488
michael@0 13489 nsCSSValue xOrColor;
michael@0 13490 bool haveColor = false;
michael@0 13491 if (!ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC,
michael@0 13492 nullptr)) {
michael@0 13493 return false;
michael@0 13494 }
michael@0 13495 if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) {
michael@0 13496 val->Item(IndexX) = xOrColor;
michael@0 13497 } else {
michael@0 13498 // Must be a color (as string or color value)
michael@0 13499 NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident ||
michael@0 13500 xOrColor.GetUnit() == eCSSUnit_EnumColor ||
michael@0 13501 xOrColor.IsNumericColorUnit(),
michael@0 13502 "Must be a color value");
michael@0 13503 val->Item(IndexColor) = xOrColor;
michael@0 13504 haveColor = true;
michael@0 13505
michael@0 13506 // X coordinate mandatory after color
michael@0 13507 if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC,
michael@0 13508 nullptr)) {
michael@0 13509 return false;
michael@0 13510 }
michael@0 13511 }
michael@0 13512
michael@0 13513 // Y coordinate; mandatory
michael@0 13514 if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC,
michael@0 13515 nullptr)) {
michael@0 13516 return false;
michael@0 13517 }
michael@0 13518
michael@0 13519 // Optional radius. Ignore errors except if they pass a negative
michael@0 13520 // value which we must reject. If we use ParseNonNegativeVariant
michael@0 13521 // we can't tell the difference between an unspecified radius
michael@0 13522 // and a negative radius.
michael@0 13523 if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC,
michael@0 13524 nullptr) &&
michael@0 13525 val->Item(IndexRadius).IsLengthUnit() &&
michael@0 13526 val->Item(IndexRadius).GetFloatValue() < 0) {
michael@0 13527 return false;
michael@0 13528 }
michael@0 13529
michael@0 13530 if (aIsBoxShadow) {
michael@0 13531 // Optional spread
michael@0 13532 ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC, nullptr);
michael@0 13533 }
michael@0 13534
michael@0 13535 if (!haveColor) {
michael@0 13536 // Optional color
michael@0 13537 ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr);
michael@0 13538 }
michael@0 13539
michael@0 13540 if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) {
michael@0 13541 // Optional inset keyword
michael@0 13542 ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
michael@0 13543 nsCSSProps::kBoxShadowTypeKTable);
michael@0 13544 }
michael@0 13545
michael@0 13546 aValue.SetArrayValue(val, eCSSUnit_Array);
michael@0 13547 return true;
michael@0 13548 }
michael@0 13549
michael@0 13550 bool
michael@0 13551 CSSParserImpl::ParseShadowList(nsCSSProperty aProperty)
michael@0 13552 {
michael@0 13553 nsAutoParseCompoundProperty compound(this);
michael@0 13554 bool isBoxShadow = aProperty == eCSSProperty_box_shadow;
michael@0 13555
michael@0 13556 nsCSSValue value;
michael@0 13557 // 'inherit', 'initial', 'unset' and 'none' must be alone
michael@0 13558 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
michael@0 13559 nsCSSValueList* cur = value.SetListValue();
michael@0 13560 for (;;) {
michael@0 13561 if (!ParseShadowItem(cur->mValue, isBoxShadow)) {
michael@0 13562 return false;
michael@0 13563 }
michael@0 13564 if (!ExpectSymbol(',', true)) {
michael@0 13565 break;
michael@0 13566 }
michael@0 13567 cur->mNext = new nsCSSValueList;
michael@0 13568 cur = cur->mNext;
michael@0 13569 }
michael@0 13570 }
michael@0 13571 AppendValue(aProperty, value);
michael@0 13572 return true;
michael@0 13573 }
michael@0 13574
michael@0 13575 int32_t
michael@0 13576 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix)
michael@0 13577 {
michael@0 13578 NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
michael@0 13579
michael@0 13580 int32_t nameSpaceID = kNameSpaceID_Unknown;
michael@0 13581 if (mNameSpaceMap) {
michael@0 13582 // user-specified identifiers are case-sensitive (bug 416106)
michael@0 13583 nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
michael@0 13584 if (!prefix) {
michael@0 13585 NS_RUNTIMEABORT("do_GetAtom failed - out of memory?");
michael@0 13586 }
michael@0 13587 nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
michael@0 13588 }
michael@0 13589 // else no declared namespaces
michael@0 13590
michael@0 13591 if (nameSpaceID == kNameSpaceID_Unknown) { // unknown prefix, dump it
michael@0 13592 REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, aPrefix);
michael@0 13593 }
michael@0 13594
michael@0 13595 return nameSpaceID;
michael@0 13596 }
michael@0 13597
michael@0 13598 void
michael@0 13599 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
michael@0 13600 {
michael@0 13601 if (mNameSpaceMap) {
michael@0 13602 aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nullptr));
michael@0 13603 } else {
michael@0 13604 aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
michael@0 13605 }
michael@0 13606 }
michael@0 13607
michael@0 13608 bool
michael@0 13609 CSSParserImpl::ParsePaint(nsCSSProperty aPropID)
michael@0 13610 {
michael@0 13611 nsCSSValue x, y;
michael@0 13612
michael@0 13613 if (!ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL |
michael@0 13614 VARIANT_OPENTYPE_SVG_KEYWORD,
michael@0 13615 nsCSSProps::kContextPatternKTable)) {
michael@0 13616 return false;
michael@0 13617 }
michael@0 13618
michael@0 13619 bool canHaveFallback = x.GetUnit() == eCSSUnit_URL ||
michael@0 13620 x.GetUnit() == eCSSUnit_Enumerated;
michael@0 13621 if (canHaveFallback) {
michael@0 13622 if (!ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr))
michael@0 13623 y.SetNoneValue();
michael@0 13624 }
michael@0 13625
michael@0 13626 if (!canHaveFallback) {
michael@0 13627 AppendValue(aPropID, x);
michael@0 13628 } else {
michael@0 13629 nsCSSValue val;
michael@0 13630 val.SetPairValue(x, y);
michael@0 13631 AppendValue(aPropID, val);
michael@0 13632 }
michael@0 13633 return true;
michael@0 13634 }
michael@0 13635
michael@0 13636 bool
michael@0 13637 CSSParserImpl::ParseDasharray()
michael@0 13638 {
michael@0 13639 nsCSSValue value;
michael@0 13640
michael@0 13641 // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
michael@0 13642 if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE |
michael@0 13643 VARIANT_OPENTYPE_SVG_KEYWORD,
michael@0 13644 nsCSSProps::kStrokeContextValueKTable)) {
michael@0 13645 nsCSSValueList *cur = value.SetListValue();
michael@0 13646 for (;;) {
michael@0 13647 if (!ParseNonNegativeVariant(cur->mValue, VARIANT_LPN, nullptr)) {
michael@0 13648 return false;
michael@0 13649 }
michael@0 13650 if (CheckEndProperty()) {
michael@0 13651 break;
michael@0 13652 }
michael@0 13653 // skip optional commas between elements
michael@0 13654 (void)ExpectSymbol(',', true);
michael@0 13655
michael@0 13656 cur->mNext = new nsCSSValueList;
michael@0 13657 cur = cur->mNext;
michael@0 13658 }
michael@0 13659 }
michael@0 13660 AppendValue(eCSSProperty_stroke_dasharray, value);
michael@0 13661 return true;
michael@0 13662 }
michael@0 13663
michael@0 13664 bool
michael@0 13665 CSSParserImpl::ParseMarker()
michael@0 13666 {
michael@0 13667 nsCSSValue marker;
michael@0 13668 if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) {
michael@0 13669 AppendValue(eCSSProperty_marker_end, marker);
michael@0 13670 AppendValue(eCSSProperty_marker_mid, marker);
michael@0 13671 AppendValue(eCSSProperty_marker_start, marker);
michael@0 13672 return true;
michael@0 13673 }
michael@0 13674 return false;
michael@0 13675 }
michael@0 13676
michael@0 13677 bool
michael@0 13678 CSSParserImpl::ParsePaintOrder()
michael@0 13679 {
michael@0 13680 static_assert
michael@0 13681 ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) > NS_STYLE_PAINT_ORDER_LAST_VALUE,
michael@0 13682 "bitfield width insufficient for paint-order constants");
michael@0 13683
michael@0 13684 static const KTableValue kPaintOrderKTable[] = {
michael@0 13685 eCSSKeyword_normal, NS_STYLE_PAINT_ORDER_NORMAL,
michael@0 13686 eCSSKeyword_fill, NS_STYLE_PAINT_ORDER_FILL,
michael@0 13687 eCSSKeyword_stroke, NS_STYLE_PAINT_ORDER_STROKE,
michael@0 13688 eCSSKeyword_markers, NS_STYLE_PAINT_ORDER_MARKERS,
michael@0 13689 eCSSKeyword_UNKNOWN,-1
michael@0 13690 };
michael@0 13691
michael@0 13692 static_assert(MOZ_ARRAY_LENGTH(kPaintOrderKTable) ==
michael@0 13693 2 * (NS_STYLE_PAINT_ORDER_LAST_VALUE + 2),
michael@0 13694 "missing paint-order values in kPaintOrderKTable");
michael@0 13695
michael@0 13696 nsCSSValue value;
michael@0 13697 if (!ParseVariant(value, VARIANT_HK, kPaintOrderKTable)) {
michael@0 13698 return false;
michael@0 13699 }
michael@0 13700
michael@0 13701 uint32_t seen = 0;
michael@0 13702 uint32_t order = 0;
michael@0 13703 uint32_t position = 0;
michael@0 13704
michael@0 13705 // Ensure that even cast to a signed int32_t when stored in CSSValue,
michael@0 13706 // we have enough space for the entire paint-order value.
michael@0 13707 static_assert
michael@0 13708 (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE < 32,
michael@0 13709 "seen and order not big enough");
michael@0 13710
michael@0 13711 if (value.GetUnit() == eCSSUnit_Enumerated) {
michael@0 13712 uint32_t component = static_cast<uint32_t>(value.GetIntValue());
michael@0 13713 if (component != NS_STYLE_PAINT_ORDER_NORMAL) {
michael@0 13714 bool parsedOK = true;
michael@0 13715 for (;;) {
michael@0 13716 if (seen & (1 << component)) {
michael@0 13717 // Already seen this component.
michael@0 13718 UngetToken();
michael@0 13719 parsedOK = false;
michael@0 13720 break;
michael@0 13721 }
michael@0 13722 seen |= (1 << component);
michael@0 13723 order |= (component << position);
michael@0 13724 position += NS_STYLE_PAINT_ORDER_BITWIDTH;
michael@0 13725 if (!ParseEnum(value, kPaintOrderKTable)) {
michael@0 13726 break;
michael@0 13727 }
michael@0 13728 component = value.GetIntValue();
michael@0 13729 if (component == NS_STYLE_PAINT_ORDER_NORMAL) {
michael@0 13730 // Can't have "normal" in the middle of the list of paint components.
michael@0 13731 UngetToken();
michael@0 13732 parsedOK = false;
michael@0 13733 break;
michael@0 13734 }
michael@0 13735 }
michael@0 13736
michael@0 13737 // Fill in the remaining paint-order components in the order of their
michael@0 13738 // constant values.
michael@0 13739 if (parsedOK) {
michael@0 13740 for (component = 1;
michael@0 13741 component <= NS_STYLE_PAINT_ORDER_LAST_VALUE;
michael@0 13742 component++) {
michael@0 13743 if (!(seen & (1 << component))) {
michael@0 13744 order |= (component << position);
michael@0 13745 position += NS_STYLE_PAINT_ORDER_BITWIDTH;
michael@0 13746 }
michael@0 13747 }
michael@0 13748 }
michael@0 13749 }
michael@0 13750
michael@0 13751 static_assert(NS_STYLE_PAINT_ORDER_NORMAL == 0,
michael@0 13752 "unexpected value for NS_STYLE_PAINT_ORDER_NORMAL");
michael@0 13753 value.SetIntValue(static_cast<int32_t>(order), eCSSUnit_Enumerated);
michael@0 13754 }
michael@0 13755
michael@0 13756 AppendValue(eCSSProperty_paint_order, value);
michael@0 13757 return true;
michael@0 13758 }
michael@0 13759
michael@0 13760 bool
michael@0 13761 CSSParserImpl::BackslashDropped()
michael@0 13762 {
michael@0 13763 return mScanner->GetEOFCharacters() &
michael@0 13764 nsCSSScanner::eEOFCharacters_DropBackslash;
michael@0 13765 }
michael@0 13766
michael@0 13767 void
michael@0 13768 CSSParserImpl::AppendImpliedEOFCharacters(nsAString& aResult)
michael@0 13769 {
michael@0 13770 nsCSSScanner::AppendImpliedEOFCharacters(mScanner->GetEOFCharacters(),
michael@0 13771 aResult);
michael@0 13772 }
michael@0 13773
michael@0 13774 bool
michael@0 13775 CSSParserImpl::ParseAll()
michael@0 13776 {
michael@0 13777 nsCSSValue value;
michael@0 13778 if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
michael@0 13779 return false;
michael@0 13780 }
michael@0 13781
michael@0 13782 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all) {
michael@0 13783 AppendValue(*p, value);
michael@0 13784 }
michael@0 13785 return true;
michael@0 13786 }
michael@0 13787
michael@0 13788 bool
michael@0 13789 CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type* aType,
michael@0 13790 nsString& aValue)
michael@0 13791 {
michael@0 13792 CSSVariableDeclarations::Type type;
michael@0 13793 nsString variableValue;
michael@0 13794 bool dropBackslash;
michael@0 13795 nsString impliedCharacters;
michael@0 13796
michael@0 13797 // Record the token stream while parsing a variable value.
michael@0 13798 if (!mInSupportsCondition) {
michael@0 13799 mScanner->StartRecording();
michael@0 13800 }
michael@0 13801 if (!ParseValueWithVariables(&type, &dropBackslash, impliedCharacters,
michael@0 13802 nullptr, nullptr)) {
michael@0 13803 if (!mInSupportsCondition) {
michael@0 13804 mScanner->StopRecording();
michael@0 13805 }
michael@0 13806 return false;
michael@0 13807 }
michael@0 13808
michael@0 13809 if (!mInSupportsCondition) {
michael@0 13810 if (type == CSSVariableDeclarations::eTokenStream) {
michael@0 13811 // This was indeed a token stream value, so store it in variableValue.
michael@0 13812 mScanner->StopRecording(variableValue);
michael@0 13813 if (dropBackslash) {
michael@0 13814 MOZ_ASSERT(!variableValue.IsEmpty() &&
michael@0 13815 variableValue[variableValue.Length() - 1] == '\\');
michael@0 13816 variableValue.Truncate(variableValue.Length() - 1);
michael@0 13817 }
michael@0 13818 variableValue.Append(impliedCharacters);
michael@0 13819 } else {
michael@0 13820 // This was either 'inherit' or 'initial'; we don't need the recorded
michael@0 13821 // input.
michael@0 13822 mScanner->StopRecording();
michael@0 13823 }
michael@0 13824 }
michael@0 13825
michael@0 13826 if (mHavePushBack && type == CSSVariableDeclarations::eTokenStream) {
michael@0 13827 // If we came to the end of a valid variable declaration and a token was
michael@0 13828 // pushed back, then it would have been ended by '!', ')', ';', ']' or '}'.
michael@0 13829 // We need to remove it from the recorded variable value.
michael@0 13830 MOZ_ASSERT(mToken.IsSymbol('!') ||
michael@0 13831 mToken.IsSymbol(')') ||
michael@0 13832 mToken.IsSymbol(';') ||
michael@0 13833 mToken.IsSymbol(']') ||
michael@0 13834 mToken.IsSymbol('}'));
michael@0 13835 if (!mInSupportsCondition) {
michael@0 13836 MOZ_ASSERT(!variableValue.IsEmpty());
michael@0 13837 MOZ_ASSERT(variableValue[variableValue.Length() - 1] == mToken.mSymbol);
michael@0 13838 variableValue.Truncate(variableValue.Length() - 1);
michael@0 13839 }
michael@0 13840 }
michael@0 13841
michael@0 13842 *aType = type;
michael@0 13843 aValue = variableValue;
michael@0 13844 return true;
michael@0 13845 }
michael@0 13846
michael@0 13847 bool
michael@0 13848 CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type* aType,
michael@0 13849 bool* aDropBackslash,
michael@0 13850 nsString& aImpliedCharacters,
michael@0 13851 void (*aFunc)(const nsAString&, void*),
michael@0 13852 void* aData)
michael@0 13853 {
michael@0 13854 // A property value is invalid if it contains variable references and also:
michael@0 13855 //
michael@0 13856 // * has unbalanced parens, brackets or braces
michael@0 13857 // * has any BAD_STRING or BAD_URL tokens
michael@0 13858 // * has any ';' or '!' tokens at the top level of a variable reference's
michael@0 13859 // fallback
michael@0 13860 //
michael@0 13861 // If the property is a custom property (i.e. a variable declaration), then
michael@0 13862 // it is also invalid if it consists of no tokens, such as:
michael@0 13863 //
michael@0 13864 // --invalid:;
michael@0 13865 //
michael@0 13866 // Note that is valid for a custom property to have a value that consists
michael@0 13867 // solely of white space, such as:
michael@0 13868 //
michael@0 13869 // --valid: ;
michael@0 13870
michael@0 13871 // Stack of closing characters for currently open constructs.
michael@0 13872 StopSymbolCharStack stack;
michael@0 13873
michael@0 13874 // Indexes into ')' characters in |stack| that correspond to "var(". This
michael@0 13875 // is used to stop parsing when we encounter a '!' or ';' at the top level
michael@0 13876 // of a variable reference's fallback.
michael@0 13877 nsAutoTArray<uint32_t, 16> references;
michael@0 13878
michael@0 13879 if (!GetToken(false)) {
michael@0 13880 // Variable value was empty since we reached EOF.
michael@0 13881 REPORT_UNEXPECTED_EOF(PEVariableEOF);
michael@0 13882 return false;
michael@0 13883 }
michael@0 13884
michael@0 13885 if (mToken.mType == eCSSToken_Symbol &&
michael@0 13886 (mToken.mSymbol == '!' ||
michael@0 13887 mToken.mSymbol == ')' ||
michael@0 13888 mToken.mSymbol == ';' ||
michael@0 13889 mToken.mSymbol == ']' ||
michael@0 13890 mToken.mSymbol == '}')) {
michael@0 13891 // Variable value was empty since we reached the end of the construct.
michael@0 13892 UngetToken();
michael@0 13893 REPORT_UNEXPECTED_TOKEN(PEVariableEmpty);
michael@0 13894 return false;
michael@0 13895 }
michael@0 13896
michael@0 13897 if (mToken.mType == eCSSToken_Whitespace) {
michael@0 13898 if (!GetToken(true)) {
michael@0 13899 // Variable value was white space only. This is valid.
michael@0 13900 MOZ_ASSERT(!BackslashDropped());
michael@0 13901 *aType = CSSVariableDeclarations::eTokenStream;
michael@0 13902 *aDropBackslash = false;
michael@0 13903 AppendImpliedEOFCharacters(aImpliedCharacters);
michael@0 13904 return true;
michael@0 13905 }
michael@0 13906 }
michael@0 13907
michael@0 13908 // Look for 'initial', 'inherit' or 'unset' as the first non-white space
michael@0 13909 // token.
michael@0 13910 CSSVariableDeclarations::Type type = CSSVariableDeclarations::eTokenStream;
michael@0 13911 if (mToken.mType == eCSSToken_Ident) {
michael@0 13912 if (mToken.mIdent.LowerCaseEqualsLiteral("initial")) {
michael@0 13913 type = CSSVariableDeclarations::eInitial;
michael@0 13914 } else if (mToken.mIdent.LowerCaseEqualsLiteral("inherit")) {
michael@0 13915 type = CSSVariableDeclarations::eInherit;
michael@0 13916 } else if (mToken.mIdent.LowerCaseEqualsLiteral("unset")) {
michael@0 13917 type = CSSVariableDeclarations::eUnset;
michael@0 13918 }
michael@0 13919 }
michael@0 13920
michael@0 13921 if (type != CSSVariableDeclarations::eTokenStream) {
michael@0 13922 if (!GetToken(true)) {
michael@0 13923 // Variable value was 'initial' or 'inherit' followed by EOF.
michael@0 13924 MOZ_ASSERT(!BackslashDropped());
michael@0 13925 *aType = type;
michael@0 13926 *aDropBackslash = false;
michael@0 13927 AppendImpliedEOFCharacters(aImpliedCharacters);
michael@0 13928 return true;
michael@0 13929 }
michael@0 13930 UngetToken();
michael@0 13931 if (mToken.mType == eCSSToken_Symbol &&
michael@0 13932 (mToken.mSymbol == '!' ||
michael@0 13933 mToken.mSymbol == ')' ||
michael@0 13934 mToken.mSymbol == ';' ||
michael@0 13935 mToken.mSymbol == ']' ||
michael@0 13936 mToken.mSymbol == '}')) {
michael@0 13937 // Variable value was 'initial' or 'inherit' followed by the end
michael@0 13938 // of the declaration.
michael@0 13939 MOZ_ASSERT(!BackslashDropped());
michael@0 13940 *aType = type;
michael@0 13941 *aDropBackslash = false;
michael@0 13942 return true;
michael@0 13943 }
michael@0 13944 }
michael@0 13945
michael@0 13946 do {
michael@0 13947 switch (mToken.mType) {
michael@0 13948 case eCSSToken_Symbol:
michael@0 13949 if (mToken.mSymbol == '(') {
michael@0 13950 stack.AppendElement(')');
michael@0 13951 } else if (mToken.mSymbol == '[') {
michael@0 13952 stack.AppendElement(']');
michael@0 13953 } else if (mToken.mSymbol == '{') {
michael@0 13954 stack.AppendElement('}');
michael@0 13955 } else if (mToken.mSymbol == ';' ||
michael@0 13956 mToken.mSymbol == '!') {
michael@0 13957 if (stack.IsEmpty()) {
michael@0 13958 UngetToken();
michael@0 13959 MOZ_ASSERT(!BackslashDropped());
michael@0 13960 *aType = CSSVariableDeclarations::eTokenStream;
michael@0 13961 *aDropBackslash = false;
michael@0 13962 return true;
michael@0 13963 } else if (!references.IsEmpty() &&
michael@0 13964 references.LastElement() == stack.Length() - 1) {
michael@0 13965 REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback);
michael@0 13966 SkipUntilAllOf(stack);
michael@0 13967 return false;
michael@0 13968 }
michael@0 13969 } else if (mToken.mSymbol == ')' ||
michael@0 13970 mToken.mSymbol == ']' ||
michael@0 13971 mToken.mSymbol == '}') {
michael@0 13972 for (;;) {
michael@0 13973 if (stack.IsEmpty()) {
michael@0 13974 UngetToken();
michael@0 13975 MOZ_ASSERT(!BackslashDropped());
michael@0 13976 *aType = CSSVariableDeclarations::eTokenStream;
michael@0 13977 *aDropBackslash = false;
michael@0 13978 return true;
michael@0 13979 }
michael@0 13980 char16_t c = stack.LastElement();
michael@0 13981 stack.TruncateLength(stack.Length() - 1);
michael@0 13982 if (!references.IsEmpty() &&
michael@0 13983 references.LastElement() == stack.Length()) {
michael@0 13984 references.TruncateLength(references.Length() - 1);
michael@0 13985 }
michael@0 13986 if (mToken.mSymbol == c) {
michael@0 13987 break;
michael@0 13988 }
michael@0 13989 }
michael@0 13990 }
michael@0 13991 break;
michael@0 13992
michael@0 13993 case eCSSToken_Function:
michael@0 13994 if (mToken.mIdent.LowerCaseEqualsLiteral("var")) {
michael@0 13995 if (!GetToken(true)) {
michael@0 13996 // EOF directly after "var(".
michael@0 13997 REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF);
michael@0 13998 return false;
michael@0 13999 }
michael@0 14000 if (mToken.mType != eCSSToken_Ident ||
michael@0 14001 !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) {
michael@0 14002 // There must be an identifier directly after the "var(" and
michael@0 14003 // it must be a custom property name.
michael@0 14004 UngetToken();
michael@0 14005 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName);
michael@0 14006 SkipUntil(')');
michael@0 14007 SkipUntilAllOf(stack);
michael@0 14008 return false;
michael@0 14009 }
michael@0 14010 if (aFunc) {
michael@0 14011 MOZ_ASSERT(Substring(mToken.mIdent, 0,
michael@0 14012 CSS_CUSTOM_NAME_PREFIX_LENGTH).
michael@0 14013 EqualsLiteral("--"));
michael@0 14014 // remove '--'
michael@0 14015 const nsDependentSubstring varName =
michael@0 14016 Substring(mToken.mIdent, CSS_CUSTOM_NAME_PREFIX_LENGTH);
michael@0 14017 aFunc(varName, aData);
michael@0 14018 }
michael@0 14019 if (!GetToken(true)) {
michael@0 14020 // EOF right after "var(<ident>".
michael@0 14021 stack.AppendElement(')');
michael@0 14022 } else if (mToken.IsSymbol(',')) {
michael@0 14023 // Variable reference with fallback.
michael@0 14024 if (!GetToken(false) || mToken.IsSymbol(')')) {
michael@0 14025 // Comma must be followed by at least one fallback token.
michael@0 14026 REPORT_UNEXPECTED(PEExpectedVariableFallback);
michael@0 14027 SkipUntilAllOf(stack);
michael@0 14028 return false;
michael@0 14029 }
michael@0 14030 UngetToken();
michael@0 14031 references.AppendElement(stack.Length());
michael@0 14032 stack.AppendElement(')');
michael@0 14033 } else if (mToken.IsSymbol(')')) {
michael@0 14034 // Correctly closed variable reference.
michael@0 14035 } else {
michael@0 14036 // Malformed variable reference.
michael@0 14037 REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen);
michael@0 14038 SkipUntil(')');
michael@0 14039 SkipUntilAllOf(stack);
michael@0 14040 return false;
michael@0 14041 }
michael@0 14042 } else {
michael@0 14043 stack.AppendElement(')');
michael@0 14044 }
michael@0 14045 break;
michael@0 14046
michael@0 14047 case eCSSToken_Bad_String:
michael@0 14048 SkipUntilAllOf(stack);
michael@0 14049 return false;
michael@0 14050
michael@0 14051 case eCSSToken_Bad_URL:
michael@0 14052 SkipUntil(')');
michael@0 14053 SkipUntilAllOf(stack);
michael@0 14054 return false;
michael@0 14055
michael@0 14056 default:
michael@0 14057 break;
michael@0 14058 }
michael@0 14059 } while (GetToken(true));
michael@0 14060
michael@0 14061 // Append any implied closing characters.
michael@0 14062 *aDropBackslash = BackslashDropped();
michael@0 14063 AppendImpliedEOFCharacters(aImpliedCharacters);
michael@0 14064 uint32_t i = stack.Length();
michael@0 14065 while (i--) {
michael@0 14066 aImpliedCharacters.Append(stack[i]);
michael@0 14067 }
michael@0 14068
michael@0 14069 *aType = type;
michael@0 14070 return true;
michael@0 14071 }
michael@0 14072
michael@0 14073 } // anonymous namespace
michael@0 14074
michael@0 14075 // Recycling of parser implementation objects
michael@0 14076
michael@0 14077 static CSSParserImpl* gFreeList = nullptr;
michael@0 14078
michael@0 14079 nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
michael@0 14080 nsCSSStyleSheet* aSheet)
michael@0 14081 {
michael@0 14082 CSSParserImpl *impl = gFreeList;
michael@0 14083 if (impl) {
michael@0 14084 gFreeList = impl->mNextFree;
michael@0 14085 impl->mNextFree = nullptr;
michael@0 14086 } else {
michael@0 14087 impl = new CSSParserImpl();
michael@0 14088 }
michael@0 14089
michael@0 14090 if (aLoader) {
michael@0 14091 impl->SetChildLoader(aLoader);
michael@0 14092 impl->SetQuirkMode(aLoader->GetCompatibilityMode() ==
michael@0 14093 eCompatibility_NavQuirks);
michael@0 14094 }
michael@0 14095 if (aSheet) {
michael@0 14096 impl->SetStyleSheet(aSheet);
michael@0 14097 }
michael@0 14098
michael@0 14099 mImpl = static_cast<void*>(impl);
michael@0 14100 }
michael@0 14101
michael@0 14102 nsCSSParser::~nsCSSParser()
michael@0 14103 {
michael@0 14104 CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl);
michael@0 14105 impl->Reset();
michael@0 14106 impl->mNextFree = gFreeList;
michael@0 14107 gFreeList = impl;
michael@0 14108 }
michael@0 14109
michael@0 14110 /* static */ void
michael@0 14111 nsCSSParser::Shutdown()
michael@0 14112 {
michael@0 14113 CSSParserImpl *tofree = gFreeList;
michael@0 14114 CSSParserImpl *next;
michael@0 14115 while (tofree)
michael@0 14116 {
michael@0 14117 next = tofree->mNextFree;
michael@0 14118 delete tofree;
michael@0 14119 tofree = next;
michael@0 14120 }
michael@0 14121 }
michael@0 14122
michael@0 14123 // Wrapper methods
michael@0 14124
michael@0 14125 nsresult
michael@0 14126 nsCSSParser::SetStyleSheet(nsCSSStyleSheet* aSheet)
michael@0 14127 {
michael@0 14128 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14129 SetStyleSheet(aSheet);
michael@0 14130 }
michael@0 14131
michael@0 14132 nsresult
michael@0 14133 nsCSSParser::SetQuirkMode(bool aQuirkMode)
michael@0 14134 {
michael@0 14135 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14136 SetQuirkMode(aQuirkMode);
michael@0 14137 }
michael@0 14138
michael@0 14139 nsresult
michael@0 14140 nsCSSParser::SetChildLoader(mozilla::css::Loader* aChildLoader)
michael@0 14141 {
michael@0 14142 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14143 SetChildLoader(aChildLoader);
michael@0 14144 }
michael@0 14145
michael@0 14146 nsresult
michael@0 14147 nsCSSParser::ParseSheet(const nsAString& aInput,
michael@0 14148 nsIURI* aSheetURI,
michael@0 14149 nsIURI* aBaseURI,
michael@0 14150 nsIPrincipal* aSheetPrincipal,
michael@0 14151 uint32_t aLineNumber,
michael@0 14152 bool aAllowUnsafeRules)
michael@0 14153 {
michael@0 14154 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14155 ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber,
michael@0 14156 aAllowUnsafeRules);
michael@0 14157 }
michael@0 14158
michael@0 14159 nsresult
michael@0 14160 nsCSSParser::ParseStyleAttribute(const nsAString& aAttributeValue,
michael@0 14161 nsIURI* aDocURI,
michael@0 14162 nsIURI* aBaseURI,
michael@0 14163 nsIPrincipal* aNodePrincipal,
michael@0 14164 css::StyleRule** aResult)
michael@0 14165 {
michael@0 14166 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14167 ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI,
michael@0 14168 aNodePrincipal, aResult);
michael@0 14169 }
michael@0 14170
michael@0 14171 nsresult
michael@0 14172 nsCSSParser::ParseDeclarations(const nsAString& aBuffer,
michael@0 14173 nsIURI* aSheetURI,
michael@0 14174 nsIURI* aBaseURI,
michael@0 14175 nsIPrincipal* aSheetPrincipal,
michael@0 14176 css::Declaration* aDeclaration,
michael@0 14177 bool* aChanged)
michael@0 14178 {
michael@0 14179 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14180 ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal,
michael@0 14181 aDeclaration, aChanged);
michael@0 14182 }
michael@0 14183
michael@0 14184 nsresult
michael@0 14185 nsCSSParser::ParseRule(const nsAString& aRule,
michael@0 14186 nsIURI* aSheetURI,
michael@0 14187 nsIURI* aBaseURI,
michael@0 14188 nsIPrincipal* aSheetPrincipal,
michael@0 14189 css::Rule** aResult)
michael@0 14190 {
michael@0 14191 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14192 ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult);
michael@0 14193 }
michael@0 14194
michael@0 14195 nsresult
michael@0 14196 nsCSSParser::ParseProperty(const nsCSSProperty aPropID,
michael@0 14197 const nsAString& aPropValue,
michael@0 14198 nsIURI* aSheetURI,
michael@0 14199 nsIURI* aBaseURI,
michael@0 14200 nsIPrincipal* aSheetPrincipal,
michael@0 14201 css::Declaration* aDeclaration,
michael@0 14202 bool* aChanged,
michael@0 14203 bool aIsImportant,
michael@0 14204 bool aIsSVGMode)
michael@0 14205 {
michael@0 14206 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14207 ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI,
michael@0 14208 aSheetPrincipal, aDeclaration, aChanged,
michael@0 14209 aIsImportant, aIsSVGMode);
michael@0 14210 }
michael@0 14211
michael@0 14212 nsresult
michael@0 14213 nsCSSParser::ParseVariable(const nsAString& aVariableName,
michael@0 14214 const nsAString& aPropValue,
michael@0 14215 nsIURI* aSheetURI,
michael@0 14216 nsIURI* aBaseURI,
michael@0 14217 nsIPrincipal* aSheetPrincipal,
michael@0 14218 css::Declaration* aDeclaration,
michael@0 14219 bool* aChanged,
michael@0 14220 bool aIsImportant)
michael@0 14221 {
michael@0 14222 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14223 ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI,
michael@0 14224 aSheetPrincipal, aDeclaration, aChanged, aIsImportant);
michael@0 14225 }
michael@0 14226
michael@0 14227 void
michael@0 14228 nsCSSParser::ParseMediaList(const nsSubstring& aBuffer,
michael@0 14229 nsIURI* aURI,
michael@0 14230 uint32_t aLineNumber,
michael@0 14231 nsMediaList* aMediaList,
michael@0 14232 bool aHTMLMode)
michael@0 14233 {
michael@0 14234 static_cast<CSSParserImpl*>(mImpl)->
michael@0 14235 ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode);
michael@0 14236 }
michael@0 14237
michael@0 14238 bool
michael@0 14239 nsCSSParser::ParseColorString(const nsSubstring& aBuffer,
michael@0 14240 nsIURI* aURI,
michael@0 14241 uint32_t aLineNumber,
michael@0 14242 nsCSSValue& aValue)
michael@0 14243 {
michael@0 14244 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14245 ParseColorString(aBuffer, aURI, aLineNumber, aValue);
michael@0 14246 }
michael@0 14247
michael@0 14248 nsresult
michael@0 14249 nsCSSParser::ParseSelectorString(const nsSubstring& aSelectorString,
michael@0 14250 nsIURI* aURI,
michael@0 14251 uint32_t aLineNumber,
michael@0 14252 nsCSSSelectorList** aSelectorList)
michael@0 14253 {
michael@0 14254 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14255 ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList);
michael@0 14256 }
michael@0 14257
michael@0 14258 already_AddRefed<nsCSSKeyframeRule>
michael@0 14259 nsCSSParser::ParseKeyframeRule(const nsSubstring& aBuffer,
michael@0 14260 nsIURI* aURI,
michael@0 14261 uint32_t aLineNumber)
michael@0 14262 {
michael@0 14263 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14264 ParseKeyframeRule(aBuffer, aURI, aLineNumber);
michael@0 14265 }
michael@0 14266
michael@0 14267 bool
michael@0 14268 nsCSSParser::ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
michael@0 14269 nsIURI* aURI,
michael@0 14270 uint32_t aLineNumber,
michael@0 14271 InfallibleTArray<float>& aSelectorList)
michael@0 14272 {
michael@0 14273 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14274 ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber,
michael@0 14275 aSelectorList);
michael@0 14276 }
michael@0 14277
michael@0 14278 bool
michael@0 14279 nsCSSParser::EvaluateSupportsDeclaration(const nsAString& aProperty,
michael@0 14280 const nsAString& aValue,
michael@0 14281 nsIURI* aDocURL,
michael@0 14282 nsIURI* aBaseURL,
michael@0 14283 nsIPrincipal* aDocPrincipal)
michael@0 14284 {
michael@0 14285 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14286 EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL,
michael@0 14287 aDocPrincipal);
michael@0 14288 }
michael@0 14289
michael@0 14290 bool
michael@0 14291 nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition,
michael@0 14292 nsIURI* aDocURL,
michael@0 14293 nsIURI* aBaseURL,
michael@0 14294 nsIPrincipal* aDocPrincipal)
michael@0 14295 {
michael@0 14296 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14297 EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL, aDocPrincipal);
michael@0 14298 }
michael@0 14299
michael@0 14300 bool
michael@0 14301 nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue,
michael@0 14302 VariableEnumFunc aFunc,
michael@0 14303 void* aData)
michael@0 14304 {
michael@0 14305 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14306 EnumerateVariableReferences(aPropertyValue, aFunc, aData);
michael@0 14307 }
michael@0 14308
michael@0 14309 bool
michael@0 14310 nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue,
michael@0 14311 const CSSVariableValues* aVariables,
michael@0 14312 nsString& aResult,
michael@0 14313 nsCSSTokenSerializationType& aFirstToken,
michael@0 14314 nsCSSTokenSerializationType& aLastToken)
michael@0 14315 {
michael@0 14316 return static_cast<CSSParserImpl*>(mImpl)->
michael@0 14317 ResolveVariableValue(aPropertyValue, aVariables,
michael@0 14318 aResult, aFirstToken, aLastToken);
michael@0 14319 }
michael@0 14320
michael@0 14321 void
michael@0 14322 nsCSSParser::ParsePropertyWithVariableReferences(
michael@0 14323 nsCSSProperty aPropertyID,
michael@0 14324 nsCSSProperty aShorthandPropertyID,
michael@0 14325 const nsAString& aValue,
michael@0 14326 const CSSVariableValues* aVariables,
michael@0 14327 nsRuleData* aRuleData,
michael@0 14328 nsIURI* aDocURL,
michael@0 14329 nsIURI* aBaseURL,
michael@0 14330 nsIPrincipal* aDocPrincipal,
michael@0 14331 nsCSSStyleSheet* aSheet,
michael@0 14332 uint32_t aLineNumber,
michael@0 14333 uint32_t aLineOffset)
michael@0 14334 {
michael@0 14335 static_cast<CSSParserImpl*>(mImpl)->
michael@0 14336 ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID,
michael@0 14337 aValue, aVariables, aRuleData, aDocURL,
michael@0 14338 aBaseURL, aDocPrincipal, aSheet,
michael@0 14339 aLineNumber, aLineOffset);
michael@0 14340 }

mercurial