Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
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 | } |