Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / style / nsCSSParser.cpp
bloba909914d26cafd2a0d1dfc363f0f2ab6cfbb4360
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * emk <VYV03354@nifty.ne.jp>
24 * Daniel Glazman <glazman@netscape.com>
25 * L. David Baron <dbaron@dbaron.org>
26 * Boris Zbarsky <bzbarsky@mit.edu>
27 * Mats Palmgren <mats.palmgren@bredband.net>
28 * Christian Biesinger <cbiesinger@web.de>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either of the GNU General Public License Version 2 or later (the "GPL"),
32 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
44 /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
46 #include "nsICSSParser.h"
47 #include "nsCSSProps.h"
48 #include "nsCSSKeywords.h"
49 #include "nsCSSScanner.h"
50 #include "nsICSSLoader.h"
51 #include "nsICSSStyleRule.h"
52 #include "nsICSSImportRule.h"
53 #include "nsCSSRules.h"
54 #include "nsICSSNameSpaceRule.h"
55 #include "nsIUnicharInputStream.h"
56 #include "nsICSSStyleSheet.h"
57 #include "nsCSSDeclaration.h"
58 #include "nsStyleConsts.h"
59 #include "nsIURL.h"
60 #include "nsNetUtil.h"
61 #include "nsCOMPtr.h"
62 #include "nsString.h"
63 #include "nsReadableUtils.h"
64 #include "nsUnicharUtils.h"
65 #include "nsIAtom.h"
66 #include "nsVoidArray.h"
67 #include "nsCOMArray.h"
68 #include "nsColor.h"
69 #include "nsStyleConsts.h"
70 #include "nsCSSPseudoClasses.h"
71 #include "nsCSSPseudoElements.h"
72 #include "nsCSSAnonBoxes.h"
73 #include "nsINameSpaceManager.h"
74 #include "nsXMLNameSpaceMap.h"
75 #include "nsThemeConstants.h"
76 #include "nsContentErrors.h"
77 #include "nsPrintfCString.h"
78 #include "nsIMediaList.h"
79 #include "nsILookAndFeel.h"
80 #include "nsStyleUtil.h"
81 #include "nsIPrincipal.h"
82 #include "prprf.h"
83 #include "math.h"
84 #include "nsContentUtils.h"
85 #include "nsDOMError.h"
86 #include "nsAutoPtr.h"
87 #include "nsTArray.h"
89 // Flags for ParseVariant method
90 #define VARIANT_KEYWORD 0x000001 // K
91 #define VARIANT_LENGTH 0x000002 // L
92 #define VARIANT_PERCENT 0x000004 // P
93 #define VARIANT_COLOR 0x000008 // C eCSSUnit_Color, eCSSUnit_String (e.g. "red")
94 #define VARIANT_URL 0x000010 // U
95 #define VARIANT_NUMBER 0x000020 // N
96 #define VARIANT_INTEGER 0x000040 // I
97 #define VARIANT_ANGLE 0x000080 // G
98 #define VARIANT_FREQUENCY 0x000100 // F
99 #define VARIANT_TIME 0x000200 // T
100 #define VARIANT_STRING 0x000400 // S
101 #define VARIANT_COUNTER 0x000800 //
102 #define VARIANT_ATTR 0x001000 //
103 #define VARIANT_IDENTIFIER 0x002000 // D
104 #define VARIANT_AUTO 0x010000 // A
105 #define VARIANT_INHERIT 0x020000 // H eCSSUnit_Initial, eCSSUnit_Inherit
106 #define VARIANT_NONE 0x040000 // O
107 #define VARIANT_NORMAL 0x080000 // M
108 #define VARIANT_SYSFONT 0x100000 // eCSSUnit_System_Font
110 // Common combinations of variants
111 #define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH)
112 #define VARIANT_LP (VARIANT_LENGTH | VARIANT_PERCENT)
113 #define VARIANT_AH (VARIANT_AUTO | VARIANT_INHERIT)
114 #define VARIANT_AHLP (VARIANT_AH | VARIANT_LP)
115 #define VARIANT_AHI (VARIANT_AH | VARIANT_INTEGER)
116 #define VARIANT_AHK (VARIANT_AH | VARIANT_KEYWORD)
117 #define VARIANT_AHKLP (VARIANT_AHLP | VARIANT_KEYWORD)
118 #define VARIANT_AUK (VARIANT_AUTO | VARIANT_URL | VARIANT_KEYWORD)
119 #define VARIANT_AHUK (VARIANT_AH | VARIANT_URL | VARIANT_KEYWORD)
120 #define VARIANT_AHL (VARIANT_AH | VARIANT_LENGTH)
121 #define VARIANT_AHKL (VARIANT_AHK | VARIANT_LENGTH)
122 #define VARIANT_HK (VARIANT_INHERIT | VARIANT_KEYWORD)
123 #define VARIANT_HKF (VARIANT_HK | VARIANT_FREQUENCY)
124 #define VARIANT_HKL (VARIANT_HK | VARIANT_LENGTH)
125 #define VARIANT_HKLP (VARIANT_HK | VARIANT_LP)
126 #define VARIANT_HKLPO (VARIANT_HKLP | VARIANT_NONE)
127 #define VARIANT_HL (VARIANT_INHERIT | VARIANT_LENGTH)
128 #define VARIANT_HI (VARIANT_INHERIT | VARIANT_INTEGER)
129 #define VARIANT_HLP (VARIANT_HL | VARIANT_PERCENT)
130 #define VARIANT_HLPN (VARIANT_HLP | VARIANT_NUMBER)
131 #define VARIANT_HLPO (VARIANT_HLP | VARIANT_NONE)
132 #define VARIANT_HTP (VARIANT_INHERIT | VARIANT_TIME | VARIANT_PERCENT)
133 #define VARIANT_HMK (VARIANT_HK | VARIANT_NORMAL)
134 #define VARIANT_HMKI (VARIANT_HMK | VARIANT_INTEGER)
135 #define VARIANT_HC (VARIANT_INHERIT | VARIANT_COLOR)
136 #define VARIANT_HCK (VARIANT_HK | VARIANT_COLOR)
137 #define VARIANT_HUO (VARIANT_INHERIT | VARIANT_URL | VARIANT_NONE)
138 #define VARIANT_AHUO (VARIANT_AUTO | VARIANT_HUO)
139 #define VARIANT_HPN (VARIANT_INHERIT | VARIANT_PERCENT | VARIANT_NUMBER)
140 #define VARIANT_HOK (VARIANT_HK | VARIANT_NONE)
141 #define VARIANT_HN (VARIANT_INHERIT | VARIANT_NUMBER)
142 #define VARIANT_HON (VARIANT_HN | VARIANT_NONE)
143 #define VARIANT_HOS (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING)
145 //----------------------------------------------------------------------
147 // Your basic top-down recursive descent style parser
148 class CSSParserImpl : public nsICSSParser {
149 public:
150 CSSParserImpl();
151 virtual ~CSSParserImpl();
153 NS_DECL_ISUPPORTS
155 NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet);
157 NS_IMETHOD SetCaseSensitive(PRBool aCaseSensitive);
159 NS_IMETHOD SetQuirkMode(PRBool aQuirkMode);
161 #ifdef MOZ_SVG
162 NS_IMETHOD SetSVGMode(PRBool aSVGMode);
163 #endif
165 NS_IMETHOD SetChildLoader(nsICSSLoader* aChildLoader);
167 NS_IMETHOD Parse(nsIUnicharInputStream* aInput,
168 nsIURI* aSheetURI,
169 nsIURI* aBaseURI,
170 nsIPrincipal* aSheetPrincipal,
171 PRUint32 aLineNumber,
172 PRBool aAllowUnsafeRules);
174 NS_IMETHOD ParseStyleAttribute(const nsAString& aAttributeValue,
175 nsIURI* aDocURL,
176 nsIURI* aBaseURL,
177 nsIPrincipal* aNodePrincipal,
178 nsICSSStyleRule** aResult);
180 NS_IMETHOD ParseAndAppendDeclaration(const nsAString& aBuffer,
181 nsIURI* aSheetURL,
182 nsIURI* aBaseURL,
183 nsIPrincipal* aSheetPrincipal,
184 nsCSSDeclaration* aDeclaration,
185 PRBool aParseOnlyOneDecl,
186 PRBool* aChanged,
187 PRBool aClearOldDecl);
189 NS_IMETHOD ParseRule(const nsAString& aRule,
190 nsIURI* aSheetURL,
191 nsIURI* aBaseURL,
192 nsIPrincipal* aSheetPrincipal,
193 nsCOMArray<nsICSSRule>& aResult);
195 NS_IMETHOD ParseProperty(const nsCSSProperty aPropID,
196 const nsAString& aPropValue,
197 nsIURI* aSheetURL,
198 nsIURI* aBaseURL,
199 nsIPrincipal* aSheetPrincipal,
200 nsCSSDeclaration* aDeclaration,
201 PRBool* aChanged);
203 NS_IMETHOD ParseMediaList(const nsSubstring& aBuffer,
204 nsIURI* aURL, // for error reporting
205 PRUint32 aLineNumber, // for error reporting
206 nsMediaList* aMediaList,
207 PRBool aHTMLMode);
209 NS_IMETHOD ParseColorString(const nsSubstring& aBuffer,
210 nsIURI* aURL, // for error reporting
211 PRUint32 aLineNumber, // for error reporting
212 nscolor* aColor);
214 NS_IMETHOD ParseSelectorString(const nsSubstring& aSelectorString,
215 nsIURI* aURL, // for error reporting
216 PRUint32 aLineNumber, // for error reporting
217 nsCSSSelectorList **aSelectorList);
219 void AppendRule(nsICSSRule* aRule);
221 protected:
222 class nsAutoParseCompoundProperty;
223 friend class nsAutoParseCompoundProperty;
226 * This helper class automatically calls SetParsingCompoundProperty in its
227 * constructor and takes care of resetting it to false in its destructor.
229 class nsAutoParseCompoundProperty {
230 public:
231 nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
233 NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
234 "already parsing compound property");
235 NS_ASSERTION(aParser, "Null parser?");
236 aParser->SetParsingCompoundProperty(PR_TRUE);
239 ~nsAutoParseCompoundProperty()
241 mParser->SetParsingCompoundProperty(PR_FALSE);
243 private:
244 CSSParserImpl* mParser;
247 void InitScanner(nsIUnicharInputStream* aInput, nsIURI* aSheetURI,
248 PRUint32 aLineNumber, nsIURI* aBaseURI,
249 nsIPrincipal* aSheetPrincipal);
250 // the caller must hold on to aBuffer until parsing is done
251 void InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
252 PRUint32 aLineNumber, nsIURI* aBaseURI,
253 nsIPrincipal* aSheetPrincipal);
254 void ReleaseScanner(void);
255 #ifdef MOZ_SVG
256 PRBool IsSVGMode() const {
257 return mScanner.IsSVGMode();
259 #endif
261 PRBool GetToken(PRBool aSkipWS);
262 PRBool GetURLToken();
263 void UngetToken();
265 void AssertInitialState() {
266 NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
267 NS_PRECONDITION(!mUnresolvablePrefixException, "Bad initial state");
268 NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
271 PRBool ExpectSymbol(PRUnichar aSymbol, PRBool aSkipWS);
272 PRBool ExpectEndProperty();
273 PRBool CheckEndProperty();
274 nsSubstring* NextIdent();
275 void SkipUntil(PRUnichar aStopSymbol);
276 void SkipUntilOneOf(const PRUnichar* aStopSymbolChars);
277 void SkipRuleSet();
278 PRBool SkipAtRule();
279 PRBool SkipDeclaration(PRBool aCheckForBraces);
280 PRBool GetNonCloseParenToken(PRBool aSkipWS);
282 PRBool PushGroup(nsICSSGroupRule* aRule);
283 void PopGroup(void);
285 PRBool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData);
286 PRBool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData);
287 PRBool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData);
288 PRBool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData);
289 PRBool GatherURL(nsString& aURL);
290 PRBool GatherMedia(nsMediaList* aMedia,
291 PRUnichar aStopSymbol);
292 PRBool ParseMediaQuery(PRUnichar aStopSymbol, nsMediaQuery **aQuery,
293 PRBool *aParsedSomething, PRBool *aHitStop);
294 PRBool ParseMediaQueryExpression(nsMediaQuery* aQuery);
295 PRBool ProcessImport(const nsString& aURLSpec,
296 nsMediaList* aMedia,
297 RuleAppendFunc aAppendFunc,
298 void* aProcessData);
299 PRBool ParseGroupRule(nsICSSGroupRule* aRule, RuleAppendFunc aAppendFunc,
300 void* aProcessData);
301 PRBool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData);
302 PRBool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData);
303 PRBool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
304 PRBool ProcessNameSpace(const nsString& aPrefix,
305 const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
306 void* aProcessData);
308 PRBool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData);
309 PRBool ParseFontDescriptor(nsCSSFontFaceRule* aRule);
310 PRBool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
311 nsCSSValue& aValue);
313 PRBool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData);
315 enum nsSelectorParsingStatus {
316 // we have parsed a selector and we saw a token that cannot be
317 // part of a selector:
318 eSelectorParsingStatus_Done,
319 // we should continue parsing the selector:
320 eSelectorParsingStatus_Continue,
321 // same as "Done" but we did not find a selector:
322 eSelectorParsingStatus_Empty,
323 // we saw an unexpected token or token value,
324 // or we saw end-of-file with an unfinished selector:
325 eSelectorParsingStatus_Error
327 nsSelectorParsingStatus ParseIDSelector(PRInt32& aDataMask,
328 nsCSSSelector& aSelector);
330 nsSelectorParsingStatus ParseClassSelector(PRInt32& aDataMask,
331 nsCSSSelector& aSelector);
333 nsSelectorParsingStatus ParsePseudoSelector(PRInt32& aDataMask,
334 nsCSSSelector& aSelector,
335 PRBool aIsNegated);
337 nsSelectorParsingStatus ParseAttributeSelector(PRInt32& aDataMask,
338 nsCSSSelector& aSelector);
340 nsSelectorParsingStatus ParseTypeOrUniversalSelector(PRInt32& aDataMask,
341 nsCSSSelector& aSelector,
342 PRBool aIsNegated);
344 nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
345 nsIAtom* aPseudo);
347 nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
348 nsIAtom* aPseudo);
350 nsSelectorParsingStatus ParseNegatedSimpleSelector(PRInt32& aDataMask,
351 nsCSSSelector& aSelector);
353 nsSelectorParsingStatus ParseSelector(nsCSSSelector& aSelectorResult);
355 // If aTerminateAtBrace is true, the selector list is done when we
356 // hit a '{'. Otherwise, it's done when we hit EOF.
357 PRBool ParseSelectorList(nsCSSSelectorList*& aListHead,
358 PRBool aTerminateAtBrace);
359 PRBool ParseSelectorGroup(nsCSSSelectorList*& aListHead);
360 nsCSSDeclaration* ParseDeclarationBlock(PRBool aCheckForBraces);
361 PRBool ParseDeclaration(nsCSSDeclaration* aDeclaration,
362 PRBool aCheckForBraces,
363 PRBool aMustCallValueAppended,
364 PRBool* aChanged);
365 // After a parse error parsing |aPropID|, clear the data in
366 // |mTempData|.
367 void ClearTempData(nsCSSProperty aPropID);
368 // After a successful parse of |aPropID|, transfer data from
369 // |mTempData| to |mData|. Set |*aChanged| to true if something
370 // changed, but leave it unmodified otherwise. If aMustCallValueAppended
371 // is false, will not call ValueAppended on aDeclaration if the property
372 // is already set in it.
373 void TransferTempData(nsCSSDeclaration* aDeclaration,
374 nsCSSProperty aPropID, PRBool aIsImportant,
375 PRBool aMustCallValueAppended,
376 PRBool* aChanged);
377 void DoTransferTempData(nsCSSDeclaration* aDeclaration,
378 nsCSSProperty aPropID, PRBool aIsImportant,
379 PRBool aMustCallValueAppended,
380 PRBool* aChanged);
381 PRBool ParseProperty(nsCSSProperty aPropID);
382 PRBool ParseSingleValueProperty(nsCSSValue& aValue,
383 nsCSSProperty aPropID);
385 #ifdef MOZ_XUL
386 PRBool ParseTreePseudoElement(nsCSSSelector& aSelector);
387 #endif
389 void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties);
391 // Property specific parsing routines
392 PRBool ParseAzimuth(nsCSSValue& aValue);
393 PRBool ParseBackground();
394 PRBool ParseBackgroundPosition();
395 PRBool ParseBackgroundPositionValues();
396 PRBool ParseBoxPosition(nsCSSValuePair& aOut);
397 PRBool ParseBoxPositionValues(nsCSSValuePair& aOut);
398 PRBool ParseBorderColor();
399 PRBool ParseBorderColors(nsCSSValueList** aResult,
400 nsCSSProperty aProperty);
401 PRBool ParseBorderImage();
402 PRBool ParseBorderSpacing();
403 PRBool ParseBorderSide(const nsCSSProperty aPropIDs[],
404 PRBool aSetAllSides);
405 PRBool ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
406 PRInt32 aSourceType);
407 PRBool ParseBorderStyle();
408 PRBool ParseBorderWidth();
409 // for 'clip' and '-moz-image-region'
410 PRBool ParseRect(nsCSSRect& aRect,
411 nsCSSProperty aPropID);
412 PRBool DoParseRect(nsCSSRect& aRect);
413 PRBool ParseContent();
414 PRBool ParseCounterData(nsCSSValuePairList** aResult,
415 nsCSSProperty aPropID);
416 PRBool ParseCue();
417 PRBool ParseCursor();
418 PRBool ParseFont();
419 PRBool ParseFontWeight(nsCSSValue& aValue);
420 PRBool ParseOneFamily(nsAString& aValue);
421 PRBool ParseFamily(nsCSSValue& aValue);
422 PRBool ParseFontSrc(nsCSSValue& aValue);
423 PRBool ParseFontSrcFormat(nsTArray<nsCSSValue>& values);
424 PRBool ParseFontRanges(nsCSSValue& aValue);
425 PRBool ParseListStyle();
426 PRBool ParseMargin();
427 PRBool ParseMarks(nsCSSValue& aValue);
428 PRBool ParseMozTransform();
429 PRBool ParseOutline();
430 PRBool ParseOverflow();
431 PRBool ParsePadding();
432 PRBool ParsePause();
433 PRBool ParseQuotes();
434 PRBool ParseSize();
435 PRBool ParseTextDecoration(nsCSSValue& aValue);
437 nsCSSValueList* ParseCSSShadowList(PRBool aUsesSpread);
438 PRBool ParseTextShadow();
439 PRBool ParseBoxShadow();
441 #ifdef MOZ_SVG
442 PRBool ParsePaint(nsCSSValuePair* aResult,
443 nsCSSProperty aPropID);
444 PRBool ParseDasharray();
445 PRBool ParseMarker();
446 #endif
448 // Reused utility parsing routines
449 void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
450 PRBool ParseBoxProperties(nsCSSRect& aResult,
451 const nsCSSProperty aPropIDs[]);
452 PRBool ParseDirectionalBoxProperty(nsCSSProperty aProperty,
453 PRInt32 aSourceType);
454 PRBool ParseBoxCornerRadius(const nsCSSProperty aPropID);
455 PRBool ParseBoxCornerRadii(nsCSSCornerSizes& aRadii,
456 const nsCSSProperty aPropIDs[]);
457 PRInt32 ParseChoice(nsCSSValue aValues[],
458 const nsCSSProperty aPropIDs[], PRInt32 aNumIDs);
459 PRBool ParseColor(nsCSSValue& aValue);
460 PRBool ParseColorComponent(PRUint8& aComponent,
461 PRInt32& aType, char aStop);
462 // ParseHSLColor parses everything starting with the opening '('
463 // up through and including the aStop char.
464 PRBool ParseHSLColor(nscolor& aColor, char aStop);
465 // ParseColorOpacity will enforce that the color ends with a ')'
466 // after the opacity
467 PRBool ParseColorOpacity(PRUint8& aOpacity);
468 PRBool ParseEnum(nsCSSValue& aValue, const PRInt32 aKeywordTable[]);
469 PRBool ParseVariant(nsCSSValue& aValue,
470 PRInt32 aVariantMask,
471 const PRInt32 aKeywordTable[]);
472 PRBool ParsePositiveVariant(nsCSSValue& aValue,
473 PRInt32 aVariantMask,
474 const PRInt32 aKeywordTable[]);
475 PRBool ParseCounter(nsCSSValue& aValue);
476 PRBool ParseAttr(nsCSSValue& aValue);
477 PRBool ParseURL(nsCSSValue& aValue);
478 PRBool TranslateDimension(nsCSSValue& aValue, PRInt32 aVariantMask,
479 float aNumber, const nsString& aUnit);
481 void SetParsingCompoundProperty(PRBool aBool) {
482 NS_ASSERTION(aBool == PR_TRUE || aBool == PR_FALSE, "bad PRBool value");
483 mParsingCompoundProperty = aBool;
485 PRBool IsParsingCompoundProperty(void) const {
486 return mParsingCompoundProperty;
489 /* Functions for -moz-transform Parsing */
490 PRBool ReadSingleTransform(nsCSSValueList**& aTail);
491 PRBool ParseFunction(const nsString &aFunction, const PRInt32 aAllowedTypes[],
492 PRUint16 aMinElems, PRUint16 aMaxElems,
493 nsCSSValue &aValue);
494 PRBool ParseFunctionInternals(const PRInt32 aVariantMask[],
495 PRUint16 aMinElems,
496 PRUint16 aMaxElems,
497 nsTArray<nsCSSValue>& aOutput);
499 /* Functions for -moz-transform-origin Parsing */
500 PRBool ParseMozTransformOrigin();
503 /* Find and return the correct namespace ID for the prefix aPrefix.
504 If the prefix cannot be resolved to a namespace, this method will
505 return false. Otherwise it will return true. When returning
506 false, it may set the low-level error code, depending on the
507 value of mUnresolvablePrefixException.
509 This method never returns kNameSpaceID_Unknown or
510 kNameSpaceID_None for aNameSpaceID while returning true.
512 PRBool GetNamespaceIdForPrefix(const nsString& aPrefix,
513 PRInt32* aNameSpaceID);
515 /* Find the correct default namespace, and set it on aSelector. */
516 void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
518 // Current token. The value is valid after calling GetToken and invalidated
519 // by UngetToken.
520 nsCSSToken mToken;
522 // Our scanner.
523 nsCSSScanner mScanner;
525 // The URI to be used as a base for relative URIs.
526 nsCOMPtr<nsIURI> mBaseURL;
528 // The URI to be used as an HTTP "Referer" and for error reporting.
529 nsCOMPtr<nsIURI> mSheetURL;
531 // The principal of the sheet involved
532 nsCOMPtr<nsIPrincipal> mSheetPrincipal;
534 // The sheet we're parsing into
535 nsCOMPtr<nsICSSStyleSheet> mSheet;
537 // Used for @import rules
538 nsICSSLoader* mChildLoader; // not ref counted, it owns us
540 // Sheet section we're in. This is used to enforce correct ordering of the
541 // various rule types (eg the fact that a @charset rule must come before
542 // anything else). Note that there are checks of similar things in various
543 // places in nsCSSStyleSheet.cpp (e.g in insertRule, RebuildChildList).
544 enum nsCSSSection {
545 eCSSSection_Charset,
546 eCSSSection_Import,
547 eCSSSection_NameSpace,
548 eCSSSection_General
550 nsCSSSection mSection;
552 nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it
554 // After an UngetToken is done this flag is true. The next call to
555 // GetToken clears the flag.
556 PRPackedBool mHavePushBack : 1;
558 // True if we are in quirks mode; false in standards or almost standards mode
559 PRPackedBool mNavQuirkMode : 1;
561 // True if unsafe rules should be allowed
562 PRPackedBool mUnsafeRulesEnabled : 1;
564 // True for parsing media lists for HTML attributes, where we have to
565 // ignore CSS comments.
566 PRPackedBool mHTMLMediaMode : 1;
568 // True if tagnames and attributes are case-sensitive
569 PRPackedBool mCaseSensitive : 1;
571 // This flag is set when parsing a non-box shorthand; it's used to not apply
572 // some quirks during shorthand parsing
573 PRPackedBool mParsingCompoundProperty : 1;
575 // If this flag is true, failure to resolve a namespace prefix
576 // should set the low-level error to NS_ERROR_DOM_NAMESPACE_ERR
577 PRPackedBool mUnresolvablePrefixException : 1;
579 // Stack of rule groups; used for @media and such.
580 nsCOMArray<nsICSSGroupRule> mGroupStack;
582 // During the parsing of a property (which may be a shorthand), the data
583 // are stored in |mTempData|. (It is needed to ensure that parser
584 // errors cause the data to be ignored, and to ensure that a
585 // non-'!important' declaration does not override an '!important'
586 // one.)
587 nsCSSExpandedDataBlock mTempData;
589 // All data from successfully parsed properties are placed into |mData|.
590 nsCSSExpandedDataBlock mData;
592 #ifdef DEBUG
593 PRPackedBool mScannerInited;
594 #endif
597 static void AppendRuleToArray(nsICSSRule* aRule, void* aArray)
599 static_cast<nsCOMArray<nsICSSRule>*>(aArray)->AppendObject(aRule);
602 static void AppendRuleToSheet(nsICSSRule* aRule, void* aParser)
604 CSSParserImpl* parser = (CSSParserImpl*) aParser;
605 parser->AppendRule(aRule);
608 nsresult
609 NS_NewCSSParser(nsICSSParser** aInstancePtrResult)
611 CSSParserImpl *it = new CSSParserImpl();
613 if (it == nsnull) {
614 return NS_ERROR_OUT_OF_MEMORY;
617 return it->QueryInterface(NS_GET_IID(nsICSSParser), (void **) aInstancePtrResult);
620 #ifdef CSS_REPORT_PARSE_ERRORS
622 #define REPORT_UNEXPECTED(msg_) \
623 mScanner.ReportUnexpected(#msg_)
625 #define REPORT_UNEXPECTED_P(msg_, params_) \
626 mScanner.ReportUnexpectedParams(#msg_, params_, NS_ARRAY_LENGTH(params_))
628 #define REPORT_UNEXPECTED_EOF(lf_) \
629 mScanner.ReportUnexpectedEOF(#lf_)
631 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
632 mScanner.ReportUnexpectedEOF(ch_)
634 #define REPORT_UNEXPECTED_TOKEN(msg_) \
635 mScanner.ReportUnexpectedToken(mToken, #msg_)
637 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_) \
638 mScanner.ReportUnexpectedTokenParams(mToken, #msg_, \
639 params_, NS_ARRAY_LENGTH(params_))
642 #define OUTPUT_ERROR() \
643 mScanner.OutputError()
645 #define CLEAR_ERROR() \
646 mScanner.ClearError()
648 #else
650 #define REPORT_UNEXPECTED(msg_)
651 #define REPORT_UNEXPECTED_P(msg_, params_)
652 #define REPORT_UNEXPECTED_EOF(lf_)
653 #define REPORT_UNEXPECTED_EOF_CHAR(ch_)
654 #define REPORT_UNEXPECTED_TOKEN(msg_)
655 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_)
656 #define OUTPUT_ERROR()
657 #define CLEAR_ERROR()
659 #endif
661 CSSParserImpl::CSSParserImpl()
662 : mToken(),
663 mScanner(),
664 mChildLoader(nsnull),
665 mSection(eCSSSection_Charset),
666 mNameSpaceMap(nsnull),
667 mHavePushBack(PR_FALSE),
668 mNavQuirkMode(PR_FALSE),
669 mUnsafeRulesEnabled(PR_FALSE),
670 mHTMLMediaMode(PR_FALSE),
671 mCaseSensitive(PR_FALSE),
672 mParsingCompoundProperty(PR_FALSE),
673 mUnresolvablePrefixException(PR_FALSE)
674 #ifdef DEBUG
675 , mScannerInited(PR_FALSE)
676 #endif
680 NS_IMPL_ISUPPORTS1(CSSParserImpl, nsICSSParser)
682 CSSParserImpl::~CSSParserImpl()
684 mData.AssertInitialState();
685 mTempData.AssertInitialState();
688 NS_IMETHODIMP
689 CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
691 if (aSheet != mSheet) {
692 // Switch to using the new sheet, if any
693 mGroupStack.Clear();
694 mSheet = aSheet;
695 if (mSheet) {
696 mNameSpaceMap = mSheet->GetNameSpaceMap();
697 } else {
698 mNameSpaceMap = nsnull;
702 return NS_OK;
705 NS_IMETHODIMP
706 CSSParserImpl::SetCaseSensitive(PRBool aCaseSensitive)
708 NS_ASSERTION(aCaseSensitive == PR_TRUE || aCaseSensitive == PR_FALSE, "bad PRBool value");
709 mCaseSensitive = aCaseSensitive;
710 return NS_OK;
713 NS_IMETHODIMP
714 CSSParserImpl::SetQuirkMode(PRBool aQuirkMode)
716 NS_ASSERTION(aQuirkMode == PR_TRUE || aQuirkMode == PR_FALSE, "bad PRBool value");
717 mNavQuirkMode = aQuirkMode;
718 return NS_OK;
721 #ifdef MOZ_SVG
722 NS_IMETHODIMP
723 CSSParserImpl::SetSVGMode(PRBool aSVGMode)
725 NS_ASSERTION(aSVGMode == PR_TRUE || aSVGMode == PR_FALSE,
726 "bad PRBool value");
727 mScanner.SetSVGMode(aSVGMode);
728 return NS_OK;
730 #endif
732 NS_IMETHODIMP
733 CSSParserImpl::SetChildLoader(nsICSSLoader* aChildLoader)
735 mChildLoader = aChildLoader; // not ref counted, it owns us
736 return NS_OK;
739 void
740 CSSParserImpl::InitScanner(nsIUnicharInputStream* aInput, nsIURI* aSheetURI,
741 PRUint32 aLineNumber, nsIURI* aBaseURI,
742 nsIPrincipal* aSheetPrincipal)
744 NS_ASSERTION(! mScannerInited, "already have scanner");
746 mScanner.Init(aInput, nsnull, 0, aSheetURI, aLineNumber);
747 #ifdef DEBUG
748 mScannerInited = PR_TRUE;
749 #endif
750 mBaseURL = aBaseURI;
751 mSheetURL = aSheetURI;
752 mSheetPrincipal = aSheetPrincipal;
754 mHavePushBack = PR_FALSE;
757 void
758 CSSParserImpl::InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
759 PRUint32 aLineNumber, nsIURI* aBaseURI,
760 nsIPrincipal* aSheetPrincipal)
762 // Having it not own the string is OK since the caller will hold on to
763 // the stream until we're done parsing.
764 NS_ASSERTION(! mScannerInited, "already have scanner");
766 mScanner.Init(nsnull, aString.BeginReading(), aString.Length(), aSheetURI, aLineNumber);
768 #ifdef DEBUG
769 mScannerInited = PR_TRUE;
770 #endif
771 mBaseURL = aBaseURI;
772 mSheetURL = aSheetURI;
773 mSheetPrincipal = aSheetPrincipal;
775 mHavePushBack = PR_FALSE;
778 void
779 CSSParserImpl::ReleaseScanner(void)
781 mScanner.Close();
782 #ifdef DEBUG
783 mScannerInited = PR_FALSE;
784 #endif
785 mBaseURL = nsnull;
786 mSheetURL = nsnull;
787 mSheetPrincipal = nsnull;
791 NS_IMETHODIMP
792 CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
793 nsIURI* aSheetURI,
794 nsIURI* aBaseURI,
795 nsIPrincipal* aSheetPrincipal,
796 PRUint32 aLineNumber,
797 PRBool aAllowUnsafeRules)
799 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
801 NS_ASSERTION(nsnull != aBaseURI, "need base URL");
802 NS_ASSERTION(nsnull != aSheetURI, "need sheet URL");
803 AssertInitialState();
805 NS_PRECONDITION(mSheet, "Must have sheet to parse into");
806 NS_ENSURE_STATE(mSheet);
808 #ifdef DEBUG
809 nsCOMPtr<nsIURI> uri;
810 mSheet->GetSheetURI(getter_AddRefs(uri));
811 PRBool equal;
812 NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
813 "Sheet URI does not match passed URI");
814 NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
815 &equal)) &&
816 equal,
817 "Sheet principal does not match passed principal");
818 #endif
820 InitScanner(aInput, aSheetURI, aLineNumber, aBaseURI, aSheetPrincipal);
822 PRInt32 ruleCount = 0;
823 mSheet->StyleRuleCount(ruleCount);
824 if (0 < ruleCount) {
825 nsICSSRule* lastRule = nsnull;
826 mSheet->GetStyleRuleAt(ruleCount - 1, lastRule);
827 if (lastRule) {
828 PRInt32 type;
829 lastRule->GetType(type);
830 switch (type) {
831 case nsICSSRule::CHARSET_RULE:
832 case nsICSSRule::IMPORT_RULE:
833 mSection = eCSSSection_Import;
834 break;
835 case nsICSSRule::NAMESPACE_RULE:
836 mSection = eCSSSection_NameSpace;
837 break;
838 default:
839 mSection = eCSSSection_General;
840 break;
842 NS_RELEASE(lastRule);
845 else {
846 mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
849 mUnsafeRulesEnabled = aAllowUnsafeRules;
851 nsCSSToken* tk = &mToken;
852 for (;;) {
853 // Get next non-whitespace token
854 if (!GetToken(PR_TRUE)) {
855 OUTPUT_ERROR();
856 break;
858 if (eCSSToken_HTMLComment == tk->mType) {
859 continue; // legal here only
861 if (eCSSToken_AtKeyword == tk->mType) {
862 ParseAtRule(AppendRuleToSheet, this);
863 continue;
865 UngetToken();
866 if (ParseRuleSet(AppendRuleToSheet, this)) {
867 mSection = eCSSSection_General;
870 ReleaseScanner();
872 mUnsafeRulesEnabled = PR_FALSE;
874 // XXX check for low level errors
875 return NS_OK;
879 * Determines whether the identifier contained in the given string is a
880 * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
882 static PRBool
883 NonMozillaVendorIdentifier(const nsAString& ident)
885 return (ident.First() == PRUnichar('-') &&
886 !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
887 ident.First() == PRUnichar('_');
891 NS_IMETHODIMP
892 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
893 nsIURI* aDocURL,
894 nsIURI* aBaseURL,
895 nsIPrincipal* aNodePrincipal,
896 nsICSSStyleRule** aResult)
898 NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
899 AssertInitialState();
901 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
903 // XXX line number?
904 InitScanner(aAttributeValue, aDocURL, 0, aBaseURL, aNodePrincipal);
906 mSection = eCSSSection_General;
908 // In quirks mode, allow style declarations to have braces or not
909 // (bug 99554).
910 PRBool haveBraces;
911 if (mNavQuirkMode && GetToken(PR_TRUE)) {
912 haveBraces = eCSSToken_Symbol == mToken.mType &&
913 '{' == mToken.mSymbol;
914 UngetToken();
916 else {
917 haveBraces = PR_FALSE;
920 nsCSSDeclaration* declaration = ParseDeclarationBlock(haveBraces);
921 if (declaration) {
922 // Create a style rule for the declaration
923 nsICSSStyleRule* rule = nsnull;
924 nsresult rv = NS_NewCSSStyleRule(&rule, nsnull, declaration);
925 if (NS_FAILED(rv)) {
926 declaration->RuleAbort();
927 ReleaseScanner();
928 return rv;
930 *aResult = rule;
932 else {
933 *aResult = nsnull;
936 ReleaseScanner();
938 // XXX check for low level errors
939 return NS_OK;
942 NS_IMETHODIMP
943 CSSParserImpl::ParseAndAppendDeclaration(const nsAString& aBuffer,
944 nsIURI* aSheetURL,
945 nsIURI* aBaseURL,
946 nsIPrincipal* aSheetPrincipal,
947 nsCSSDeclaration* aDeclaration,
948 PRBool aParseOnlyOneDecl,
949 PRBool* aChanged,
950 PRBool aClearOldDecl)
952 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
953 AssertInitialState();
955 *aChanged = PR_FALSE;
957 InitScanner(aBuffer, aSheetURL, 0, aBaseURL, aSheetPrincipal);
959 mSection = eCSSSection_General;
961 if (aClearOldDecl) {
962 mData.AssertInitialState();
963 aDeclaration->ClearData();
964 // We could check if it was already empty, but...
965 *aChanged = PR_TRUE;
966 } else {
967 aDeclaration->ExpandTo(&mData);
970 nsresult rv = NS_OK;
971 do {
972 // If we cleared the old decl, then we want to be calling
973 // ValueAppended as we parse.
974 if (!ParseDeclaration(aDeclaration, PR_FALSE, aClearOldDecl, aChanged)) {
975 rv = mScanner.GetLowLevelError();
976 if (NS_FAILED(rv))
977 break;
979 if (!SkipDeclaration(PR_FALSE)) {
980 rv = mScanner.GetLowLevelError();
981 break;
984 } while (!aParseOnlyOneDecl);
985 aDeclaration->CompressFrom(&mData);
987 ReleaseScanner();
988 return rv;
991 NS_IMETHODIMP
992 CSSParserImpl::ParseRule(const nsAString& aRule,
993 nsIURI* aSheetURL,
994 nsIURI* aBaseURL,
995 nsIPrincipal* aSheetPrincipal,
996 nsCOMArray<nsICSSRule>& aResult)
998 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
999 AssertInitialState();
1001 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
1003 InitScanner(aRule, aSheetURL, 0, aBaseURL, aSheetPrincipal);
1005 mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
1007 nsCSSToken* tk = &mToken;
1008 // Get first non-whitespace token
1009 if (!GetToken(PR_TRUE)) {
1010 REPORT_UNEXPECTED(PEParseRuleWSOnly);
1011 OUTPUT_ERROR();
1012 } else if (eCSSToken_AtKeyword == tk->mType) {
1013 ParseAtRule(AppendRuleToArray, &aResult);
1015 else {
1016 UngetToken();
1017 ParseRuleSet(AppendRuleToArray, &aResult);
1019 OUTPUT_ERROR();
1020 ReleaseScanner();
1021 // XXX check for low-level errors
1022 return NS_OK;
1025 NS_IMETHODIMP
1026 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
1027 const nsAString& aPropValue,
1028 nsIURI* aSheetURL,
1029 nsIURI* aBaseURL,
1030 nsIPrincipal* aSheetPrincipal,
1031 nsCSSDeclaration* aDeclaration,
1032 PRBool* aChanged)
1034 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1035 AssertInitialState();
1037 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
1038 NS_ASSERTION(nsnull != aDeclaration, "Need declaration to parse into!");
1039 *aChanged = PR_FALSE;
1041 InitScanner(aPropValue, aSheetURL, 0, aBaseURL, aSheetPrincipal);
1043 mSection = eCSSSection_General;
1045 if (eCSSProperty_UNKNOWN == aPropID) { // unknown property
1046 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1047 const PRUnichar *params[] = {
1048 propName.get()
1050 REPORT_UNEXPECTED_P(PEUnknownProperty, params);
1051 REPORT_UNEXPECTED(PEDeclDropped);
1052 OUTPUT_ERROR();
1053 ReleaseScanner();
1054 return NS_OK;
1057 mData.AssertInitialState();
1058 mTempData.AssertInitialState();
1059 aDeclaration->ExpandTo(&mData);
1060 nsresult result = NS_OK;
1061 PRBool parsedOK = ParseProperty(aPropID);
1062 if (parsedOK && !GetToken(PR_TRUE)) {
1063 TransferTempData(aDeclaration, aPropID, PR_FALSE, PR_FALSE, aChanged);
1064 } else {
1065 if (parsedOK) {
1066 // Junk at end of property value.
1067 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1069 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1070 const PRUnichar *params[] = {
1071 propName.get()
1073 REPORT_UNEXPECTED_P(PEValueParsingError, params);
1074 REPORT_UNEXPECTED(PEDeclDropped);
1075 OUTPUT_ERROR();
1076 ClearTempData(aPropID);
1077 result = mScanner.GetLowLevelError();
1079 CLEAR_ERROR();
1081 aDeclaration->CompressFrom(&mData);
1083 ReleaseScanner();
1084 return result;
1087 NS_IMETHODIMP
1088 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
1089 nsIURI* aURL, // for error reporting
1090 PRUint32 aLineNumber, // for error reporting
1091 nsMediaList* aMediaList,
1092 PRBool aHTMLMode)
1094 // XXX Are there cases where the caller wants to keep what it already
1095 // has in case of parser error?
1096 aMediaList->Clear();
1098 // fake base URL since media lists don't have URLs in them
1099 InitScanner(aBuffer, aURL, aLineNumber, aURL, nsnull);
1101 AssertInitialState();
1102 NS_ASSERTION(aHTMLMode == PR_TRUE || aHTMLMode == PR_FALSE,
1103 "invalid PRBool");
1104 mHTMLMediaMode = aHTMLMode;
1106 // XXXldb We need to make the scanner not skip CSS comments! (Or
1107 // should we?)
1109 // For aHTMLMode, we used to follow the parsing rules in
1110 // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
1111 // which wouldn't work for media queries since they remove all but the
1112 // first word. However, they're changed in
1113 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
1114 // (as of 2008-05-29) which says that the media attribute just points
1115 // to a media query. (The main substative difference is the relative
1116 // precedence of commas and paretheses.)
1118 if (!GatherMedia(aMediaList, PRUnichar(0))) {
1119 aMediaList->Clear();
1120 aMediaList->SetNonEmpty(); // don't match anything
1121 if (!mHTMLMediaMode) {
1122 OUTPUT_ERROR();
1125 nsresult rv = mScanner.GetLowLevelError();
1126 CLEAR_ERROR();
1127 ReleaseScanner();
1128 mHTMLMediaMode = PR_FALSE;
1130 return rv;
1133 NS_IMETHODIMP
1134 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
1135 nsIURI* aURL, // for error reporting
1136 PRUint32 aLineNumber, // for error reporting
1137 nscolor* aColor)
1139 AssertInitialState();
1140 InitScanner(aBuffer, aURL, aLineNumber, aURL, nsnull);
1142 nsCSSValue value;
1143 PRBool colorParsed = ParseColor(value);
1144 nsresult rv = mScanner.GetLowLevelError();
1145 OUTPUT_ERROR();
1146 ReleaseScanner();
1148 if (!colorParsed) {
1149 return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
1152 if (value.GetUnit() == eCSSUnit_String) {
1153 nscolor rgba;
1154 if (NS_ColorNameToRGB(nsDependentString(value.GetStringBufferValue()), &rgba)) {
1155 (*aColor) = rgba;
1156 rv = NS_OK;
1158 } else if (value.GetUnit() == eCSSUnit_Color) {
1159 (*aColor) = value.GetColorValue();
1160 rv = NS_OK;
1161 } else if (value.GetUnit() == eCSSUnit_EnumColor) {
1162 PRInt32 intValue = value.GetIntValue();
1163 if (intValue >= 0) {
1164 nsCOMPtr<nsILookAndFeel> lfSvc = do_GetService("@mozilla.org/widget/lookandfeel;1");
1165 if (lfSvc) {
1166 nscolor rgba;
1167 rv = lfSvc->GetColor((nsILookAndFeel::nsColorID) value.GetIntValue(), rgba);
1168 if (NS_SUCCEEDED(rv))
1169 (*aColor) = rgba;
1171 } else {
1172 // XXX - this is NS_COLOR_CURRENTCOLOR, NS_COLOR_MOZ_HYPERLINKTEXT, etc.
1173 // which we don't handle as per the ParseColorString definition. Should
1174 // remove this limitation at some point.
1175 rv = NS_ERROR_FAILURE;
1179 return rv;
1182 NS_IMETHODIMP
1183 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
1184 nsIURI* aURL, // for error reporting
1185 PRUint32 aLineNumber, // for error reporting
1186 nsCSSSelectorList **aSelectorList)
1188 InitScanner(aSelectorString, aURL, aLineNumber, aURL, nsnull);
1190 AssertInitialState();
1192 mUnresolvablePrefixException = PR_TRUE;
1194 PRBool success = ParseSelectorList(*aSelectorList, PR_FALSE);
1195 nsresult rv = mScanner.GetLowLevelError();
1196 OUTPUT_ERROR();
1197 ReleaseScanner();
1199 mUnresolvablePrefixException = PR_FALSE;
1201 if (success) {
1202 NS_ASSERTION(*aSelectorList, "Should have list!");
1203 return NS_OK;
1206 NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
1207 if (NS_SUCCEEDED(rv)) {
1208 rv = NS_ERROR_DOM_SYNTAX_ERR;
1210 return rv;
1213 //----------------------------------------------------------------------
1215 PRBool
1216 CSSParserImpl::GetToken(PRBool aSkipWS)
1218 for (;;) {
1219 if (!mHavePushBack) {
1220 if (!mScanner.Next(mToken)) {
1221 break;
1224 mHavePushBack = PR_FALSE;
1225 if (aSkipWS && (eCSSToken_WhiteSpace == mToken.mType)) {
1226 continue;
1228 return PR_TRUE;
1230 return PR_FALSE;
1233 PRBool
1234 CSSParserImpl::GetURLToken()
1236 for (;;) {
1237 // XXXldb This pushback code doesn't make sense.
1238 if (! mHavePushBack) {
1239 if (! mScanner.NextURL(mToken)) {
1240 break;
1243 mHavePushBack = PR_FALSE;
1244 if (eCSSToken_WhiteSpace != mToken.mType) {
1245 return PR_TRUE;
1248 return PR_FALSE;
1251 void
1252 CSSParserImpl::UngetToken()
1254 NS_PRECONDITION(mHavePushBack == PR_FALSE, "double pushback");
1255 mHavePushBack = PR_TRUE;
1258 PRBool
1259 CSSParserImpl::ExpectSymbol(PRUnichar aSymbol,
1260 PRBool aSkipWS)
1262 if (!GetToken(aSkipWS)) {
1263 // CSS2.1 specifies that all "open constructs" are to be closed at
1264 // EOF. It simplifies higher layers if we claim to have found an
1265 // ), ], }, or ; if we encounter EOF while looking for one of them.
1266 // Do still issue a diagnostic, to aid debugging.
1267 if (aSymbol == ')' || aSymbol == ']' ||
1268 aSymbol == '}' || aSymbol == ';') {
1269 REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
1270 return PR_TRUE;
1272 else
1273 return PR_FALSE;
1275 if (mToken.IsSymbol(aSymbol)) {
1276 return PR_TRUE;
1278 UngetToken();
1279 return PR_FALSE;
1282 // Checks to see if we're at the end of a property. If an error occurs during
1283 // the check, does not signal a parse error.
1284 PRBool
1285 CSSParserImpl::CheckEndProperty()
1287 if (!GetToken(PR_TRUE)) {
1288 return PR_TRUE; // properties may end with eof
1290 if ((eCSSToken_Symbol == mToken.mType) &&
1291 ((';' == mToken.mSymbol) ||
1292 ('!' == mToken.mSymbol) ||
1293 ('}' == mToken.mSymbol))) {
1294 // XXX need to verify that ! is only followed by "important [;|}]
1295 // XXX this requires a multi-token pushback buffer
1296 UngetToken();
1297 return PR_TRUE;
1299 UngetToken();
1300 return PR_FALSE;
1303 // Checks if we're at the end of a property, raising an error if we're not.
1304 PRBool
1305 CSSParserImpl::ExpectEndProperty()
1307 if (CheckEndProperty())
1308 return PR_TRUE;
1310 // If we're here, we read something incorrect, so we should report it.
1311 REPORT_UNEXPECTED_TOKEN(PRExpectEndValue);
1312 return PR_FALSE;
1316 nsSubstring*
1317 CSSParserImpl::NextIdent()
1319 // XXX Error reporting?
1320 if (!GetToken(PR_TRUE)) {
1321 return nsnull;
1323 if (eCSSToken_Ident != mToken.mType) {
1324 UngetToken();
1325 return nsnull;
1327 return &mToken.mIdent;
1330 PRBool
1331 CSSParserImpl::SkipAtRule()
1333 for (;;) {
1334 if (!GetToken(PR_TRUE)) {
1335 REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF);
1336 return PR_FALSE;
1338 if (eCSSToken_Symbol == mToken.mType) {
1339 PRUnichar symbol = mToken.mSymbol;
1340 if (symbol == ';') {
1341 break;
1343 if (symbol == '{') {
1344 SkipUntil('}');
1345 break;
1346 } else if (symbol == '(') {
1347 SkipUntil(')');
1348 } else if (symbol == '[') {
1349 SkipUntil(']');
1353 return PR_TRUE;
1356 PRBool
1357 CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc,
1358 void* aData)
1360 if ((mSection <= eCSSSection_Charset) &&
1361 (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
1362 if (ParseCharsetRule(aAppendFunc, aData)) {
1363 mSection = eCSSSection_Import; // only one charset allowed
1364 return PR_TRUE;
1367 if ((mSection <= eCSSSection_Import) &&
1368 mToken.mIdent.LowerCaseEqualsLiteral("import")) {
1369 if (ParseImportRule(aAppendFunc, aData)) {
1370 mSection = eCSSSection_Import;
1371 return PR_TRUE;
1374 if ((mSection <= eCSSSection_NameSpace) &&
1375 mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
1376 if (ParseNameSpaceRule(aAppendFunc, aData)) {
1377 mSection = eCSSSection_NameSpace;
1378 return PR_TRUE;
1381 if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
1382 if (ParseMediaRule(aAppendFunc, aData)) {
1383 mSection = eCSSSection_General;
1384 return PR_TRUE;
1387 if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
1388 if (ParseMozDocumentRule(aAppendFunc, aData)) {
1389 mSection = eCSSSection_General;
1390 return PR_TRUE;
1393 if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
1394 if (ParseFontFaceRule(aAppendFunc, aData)) {
1395 mSection = eCSSSection_General;
1396 return PR_TRUE;
1399 if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
1400 if (ParsePageRule(aAppendFunc, aData)) {
1401 mSection = eCSSSection_General;
1402 return PR_TRUE;
1406 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
1407 REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
1408 OUTPUT_ERROR();
1411 // Skip over unsupported at rule, don't advance section
1412 return SkipAtRule();
1415 PRBool
1416 CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc,
1417 void* aData)
1419 if (!GetToken(PR_TRUE)) {
1420 REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
1421 return PR_FALSE;
1424 if (eCSSToken_String != mToken.mType) {
1425 REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
1426 return PR_FALSE;
1429 nsAutoString charset = mToken.mIdent;
1431 if (!ExpectSymbol(';', PR_TRUE)) {
1432 return PR_FALSE;
1435 nsCOMPtr<nsICSSRule> rule;
1436 NS_NewCSSCharsetRule(getter_AddRefs(rule), charset);
1438 if (rule) {
1439 (*aAppendFunc)(rule, aData);
1442 return PR_TRUE;
1445 PRBool
1446 CSSParserImpl::GatherURL(nsString& aURL)
1448 if (!GetToken(PR_TRUE)) {
1449 return PR_FALSE;
1451 if (eCSSToken_String == mToken.mType) {
1452 aURL = mToken.mIdent;
1453 return PR_TRUE;
1455 else if (eCSSToken_Function == mToken.mType &&
1456 mToken.mIdent.LowerCaseEqualsLiteral("url") &&
1457 ExpectSymbol('(', PR_FALSE) &&
1458 GetURLToken() &&
1459 (eCSSToken_String == mToken.mType ||
1460 eCSSToken_URL == mToken.mType)) {
1461 aURL = mToken.mIdent;
1462 if (ExpectSymbol(')', PR_TRUE)) {
1463 return PR_TRUE;
1466 return PR_FALSE;
1469 PRBool
1470 CSSParserImpl::ParseMediaQuery(PRUnichar aStopSymbol,
1471 nsMediaQuery **aQuery,
1472 PRBool *aParsedSomething,
1473 PRBool *aHitStop)
1475 *aQuery = nsnull;
1476 *aParsedSomething = PR_FALSE;
1477 *aHitStop = PR_FALSE;
1479 // "If the comma-separated list is the empty list it is assumed to
1480 // specify the media query 'all'." (css3-mediaqueries, section
1481 // "Media Queries")
1482 if (!GetToken(PR_TRUE)) {
1483 *aHitStop = PR_TRUE;
1484 // expected termination by EOF
1485 if (aStopSymbol == PRUnichar(0))
1486 return PR_TRUE;
1488 // unexpected termination by EOF
1489 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1490 return PR_TRUE;
1493 if (eCSSToken_Symbol == mToken.mType &&
1494 mToken.mSymbol == aStopSymbol) {
1495 *aHitStop = PR_TRUE;
1496 UngetToken();
1497 return PR_TRUE;
1499 UngetToken();
1501 *aParsedSomething = PR_TRUE;
1503 nsAutoPtr<nsMediaQuery> query(new nsMediaQuery);
1504 if (!query) {
1505 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1506 return PR_FALSE;
1509 if (ExpectSymbol('(', PR_TRUE)) {
1510 // we got an expression without a media type
1511 UngetToken(); // so ParseMediaQueryExpression can handle it
1512 query->SetType(nsGkAtoms::all);
1513 query->SetTypeOmitted();
1514 // Just parse the first expression here.
1515 if (!ParseMediaQueryExpression(query)) {
1516 OUTPUT_ERROR();
1517 query->SetHadUnknownExpression();
1519 } else {
1520 nsCOMPtr<nsIAtom> mediaType;
1521 PRBool gotNotOrOnly = PR_FALSE;
1522 for (;;) {
1523 if (!GetToken(PR_TRUE)) {
1524 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1525 return PR_FALSE;
1527 if (eCSSToken_Ident != mToken.mType) {
1528 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
1529 UngetToken();
1530 return PR_FALSE;
1532 // case insensitive from CSS - must be lower cased
1533 ToLowerCase(mToken.mIdent);
1534 mediaType = do_GetAtom(mToken.mIdent);
1535 if (gotNotOrOnly ||
1536 (mediaType != nsGkAtoms::_not && mediaType != nsGkAtoms::only))
1537 break;
1538 gotNotOrOnly = PR_TRUE;
1539 if (mediaType == nsGkAtoms::_not)
1540 query->SetNegated();
1541 else
1542 query->SetHasOnly();
1544 query->SetType(mediaType);
1547 for (;;) {
1548 if (!GetToken(PR_TRUE)) {
1549 *aHitStop = PR_TRUE;
1550 // expected termination by EOF
1551 if (aStopSymbol == PRUnichar(0))
1552 break;
1554 // unexpected termination by EOF
1555 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1556 break;
1559 if (eCSSToken_Symbol == mToken.mType &&
1560 mToken.mSymbol == aStopSymbol) {
1561 *aHitStop = PR_TRUE;
1562 UngetToken();
1563 break;
1565 if (eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
1566 // Done with the expressions for this query
1567 break;
1569 if (eCSSToken_Ident != mToken.mType ||
1570 !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
1571 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
1572 UngetToken();
1573 return PR_FALSE;
1575 if (!ParseMediaQueryExpression(query)) {
1576 OUTPUT_ERROR();
1577 query->SetHadUnknownExpression();
1580 *aQuery = query.forget();
1581 return PR_TRUE;
1584 // Returns false only when there is a low-level error in the scanner
1585 // (out-of-memory).
1586 PRBool
1587 CSSParserImpl::GatherMedia(nsMediaList* aMedia,
1588 PRUnichar aStopSymbol)
1590 for (;;) {
1591 nsAutoPtr<nsMediaQuery> query;
1592 PRBool parsedSomething, hitStop;
1593 if (!ParseMediaQuery(aStopSymbol, getter_Transfers(query),
1594 &parsedSomething, &hitStop)) {
1595 NS_ASSERTION(!hitStop, "should return true when hit stop");
1596 if (NS_FAILED(mScanner.GetLowLevelError())) {
1597 return PR_FALSE;
1599 const PRUnichar stopChars[] =
1600 { PRUnichar(','), aStopSymbol /* may be null */, PRUnichar(0) };
1601 SkipUntilOneOf(stopChars);
1602 // Rely on SkipUntilOneOf leaving mToken around as the last token read.
1603 if (mToken.mType == eCSSToken_Symbol && mToken.mSymbol == aStopSymbol) {
1604 UngetToken();
1605 hitStop = PR_TRUE;
1608 if (parsedSomething) {
1609 aMedia->SetNonEmpty();
1611 if (query) {
1612 nsresult rv = aMedia->AppendQuery(query);
1613 if (NS_FAILED(rv)) {
1614 mScanner.SetLowLevelError(rv);
1615 return PR_FALSE;
1618 if (hitStop) {
1619 break;
1622 return PR_TRUE;
1625 PRBool
1626 CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
1628 if (!ExpectSymbol('(', PR_TRUE)) {
1629 REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
1630 return PR_FALSE;
1632 if (! GetToken(PR_TRUE)) {
1633 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
1634 return PR_FALSE;
1636 if (eCSSToken_Ident != mToken.mType) {
1637 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
1638 SkipUntil(')');
1639 return PR_FALSE;
1642 nsMediaExpression *expr = aQuery->NewExpression();
1643 if (!expr) {
1644 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1645 SkipUntil(')');
1646 return PR_FALSE;
1649 // case insensitive from CSS - must be lower cased
1650 ToLowerCase(mToken.mIdent);
1651 const PRUnichar *featureString;
1652 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
1653 expr->mRange = nsMediaExpression::eMin;
1654 featureString = mToken.mIdent.get() + 4;
1655 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
1656 expr->mRange = nsMediaExpression::eMax;
1657 featureString = mToken.mIdent.get() + 4;
1658 } else {
1659 expr->mRange = nsMediaExpression::eEqual;
1660 featureString = mToken.mIdent.get();
1663 nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
1664 const nsMediaFeature *feature = nsMediaFeatures::features;
1665 for (; feature->mName; ++feature) {
1666 if (*(feature->mName) == mediaFeatureAtom) {
1667 break;
1670 if (!feature->mName ||
1671 (expr->mRange != nsMediaExpression::eEqual &&
1672 feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
1673 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
1674 SkipUntil(')');
1675 return PR_FALSE;
1677 expr->mFeature = feature;
1679 if (!GetToken(PR_TRUE) || mToken.IsSymbol(')')) {
1680 // Query expressions for any feature can be given without a value.
1681 // However, min/max prefixes are not allowed.
1682 if (expr->mRange != nsMediaExpression::eEqual) {
1683 REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue);
1684 return PR_FALSE;
1686 expr->mValue.Reset();
1687 return PR_TRUE;
1690 if (!mToken.IsSymbol(':')) {
1691 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
1692 SkipUntil(')');
1693 return PR_FALSE;
1696 PRBool rv;
1697 switch (feature->mValueType) {
1698 case nsMediaFeature::eLength:
1699 rv = ParsePositiveVariant(expr->mValue, VARIANT_LENGTH, nsnull);
1700 break;
1701 case nsMediaFeature::eInteger:
1702 case nsMediaFeature::eBoolInteger:
1703 rv = ParsePositiveVariant(expr->mValue, VARIANT_INTEGER, nsnull);
1704 // Enforce extra restrictions for eBoolInteger
1705 if (rv &&
1706 feature->mValueType == nsMediaFeature::eBoolInteger &&
1707 expr->mValue.GetIntValue() > 1)
1708 rv = PR_FALSE;
1709 break;
1710 case nsMediaFeature::eIntRatio:
1712 // Two integers separated by '/', with optional whitespace on
1713 // either side of the '/'.
1714 nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
1715 if (!a) {
1716 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1717 SkipUntil(')');
1718 return PR_FALSE;
1720 expr->mValue.SetArrayValue(a, eCSSUnit_Array);
1721 // We don't bother with ParsePositiveVariant since we have to
1722 // check for != 0 as well; no need to worry about the UngetToken
1723 // since we're throwing out up to the next ')' anyway.
1724 rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nsnull) &&
1725 a->Item(0).GetIntValue() > 0 &&
1726 ExpectSymbol('/', PR_TRUE) &&
1727 ParseVariant(a->Item(1), VARIANT_INTEGER, nsnull) &&
1728 a->Item(1).GetIntValue() > 0;
1730 break;
1731 case nsMediaFeature::eResolution:
1732 rv = GetToken(PR_TRUE) && mToken.IsDimension() &&
1733 mToken.mIntegerValid && mToken.mNumber > 0.0f;
1734 if (rv) {
1735 // No worries about whether unitless zero is allowed, since the
1736 // value must be positive (and we checked that above).
1737 NS_ASSERTION(!mToken.mIdent.IsEmpty(), "IsDimension lied");
1738 if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
1739 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
1740 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
1741 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
1742 } else {
1743 rv = PR_FALSE;
1746 break;
1747 case nsMediaFeature::eEnumerated:
1748 rv = ParseVariant(expr->mValue, VARIANT_KEYWORD,
1749 feature->mKeywordTable);
1750 break;
1752 if (!rv || !ExpectSymbol(')', PR_TRUE)) {
1753 REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
1754 SkipUntil(')');
1755 return PR_FALSE;
1758 return PR_TRUE;
1761 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
1762 PRBool
1763 CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData)
1765 nsCOMPtr<nsMediaList> media = new nsMediaList();
1766 if (!media) {
1767 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1768 return PR_FALSE;
1771 nsAutoString url;
1772 if (!GatherURL(url)) {
1773 REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
1774 return PR_FALSE;
1777 if (!ExpectSymbol(';', PR_TRUE)) {
1778 if (!GatherMedia(media, ';') ||
1779 !ExpectSymbol(';', PR_TRUE)) {
1780 REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
1781 // don't advance section, simply ignore invalid @import
1782 return PR_FALSE;
1785 // Safe to assert this, since we ensured that there is something
1786 // other than the ';' coming after the @import's url() token.
1787 NS_ASSERTION(media->Count() != 0, "media list must be nonempty");
1790 ProcessImport(url, media, aAppendFunc, aData);
1791 return PR_TRUE;
1795 PRBool
1796 CSSParserImpl::ProcessImport(const nsString& aURLSpec,
1797 nsMediaList* aMedia,
1798 RuleAppendFunc aAppendFunc,
1799 void* aData)
1801 nsCOMPtr<nsICSSImportRule> rule;
1802 nsresult rv = NS_NewCSSImportRule(getter_AddRefs(rule), aURLSpec, aMedia);
1803 if (NS_FAILED(rv)) {
1804 mScanner.SetLowLevelError(rv);
1805 return PR_FALSE;
1807 (*aAppendFunc)(rule, aData);
1809 if (mChildLoader) {
1810 nsCOMPtr<nsIURI> url;
1811 // XXX should pass a charset!
1812 rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nsnull, mBaseURL);
1814 if (NS_FAILED(rv)) {
1815 // import url is bad
1816 // XXX log this somewhere for easier web page debugging
1817 mScanner.SetLowLevelError(rv);
1818 return PR_FALSE;
1821 mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
1824 return PR_TRUE;
1827 // Parse the {} part of an @media or @-moz-document rule.
1828 PRBool
1829 CSSParserImpl::ParseGroupRule(nsICSSGroupRule* aRule,
1830 RuleAppendFunc aAppendFunc,
1831 void* aData)
1833 // XXXbz this could use better error reporting throughout the method
1834 if (!ExpectSymbol('{', PR_TRUE)) {
1835 return PR_FALSE;
1838 // push rule on stack, loop over children
1839 if (!PushGroup(aRule)) {
1840 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1841 return PR_FALSE;
1843 nsCSSSection holdSection = mSection;
1844 mSection = eCSSSection_General;
1846 for (;;) {
1847 // Get next non-whitespace token
1848 if (! GetToken(PR_TRUE)) {
1849 REPORT_UNEXPECTED_EOF(PEGroupRuleEOF);
1850 break;
1852 if (mToken.IsSymbol('}')) { // done!
1853 UngetToken();
1854 break;
1856 if (eCSSToken_AtKeyword == mToken.mType) {
1857 SkipAtRule(); // group rules cannot contain @rules
1858 continue;
1860 UngetToken();
1861 ParseRuleSet(AppendRuleToSheet, this);
1863 PopGroup();
1865 if (!ExpectSymbol('}', PR_TRUE)) {
1866 mSection = holdSection;
1867 return PR_FALSE;
1869 (*aAppendFunc)(aRule, aData);
1870 return PR_TRUE;
1873 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
1874 PRBool
1875 CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData)
1877 nsCOMPtr<nsMediaList> media = new nsMediaList();
1878 if (!media) {
1879 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1880 return PR_FALSE;
1883 if (GatherMedia(media, '{')) {
1884 // XXXbz this could use better error reporting throughout the method
1885 nsRefPtr<nsCSSMediaRule> rule(new nsCSSMediaRule());
1886 // Append first, so when we do SetMedia() the rule
1887 // knows what its stylesheet is.
1888 if (rule && ParseGroupRule(rule, aAppendFunc, aData)) {
1889 rule->SetMedia(media);
1890 return PR_TRUE;
1894 return PR_FALSE;
1897 // Parse a @-moz-document rule. This is like an @media rule, but instead
1898 // of a medium it has a nonempty list of items where each item is either
1899 // url(), url-prefix(), or domain().
1900 PRBool
1901 CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData)
1903 nsCSSDocumentRule::URL *urls = nsnull;
1904 nsCSSDocumentRule::URL **next = &urls;
1905 do {
1906 if (!GetToken(PR_TRUE) ||
1907 eCSSToken_Function != mToken.mType ||
1908 !(mToken.mIdent.LowerCaseEqualsLiteral("url") ||
1909 mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
1910 mToken.mIdent.LowerCaseEqualsLiteral("domain"))) {
1911 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc);
1912 delete urls;
1913 return PR_FALSE;
1915 nsCSSDocumentRule::URL *cur = *next = new nsCSSDocumentRule::URL;
1916 if (!cur) {
1917 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1918 delete urls;
1919 return PR_FALSE;
1921 next = &cur->next;
1922 if (mToken.mIdent.LowerCaseEqualsLiteral("url")) {
1923 cur->func = nsCSSDocumentRule::eURL;
1924 } else if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
1925 cur->func = nsCSSDocumentRule::eURLPrefix;
1926 } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
1927 cur->func = nsCSSDocumentRule::eDomain;
1930 if (!ExpectSymbol('(', PR_FALSE) ||
1931 !GetURLToken() ||
1932 (eCSSToken_String != mToken.mType &&
1933 eCSSToken_URL != mToken.mType)) {
1934 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
1935 delete urls;
1936 return PR_FALSE;
1938 if (!ExpectSymbol(')', PR_TRUE)) {
1939 delete urls;
1940 return PR_FALSE;
1943 // We could try to make the URL (as long as it's not domain())
1944 // canonical and absolute with NS_NewURI and GetSpec, but I'm
1945 // inclined to think we shouldn't.
1946 CopyUTF16toUTF8(mToken.mIdent, cur->url);
1947 } while (ExpectSymbol(',', PR_TRUE));
1949 nsRefPtr<nsCSSDocumentRule> rule(new nsCSSDocumentRule());
1950 if (!rule) {
1951 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
1952 delete urls;
1953 return PR_FALSE;
1955 rule->SetURLs(urls);
1957 return ParseGroupRule(rule, aAppendFunc, aData);
1960 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
1961 PRBool
1962 CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData)
1964 if (!GetToken(PR_TRUE)) {
1965 REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
1966 return PR_FALSE;
1969 nsAutoString prefix;
1970 nsAutoString url;
1972 if (eCSSToken_Ident == mToken.mType) {
1973 prefix = mToken.mIdent;
1974 // user-specified identifiers are case-sensitive (bug 416106)
1975 if (! GetToken(PR_TRUE)) {
1976 REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
1977 return PR_FALSE;
1981 if (eCSSToken_String == mToken.mType) {
1982 url = mToken.mIdent;
1983 if (ExpectSymbol(';', PR_TRUE)) {
1984 ProcessNameSpace(prefix, url, aAppendFunc, aData);
1985 return PR_TRUE;
1988 else if ((eCSSToken_Function == mToken.mType) &&
1989 (mToken.mIdent.LowerCaseEqualsLiteral("url"))) {
1990 if (ExpectSymbol('(', PR_FALSE)) {
1991 if (GetURLToken()) {
1992 if ((eCSSToken_String == mToken.mType) || (eCSSToken_URL == mToken.mType)) {
1993 url = mToken.mIdent;
1994 if (ExpectSymbol(')', PR_TRUE)) {
1995 if (ExpectSymbol(';', PR_TRUE)) {
1996 ProcessNameSpace(prefix, url, aAppendFunc, aData);
1997 return PR_TRUE;
2004 REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
2006 return PR_FALSE;
2009 PRBool
2010 CSSParserImpl::ProcessNameSpace(const nsString& aPrefix,
2011 const nsString& aURLSpec,
2012 RuleAppendFunc aAppendFunc,
2013 void* aData)
2015 PRBool result = PR_FALSE;
2017 nsCOMPtr<nsICSSNameSpaceRule> rule;
2018 nsCOMPtr<nsIAtom> prefix;
2020 if (!aPrefix.IsEmpty()) {
2021 prefix = do_GetAtom(aPrefix);
2024 NS_NewCSSNameSpaceRule(getter_AddRefs(rule), prefix, aURLSpec);
2025 if (rule) {
2026 (*aAppendFunc)(rule, aData);
2028 // If this was the first namespace rule encountered, it will trigger
2029 // creation of a namespace map.
2030 if (!mNameSpaceMap) {
2031 mNameSpaceMap = mSheet->GetNameSpaceMap();
2035 return result;
2038 // font-face-rule: '@font-face' '{' font-description '}'
2039 // font-description: font-descriptor+
2040 PRBool
2041 CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData)
2043 if (!ExpectSymbol('{', PR_TRUE))
2044 return PR_FALSE;
2046 nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule());
2047 if (!rule) {
2048 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2049 return PR_FALSE;
2052 for (;;) {
2053 if (!GetToken(PR_TRUE)) {
2054 REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
2055 break;
2057 if (mToken.IsSymbol('}')) { // done!
2058 UngetToken();
2059 break;
2062 // ignore extra semicolons
2063 if (mToken.IsSymbol(';'))
2064 continue;
2066 if (!ParseFontDescriptor(rule)) {
2067 REPORT_UNEXPECTED(PEDeclSkipped);
2068 OUTPUT_ERROR();
2069 if (!SkipDeclaration(PR_TRUE))
2070 break;
2073 if (!ExpectSymbol('}', PR_TRUE))
2074 return PR_FALSE;
2075 (*aAppendFunc)(rule, aData);
2076 return PR_TRUE;
2079 // font-descriptor: font-family-desc
2080 // | font-style-desc
2081 // | font-weight-desc
2082 // | font-stretch-desc
2083 // | font-src-desc
2084 // | unicode-range-desc
2086 // All font-*-desc productions follow the pattern
2087 // IDENT ':' value ';'
2089 // On entry to this function, mToken is the IDENT.
2091 PRBool
2092 CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
2094 if (eCSSToken_Ident != mToken.mType) {
2095 REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
2096 return PR_FALSE;
2099 nsString descName = mToken.mIdent;
2100 if (!ExpectSymbol(':', PR_TRUE)) {
2101 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
2102 OUTPUT_ERROR();
2103 return PR_FALSE;
2106 nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
2107 nsCSSValue value;
2109 if (descID == eCSSFontDesc_UNKNOWN) {
2110 if (NonMozillaVendorIdentifier(descName)) {
2111 // silently skip other vendors' extensions
2112 SkipDeclaration(PR_TRUE);
2113 return PR_TRUE;
2114 } else {
2115 const PRUnichar *params[] = {
2116 descName.get()
2118 REPORT_UNEXPECTED_P(PEUnknownFontDesc, params);
2119 return PR_FALSE;
2123 if (!ParseFontDescriptorValue(descID, value)) {
2124 const PRUnichar *params[] = {
2125 descName.get()
2127 REPORT_UNEXPECTED_P(PEValueParsingError, params);
2128 return PR_FALSE;
2131 if (!ExpectEndProperty())
2132 return PR_FALSE;
2134 aRule->SetDesc(descID, value);
2135 return PR_TRUE;
2139 PRBool
2140 CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData)
2142 // XXX not yet implemented
2143 return PR_FALSE;
2146 void
2147 CSSParserImpl::SkipUntil(PRUnichar aStopSymbol)
2149 nsCSSToken* tk = &mToken;
2150 nsAutoTArray<PRUnichar, 16> stack;
2151 stack.AppendElement(aStopSymbol);
2152 for (;;) {
2153 if (!GetToken(PR_TRUE)) {
2154 break;
2156 if (eCSSToken_Symbol == tk->mType) {
2157 PRUnichar symbol = tk->mSymbol;
2158 PRUint32 stackTopIndex = stack.Length() - 1;
2159 if (symbol == stack.ElementAt(stackTopIndex)) {
2160 stack.RemoveElementAt(stackTopIndex);
2161 if (stackTopIndex == 0) {
2162 break;
2164 } else if ('{' == symbol) {
2165 // In this case and the two below, just handle out-of-memory by
2166 // parsing incorrectly. It's highly unlikely we're dealing with
2167 // a legitimate style sheet anyway.
2168 stack.AppendElement('}');
2169 } else if ('[' == symbol) {
2170 stack.AppendElement(']');
2171 } else if ('(' == symbol) {
2172 stack.AppendElement(')');
2178 void
2179 CSSParserImpl::SkipUntilOneOf(const PRUnichar* aStopSymbolChars)
2181 nsCSSToken* tk = &mToken;
2182 nsDependentString stopSymbolChars(aStopSymbolChars);
2183 for (;;) {
2184 if (!GetToken(PR_TRUE)) {
2185 break;
2187 if (eCSSToken_Symbol == tk->mType) {
2188 PRUnichar symbol = tk->mSymbol;
2189 if (stopSymbolChars.FindChar(symbol) != -1) {
2190 break;
2191 } else if ('{' == symbol) {
2192 SkipUntil('}');
2193 } else if ('[' == symbol) {
2194 SkipUntil(']');
2195 } else if ('(' == symbol) {
2196 SkipUntil(')');
2202 PRBool
2203 CSSParserImpl::GetNonCloseParenToken(PRBool aSkipWS)
2205 if (!GetToken(aSkipWS))
2206 return PR_FALSE;
2207 if (mToken.mType == eCSSToken_Symbol && mToken.mSymbol == ')') {
2208 UngetToken();
2209 return PR_FALSE;
2211 return PR_TRUE;
2214 PRBool
2215 CSSParserImpl::SkipDeclaration(PRBool aCheckForBraces)
2217 nsCSSToken* tk = &mToken;
2218 for (;;) {
2219 if (!GetToken(PR_TRUE)) {
2220 if (aCheckForBraces) {
2221 REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
2223 return PR_FALSE;
2225 if (eCSSToken_Symbol == tk->mType) {
2226 PRUnichar symbol = tk->mSymbol;
2227 if (';' == symbol) {
2228 break;
2230 if (aCheckForBraces) {
2231 if ('}' == symbol) {
2232 UngetToken();
2233 break;
2236 if ('{' == symbol) {
2237 SkipUntil('}');
2238 } else if ('(' == symbol) {
2239 SkipUntil(')');
2240 } else if ('[' == symbol) {
2241 SkipUntil(']');
2245 return PR_TRUE;
2248 void
2249 CSSParserImpl::SkipRuleSet()
2251 nsCSSToken* tk = &mToken;
2252 for (;;) {
2253 if (!GetToken(PR_TRUE)) {
2254 REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
2255 break;
2257 if (eCSSToken_Symbol == tk->mType) {
2258 PRUnichar symbol = tk->mSymbol;
2259 if ('{' == symbol) {
2260 SkipUntil('}');
2261 break;
2263 if ('(' == symbol) {
2264 SkipUntil(')');
2265 } else if ('[' == symbol) {
2266 SkipUntil(']');
2272 PRBool
2273 CSSParserImpl::PushGroup(nsICSSGroupRule* aRule)
2275 if (mGroupStack.AppendObject(aRule))
2276 return PR_TRUE;
2278 return PR_FALSE;
2281 void
2282 CSSParserImpl::PopGroup(void)
2284 PRInt32 count = mGroupStack.Count();
2285 if (0 < count) {
2286 mGroupStack.RemoveObjectAt(count - 1);
2290 void
2291 CSSParserImpl::AppendRule(nsICSSRule* aRule)
2293 PRInt32 count = mGroupStack.Count();
2294 if (0 < count) {
2295 mGroupStack[count - 1]->AppendStyleRule(aRule);
2297 else {
2298 mSheet->AppendStyleRule(aRule);
2302 PRBool
2303 CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData)
2305 // First get the list of selectors for the rule
2306 nsCSSSelectorList* slist = nsnull;
2307 PRUint32 linenum = mScanner.GetLineNumber();
2308 if (! ParseSelectorList(slist, PR_TRUE)) {
2309 REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
2310 OUTPUT_ERROR();
2311 SkipRuleSet();
2312 return PR_FALSE;
2314 NS_ASSERTION(nsnull != slist, "null selector list");
2315 CLEAR_ERROR();
2317 // Next parse the declaration block
2318 nsCSSDeclaration* declaration = ParseDeclarationBlock(PR_TRUE);
2319 if (nsnull == declaration) {
2320 // XXX skip something here
2321 delete slist;
2322 return PR_FALSE;
2325 #if 0
2326 slist->Dump();
2327 fputs("{\n", stdout);
2328 declaration->List();
2329 fputs("}\n", stdout);
2330 #endif
2332 // Translate the selector list and declaration block into style data
2334 nsCOMPtr<nsICSSStyleRule> rule;
2335 NS_NewCSSStyleRule(getter_AddRefs(rule), slist, declaration);
2336 if (!rule) {
2337 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2338 delete slist;
2339 return PR_FALSE;
2341 rule->SetLineNumber(linenum);
2342 (*aAppendFunc)(rule, aData);
2344 return PR_TRUE;
2347 PRBool
2348 CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead,
2349 PRBool aTerminateAtBrace)
2351 nsCSSSelectorList* list = nsnull;
2352 if (! ParseSelectorGroup(list)) {
2353 // must have at least one selector group
2354 aListHead = nsnull;
2355 return PR_FALSE;
2357 NS_ASSERTION(nsnull != list, "no selector list");
2358 aListHead = list;
2360 // After that there must either be a "," or a "{" (the latter if
2361 // aTerminateAtBrace is true)
2362 nsCSSToken* tk = &mToken;
2363 for (;;) {
2364 if (! GetToken(PR_TRUE)) {
2365 if (!aTerminateAtBrace) {
2366 return PR_TRUE;
2369 REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
2370 break;
2373 if (eCSSToken_Symbol == tk->mType) {
2374 if (',' == tk->mSymbol) {
2375 nsCSSSelectorList* newList = nsnull;
2376 // Another selector group must follow
2377 if (! ParseSelectorGroup(newList)) {
2378 break;
2380 // add new list to the end of the selector list
2381 list->mNext = newList;
2382 list = newList;
2383 continue;
2384 } else if ('{' == tk->mSymbol && aTerminateAtBrace) {
2385 UngetToken();
2386 return PR_TRUE;
2389 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
2390 UngetToken();
2391 break;
2394 delete aListHead;
2395 aListHead = nsnull;
2396 return PR_FALSE;
2399 static PRBool IsSinglePseudoClass(const nsCSSSelector& aSelector)
2401 return PRBool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
2402 (aSelector.mTag == nsnull) &&
2403 (aSelector.mIDList == nsnull) &&
2404 (aSelector.mClassList == nsnull) &&
2405 (aSelector.mAttrList == nsnull) &&
2406 (aSelector.mNegations == nsnull) &&
2407 (aSelector.mPseudoClassList != nsnull) &&
2408 (aSelector.mPseudoClassList->mNext == nsnull));
2411 #ifdef MOZ_XUL
2412 static PRBool IsTreePseudoElement(nsIAtom* aPseudo)
2414 const char* str;
2415 aPseudo->GetUTF8String(&str);
2416 static const char moz_tree[] = ":-moz-tree-";
2417 return nsCRT::strncmp(str, moz_tree, PRInt32(sizeof(moz_tree)-1)) == 0;
2419 #endif
2421 PRBool
2422 CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList)
2424 nsAutoPtr<nsCSSSelectorList> list;
2425 PRUnichar combinator = PRUnichar(0);
2426 PRInt32 weight = 0;
2427 PRBool havePseudoElement = PR_FALSE;
2428 PRBool done = PR_FALSE;
2429 while (!done) {
2430 nsAutoPtr<nsCSSSelector> newSelector(new nsCSSSelector());
2431 if (!newSelector) {
2432 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2433 return PR_FALSE;
2435 nsSelectorParsingStatus parsingStatus =
2436 ParseSelector(*newSelector);
2437 if (parsingStatus == eSelectorParsingStatus_Empty) {
2438 if (!list) {
2439 REPORT_UNEXPECTED(PESelectorGroupNoSelector);
2441 break;
2443 if (parsingStatus == eSelectorParsingStatus_Error) {
2444 list = nsnull;
2445 break;
2447 if (nsnull == list) {
2448 list = new nsCSSSelectorList();
2449 if (nsnull == list) {
2450 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2451 return PR_FALSE;
2454 list->AddSelector(newSelector);
2455 nsCSSSelector* listSel = list->mSelectors;
2457 // pull out pseudo elements here
2458 nsPseudoClassList* prevList = nsnull;
2459 nsPseudoClassList* pseudoClassList = listSel->mPseudoClassList;
2460 while (nsnull != pseudoClassList) {
2461 if (! nsCSSPseudoClasses::IsPseudoClass(pseudoClassList->mAtom)) {
2462 havePseudoElement = PR_TRUE;
2463 if (IsSinglePseudoClass(*listSel)) { // convert to pseudo element selector
2464 nsIAtom* pseudoElement = pseudoClassList->mAtom; // steal ref count
2465 pseudoClassList->mAtom = nsnull;
2466 listSel->Reset();
2467 if (listSel->mNext) {// more to the selector
2468 listSel->mOperator = PRUnichar('>');
2469 nsAutoPtr<nsCSSSelector> empty(new nsCSSSelector());
2470 if (!empty) {
2471 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2472 return PR_FALSE;
2474 list->AddSelector(empty); // leave a blank (universal) selector in the middle
2475 listSel = list->mSelectors; // use the new one for the pseudo
2477 listSel->mTag = pseudoElement;
2479 else { // append new pseudo element selector
2480 nsAutoPtr<nsCSSSelector> pseudoTagSelector(new nsCSSSelector());
2481 if (!pseudoTagSelector) {
2482 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
2483 return PR_FALSE;
2485 pseudoTagSelector->mTag = pseudoClassList->mAtom; // steal ref count
2486 #ifdef MOZ_XUL
2487 if (IsTreePseudoElement(pseudoTagSelector->mTag)) {
2488 // Take the remaining "pseudoclasses" that we parsed
2489 // inside the tree pseudoelement's ()-list, and
2490 // make our new selector have these pseudoclasses
2491 // in its pseudoclass list.
2492 pseudoTagSelector->mPseudoClassList = pseudoClassList->mNext;
2493 pseudoClassList->mNext = nsnull;
2495 #endif
2496 list->AddSelector(pseudoTagSelector);
2497 pseudoClassList->mAtom = nsnull;
2498 listSel->mOperator = PRUnichar('>');
2499 if (nsnull == prevList) { // delete list entry
2500 listSel->mPseudoClassList = pseudoClassList->mNext;
2502 else {
2503 prevList->mNext = pseudoClassList->mNext;
2505 pseudoClassList->mNext = nsnull;
2506 delete pseudoClassList;
2507 weight += listSel->CalcWeight(); // capture weight from remainder
2509 break; // only one pseudo element per selector
2511 prevList = pseudoClassList;
2512 pseudoClassList = pseudoClassList->mNext;
2515 combinator = PRUnichar(0);
2516 if (!GetToken(PR_FALSE)) {
2517 break;
2520 // Assume we are done unless we find a combinator here.
2521 done = PR_TRUE;
2522 if (eCSSToken_WhiteSpace == mToken.mType) {
2523 if (!GetToken(PR_TRUE)) {
2524 break;
2526 done = PR_FALSE;
2529 if (eCSSToken_Symbol == mToken.mType &&
2530 ('+' == mToken.mSymbol ||
2531 '>' == mToken.mSymbol ||
2532 '~' == mToken.mSymbol)) {
2533 done = PR_FALSE;
2534 combinator = mToken.mSymbol;
2535 list->mSelectors->SetOperator(combinator);
2537 else {
2538 if (eCSSToken_Symbol == mToken.mType &&
2539 ('{' == mToken.mSymbol ||
2540 ',' == mToken.mSymbol)) {
2541 // End of this selector group
2542 done = PR_TRUE;
2544 UngetToken(); // give it back to selector if we're not done, or make sure
2545 // we see it as the end of the selector if we are.
2548 if (havePseudoElement) {
2549 break;
2551 else {
2552 weight += listSel->CalcWeight();
2556 if (PRUnichar(0) != combinator) { // no dangling combinators
2557 list = nsnull;
2558 // This should report the problematic combinator
2559 REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
2561 aList = list.forget();
2562 if (aList) {
2563 aList->mWeight = weight;
2565 return PRBool(nsnull != aList);
2568 #define SEL_MASK_NSPACE 0x01
2569 #define SEL_MASK_ELEM 0x02
2570 #define SEL_MASK_ID 0x04
2571 #define SEL_MASK_CLASS 0x08
2572 #define SEL_MASK_ATTRIB 0x10
2573 #define SEL_MASK_PCLASS 0x20
2574 #define SEL_MASK_PELEM 0x40
2577 // Parses an ID selector #name
2579 CSSParserImpl::nsSelectorParsingStatus
2580 CSSParserImpl::ParseIDSelector(PRInt32& aDataMask,
2581 nsCSSSelector& aSelector)
2583 NS_ASSERTION(!mToken.mIdent.IsEmpty(),
2584 "Empty mIdent in eCSSToken_ID token?");
2585 aDataMask |= SEL_MASK_ID;
2586 aSelector.AddID(mToken.mIdent);
2587 return eSelectorParsingStatus_Continue;
2591 // Parses a class selector .name
2593 CSSParserImpl::nsSelectorParsingStatus
2594 CSSParserImpl::ParseClassSelector(PRInt32& aDataMask,
2595 nsCSSSelector& aSelector)
2597 if (! GetToken(PR_FALSE)) { // get ident
2598 REPORT_UNEXPECTED_EOF(PEClassSelEOF);
2599 return eSelectorParsingStatus_Error;
2601 if (eCSSToken_Ident != mToken.mType) { // malformed selector
2602 REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
2603 UngetToken();
2604 return eSelectorParsingStatus_Error;
2606 aDataMask |= SEL_MASK_CLASS;
2608 aSelector.AddClass(mToken.mIdent);
2610 return eSelectorParsingStatus_Continue;
2614 // Parse a type element selector or a universal selector
2615 // namespace|type or namespace|* or *|* or *
2617 CSSParserImpl::nsSelectorParsingStatus
2618 CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask,
2619 nsCSSSelector& aSelector,
2620 PRBool aIsNegated)
2622 nsAutoString buffer;
2623 if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
2624 if (ExpectSymbol('|', PR_FALSE)) { // was namespace
2625 aDataMask |= SEL_MASK_NSPACE;
2626 aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
2628 if (! GetToken(PR_FALSE)) {
2629 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2630 return eSelectorParsingStatus_Error;
2632 if (eCSSToken_Ident == mToken.mType) { // element name
2633 aDataMask |= SEL_MASK_ELEM;
2634 if (mCaseSensitive) {
2635 aSelector.SetTag(mToken.mIdent);
2637 else {
2638 ToLowerCase(mToken.mIdent, buffer);
2639 aSelector.SetTag(buffer);
2642 else if (mToken.IsSymbol('*')) { // universal selector
2643 aDataMask |= SEL_MASK_ELEM;
2644 // don't set tag
2646 else {
2647 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2648 UngetToken();
2649 return eSelectorParsingStatus_Error;
2652 else { // was universal element selector
2653 SetDefaultNamespaceOnSelector(aSelector);
2654 aDataMask |= SEL_MASK_ELEM;
2655 // don't set any tag in the selector
2657 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
2658 return eSelectorParsingStatus_Done;
2661 else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
2662 buffer = mToken.mIdent; // hang on to ident
2664 if (ExpectSymbol('|', PR_FALSE)) { // was namespace
2665 aDataMask |= SEL_MASK_NSPACE;
2666 PRInt32 nameSpaceID;
2667 if (!GetNamespaceIdForPrefix(buffer, &nameSpaceID)) {
2668 return eSelectorParsingStatus_Error;
2670 aSelector.SetNameSpace(nameSpaceID);
2672 if (! GetToken(PR_FALSE)) {
2673 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2674 return eSelectorParsingStatus_Error;
2676 if (eCSSToken_Ident == mToken.mType) { // element name
2677 aDataMask |= SEL_MASK_ELEM;
2678 if (mCaseSensitive) {
2679 aSelector.SetTag(mToken.mIdent);
2681 else {
2682 ToLowerCase(mToken.mIdent, buffer);
2683 aSelector.SetTag(buffer);
2686 else if (mToken.IsSymbol('*')) { // universal selector
2687 aDataMask |= SEL_MASK_ELEM;
2688 // don't set tag
2690 else {
2691 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2692 UngetToken();
2693 return eSelectorParsingStatus_Error;
2696 else { // was element name
2697 SetDefaultNamespaceOnSelector(aSelector);
2698 if (mCaseSensitive) {
2699 aSelector.SetTag(buffer);
2701 else {
2702 ToLowerCase(buffer);
2703 aSelector.SetTag(buffer);
2705 aDataMask |= SEL_MASK_ELEM;
2707 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
2708 return eSelectorParsingStatus_Done;
2711 else if (mToken.IsSymbol('|')) { // No namespace
2712 aDataMask |= SEL_MASK_NSPACE;
2713 aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
2715 // get mandatory tag
2716 if (! GetToken(PR_FALSE)) {
2717 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2718 return eSelectorParsingStatus_Error;
2720 if (eCSSToken_Ident == mToken.mType) { // element name
2721 aDataMask |= SEL_MASK_ELEM;
2722 if (mCaseSensitive) {
2723 aSelector.SetTag(mToken.mIdent);
2725 else {
2726 ToLowerCase(mToken.mIdent, buffer);
2727 aSelector.SetTag(buffer);
2730 else if (mToken.IsSymbol('*')) { // universal selector
2731 aDataMask |= SEL_MASK_ELEM;
2732 // don't set tag
2734 else {
2735 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2736 UngetToken();
2737 return eSelectorParsingStatus_Error;
2739 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
2740 return eSelectorParsingStatus_Done;
2743 else {
2744 SetDefaultNamespaceOnSelector(aSelector);
2747 if (aIsNegated) {
2748 // restore last token read in case of a negated type selector
2749 UngetToken();
2751 return eSelectorParsingStatus_Continue;
2755 // Parse attribute selectors [attr], [attr=value], [attr|=value],
2756 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
2758 CSSParserImpl::nsSelectorParsingStatus
2759 CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask,
2760 nsCSSSelector& aSelector)
2762 if (! GetToken(PR_TRUE)) { // premature EOF
2763 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2764 return eSelectorParsingStatus_Error;
2767 PRInt32 nameSpaceID = kNameSpaceID_None;
2768 nsAutoString attr;
2769 if (mToken.IsSymbol('*')) { // wildcard namespace
2770 nameSpaceID = kNameSpaceID_Unknown;
2771 if (ExpectSymbol('|', PR_FALSE)) {
2772 if (! GetToken(PR_FALSE)) { // premature EOF
2773 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2774 return eSelectorParsingStatus_Error;
2776 if (eCSSToken_Ident == mToken.mType) { // attr name
2777 attr = mToken.mIdent;
2779 else {
2780 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2781 UngetToken();
2782 return eSelectorParsingStatus_Error;
2785 else {
2786 REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
2787 return eSelectorParsingStatus_Error;
2790 else if (mToken.IsSymbol('|')) { // NO namespace
2791 if (! GetToken(PR_FALSE)) { // premature EOF
2792 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2793 return eSelectorParsingStatus_Error;
2795 if (eCSSToken_Ident == mToken.mType) { // attr name
2796 attr = mToken.mIdent;
2798 else {
2799 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2800 UngetToken();
2801 return eSelectorParsingStatus_Error;
2804 else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
2805 attr = mToken.mIdent; // hang on to it
2806 if (ExpectSymbol('|', PR_FALSE)) { // was a namespace
2807 if (!GetNamespaceIdForPrefix(attr, &nameSpaceID)) {
2808 return eSelectorParsingStatus_Error;
2810 if (! GetToken(PR_FALSE)) { // premature EOF
2811 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2812 return eSelectorParsingStatus_Error;
2814 if (eCSSToken_Ident == mToken.mType) { // attr name
2815 attr = mToken.mIdent;
2817 else {
2818 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2819 UngetToken();
2820 return eSelectorParsingStatus_Error;
2824 else { // malformed
2825 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
2826 UngetToken();
2827 return eSelectorParsingStatus_Error;
2830 if (! mCaseSensitive) {
2831 ToLowerCase(attr);
2833 if (! GetToken(PR_TRUE)) { // premature EOF
2834 REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
2835 return eSelectorParsingStatus_Error;
2837 if ((eCSSToken_Symbol == mToken.mType) ||
2838 (eCSSToken_Includes == mToken.mType) ||
2839 (eCSSToken_Dashmatch == mToken.mType) ||
2840 (eCSSToken_Beginsmatch == mToken.mType) ||
2841 (eCSSToken_Endsmatch == mToken.mType) ||
2842 (eCSSToken_Containsmatch == mToken.mType)) {
2843 PRUint8 func;
2844 if (eCSSToken_Includes == mToken.mType) {
2845 func = NS_ATTR_FUNC_INCLUDES;
2847 else if (eCSSToken_Dashmatch == mToken.mType) {
2848 func = NS_ATTR_FUNC_DASHMATCH;
2850 else if (eCSSToken_Beginsmatch == mToken.mType) {
2851 func = NS_ATTR_FUNC_BEGINSMATCH;
2853 else if (eCSSToken_Endsmatch == mToken.mType) {
2854 func = NS_ATTR_FUNC_ENDSMATCH;
2856 else if (eCSSToken_Containsmatch == mToken.mType) {
2857 func = NS_ATTR_FUNC_CONTAINSMATCH;
2859 else if (']' == mToken.mSymbol) {
2860 aDataMask |= SEL_MASK_ATTRIB;
2861 aSelector.AddAttribute(nameSpaceID, attr);
2862 func = NS_ATTR_FUNC_SET;
2864 else if ('=' == mToken.mSymbol) {
2865 func = NS_ATTR_FUNC_EQUALS;
2867 else {
2868 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
2869 UngetToken(); // bad function
2870 return eSelectorParsingStatus_Error;
2872 if (NS_ATTR_FUNC_SET != func) { // get value
2873 if (! GetToken(PR_TRUE)) { // premature EOF
2874 REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
2875 return eSelectorParsingStatus_Error;
2877 if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
2878 nsAutoString value(mToken.mIdent);
2879 if (! GetToken(PR_TRUE)) { // premature EOF
2880 REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
2881 return eSelectorParsingStatus_Error;
2883 if (mToken.IsSymbol(']')) {
2884 PRBool isCaseSensitive = PR_TRUE;
2886 // If we're parsing a style sheet for an HTML document, and
2887 // the attribute selector is for a non-namespaced attribute,
2888 // then check to see if it's one of the known attributes whose
2889 // VALUE is case-insensitive.
2890 if (!mCaseSensitive && nameSpaceID == kNameSpaceID_None) {
2891 static const char* caseInsensitiveHTMLAttribute[] = {
2892 // list based on http://www.w3.org/TR/html4/
2893 "lang",
2894 "dir",
2895 "http-equiv",
2896 "text",
2897 "link",
2898 "vlink",
2899 "alink",
2900 "compact",
2901 "align",
2902 "frame",
2903 "rules",
2904 "valign",
2905 "scope",
2906 "axis",
2907 "nowrap",
2908 "hreflang",
2909 "rel",
2910 "rev",
2911 "charset",
2912 "codetype",
2913 "declare",
2914 "valuetype",
2915 "shape",
2916 "nohref",
2917 "media",
2918 "bgcolor",
2919 "clear",
2920 "color",
2921 "face",
2922 "noshade",
2923 "noresize",
2924 "scrolling",
2925 "target",
2926 "method",
2927 "enctype",
2928 "accept-charset",
2929 "accept",
2930 "checked",
2931 "multiple",
2932 "selected",
2933 "disabled",
2934 "readonly",
2935 "language",
2936 "defer",
2937 "type",
2938 // additional attributes not in HTML4
2939 "direction", // marquee
2940 nsnull
2942 short i = 0;
2943 const char* htmlAttr;
2944 while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
2945 if (attr.EqualsIgnoreCase(htmlAttr)) {
2946 isCaseSensitive = PR_FALSE;
2947 break;
2951 aDataMask |= SEL_MASK_ATTRIB;
2952 aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
2954 else {
2955 REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
2956 UngetToken();
2957 return eSelectorParsingStatus_Error;
2960 else {
2961 REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
2962 UngetToken();
2963 return eSelectorParsingStatus_Error;
2967 else {
2968 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
2969 UngetToken(); // bad dog, no biscut!
2970 return eSelectorParsingStatus_Error;
2972 return eSelectorParsingStatus_Continue;
2976 // Parse pseudo-classes and pseudo-elements
2978 CSSParserImpl::nsSelectorParsingStatus
2979 CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask,
2980 nsCSSSelector& aSelector,
2981 PRBool aIsNegated)
2983 if (! GetToken(PR_FALSE)) { // premature eof
2984 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
2985 return eSelectorParsingStatus_Error;
2988 // First, find out whether we are parsing a CSS3 pseudo-element
2989 PRBool parsingPseudoElement = PR_FALSE;
2990 if (mToken.IsSymbol(':')) {
2991 parsingPseudoElement = PR_TRUE;
2992 if (! GetToken(PR_FALSE)) { // premature eof
2993 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
2994 return eSelectorParsingStatus_Error;
2998 // Do some sanity-checking on the token
2999 if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
3000 // malformed selector
3001 REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
3002 UngetToken();
3003 return eSelectorParsingStatus_Error;
3006 // OK, now we know we have an mIdent. Atomize it. All the atoms, for
3007 // pseudo-classes as well as pseudo-elements, start with a single ':'.
3008 nsAutoString buffer;
3009 buffer.Append(PRUnichar(':'));
3010 buffer.Append(mToken.mIdent);
3011 ToLowerCase(buffer);
3012 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
3014 // stash away some info about this pseudo so we only have to get it once.
3015 PRBool isTreePseudo = PR_FALSE;
3016 #ifdef MOZ_XUL
3017 isTreePseudo = IsTreePseudoElement(pseudo);
3018 // If a tree pseudo-element is using the function syntax, it will
3019 // get isTree set here and will pass the check below that only
3020 // allows functions if they are in our list of things allowed to be
3021 // functions. If it is _not_ using the function syntax, isTree will
3022 // be false, and it will still pass that check. So the tree
3023 // pseudo-elements are allowed to be either functions or not, as
3024 // desired.
3025 PRBool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
3026 #endif
3027 PRBool isPseudoElement = nsCSSPseudoElements::IsPseudoElement(pseudo);
3028 // anonymous boxes are only allowed if they're the tree boxes or we have
3029 // enabled unsafe rules
3030 PRBool isAnonBox = nsCSSAnonBoxes::IsAnonBox(pseudo) &&
3031 (mUnsafeRulesEnabled || isTreePseudo);
3032 PRBool isPseudoClass = nsCSSPseudoClasses::IsPseudoClass(pseudo);
3034 if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
3035 // Not a pseudo-class, not a pseudo-element.... forget it
3036 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
3037 UngetToken();
3038 return eSelectorParsingStatus_Error;
3041 // If it's a function token, it better be on our "ok" list, and if the name
3042 // is that of a function pseudo it better be a function token
3043 if ((eCSSToken_Function == mToken.mType) !=
3045 #ifdef MOZ_XUL
3046 isTree ||
3047 #endif
3048 nsCSSPseudoClasses::notPseudo == pseudo ||
3049 nsCSSPseudoClasses::HasStringArg(pseudo) ||
3050 nsCSSPseudoClasses::HasNthPairArg(pseudo))) {
3051 // There are no other function pseudos
3052 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
3053 UngetToken();
3054 return eSelectorParsingStatus_Error;
3057 // If it starts with "::", it better be a pseudo-element
3058 if (parsingPseudoElement &&
3059 !isPseudoElement &&
3060 !isAnonBox) {
3061 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
3062 UngetToken();
3063 return eSelectorParsingStatus_Error;
3066 if (!parsingPseudoElement && nsCSSPseudoClasses::notPseudo == pseudo) {
3067 if (aIsNegated) { // :not() can't be itself negated
3068 REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
3069 UngetToken();
3070 return eSelectorParsingStatus_Error;
3072 // CSS 3 Negation pseudo-class takes one simple selector as argument
3073 nsSelectorParsingStatus parsingStatus =
3074 ParseNegatedSimpleSelector(aDataMask, aSelector);
3075 if (eSelectorParsingStatus_Continue != parsingStatus) {
3076 return parsingStatus;
3079 else if (!parsingPseudoElement && isPseudoClass) {
3080 aDataMask |= SEL_MASK_PCLASS;
3081 if (nsCSSPseudoClasses::HasStringArg(pseudo)) {
3082 nsSelectorParsingStatus parsingStatus =
3083 ParsePseudoClassWithIdentArg(aSelector, pseudo);
3084 if (eSelectorParsingStatus_Continue != parsingStatus) {
3085 return parsingStatus;
3088 else if (nsCSSPseudoClasses::HasNthPairArg(pseudo)) {
3089 nsSelectorParsingStatus parsingStatus =
3090 ParsePseudoClassWithNthPairArg(aSelector, pseudo);
3091 if (eSelectorParsingStatus_Continue != parsingStatus) {
3092 return parsingStatus;
3095 else {
3096 aSelector.AddPseudoClass(pseudo);
3099 else if (isPseudoElement || isAnonBox) {
3100 // Pseudo-element. Make some more sanity checks.
3102 if (aIsNegated) { // pseudo-elements can't be negated
3103 REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
3104 UngetToken();
3105 return eSelectorParsingStatus_Error;
3107 // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
3108 // to have a single ':' on them. Others (CSS3+ pseudo-elements and
3109 // various -moz-* pseudo-elements) must have |parsingPseudoElement|
3110 // set.
3111 if (!parsingPseudoElement &&
3112 !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
3113 #ifdef MOZ_XUL
3114 && !isTreePseudo
3115 #endif
3117 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
3118 UngetToken();
3119 return eSelectorParsingStatus_Error;
3122 if (0 == (aDataMask & SEL_MASK_PELEM)) {
3123 aDataMask |= SEL_MASK_PELEM;
3124 aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later
3126 #ifdef MOZ_XUL
3127 if (isTree) {
3128 // We have encountered a pseudoelement of the form
3129 // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each
3130 // item in the list to the pseudoclass list. They will be pulled
3131 // from the list later along with the pseudo-element.
3132 if (!ParseTreePseudoElement(aSelector)) {
3133 return eSelectorParsingStatus_Error;
3136 #endif
3138 // ensure selector ends here, must be followed by EOF, space, '{' or ','
3139 if (GetToken(PR_FALSE)) { // premature eof is ok (here!)
3140 if ((eCSSToken_WhiteSpace == mToken.mType) ||
3141 (mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
3142 UngetToken();
3143 return eSelectorParsingStatus_Done;
3145 REPORT_UNEXPECTED_TOKEN(PEPseudoSelTrailing);
3146 UngetToken();
3147 return eSelectorParsingStatus_Error;
3150 else { // multiple pseudo elements, not legal
3151 REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
3152 UngetToken();
3153 return eSelectorParsingStatus_Error;
3156 #ifdef DEBUG
3157 else {
3158 // We should never end up here. Indeed, if we ended up here, we know (from
3159 // the current if/else cascade) that !isPseudoElement and !isAnonBox. But
3160 // then due to our earlier check we know that isPseudoClass. Since we
3161 // didn't fall into the isPseudoClass case in this cascade, we must have
3162 // parsingPseudoElement. But we've already checked the
3163 // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
3164 // it's happened.
3165 NS_NOTREACHED("How did this happen?");
3167 #endif
3168 return eSelectorParsingStatus_Continue;
3172 // Parse the argument of a negation pseudo-class :not()
3174 CSSParserImpl::nsSelectorParsingStatus
3175 CSSParserImpl::ParseNegatedSimpleSelector(PRInt32& aDataMask,
3176 nsCSSSelector& aSelector)
3178 // Check if we have the first parenthesis
3179 if (!ExpectSymbol('(', PR_FALSE)) {
3180 REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
3181 return eSelectorParsingStatus_Error;
3184 if (! GetToken(PR_TRUE)) { // premature eof
3185 REPORT_UNEXPECTED_EOF(PENegationEOF);
3186 return eSelectorParsingStatus_Error;
3189 // Create a new nsCSSSelector and add it to the end of
3190 // aSelector.mNegations.
3191 // Given the current parsing rules, every selector in mNegations
3192 // contains only one simple selector (css3 definition) within it.
3193 // This could easily change in future versions of CSS, and the only
3194 // thing we need to change to support that is this parsing code and the
3195 // serialization code for nsCSSSelector.
3196 nsCSSSelector *newSel = new nsCSSSelector();
3197 if (!newSel) {
3198 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
3199 return eSelectorParsingStatus_Error;
3201 nsCSSSelector* negations = &aSelector;
3202 while (negations->mNegations) {
3203 negations = negations->mNegations;
3205 negations->mNegations = newSel;
3207 nsSelectorParsingStatus parsingStatus;
3208 if (eCSSToken_ID == mToken.mType) { // #id
3209 parsingStatus = ParseIDSelector(aDataMask, *newSel);
3211 else if (mToken.IsSymbol('.')) { // .class
3212 parsingStatus = ParseClassSelector(aDataMask, *newSel);
3214 else if (mToken.IsSymbol(':')) { // :pseudo
3215 parsingStatus = ParsePseudoSelector(aDataMask, *newSel, PR_TRUE);
3217 else if (mToken.IsSymbol('[')) { // [attribute
3218 parsingStatus = ParseAttributeSelector(aDataMask, *newSel);
3220 else {
3221 // then it should be a type element or universal selector
3222 parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, PR_TRUE);
3224 if (eSelectorParsingStatus_Error == parsingStatus) {
3225 REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
3226 return parsingStatus;
3228 // close the parenthesis
3229 if (!ExpectSymbol(')', PR_TRUE)) {
3230 REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
3231 return eSelectorParsingStatus_Error;
3234 NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown ||
3235 (!newSel->mIDList && !newSel->mClassList &&
3236 !newSel->mPseudoClassList && !newSel->mAttrList),
3237 "Need to fix the serialization code to deal with this");
3239 return eSelectorParsingStatus_Continue;
3243 // Parse the argument of a pseudo-class that has an ident arg
3245 CSSParserImpl::nsSelectorParsingStatus
3246 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
3247 nsIAtom* aPseudo)
3249 // Check if we have the first parenthesis
3250 if (!ExpectSymbol('(', PR_FALSE)) {
3251 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoArg);
3252 return eSelectorParsingStatus_Error;
3255 if (! GetToken(PR_TRUE)) { // premature eof
3256 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3257 return eSelectorParsingStatus_Error;
3259 // We expect an identifier with a language abbreviation
3260 if (eCSSToken_Ident != mToken.mType) {
3261 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
3262 UngetToken();
3263 // XXX Call SkipUntil to the next ")"?
3264 return eSelectorParsingStatus_Error;
3267 // Add the pseudo with the language parameter
3268 aSelector.AddPseudoClass(aPseudo, mToken.mIdent.get());
3270 // close the parenthesis
3271 if (!ExpectSymbol(')', PR_TRUE)) {
3272 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3273 // XXX Call SkipUntil to the next ")"?
3274 return eSelectorParsingStatus_Error;
3277 return eSelectorParsingStatus_Continue;
3280 CSSParserImpl::nsSelectorParsingStatus
3281 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
3282 nsIAtom* aPseudo)
3284 PRInt32 numbers[2] = { 0, 0 };
3285 PRBool lookForB = PR_TRUE;
3287 // Check whether we have the first parenthesis
3288 if (!ExpectSymbol('(', PR_FALSE)) {
3289 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoArg);
3290 return eSelectorParsingStatus_Error;
3293 // Follow the whitespace rules as proposed in
3294 // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
3296 if (! GetToken(PR_TRUE)) {
3297 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3298 return eSelectorParsingStatus_Error;
3301 if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
3302 // The CSS tokenization doesn't handle :nth-child() containing - well:
3303 // 2n-1 is a dimension
3304 // n-1 is an identifier
3305 // The easiest way to deal with that is to push everything from the
3306 // minus on back onto the scanner's pushback buffer.
3307 PRUint32 truncAt = 0;
3308 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
3309 truncAt = 1;
3310 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-"))) {
3311 truncAt = 2;
3313 if (truncAt != 0) {
3314 for (PRUint32 i = mToken.mIdent.Length() - 1; i >= truncAt; --i) {
3315 mScanner.Pushback(mToken.mIdent[i]);
3317 mToken.mIdent.Truncate(truncAt);
3321 if (eCSSToken_Ident == mToken.mType) {
3322 if (mToken.mIdent.EqualsIgnoreCase("odd")) {
3323 numbers[0] = 2;
3324 numbers[1] = 1;
3325 lookForB = PR_FALSE;
3327 else if (mToken.mIdent.EqualsIgnoreCase("even")) {
3328 numbers[0] = 2;
3329 numbers[1] = 0;
3330 lookForB = PR_FALSE;
3332 else if (mToken.mIdent.EqualsIgnoreCase("n")) {
3333 numbers[0] = 1;
3335 else if (mToken.mIdent.EqualsIgnoreCase("-n")) {
3336 numbers[0] = -1;
3338 else {
3339 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3340 // XXX Call SkipUntil to the next ")"?
3341 return eSelectorParsingStatus_Error;
3344 else if (eCSSToken_Number == mToken.mType) {
3345 if (!mToken.mIntegerValid) {
3346 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3347 // XXX Call SkipUntil to the next ")"?
3348 return eSelectorParsingStatus_Error;
3350 numbers[1] = mToken.mInteger;
3351 lookForB = PR_FALSE;
3353 else if (eCSSToken_Dimension == mToken.mType) {
3354 if (!mToken.mIntegerValid || !mToken.mIdent.EqualsIgnoreCase("n")) {
3355 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3356 // XXX Call SkipUntil to the next ")"?
3357 return eSelectorParsingStatus_Error;
3359 numbers[0] = mToken.mInteger;
3361 // XXX If it's a ')', is that valid? (as 0n+0)
3362 else {
3363 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3364 // XXX Call SkipUntil to the next ")" (unless this is one already)?
3365 return eSelectorParsingStatus_Error;
3368 if (! GetToken(PR_TRUE)) {
3369 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3370 return eSelectorParsingStatus_Error;
3372 if (lookForB && !mToken.IsSymbol(')')) {
3373 // The '+' or '-' sign can optionally be separated by whitespace.
3374 // If it is separated by whitespace from what follows it, it appears
3375 // as a separate token rather than part of the number token.
3376 PRBool haveSign = PR_FALSE;
3377 PRInt32 sign = 1;
3378 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
3379 haveSign = PR_TRUE;
3380 if (mToken.IsSymbol('-')) {
3381 sign = -1;
3383 if (! GetToken(PR_TRUE)) {
3384 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3385 return eSelectorParsingStatus_Error;
3388 if (eCSSToken_Number != mToken.mType ||
3389 !mToken.mIntegerValid || mToken.mHasSign == haveSign) {
3390 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3391 // XXX Call SkipUntil to the next ")"?
3392 return eSelectorParsingStatus_Error;
3394 numbers[1] = mToken.mInteger * sign;
3395 if (! GetToken(PR_TRUE)) {
3396 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3397 return eSelectorParsingStatus_Error;
3400 if (!mToken.IsSymbol(')')) {
3401 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3402 // XXX Call SkipUntil to the next ")"?
3403 return eSelectorParsingStatus_Error;
3405 aSelector.AddPseudoClass(aPseudo, numbers);
3406 return eSelectorParsingStatus_Continue;
3411 * This is the format for selectors:
3412 * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
3414 CSSParserImpl::nsSelectorParsingStatus
3415 CSSParserImpl::ParseSelector(nsCSSSelector& aSelector)
3417 if (! GetToken(PR_TRUE)) {
3418 REPORT_UNEXPECTED_EOF(PESelectorEOF);
3419 return eSelectorParsingStatus_Error;
3422 PRInt32 dataMask = 0;
3423 nsSelectorParsingStatus parsingStatus =
3424 ParseTypeOrUniversalSelector(dataMask, aSelector, PR_FALSE);
3425 if (parsingStatus != eSelectorParsingStatus_Continue) {
3426 return parsingStatus;
3429 for (;;) {
3430 if (eCSSToken_ID == mToken.mType) { // #id
3431 parsingStatus = ParseIDSelector(dataMask, aSelector);
3433 else if (mToken.IsSymbol('.')) { // .class
3434 parsingStatus = ParseClassSelector(dataMask, aSelector);
3436 else if (mToken.IsSymbol(':')) { // :pseudo
3437 parsingStatus = ParsePseudoSelector(dataMask, aSelector, PR_FALSE);
3439 else if (mToken.IsSymbol('[')) { // [attribute
3440 parsingStatus = ParseAttributeSelector(dataMask, aSelector);
3442 else { // not a selector token, we're done
3443 parsingStatus = eSelectorParsingStatus_Done;
3444 break;
3447 if (parsingStatus != eSelectorParsingStatus_Continue) {
3448 return parsingStatus;
3451 if (! GetToken(PR_FALSE)) { // premature eof is ok (here!)
3452 return eSelectorParsingStatus_Done;
3455 UngetToken();
3456 return dataMask ? parsingStatus : eSelectorParsingStatus_Empty;
3459 nsCSSDeclaration*
3460 CSSParserImpl::ParseDeclarationBlock(PRBool aCheckForBraces)
3462 if (aCheckForBraces) {
3463 if (!ExpectSymbol('{', PR_TRUE)) {
3464 REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
3465 OUTPUT_ERROR();
3466 return nsnull;
3469 nsCSSDeclaration* declaration = new nsCSSDeclaration();
3470 mData.AssertInitialState();
3471 if (declaration) {
3472 for (;;) {
3473 PRBool changed;
3474 if (!ParseDeclaration(declaration, aCheckForBraces,
3475 PR_TRUE, &changed)) {
3476 if (!SkipDeclaration(aCheckForBraces)) {
3477 break;
3479 if (aCheckForBraces) {
3480 if (ExpectSymbol('}', PR_TRUE)) {
3481 break;
3484 // Since the skipped declaration didn't end the block we parse
3485 // the next declaration.
3488 declaration->CompressFrom(&mData);
3490 return declaration;
3493 // The types to pass to ParseColorComponent. These correspond to the
3494 // various datatypes that can go within rgb().
3495 #define COLOR_TYPE_UNKNOWN 0
3496 #define COLOR_TYPE_INTEGERS 1
3497 #define COLOR_TYPE_PERCENTAGES 2
3499 PRBool
3500 CSSParserImpl::ParseColor(nsCSSValue& aValue)
3502 if (!GetToken(PR_TRUE)) {
3503 REPORT_UNEXPECTED_EOF(PEColorEOF);
3504 return PR_FALSE;
3507 nsCSSToken* tk = &mToken;
3508 nscolor rgba;
3509 switch (tk->mType) {
3510 case eCSSToken_ID:
3511 case eCSSToken_Ref:
3512 // #xxyyzz
3513 if (NS_HexToRGB(tk->mIdent, &rgba)) {
3514 aValue.SetColorValue(rgba);
3515 return PR_TRUE;
3517 break;
3519 case eCSSToken_Ident:
3520 if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
3521 aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
3522 return PR_TRUE;
3524 else {
3525 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
3526 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
3527 PRInt32 value;
3528 if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
3529 aValue.SetIntValue(value, eCSSUnit_EnumColor);
3530 return PR_TRUE;
3534 break;
3535 case eCSSToken_Function:
3536 if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
3537 // rgb ( component , component , component )
3538 PRUint8 r, g, b;
3539 PRInt32 type = COLOR_TYPE_UNKNOWN;
3540 if (ExpectSymbol('(', PR_FALSE) && // this won't fail
3541 ParseColorComponent(r, type, ',') &&
3542 ParseColorComponent(g, type, ',') &&
3543 ParseColorComponent(b, type, ')')) {
3544 aValue.SetColorValue(NS_RGB(r,g,b));
3545 return PR_TRUE;
3547 return PR_FALSE; // already pushed back
3549 else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
3550 mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
3551 // rgba ( component , component , component , opacity )
3552 PRUint8 r, g, b, a;
3553 PRInt32 type = COLOR_TYPE_UNKNOWN;
3554 if (ExpectSymbol('(', PR_FALSE) && // this won't fail
3555 ParseColorComponent(r, type, ',') &&
3556 ParseColorComponent(g, type, ',') &&
3557 ParseColorComponent(b, type, ',') &&
3558 ParseColorOpacity(a)) {
3559 aValue.SetColorValue(NS_RGBA(r, g, b, a));
3560 return PR_TRUE;
3562 return PR_FALSE; // already pushed back
3564 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
3565 // hsl ( hue , saturation , lightness )
3566 // "hue" is a number, "saturation" and "lightness" are percentages.
3567 if (ParseHSLColor(rgba, ')')) {
3568 aValue.SetColorValue(rgba);
3569 return PR_TRUE;
3571 return PR_FALSE;
3573 else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
3574 mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
3575 // hsla ( hue , saturation , lightness , opacity )
3576 // "hue" is a number, "saturation" and "lightness" are percentages,
3577 // "opacity" is a number.
3578 PRUint8 a;
3579 if (ParseHSLColor(rgba, ',') &&
3580 ParseColorOpacity(a)) {
3581 aValue.SetColorValue(NS_RGBA(NS_GET_R(rgba), NS_GET_G(rgba),
3582 NS_GET_B(rgba), a));
3583 return PR_TRUE;
3585 return PR_FALSE;
3587 break;
3588 default:
3589 break;
3592 // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
3593 if (mNavQuirkMode && !IsParsingCompoundProperty()) {
3594 // - If the string starts with 'a-f', the nsCSSScanner builds the
3595 // token as a eCSSToken_Ident and we can parse the string as a
3596 // 'xxyyzz' RGB color.
3597 // - If it only contains '0-9' digits, the token is a
3598 // eCSSToken_Number and it must be converted back to a 6
3599 // characters string to be parsed as a RGB color.
3600 // - If it starts with '0-9' and contains any 'a-f', the token is a
3601 // eCSSToken_Dimension, the mNumber part must be converted back to
3602 // a string and the mIdent part must be appended to that string so
3603 // that the resulting string has 6 characters.
3604 // Note: This is a hack for Nav compatibility. Do not attempt to
3605 // simplify it by hacking into the ncCSSScanner. This would be very
3606 // bad.
3607 nsAutoString str;
3608 char buffer[20];
3609 switch (tk->mType) {
3610 case eCSSToken_Ident:
3611 str.Assign(tk->mIdent);
3612 break;
3614 case eCSSToken_Number:
3615 if (tk->mIntegerValid) {
3616 PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
3617 str.AssignWithConversion(buffer);
3619 break;
3621 case eCSSToken_Dimension:
3622 if (tk->mIdent.Length() <= 6) {
3623 PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
3624 nsAutoString temp;
3625 temp.AssignWithConversion(buffer);
3626 temp.Right(str, 6 - tk->mIdent.Length());
3627 str.Append(tk->mIdent);
3629 break;
3630 default:
3631 // There is a whole bunch of cases that are
3632 // not handled by this switch. Ignore them.
3633 break;
3635 if (NS_HexToRGB(str, &rgba)) {
3636 aValue.SetColorValue(rgba);
3637 return PR_TRUE;
3641 // It's not a color
3642 REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
3643 UngetToken();
3644 return PR_FALSE;
3647 // aType will be set if we have already parsed other color components
3648 // in this color spec
3649 PRBool
3650 CSSParserImpl::ParseColorComponent(PRUint8& aComponent,
3651 PRInt32& aType,
3652 char aStop)
3654 if (!GetToken(PR_TRUE)) {
3655 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
3656 return PR_FALSE;
3658 float value;
3659 nsCSSToken* tk = &mToken;
3660 switch (tk->mType) {
3661 case eCSSToken_Number:
3662 switch (aType) {
3663 case COLOR_TYPE_UNKNOWN:
3664 aType = COLOR_TYPE_INTEGERS;
3665 break;
3666 case COLOR_TYPE_INTEGERS:
3667 break;
3668 case COLOR_TYPE_PERCENTAGES:
3669 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3670 UngetToken();
3671 return PR_FALSE;
3672 default:
3673 NS_NOTREACHED("Someone forgot to add the new color component type in here");
3676 if (!mToken.mIntegerValid) {
3677 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
3678 UngetToken();
3679 return PR_FALSE;
3681 value = tk->mNumber;
3682 break;
3683 case eCSSToken_Percentage:
3684 switch (aType) {
3685 case COLOR_TYPE_UNKNOWN:
3686 aType = COLOR_TYPE_PERCENTAGES;
3687 break;
3688 case COLOR_TYPE_INTEGERS:
3689 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
3690 UngetToken();
3691 return PR_FALSE;
3692 case COLOR_TYPE_PERCENTAGES:
3693 break;
3694 default:
3695 NS_NOTREACHED("Someone forgot to add the new color component type in here");
3697 value = tk->mNumber * 255.0f;
3698 break;
3699 default:
3700 REPORT_UNEXPECTED_TOKEN(PEColorBadRGBContents);
3701 UngetToken();
3702 return PR_FALSE;
3704 if (ExpectSymbol(aStop, PR_TRUE)) {
3705 if (value < 0.0f) value = 0.0f;
3706 if (value > 255.0f) value = 255.0f;
3707 aComponent = NSToIntRound(value);
3708 return PR_TRUE;
3710 const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
3711 const PRUnichar *params[] = {
3712 nsnull,
3713 stopString
3715 REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
3716 return PR_FALSE;
3720 PRBool
3721 CSSParserImpl::ParseHSLColor(nscolor& aColor,
3722 char aStop)
3724 float h, s, l;
3725 if (!ExpectSymbol('(', PR_FALSE)) {
3726 NS_ERROR("How did this get to be a function token?");
3727 return PR_FALSE;
3730 // Get the hue
3731 if (!GetToken(PR_TRUE)) {
3732 REPORT_UNEXPECTED_EOF(PEColorHueEOF);
3733 return PR_FALSE;
3735 if (mToken.mType != eCSSToken_Number) {
3736 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
3737 UngetToken();
3738 return PR_FALSE;
3740 h = mToken.mNumber;
3741 h /= 360.0f;
3742 // hue values are wraparound
3743 h = h - floor(h);
3745 if (!ExpectSymbol(',', PR_TRUE)) {
3746 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
3747 return PR_FALSE;
3750 // Get the saturation
3751 if (!GetToken(PR_TRUE)) {
3752 REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
3753 return PR_FALSE;
3755 if (mToken.mType != eCSSToken_Percentage) {
3756 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3757 UngetToken();
3758 return PR_FALSE;
3760 s = mToken.mNumber;
3761 if (s < 0.0f) s = 0.0f;
3762 if (s > 1.0f) s = 1.0f;
3764 if (!ExpectSymbol(',', PR_TRUE)) {
3765 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
3766 return PR_FALSE;
3769 // Get the lightness
3770 if (!GetToken(PR_TRUE)) {
3771 REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
3772 return PR_FALSE;
3774 if (mToken.mType != eCSSToken_Percentage) {
3775 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3776 UngetToken();
3777 return PR_FALSE;
3779 l = mToken.mNumber;
3780 if (l < 0.0f) l = 0.0f;
3781 if (l > 1.0f) l = 1.0f;
3783 if (ExpectSymbol(aStop, PR_TRUE)) {
3784 aColor = NS_HSL2RGB(h, s, l);
3785 return PR_TRUE;
3788 const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
3789 const PRUnichar *params[] = {
3790 nsnull,
3791 stopString
3793 REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
3794 return PR_FALSE;
3798 PRBool
3799 CSSParserImpl::ParseColorOpacity(PRUint8& aOpacity)
3801 if (!GetToken(PR_TRUE)) {
3802 REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
3803 return PR_FALSE;
3806 if (mToken.mType != eCSSToken_Number) {
3807 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
3808 UngetToken();
3809 return PR_FALSE;
3812 if (mToken.mNumber < 0.0f) {
3813 mToken.mNumber = 0.0f;
3814 } else if (mToken.mNumber > 1.0f) {
3815 mToken.mNumber = 1.0f;
3818 PRUint8 value = nsStyleUtil::FloatToColorComponent(mToken.mNumber);
3819 NS_ASSERTION(fabs(mToken.mNumber - value/255.0f) <= 0.5f,
3820 "FloatToColorComponent did something weird");
3822 if (!ExpectSymbol(')', PR_TRUE)) {
3823 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
3824 return PR_FALSE;
3827 aOpacity = value;
3829 return PR_TRUE;
3832 #ifdef MOZ_XUL
3833 PRBool
3834 CSSParserImpl::ParseTreePseudoElement(nsCSSSelector& aSelector)
3836 if (ExpectSymbol('(', PR_FALSE)) {
3837 while (!ExpectSymbol(')', PR_TRUE)) {
3838 if (!GetToken(PR_TRUE)) {
3839 return PR_FALSE;
3841 else if (eCSSToken_Ident == mToken.mType) {
3842 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(mToken.mIdent);
3843 aSelector.AddPseudoClass(pseudo);
3845 else if (eCSSToken_Symbol == mToken.mType) {
3846 if (!mToken.IsSymbol(','))
3847 return PR_FALSE;
3849 else return PR_FALSE;
3851 return PR_TRUE;
3853 return PR_FALSE;
3855 #endif
3857 //----------------------------------------------------------------------
3859 PRBool
3860 CSSParserImpl::ParseDeclaration(nsCSSDeclaration* aDeclaration,
3861 PRBool aCheckForBraces,
3862 PRBool aMustCallValueAppended,
3863 PRBool* aChanged)
3865 mTempData.AssertInitialState();
3867 // Get property name
3868 nsCSSToken* tk = &mToken;
3869 nsAutoString propertyName;
3870 for (;;) {
3871 if (!GetToken(PR_TRUE)) {
3872 if (aCheckForBraces) {
3873 REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
3875 return PR_FALSE;
3877 if (eCSSToken_Ident == tk->mType) {
3878 propertyName = tk->mIdent;
3879 // grab the ident before the ExpectSymbol trashes the token
3880 if (!ExpectSymbol(':', PR_TRUE)) {
3881 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3882 REPORT_UNEXPECTED(PEDeclDropped);
3883 OUTPUT_ERROR();
3884 return PR_FALSE;
3886 break;
3888 if (tk->IsSymbol(';')) {
3889 // dangling semicolons are skipped
3890 continue;
3893 if (!tk->IsSymbol('}')) {
3894 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
3895 REPORT_UNEXPECTED(PEDeclSkipped);
3896 OUTPUT_ERROR();
3898 // Not a declaration...
3899 UngetToken();
3900 return PR_FALSE;
3903 // Map property name to its ID and then parse the property
3904 nsCSSProperty propID = nsCSSProps::LookupProperty(propertyName);
3905 if (eCSSProperty_UNKNOWN == propID) { // unknown property
3906 if (!NonMozillaVendorIdentifier(propertyName)) {
3907 const PRUnichar *params[] = {
3908 propertyName.get()
3910 REPORT_UNEXPECTED_P(PEUnknownProperty, params);
3911 REPORT_UNEXPECTED(PEDeclDropped);
3912 OUTPUT_ERROR();
3915 return PR_FALSE;
3917 if (! ParseProperty(propID)) {
3918 // XXX Much better to put stuff in the value parsers instead...
3919 const PRUnichar *params[] = {
3920 propertyName.get()
3922 REPORT_UNEXPECTED_P(PEValueParsingError, params);
3923 REPORT_UNEXPECTED(PEDeclDropped);
3924 OUTPUT_ERROR();
3925 ClearTempData(propID);
3926 return PR_FALSE;
3928 CLEAR_ERROR();
3930 // See if the declaration is followed by a "!important" declaration
3931 PRBool isImportant = PR_FALSE;
3932 if (!GetToken(PR_TRUE)) {
3933 // EOF is a perfectly good way to end a declaration and declaration block
3934 TransferTempData(aDeclaration, propID, isImportant,
3935 aMustCallValueAppended, aChanged);
3936 return PR_TRUE;
3939 if (eCSSToken_Symbol == tk->mType && '!' == tk->mSymbol) {
3940 // Look for important ident
3941 if (!GetToken(PR_TRUE)) {
3942 // Premature eof is not ok
3943 REPORT_UNEXPECTED_EOF(PEImportantEOF);
3944 ClearTempData(propID);
3945 return PR_FALSE;
3947 if ((eCSSToken_Ident != tk->mType) ||
3948 !tk->mIdent.LowerCaseEqualsLiteral("important")) {
3949 REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
3950 OUTPUT_ERROR();
3951 UngetToken();
3952 ClearTempData(propID);
3953 return PR_FALSE;
3955 isImportant = PR_TRUE;
3957 else {
3958 // Not a !important declaration
3959 UngetToken();
3962 // Make sure valid property declaration is terminated with either a
3963 // semicolon, EOF or a right-curly-brace (this last only when
3964 // aCheckForBraces is true).
3965 if (!GetToken(PR_TRUE)) {
3966 // EOF is a perfectly good way to end a declaration and declaration block
3967 TransferTempData(aDeclaration, propID, isImportant,
3968 aMustCallValueAppended, aChanged);
3969 return PR_TRUE;
3971 if (eCSSToken_Symbol == tk->mType) {
3972 if (';' == tk->mSymbol) {
3973 TransferTempData(aDeclaration, propID, isImportant,
3974 aMustCallValueAppended, aChanged);
3975 return PR_TRUE;
3977 if (aCheckForBraces && '}' == tk->mSymbol) {
3978 // Unget the '}' so we'll be able to tell that this is the end
3979 // of the declaration block when we unwind from here.
3980 UngetToken();
3981 TransferTempData(aDeclaration, propID, isImportant,
3982 aMustCallValueAppended, aChanged);
3983 return PR_TRUE;
3986 if (aCheckForBraces)
3987 REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
3988 else
3989 REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
3990 REPORT_UNEXPECTED(PEDeclDropped);
3991 OUTPUT_ERROR();
3992 ClearTempData(propID);
3993 return PR_FALSE;
3996 void
3997 CSSParserImpl::ClearTempData(nsCSSProperty aPropID)
3999 if (nsCSSProps::IsShorthand(aPropID)) {
4000 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
4001 mTempData.ClearProperty(*p);
4003 } else {
4004 mTempData.ClearProperty(aPropID);
4006 mTempData.AssertInitialState();
4009 void
4010 CSSParserImpl::TransferTempData(nsCSSDeclaration* aDeclaration,
4011 nsCSSProperty aPropID, PRBool aIsImportant,
4012 PRBool aMustCallValueAppended,
4013 PRBool* aChanged)
4015 if (nsCSSProps::IsShorthand(aPropID)) {
4016 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
4017 DoTransferTempData(aDeclaration, *p, aIsImportant,
4018 aMustCallValueAppended, aChanged);
4020 } else {
4021 DoTransferTempData(aDeclaration, aPropID, aIsImportant,
4022 aMustCallValueAppended, aChanged);
4024 mTempData.AssertInitialState();
4027 // Perhaps the transferring code should be in nsCSSExpandedDataBlock, in
4028 // case some other caller wants to use it in the future (although I
4029 // can't think of why).
4030 void
4031 CSSParserImpl::DoTransferTempData(nsCSSDeclaration* aDeclaration,
4032 nsCSSProperty aPropID, PRBool aIsImportant,
4033 PRBool aMustCallValueAppended,
4034 PRBool* aChanged)
4036 NS_ASSERTION(mTempData.HasPropertyBit(aPropID), "oops");
4037 if (aIsImportant) {
4038 if (!mData.HasImportantBit(aPropID))
4039 *aChanged = PR_TRUE;
4040 mData.SetImportantBit(aPropID);
4041 } else {
4042 if (mData.HasImportantBit(aPropID)) {
4043 mTempData.ClearProperty(aPropID);
4044 return;
4048 if (aMustCallValueAppended || !mData.HasPropertyBit(aPropID)) {
4049 aDeclaration->ValueAppended(aPropID);
4052 mData.SetPropertyBit(aPropID);
4053 mTempData.ClearPropertyBit(aPropID);
4056 * Save needless copying and allocation by calling the destructor in
4057 * the destination, copying memory directly, and then using placement
4058 * new.
4060 void *v_source = mTempData.PropertyAt(aPropID);
4061 void *v_dest = mData.PropertyAt(aPropID);
4062 switch (nsCSSProps::kTypeTable[aPropID]) {
4063 case eCSSType_Value: {
4064 nsCSSValue *source = static_cast<nsCSSValue*>(v_source);
4065 nsCSSValue *dest = static_cast<nsCSSValue*>(v_dest);
4066 if (*source != *dest)
4067 *aChanged = PR_TRUE;
4068 dest->~nsCSSValue();
4069 memcpy(dest, source, sizeof(nsCSSValue));
4070 new (source) nsCSSValue();
4071 } break;
4073 case eCSSType_Rect: {
4074 nsCSSRect *source = static_cast<nsCSSRect*>(v_source);
4075 nsCSSRect *dest = static_cast<nsCSSRect*>(v_dest);
4076 if (*source != *dest)
4077 *aChanged = PR_TRUE;
4078 dest->~nsCSSRect();
4079 memcpy(dest, source, sizeof(nsCSSRect));
4080 new (source) nsCSSRect();
4081 } break;
4083 case eCSSType_ValuePair: {
4084 nsCSSValuePair *source = static_cast<nsCSSValuePair*>(v_source);
4085 nsCSSValuePair *dest = static_cast<nsCSSValuePair*>(v_dest);
4086 if (*source != *dest)
4087 *aChanged = PR_TRUE;
4088 dest->~nsCSSValuePair();
4089 memcpy(dest, source, sizeof(nsCSSValuePair));
4090 new (source) nsCSSValuePair();
4091 } break;
4093 case eCSSType_ValueList: {
4094 nsCSSValueList **source = static_cast<nsCSSValueList**>(v_source);
4095 nsCSSValueList **dest = static_cast<nsCSSValueList**>(v_dest);
4096 if (!nsCSSValueList::Equal(*source, *dest))
4097 *aChanged = PR_TRUE;
4098 delete *dest;
4099 *dest = *source;
4100 *source = nsnull;
4101 } break;
4103 case eCSSType_ValuePairList: {
4104 nsCSSValuePairList **source =
4105 static_cast<nsCSSValuePairList**>(v_source);
4106 nsCSSValuePairList **dest =
4107 static_cast<nsCSSValuePairList**>(v_dest);
4108 if (!nsCSSValuePairList::Equal(*source, *dest))
4109 *aChanged = PR_TRUE;
4110 delete *dest;
4111 *dest = *source;
4112 *source = nsnull;
4113 } break;
4117 static const nsCSSProperty kBorderTopIDs[] = {
4118 eCSSProperty_border_top_width,
4119 eCSSProperty_border_top_style,
4120 eCSSProperty_border_top_color
4122 static const nsCSSProperty kBorderRightIDs[] = {
4123 eCSSProperty_border_right_width_value,
4124 eCSSProperty_border_right_style_value,
4125 eCSSProperty_border_right_color_value,
4126 eCSSProperty_border_right_width,
4127 eCSSProperty_border_right_style,
4128 eCSSProperty_border_right_color
4130 static const nsCSSProperty kBorderBottomIDs[] = {
4131 eCSSProperty_border_bottom_width,
4132 eCSSProperty_border_bottom_style,
4133 eCSSProperty_border_bottom_color
4135 static const nsCSSProperty kBorderLeftIDs[] = {
4136 eCSSProperty_border_left_width_value,
4137 eCSSProperty_border_left_style_value,
4138 eCSSProperty_border_left_color_value,
4139 eCSSProperty_border_left_width,
4140 eCSSProperty_border_left_style,
4141 eCSSProperty_border_left_color
4143 static const nsCSSProperty kBorderStartIDs[] = {
4144 eCSSProperty_border_start_width_value,
4145 eCSSProperty_border_start_style_value,
4146 eCSSProperty_border_start_color_value,
4147 eCSSProperty_border_start_width,
4148 eCSSProperty_border_start_style,
4149 eCSSProperty_border_start_color
4151 static const nsCSSProperty kBorderEndIDs[] = {
4152 eCSSProperty_border_end_width_value,
4153 eCSSProperty_border_end_style_value,
4154 eCSSProperty_border_end_color_value,
4155 eCSSProperty_border_end_width,
4156 eCSSProperty_border_end_style,
4157 eCSSProperty_border_end_color
4159 static const nsCSSProperty kColumnRuleIDs[] = {
4160 eCSSProperty__moz_column_rule_width,
4161 eCSSProperty__moz_column_rule_style,
4162 eCSSProperty__moz_column_rule_color
4165 PRBool
4166 CSSParserImpl::ParseEnum(nsCSSValue& aValue,
4167 const PRInt32 aKeywordTable[])
4169 nsSubstring* ident = NextIdent();
4170 if (nsnull == ident) {
4171 return PR_FALSE;
4173 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
4174 if (eCSSKeyword_UNKNOWN < keyword) {
4175 PRInt32 value;
4176 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
4177 aValue.SetIntValue(value, eCSSUnit_Enumerated);
4178 return PR_TRUE;
4182 // Put the unknown identifier back and return
4183 UngetToken();
4184 return PR_FALSE;
4188 struct UnitInfo {
4189 char name[5]; // needs to be long enough for the longest unit, with
4190 // terminating null.
4191 PRUint32 length;
4192 nsCSSUnit unit;
4193 PRInt32 type;
4196 #define STR_WITH_LEN(_str) \
4197 _str, sizeof(_str) - 1
4199 const UnitInfo UnitData[] = {
4200 { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
4201 { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
4202 { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
4203 { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
4204 { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
4205 { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
4206 { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
4207 { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
4208 { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
4209 { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
4210 { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
4211 { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
4212 { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
4213 { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
4214 { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
4215 { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
4218 #undef STR_WITH_LEN
4220 PRBool
4221 CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
4222 PRInt32 aVariantMask,
4223 float aNumber,
4224 const nsString& aUnit)
4226 nsCSSUnit units;
4227 PRInt32 type = 0;
4228 if (!aUnit.IsEmpty()) {
4229 PRUint32 i;
4230 for (i = 0; i < NS_ARRAY_LENGTH(UnitData); ++i) {
4231 if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
4232 UnitData[i].length)) {
4233 units = UnitData[i].unit;
4234 type = UnitData[i].type;
4235 break;
4239 if (i == NS_ARRAY_LENGTH(UnitData)) {
4240 // Unknown unit
4241 return PR_FALSE;
4243 } else {
4244 // Must be a zero number...
4245 NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
4246 if ((VARIANT_LENGTH & aVariantMask) != 0) {
4247 units = eCSSUnit_Point;
4248 type = VARIANT_LENGTH;
4250 else if ((VARIANT_ANGLE & aVariantMask) != 0) {
4251 units = eCSSUnit_Degree;
4252 type = VARIANT_ANGLE;
4254 else if ((VARIANT_FREQUENCY & aVariantMask) != 0) {
4255 units = eCSSUnit_Hertz;
4256 type = VARIANT_FREQUENCY;
4258 else if ((VARIANT_TIME & aVariantMask) != 0) {
4259 units = eCSSUnit_Seconds;
4260 type = VARIANT_TIME;
4262 else {
4263 NS_ERROR("Variant mask does not include dimension; why were we called?");
4264 return PR_FALSE;
4267 if ((type & aVariantMask) != 0) {
4268 aValue.SetFloatValue(aNumber, units);
4269 return PR_TRUE;
4271 return PR_FALSE;
4274 PRBool
4275 CSSParserImpl::ParsePositiveVariant(nsCSSValue& aValue,
4276 PRInt32 aVariantMask,
4277 const PRInt32 aKeywordTable[])
4279 if (ParseVariant(aValue, aVariantMask, aKeywordTable)) {
4280 if (eCSSUnit_Number == aValue.GetUnit() ||
4281 aValue.IsLengthUnit()){
4282 if (aValue.GetFloatValue() < 0) {
4283 UngetToken();
4284 return PR_FALSE;
4287 else if (aValue.GetUnit() == eCSSUnit_Percent) {
4288 if (aValue.GetPercentValue() < 0) {
4289 UngetToken();
4290 return PR_FALSE;
4292 } else if (aValue.GetUnit() == eCSSUnit_Integer) {
4293 if (aValue.GetIntValue() < 0) {
4294 UngetToken();
4295 return PR_FALSE;
4298 return PR_TRUE;
4300 return PR_FALSE;
4303 // Assigns to aValue iff it returns PR_TRUE.
4304 PRBool
4305 CSSParserImpl::ParseVariant(nsCSSValue& aValue,
4306 PRInt32 aVariantMask,
4307 const PRInt32 aKeywordTable[])
4309 NS_ASSERTION(IsParsingCompoundProperty() ||
4310 ((~aVariantMask) & (VARIANT_LENGTH|VARIANT_COLOR)),
4311 "cannot distinguish lengths and colors in quirks mode");
4313 if (!GetToken(PR_TRUE)) {
4314 return PR_FALSE;
4316 nsCSSToken* tk = &mToken;
4317 if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE)) != 0) &&
4318 (eCSSToken_Ident == tk->mType)) {
4319 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
4320 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
4321 if ((aVariantMask & VARIANT_AUTO) != 0) {
4322 if (eCSSKeyword_auto == keyword) {
4323 aValue.SetAutoValue();
4324 return PR_TRUE;
4327 if ((aVariantMask & VARIANT_INHERIT) != 0) {
4328 // XXX Should we check IsParsingCompoundProperty, or do all
4329 // callers handle it? (Not all callers set it, though, since
4330 // they want the quirks that are disabled by setting it.)
4331 if (eCSSKeyword_inherit == keyword) {
4332 aValue.SetInheritValue();
4333 return PR_TRUE;
4335 else if (eCSSKeyword__moz_initial == keyword) { // anything that can inherit can also take an initial val.
4336 aValue.SetInitialValue();
4337 return PR_TRUE;
4340 if ((aVariantMask & VARIANT_NONE) != 0) {
4341 if (eCSSKeyword_none == keyword) {
4342 aValue.SetNoneValue();
4343 return PR_TRUE;
4346 if ((aVariantMask & VARIANT_NORMAL) != 0) {
4347 if (eCSSKeyword_normal == keyword) {
4348 aValue.SetNormalValue();
4349 return PR_TRUE;
4352 if ((aVariantMask & VARIANT_SYSFONT) != 0) {
4353 if (eCSSKeyword__moz_use_system_font == keyword &&
4354 !IsParsingCompoundProperty()) {
4355 aValue.SetSystemFontValue();
4356 return PR_TRUE;
4359 if ((aVariantMask & VARIANT_KEYWORD) != 0) {
4360 PRInt32 value;
4361 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
4362 aValue.SetIntValue(value, eCSSUnit_Enumerated);
4363 return PR_TRUE;
4368 if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | VARIANT_FREQUENCY | VARIANT_TIME)) != 0) &&
4369 tk->IsDimension()) {
4370 if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
4371 return PR_TRUE;
4373 // Put the token back; we didn't parse it, so we shouldn't consume it
4374 UngetToken();
4375 return PR_FALSE;
4377 if (((aVariantMask & VARIANT_PERCENT) != 0) &&
4378 (eCSSToken_Percentage == tk->mType)) {
4379 aValue.SetPercentValue(tk->mNumber);
4380 return PR_TRUE;
4382 if (((aVariantMask & VARIANT_NUMBER) != 0) &&
4383 (eCSSToken_Number == tk->mType)) {
4384 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
4385 return PR_TRUE;
4387 if (((aVariantMask & VARIANT_INTEGER) != 0) &&
4388 (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
4389 aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
4390 return PR_TRUE;
4392 if (mNavQuirkMode && !IsParsingCompoundProperty()) { // NONSTANDARD: Nav interprets unitless numbers as px
4393 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
4394 (eCSSToken_Number == tk->mType)) {
4395 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
4396 return PR_TRUE;
4400 #ifdef MOZ_SVG
4401 if (IsSVGMode() && !IsParsingCompoundProperty()) {
4402 // STANDARD: SVG Spec states that lengths and coordinates can be unitless
4403 // in which case they default to user-units (1 px = 1 user unit)
4404 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
4405 (eCSSToken_Number == tk->mType)) {
4406 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
4407 return PR_TRUE;
4410 #endif
4412 if (((aVariantMask & VARIANT_URL) != 0) &&
4413 (eCSSToken_Function == tk->mType) &&
4414 tk->mIdent.LowerCaseEqualsLiteral("url")) {
4415 if (ParseURL(aValue)) {
4416 return PR_TRUE;
4418 return PR_FALSE;
4420 if ((aVariantMask & VARIANT_COLOR) != 0) {
4421 if ((mNavQuirkMode && !IsParsingCompoundProperty()) || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
4422 (eCSSToken_ID == tk->mType) ||
4423 (eCSSToken_Ref == tk->mType) ||
4424 (eCSSToken_Ident == tk->mType) ||
4425 ((eCSSToken_Function == tk->mType) &&
4426 (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
4427 tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
4428 tk->mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
4429 tk->mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
4430 tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
4431 tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
4433 // Put token back so that parse color can get it
4434 UngetToken();
4435 if (ParseColor(aValue)) {
4436 return PR_TRUE;
4438 return PR_FALSE;
4441 if (((aVariantMask & VARIANT_STRING) != 0) &&
4442 (eCSSToken_String == tk->mType)) {
4443 nsAutoString buffer;
4444 buffer.Append(tk->mSymbol);
4445 buffer.Append(tk->mIdent);
4446 buffer.Append(tk->mSymbol);
4447 aValue.SetStringValue(buffer, eCSSUnit_String);
4448 return PR_TRUE;
4450 if (((aVariantMask & VARIANT_IDENTIFIER) != 0) &&
4451 (eCSSToken_Ident == tk->mType)) {
4452 aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
4453 return PR_TRUE;
4455 if (((aVariantMask & VARIANT_COUNTER) != 0) &&
4456 (eCSSToken_Function == tk->mType) &&
4457 (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
4458 tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
4459 return ParseCounter(aValue);
4461 if (((aVariantMask & VARIANT_ATTR) != 0) &&
4462 (eCSSToken_Function == tk->mType) &&
4463 tk->mIdent.LowerCaseEqualsLiteral("attr")) {
4464 return ParseAttr(aValue);
4467 UngetToken();
4468 return PR_FALSE;
4472 PRBool
4473 CSSParserImpl::ParseCounter(nsCSSValue& aValue)
4475 nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
4476 eCSSUnit_Counter : eCSSUnit_Counters);
4478 if (!ExpectSymbol('(', PR_FALSE))
4479 return PR_FALSE;
4481 if (!GetNonCloseParenToken(PR_TRUE) ||
4482 eCSSToken_Ident != mToken.mType) {
4483 SkipUntil(')');
4484 return PR_FALSE;
4487 nsRefPtr<nsCSSValue::Array> val =
4488 nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
4489 if (!val) {
4490 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
4491 return PR_FALSE;
4494 val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_String);
4496 if (eCSSUnit_Counters == unit) {
4497 // get mandatory separator string
4498 if (!ExpectSymbol(',', PR_TRUE) ||
4499 !(GetNonCloseParenToken(PR_TRUE) &&
4500 eCSSToken_String == mToken.mType)) {
4501 SkipUntil(')');
4502 return PR_FALSE;
4504 val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
4507 // get optional type
4508 PRInt32 type = NS_STYLE_LIST_STYLE_DECIMAL;
4509 if (ExpectSymbol(',', PR_TRUE)) {
4510 nsCSSKeyword keyword;
4511 PRBool success = GetNonCloseParenToken(PR_TRUE) &&
4512 eCSSToken_Ident == mToken.mType &&
4513 (keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) !=
4514 eCSSKeyword_UNKNOWN;
4515 if (success) {
4516 if (keyword == eCSSKeyword_none) {
4517 type = NS_STYLE_LIST_STYLE_NONE;
4518 } else {
4519 success = nsCSSProps::FindKeyword(keyword,
4520 nsCSSProps::kListStyleKTable, type);
4523 if (!success) {
4524 SkipUntil(')');
4525 return PR_FALSE;
4528 PRInt32 typeItem = eCSSUnit_Counters == unit ? 2 : 1;
4529 val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
4531 if (!ExpectSymbol(')', PR_TRUE)) {
4532 SkipUntil(')');
4533 return PR_FALSE;
4536 aValue.SetArrayValue(val, unit);
4537 return PR_TRUE;
4540 PRBool
4541 CSSParserImpl::ParseAttr(nsCSSValue& aValue)
4543 if (ExpectSymbol('(', PR_FALSE)) {
4544 if (GetToken(PR_TRUE)) {
4545 nsAutoString attr;
4546 if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
4547 nsAutoString holdIdent(mToken.mIdent);
4548 if (ExpectSymbol('|', PR_FALSE)) { // namespace
4549 PRInt32 nameSpaceID;
4550 if (!GetNamespaceIdForPrefix(holdIdent, &nameSpaceID)) {
4551 return PR_FALSE;
4553 attr.AppendInt(nameSpaceID, 10);
4554 attr.Append(PRUnichar('|'));
4555 if (! GetToken(PR_FALSE)) {
4556 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4557 return PR_FALSE;
4559 if (eCSSToken_Ident == mToken.mType) {
4560 if (mCaseSensitive) {
4561 attr.Append(mToken.mIdent);
4562 } else {
4563 nsAutoString buffer;
4564 ToLowerCase(mToken.mIdent, buffer);
4565 attr.Append(buffer);
4568 else {
4569 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4570 UngetToken();
4571 return PR_FALSE;
4574 else { // no namespace
4575 if (mCaseSensitive) {
4576 attr = holdIdent;
4578 else {
4579 ToLowerCase(holdIdent, attr);
4583 else if (mToken.IsSymbol('*')) { // namespace wildcard
4584 // Wildcard namespace makes no sense here and is not allowed
4585 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4586 UngetToken();
4587 return PR_FALSE;
4589 else if (mToken.IsSymbol('|')) { // explicit NO namespace
4590 if (! GetToken(PR_FALSE)) {
4591 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4592 return PR_FALSE;
4594 if (eCSSToken_Ident == mToken.mType) {
4595 if (mCaseSensitive) {
4596 attr.Append(mToken.mIdent);
4597 } else {
4598 nsAutoString buffer;
4599 ToLowerCase(mToken.mIdent, buffer);
4600 attr.Append(buffer);
4603 else {
4604 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4605 UngetToken();
4606 return PR_FALSE;
4609 else {
4610 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
4611 UngetToken();
4612 return PR_FALSE;
4614 if (ExpectSymbol(')', PR_TRUE)) {
4615 aValue.SetStringValue(attr, eCSSUnit_Attr);
4616 return PR_TRUE;
4620 return PR_FALSE;
4623 PRBool
4624 CSSParserImpl::ParseURL(nsCSSValue& aValue)
4626 if (!mSheetPrincipal) {
4627 NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
4628 "origin principal");
4629 return PR_FALSE;
4632 if (!ExpectSymbol('(', PR_FALSE))
4633 return PR_FALSE;
4634 if (!GetURLToken())
4635 return PR_FALSE;
4637 nsCSSToken* tk = &mToken;
4638 if (eCSSToken_String != tk->mType && eCSSToken_URL != tk->mType)
4639 return PR_FALSE;
4641 nsString url = tk->mIdent;
4642 if (!ExpectSymbol(')', PR_TRUE))
4643 return PR_FALSE;
4645 // Translate url into an absolute url if the url is relative to the
4646 // style sheet.
4647 nsCOMPtr<nsIURI> uri;
4648 NS_NewURI(getter_AddRefs(uri), url, nsnull, mBaseURL);
4650 nsStringBuffer* buffer = nsCSSValue::BufferFromString(url);
4651 if (NS_UNLIKELY(!buffer)) {
4652 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
4653 return PR_FALSE;
4655 nsCSSValue::URL *urlVal =
4656 new nsCSSValue::URL(uri, buffer, mSheetURL, mSheetPrincipal);
4658 buffer->Release();
4659 if (NS_UNLIKELY(!urlVal)) {
4660 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
4661 return PR_FALSE;
4663 aValue.SetURLValue(urlVal);
4664 return PR_TRUE;
4667 PRInt32
4668 CSSParserImpl::ParseChoice(nsCSSValue aValues[],
4669 const nsCSSProperty aPropIDs[], PRInt32 aNumIDs)
4671 PRInt32 found = 0;
4672 nsAutoParseCompoundProperty compound(this);
4674 PRInt32 loop;
4675 for (loop = 0; loop < aNumIDs; loop++) {
4676 // Try each property parser in order
4677 PRInt32 hadFound = found;
4678 PRInt32 index;
4679 for (index = 0; index < aNumIDs; index++) {
4680 PRInt32 bit = 1 << index;
4681 if ((found & bit) == 0) {
4682 if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) {
4683 found |= bit;
4687 if (found == hadFound) { // found nothing new
4688 break;
4691 if (0 < found) {
4692 if (1 == found) { // only first property
4693 if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
4694 for (loop = 1; loop < aNumIDs; loop++) {
4695 aValues[loop].SetInheritValue();
4697 found = ((1 << aNumIDs) - 1);
4699 else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
4700 for (loop = 1; loop < aNumIDs; loop++) {
4701 aValues[loop].SetInitialValue();
4703 found = ((1 << aNumIDs) - 1);
4706 else { // more than one value, verify no inherits or initials
4707 for (loop = 0; loop < aNumIDs; loop++) {
4708 if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
4709 found = -1;
4710 break;
4712 else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
4713 found = -1;
4714 break;
4719 return found;
4722 void
4723 CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue)
4725 NS_ASSERTION(0 <= aPropID && aPropID < eCSSProperty_COUNT_no_shorthands,
4726 "property out of range");
4727 NS_ASSERTION(nsCSSProps::kTypeTable[aPropID] == eCSSType_Value,
4728 nsPrintfCString(64, "type error (property=\'%s\')",
4729 nsCSSProps::GetStringValue(aPropID).get()).get());
4730 nsCSSValue& storage =
4731 *static_cast<nsCSSValue*>(mTempData.PropertyAt(aPropID));
4732 storage = aValue;
4733 mTempData.SetPropertyBit(aPropID);
4737 * Parse a "box" property. Box properties have 1 to 4 values. When less
4738 * than 4 values are provided a standard mapping is used to replicate
4739 * existing values.
4741 PRBool
4742 CSSParserImpl::ParseBoxProperties(nsCSSRect& aResult,
4743 const nsCSSProperty aPropIDs[])
4745 // Get up to four values for the property
4746 PRInt32 count = 0;
4747 nsCSSRect result;
4748 NS_FOR_CSS_SIDES (index) {
4749 if (! ParseSingleValueProperty(result.*(nsCSSRect::sides[index]),
4750 aPropIDs[index])) {
4751 break;
4753 count++;
4755 if ((count == 0) || (PR_FALSE == ExpectEndProperty())) {
4756 return PR_FALSE;
4759 if (1 < count) { // verify no more than single inherit or initial
4760 NS_FOR_CSS_SIDES (index) {
4761 nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
4762 if (eCSSUnit_Inherit == unit || eCSSUnit_Initial == unit) {
4763 return PR_FALSE;
4768 // Provide missing values by replicating some of the values found
4769 switch (count) {
4770 case 1: // Make right == top
4771 result.mRight = result.mTop;
4772 case 2: // Make bottom == top
4773 result.mBottom = result.mTop;
4774 case 3: // Make left == right
4775 result.mLeft = result.mRight;
4778 NS_FOR_CSS_SIDES (index) {
4779 mTempData.SetPropertyBit(aPropIDs[index]);
4781 aResult = result;
4782 return PR_TRUE;
4785 PRBool
4786 CSSParserImpl::ParseDirectionalBoxProperty(nsCSSProperty aProperty,
4787 PRInt32 aSourceType)
4789 const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty);
4790 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
4791 "not box property with physical vs. logical cascading");
4792 nsCSSValue value;
4793 if (!ParseSingleValueProperty(value, subprops[0]) ||
4794 !ExpectEndProperty())
4795 return PR_FALSE;
4797 AppendValue(subprops[0], value);
4798 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
4799 AppendValue(subprops[1], typeVal);
4800 AppendValue(subprops[2], typeVal);
4801 return PR_TRUE;
4804 PRBool
4805 CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID)
4807 nsCSSValue dimenX, dimenY;
4808 // required first value
4809 if (! ParsePositiveVariant(dimenX, VARIANT_HLP, nsnull))
4810 return PR_FALSE;
4811 // optional second value (forbidden if first value is inherit/initial)
4812 if (dimenX.GetUnit() == eCSSUnit_Inherit ||
4813 dimenX.GetUnit() == eCSSUnit_Initial ||
4814 ! ParsePositiveVariant(dimenY, VARIANT_LP, nsnull))
4815 dimenY = dimenX;
4817 NS_ASSERTION(nsCSSProps::kTypeTable[aPropID] == eCSSType_ValuePair,
4818 nsPrintfCString(64, "type error (property='%s')",
4819 nsCSSProps::GetStringValue(aPropID).get())
4820 .get());
4821 nsCSSValuePair& storage =
4822 *static_cast<nsCSSValuePair*>(mTempData.PropertyAt(aPropID));
4823 storage.mXValue = dimenX;
4824 storage.mYValue = dimenY;
4825 mTempData.SetPropertyBit(aPropID);
4826 return PR_TRUE;
4829 PRBool
4830 CSSParserImpl::ParseBoxCornerRadii(nsCSSCornerSizes& aRadii,
4831 const nsCSSProperty aPropIDs[])
4833 // Rectangles are used as scratch storage.
4834 // top => top-left, right => top-right,
4835 // bottom => bottom-right, left => bottom-left.
4836 nsCSSRect dimenX, dimenY;
4837 PRInt32 countX = 0, countY = 0;
4839 NS_FOR_CSS_SIDES (side) {
4840 if (! ParsePositiveVariant(dimenX.*nsCSSRect::sides[side],
4841 side > 0 ? VARIANT_LP : VARIANT_HLP, nsnull))
4842 break;
4843 countX++;
4845 if (countX == 0)
4846 return PR_FALSE;
4848 if (ExpectSymbol('/', PR_TRUE)) {
4849 NS_FOR_CSS_SIDES (side) {
4850 if (! ParsePositiveVariant(dimenY.*nsCSSRect::sides[side],
4851 VARIANT_LP, nsnull))
4852 break;
4853 countY++;
4855 if (countY == 0)
4856 return PR_FALSE;
4858 if (!ExpectEndProperty())
4859 return PR_FALSE;
4861 // if 'initial' or 'inherit' was used, it must be the only value
4862 if (countX > 1 || countY > 0) {
4863 nsCSSUnit unit = dimenX.mTop.GetUnit();
4864 if (eCSSUnit_Inherit == unit || eCSSUnit_Initial == unit)
4865 return PR_FALSE;
4868 // if we have no Y-values, use the X-values
4869 if (countY == 0) {
4870 dimenY = dimenX;
4871 countY = countX;
4874 // Provide missing values by replicating some of the values found
4875 switch (countX) {
4876 case 1: dimenX.mRight = dimenX.mTop; // top-right same as top-left, and
4877 case 2: dimenX.mBottom = dimenX.mTop; // bottom-right same as top-left, and
4878 case 3: dimenX.mLeft = dimenX.mRight; // bottom-left same as top-right
4881 switch (countY) {
4882 case 1: dimenY.mRight = dimenY.mTop; // top-right same as top-left, and
4883 case 2: dimenY.mBottom = dimenY.mTop; // bottom-right same as top-left, and
4884 case 3: dimenY.mLeft = dimenY.mRight; // bottom-left same as top-right
4887 NS_FOR_CSS_SIDES(side) {
4888 nsCSSValuePair& corner =
4889 aRadii.GetFullCorner(NS_SIDE_TO_FULL_CORNER(side, PR_FALSE));
4890 corner.mXValue = dimenX.*nsCSSRect::sides[side];
4891 corner.mYValue = dimenY.*nsCSSRect::sides[side];
4892 mTempData.SetPropertyBit(aPropIDs[side]);
4894 return PR_TRUE;
4897 // These must be in CSS order (top,right,bottom,left) for indexing to work
4898 static const nsCSSProperty kBorderStyleIDs[] = {
4899 eCSSProperty_border_top_style,
4900 eCSSProperty_border_right_style_value,
4901 eCSSProperty_border_bottom_style,
4902 eCSSProperty_border_left_style_value
4904 static const nsCSSProperty kBorderWidthIDs[] = {
4905 eCSSProperty_border_top_width,
4906 eCSSProperty_border_right_width_value,
4907 eCSSProperty_border_bottom_width,
4908 eCSSProperty_border_left_width_value
4910 static const nsCSSProperty kBorderColorIDs[] = {
4911 eCSSProperty_border_top_color,
4912 eCSSProperty_border_right_color_value,
4913 eCSSProperty_border_bottom_color,
4914 eCSSProperty_border_left_color_value
4916 static const nsCSSProperty kBorderRadiusIDs[] = {
4917 eCSSProperty__moz_border_radius_topLeft,
4918 eCSSProperty__moz_border_radius_topRight,
4919 eCSSProperty__moz_border_radius_bottomRight,
4920 eCSSProperty__moz_border_radius_bottomLeft
4922 static const nsCSSProperty kOutlineRadiusIDs[] = {
4923 eCSSProperty__moz_outline_radius_topLeft,
4924 eCSSProperty__moz_outline_radius_topRight,
4925 eCSSProperty__moz_outline_radius_bottomRight,
4926 eCSSProperty__moz_outline_radius_bottomLeft
4929 PRBool
4930 CSSParserImpl::ParseProperty(nsCSSProperty aPropID)
4932 NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
4934 switch (aPropID) { // handle shorthand or multiple properties
4935 case eCSSProperty_background:
4936 return ParseBackground();
4937 case eCSSProperty_background_position:
4938 return ParseBackgroundPosition();
4939 case eCSSProperty_border:
4940 return ParseBorderSide(kBorderTopIDs, PR_TRUE);
4941 case eCSSProperty_border_color:
4942 return ParseBorderColor();
4943 case eCSSProperty_border_spacing:
4944 return ParseBorderSpacing();
4945 case eCSSProperty_border_style:
4946 return ParseBorderStyle();
4947 case eCSSProperty_border_bottom:
4948 return ParseBorderSide(kBorderBottomIDs, PR_FALSE);
4949 case eCSSProperty_border_end:
4950 return ParseDirectionalBorderSide(kBorderEndIDs,
4951 NS_BOXPROP_SOURCE_LOGICAL);
4952 case eCSSProperty_border_left:
4953 return ParseDirectionalBorderSide(kBorderLeftIDs,
4954 NS_BOXPROP_SOURCE_PHYSICAL);
4955 case eCSSProperty_border_right:
4956 return ParseDirectionalBorderSide(kBorderRightIDs,
4957 NS_BOXPROP_SOURCE_PHYSICAL);
4958 case eCSSProperty_border_start:
4959 return ParseDirectionalBorderSide(kBorderStartIDs,
4960 NS_BOXPROP_SOURCE_LOGICAL);
4961 case eCSSProperty_border_top:
4962 return ParseBorderSide(kBorderTopIDs, PR_FALSE);
4963 case eCSSProperty_border_bottom_colors:
4964 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mBottom,
4965 aPropID);
4966 case eCSSProperty_border_left_colors:
4967 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mLeft,
4968 aPropID);
4969 case eCSSProperty_border_right_colors:
4970 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mRight,
4971 aPropID);
4972 case eCSSProperty_border_top_colors:
4973 return ParseBorderColors(&mTempData.mMargin.mBorderColors.mTop,
4974 aPropID);
4975 case eCSSProperty_border_image:
4976 return ParseBorderImage();
4977 case eCSSProperty_border_width:
4978 return ParseBorderWidth();
4979 case eCSSProperty_border_end_color:
4980 return ParseDirectionalBoxProperty(eCSSProperty_border_end_color,
4981 NS_BOXPROP_SOURCE_LOGICAL);
4982 case eCSSProperty_border_left_color:
4983 return ParseDirectionalBoxProperty(eCSSProperty_border_left_color,
4984 NS_BOXPROP_SOURCE_PHYSICAL);
4985 case eCSSProperty_border_right_color:
4986 return ParseDirectionalBoxProperty(eCSSProperty_border_right_color,
4987 NS_BOXPROP_SOURCE_PHYSICAL);
4988 case eCSSProperty_border_start_color:
4989 return ParseDirectionalBoxProperty(eCSSProperty_border_start_color,
4990 NS_BOXPROP_SOURCE_LOGICAL);
4991 case eCSSProperty_border_end_width:
4992 return ParseDirectionalBoxProperty(eCSSProperty_border_end_width,
4993 NS_BOXPROP_SOURCE_LOGICAL);
4994 case eCSSProperty_border_left_width:
4995 return ParseDirectionalBoxProperty(eCSSProperty_border_left_width,
4996 NS_BOXPROP_SOURCE_PHYSICAL);
4997 case eCSSProperty_border_right_width:
4998 return ParseDirectionalBoxProperty(eCSSProperty_border_right_width,
4999 NS_BOXPROP_SOURCE_PHYSICAL);
5000 case eCSSProperty_border_start_width:
5001 return ParseDirectionalBoxProperty(eCSSProperty_border_start_width,
5002 NS_BOXPROP_SOURCE_LOGICAL);
5003 case eCSSProperty_border_end_style:
5004 return ParseDirectionalBoxProperty(eCSSProperty_border_end_style,
5005 NS_BOXPROP_SOURCE_LOGICAL);
5006 case eCSSProperty_border_left_style:
5007 return ParseDirectionalBoxProperty(eCSSProperty_border_left_style,
5008 NS_BOXPROP_SOURCE_PHYSICAL);
5009 case eCSSProperty_border_right_style:
5010 return ParseDirectionalBoxProperty(eCSSProperty_border_right_style,
5011 NS_BOXPROP_SOURCE_PHYSICAL);
5012 case eCSSProperty_border_start_style:
5013 return ParseDirectionalBoxProperty(eCSSProperty_border_start_style,
5014 NS_BOXPROP_SOURCE_LOGICAL);
5015 case eCSSProperty__moz_border_radius:
5016 return ParseBoxCornerRadii(mTempData.mMargin.mBorderRadius,
5017 kBorderRadiusIDs);
5018 case eCSSProperty__moz_outline_radius:
5019 return ParseBoxCornerRadii(mTempData.mMargin.mOutlineRadius,
5020 kOutlineRadiusIDs);
5022 case eCSSProperty__moz_border_radius_topLeft:
5023 case eCSSProperty__moz_border_radius_topRight:
5024 case eCSSProperty__moz_border_radius_bottomRight:
5025 case eCSSProperty__moz_border_radius_bottomLeft:
5026 case eCSSProperty__moz_outline_radius_topLeft:
5027 case eCSSProperty__moz_outline_radius_topRight:
5028 case eCSSProperty__moz_outline_radius_bottomRight:
5029 case eCSSProperty__moz_outline_radius_bottomLeft:
5030 return ParseBoxCornerRadius(aPropID);
5032 case eCSSProperty_box_shadow:
5033 return ParseBoxShadow();
5034 case eCSSProperty_clip:
5035 return ParseRect(mTempData.mDisplay.mClip, eCSSProperty_clip);
5036 case eCSSProperty__moz_column_rule:
5037 return ParseBorderSide(kColumnRuleIDs, PR_FALSE);
5038 case eCSSProperty_content:
5039 return ParseContent();
5040 case eCSSProperty_counter_increment:
5041 return ParseCounterData(&mTempData.mContent.mCounterIncrement,
5042 aPropID);
5043 case eCSSProperty_counter_reset:
5044 return ParseCounterData(&mTempData.mContent.mCounterReset,
5045 aPropID);
5046 case eCSSProperty_cue:
5047 return ParseCue();
5048 case eCSSProperty_cursor:
5049 return ParseCursor();
5050 case eCSSProperty_font:
5051 return ParseFont();
5052 case eCSSProperty_image_region:
5053 return ParseRect(mTempData.mList.mImageRegion,
5054 eCSSProperty_image_region);
5055 case eCSSProperty_list_style:
5056 return ParseListStyle();
5057 case eCSSProperty_margin:
5058 return ParseMargin();
5059 case eCSSProperty_margin_end:
5060 return ParseDirectionalBoxProperty(eCSSProperty_margin_end,
5061 NS_BOXPROP_SOURCE_LOGICAL);
5062 case eCSSProperty_margin_left:
5063 return ParseDirectionalBoxProperty(eCSSProperty_margin_left,
5064 NS_BOXPROP_SOURCE_PHYSICAL);
5065 case eCSSProperty_margin_right:
5066 return ParseDirectionalBoxProperty(eCSSProperty_margin_right,
5067 NS_BOXPROP_SOURCE_PHYSICAL);
5068 case eCSSProperty_margin_start:
5069 return ParseDirectionalBoxProperty(eCSSProperty_margin_start,
5070 NS_BOXPROP_SOURCE_LOGICAL);
5071 case eCSSProperty_outline:
5072 return ParseOutline();
5073 case eCSSProperty_overflow:
5074 return ParseOverflow();
5075 case eCSSProperty_padding:
5076 return ParsePadding();
5077 case eCSSProperty_padding_end:
5078 return ParseDirectionalBoxProperty(eCSSProperty_padding_end,
5079 NS_BOXPROP_SOURCE_LOGICAL);
5080 case eCSSProperty_padding_left:
5081 return ParseDirectionalBoxProperty(eCSSProperty_padding_left,
5082 NS_BOXPROP_SOURCE_PHYSICAL);
5083 case eCSSProperty_padding_right:
5084 return ParseDirectionalBoxProperty(eCSSProperty_padding_right,
5085 NS_BOXPROP_SOURCE_PHYSICAL);
5086 case eCSSProperty_padding_start:
5087 return ParseDirectionalBoxProperty(eCSSProperty_padding_start,
5088 NS_BOXPROP_SOURCE_LOGICAL);
5089 case eCSSProperty_pause:
5090 return ParsePause();
5091 case eCSSProperty_quotes:
5092 return ParseQuotes();
5093 case eCSSProperty_size:
5094 return ParseSize();
5095 case eCSSProperty_text_shadow:
5096 return ParseTextShadow();
5097 case eCSSProperty__moz_transform:
5098 return ParseMozTransform();
5099 case eCSSProperty__moz_transform_origin:
5100 return ParseMozTransformOrigin();
5102 #ifdef MOZ_SVG
5103 case eCSSProperty_fill:
5104 return ParsePaint(&mTempData.mSVG.mFill, eCSSProperty_fill);
5105 case eCSSProperty_stroke:
5106 return ParsePaint(&mTempData.mSVG.mStroke, eCSSProperty_stroke);
5107 case eCSSProperty_stroke_dasharray:
5108 return ParseDasharray();
5109 case eCSSProperty_marker:
5110 return ParseMarker();
5111 #endif
5113 // Strip out properties we use internally.
5114 case eCSSProperty__x_system_font:
5115 case eCSSProperty_margin_end_value:
5116 case eCSSProperty_margin_left_value:
5117 case eCSSProperty_margin_right_value:
5118 case eCSSProperty_margin_start_value:
5119 case eCSSProperty_margin_left_ltr_source:
5120 case eCSSProperty_margin_left_rtl_source:
5121 case eCSSProperty_margin_right_ltr_source:
5122 case eCSSProperty_margin_right_rtl_source:
5123 case eCSSProperty_padding_end_value:
5124 case eCSSProperty_padding_left_value:
5125 case eCSSProperty_padding_right_value:
5126 case eCSSProperty_padding_start_value:
5127 case eCSSProperty_padding_left_ltr_source:
5128 case eCSSProperty_padding_left_rtl_source:
5129 case eCSSProperty_padding_right_ltr_source:
5130 case eCSSProperty_padding_right_rtl_source:
5131 case eCSSProperty_border_end_color_value:
5132 case eCSSProperty_border_left_color_value:
5133 case eCSSProperty_border_right_color_value:
5134 case eCSSProperty_border_start_color_value:
5135 case eCSSProperty_border_left_color_ltr_source:
5136 case eCSSProperty_border_left_color_rtl_source:
5137 case eCSSProperty_border_right_color_ltr_source:
5138 case eCSSProperty_border_right_color_rtl_source:
5139 case eCSSProperty_border_end_style_value:
5140 case eCSSProperty_border_left_style_value:
5141 case eCSSProperty_border_right_style_value:
5142 case eCSSProperty_border_start_style_value:
5143 case eCSSProperty_border_left_style_ltr_source:
5144 case eCSSProperty_border_left_style_rtl_source:
5145 case eCSSProperty_border_right_style_ltr_source:
5146 case eCSSProperty_border_right_style_rtl_source:
5147 case eCSSProperty_border_end_width_value:
5148 case eCSSProperty_border_left_width_value:
5149 case eCSSProperty_border_right_width_value:
5150 case eCSSProperty_border_start_width_value:
5151 case eCSSProperty_border_left_width_ltr_source:
5152 case eCSSProperty_border_left_width_rtl_source:
5153 case eCSSProperty_border_right_width_ltr_source:
5154 case eCSSProperty_border_right_width_rtl_source:
5155 // The user can't use these
5156 REPORT_UNEXPECTED(PEInaccessibleProperty2);
5157 return PR_FALSE;
5158 default: // must be single property
5160 nsCSSValue value;
5161 if (ParseSingleValueProperty(value, aPropID)) {
5162 if (ExpectEndProperty()) {
5163 AppendValue(aPropID, value);
5164 return PR_TRUE;
5166 // XXX Report errors?
5168 // XXX Report errors?
5171 return PR_FALSE;
5174 // Bits used in determining which background position info we have
5175 #define BG_CENTER NS_STYLE_BG_POSITION_CENTER
5176 #define BG_TOP NS_STYLE_BG_POSITION_TOP
5177 #define BG_BOTTOM NS_STYLE_BG_POSITION_BOTTOM
5178 #define BG_LEFT NS_STYLE_BG_POSITION_LEFT
5179 #define BG_RIGHT NS_STYLE_BG_POSITION_RIGHT
5180 #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM)
5181 #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT)
5183 PRBool
5184 CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
5185 nsCSSProperty aPropID)
5187 switch (aPropID) {
5188 case eCSSProperty_UNKNOWN:
5189 case eCSSProperty_background:
5190 case eCSSProperty_background_position:
5191 case eCSSProperty_border:
5192 case eCSSProperty_border_color:
5193 case eCSSProperty_border_bottom_colors:
5194 case eCSSProperty_border_image:
5195 case eCSSProperty_border_left_colors:
5196 case eCSSProperty_border_right_colors:
5197 case eCSSProperty_border_end_color:
5198 case eCSSProperty_border_left_color:
5199 case eCSSProperty_border_right_color:
5200 case eCSSProperty_border_start_color:
5201 case eCSSProperty_border_end_style:
5202 case eCSSProperty_border_left_style:
5203 case eCSSProperty_border_right_style:
5204 case eCSSProperty_border_start_style:
5205 case eCSSProperty_border_end_width:
5206 case eCSSProperty_border_left_width:
5207 case eCSSProperty_border_right_width:
5208 case eCSSProperty_border_start_width:
5209 case eCSSProperty_border_top_colors:
5210 case eCSSProperty_border_spacing:
5211 case eCSSProperty_border_style:
5212 case eCSSProperty_border_bottom:
5213 case eCSSProperty_border_end:
5214 case eCSSProperty_border_left:
5215 case eCSSProperty_border_right:
5216 case eCSSProperty_border_start:
5217 case eCSSProperty_border_top:
5218 case eCSSProperty_border_width:
5219 case eCSSProperty__moz_border_radius:
5220 case eCSSProperty__moz_border_radius_topLeft:
5221 case eCSSProperty__moz_border_radius_topRight:
5222 case eCSSProperty__moz_border_radius_bottomRight:
5223 case eCSSProperty__moz_border_radius_bottomLeft:
5224 case eCSSProperty_box_shadow:
5225 case eCSSProperty_clip:
5226 case eCSSProperty__moz_column_rule:
5227 case eCSSProperty_content:
5228 case eCSSProperty_counter_increment:
5229 case eCSSProperty_counter_reset:
5230 case eCSSProperty_cue:
5231 case eCSSProperty_cursor:
5232 case eCSSProperty_font:
5233 case eCSSProperty_image_region:
5234 case eCSSProperty_list_style:
5235 case eCSSProperty_margin:
5236 case eCSSProperty_margin_end:
5237 case eCSSProperty_margin_left:
5238 case eCSSProperty_margin_right:
5239 case eCSSProperty_margin_start:
5240 case eCSSProperty_outline:
5241 case eCSSProperty__moz_outline_radius:
5242 case eCSSProperty__moz_outline_radius_topLeft:
5243 case eCSSProperty__moz_outline_radius_topRight:
5244 case eCSSProperty__moz_outline_radius_bottomRight:
5245 case eCSSProperty__moz_outline_radius_bottomLeft:
5246 case eCSSProperty_overflow:
5247 case eCSSProperty_padding:
5248 case eCSSProperty_padding_end:
5249 case eCSSProperty_padding_left:
5250 case eCSSProperty_padding_right:
5251 case eCSSProperty_padding_start:
5252 case eCSSProperty_pause:
5253 case eCSSProperty_quotes:
5254 case eCSSProperty_size:
5255 case eCSSProperty_text_shadow:
5256 case eCSSProperty__moz_transform:
5257 case eCSSProperty__moz_transform_origin:
5258 case eCSSProperty_COUNT:
5259 #ifdef MOZ_SVG
5260 case eCSSProperty_fill:
5261 case eCSSProperty_stroke:
5262 case eCSSProperty_stroke_dasharray:
5263 case eCSSProperty_marker:
5264 #endif
5265 NS_ERROR("not a single value property");
5266 return PR_FALSE;
5268 case eCSSProperty__x_system_font:
5269 case eCSSProperty_margin_left_ltr_source:
5270 case eCSSProperty_margin_left_rtl_source:
5271 case eCSSProperty_margin_right_ltr_source:
5272 case eCSSProperty_margin_right_rtl_source:
5273 case eCSSProperty_padding_left_ltr_source:
5274 case eCSSProperty_padding_left_rtl_source:
5275 case eCSSProperty_padding_right_ltr_source:
5276 case eCSSProperty_padding_right_rtl_source:
5277 case eCSSProperty_border_left_color_ltr_source:
5278 case eCSSProperty_border_left_color_rtl_source:
5279 case eCSSProperty_border_right_color_ltr_source:
5280 case eCSSProperty_border_right_color_rtl_source:
5281 case eCSSProperty_border_left_style_ltr_source:
5282 case eCSSProperty_border_left_style_rtl_source:
5283 case eCSSProperty_border_right_style_ltr_source:
5284 case eCSSProperty_border_right_style_rtl_source:
5285 case eCSSProperty_border_left_width_ltr_source:
5286 case eCSSProperty_border_left_width_rtl_source:
5287 case eCSSProperty_border_right_width_ltr_source:
5288 case eCSSProperty_border_right_width_rtl_source:
5289 #ifdef MOZ_MATHML
5290 case eCSSProperty_script_size_multiplier:
5291 case eCSSProperty_script_min_size:
5292 #endif
5293 NS_ERROR("not currently parsed here");
5294 return PR_FALSE;
5296 case eCSSProperty_appearance:
5297 return ParseVariant(aValue, VARIANT_HK,
5298 nsCSSProps::kAppearanceKTable);
5299 case eCSSProperty_azimuth:
5300 return ParseAzimuth(aValue);
5301 case eCSSProperty_background_attachment:
5302 return ParseVariant(aValue, VARIANT_HK,
5303 nsCSSProps::kBackgroundAttachmentKTable);
5304 case eCSSProperty__moz_background_clip:
5305 return ParseVariant(aValue, VARIANT_HK,
5306 nsCSSProps::kBackgroundClipKTable);
5307 case eCSSProperty_background_color:
5308 return ParseVariant(aValue, VARIANT_HC, nsnull);
5309 case eCSSProperty_background_image:
5310 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5311 case eCSSProperty__moz_background_inline_policy:
5312 return ParseVariant(aValue, VARIANT_HK,
5313 nsCSSProps::kBackgroundInlinePolicyKTable);
5314 case eCSSProperty__moz_background_origin:
5315 return ParseVariant(aValue, VARIANT_HK,
5316 nsCSSProps::kBackgroundOriginKTable);
5317 case eCSSProperty_background_repeat:
5318 return ParseVariant(aValue, VARIANT_HK,
5319 nsCSSProps::kBackgroundRepeatKTable);
5320 case eCSSProperty_binding:
5321 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5322 case eCSSProperty_border_collapse:
5323 return ParseVariant(aValue, VARIANT_HK,
5324 nsCSSProps::kBorderCollapseKTable);
5325 case eCSSProperty_border_bottom_color:
5326 case eCSSProperty_border_end_color_value: // for internal use
5327 case eCSSProperty_border_left_color_value: // for internal use
5328 case eCSSProperty_border_right_color_value: // for internal use
5329 case eCSSProperty_border_start_color_value: // for internal use
5330 case eCSSProperty_border_top_color:
5331 case eCSSProperty__moz_column_rule_color:
5332 return ParseVariant(aValue, VARIANT_HCK,
5333 nsCSSProps::kBorderColorKTable);
5334 case eCSSProperty_border_bottom_style:
5335 case eCSSProperty_border_end_style_value: // for internal use
5336 case eCSSProperty_border_left_style_value: // for internal use
5337 case eCSSProperty_border_right_style_value: // for internal use
5338 case eCSSProperty_border_start_style_value: // for internal use
5339 case eCSSProperty_border_top_style:
5340 case eCSSProperty__moz_column_rule_style:
5341 return ParseVariant(aValue, VARIANT_HOK,
5342 nsCSSProps::kBorderStyleKTable);
5343 case eCSSProperty_border_bottom_width:
5344 case eCSSProperty_border_end_width_value: // for internal use
5345 case eCSSProperty_border_left_width_value: // for internal use
5346 case eCSSProperty_border_right_width_value: // for internal use
5347 case eCSSProperty_border_start_width_value: // for internal use
5348 case eCSSProperty_border_top_width:
5349 case eCSSProperty__moz_column_rule_width:
5350 return ParsePositiveVariant(aValue, VARIANT_HKL,
5351 nsCSSProps::kBorderWidthKTable);
5352 case eCSSProperty__moz_column_count:
5353 return ParsePositiveVariant(aValue, VARIANT_AHI, nsnull);
5354 case eCSSProperty__moz_column_width:
5355 return ParsePositiveVariant(aValue, VARIANT_AHL, nsnull);
5356 case eCSSProperty__moz_column_gap:
5357 return ParsePositiveVariant(aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
5358 case eCSSProperty_bottom:
5359 case eCSSProperty_top:
5360 case eCSSProperty_left:
5361 case eCSSProperty_right:
5362 return ParseVariant(aValue, VARIANT_AHLP, nsnull);
5363 case eCSSProperty_box_align:
5364 return ParseVariant(aValue, VARIANT_HK,
5365 nsCSSProps::kBoxAlignKTable);
5366 case eCSSProperty_box_direction:
5367 return ParseVariant(aValue, VARIANT_HK,
5368 nsCSSProps::kBoxDirectionKTable);
5369 case eCSSProperty_box_flex:
5370 return ParsePositiveVariant(aValue, VARIANT_HN, nsnull);
5371 case eCSSProperty_box_orient:
5372 return ParseVariant(aValue, VARIANT_HK,
5373 nsCSSProps::kBoxOrientKTable);
5374 case eCSSProperty_box_pack:
5375 return ParseVariant(aValue, VARIANT_HK,
5376 nsCSSProps::kBoxPackKTable);
5377 case eCSSProperty_box_ordinal_group:
5378 return ParseVariant(aValue, VARIANT_HI, nsnull);
5379 #ifdef MOZ_SVG
5380 case eCSSProperty_clip_path:
5381 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5382 case eCSSProperty_clip_rule:
5383 return ParseVariant(aValue, VARIANT_HK,
5384 nsCSSProps::kFillRuleKTable);
5385 case eCSSProperty_color_interpolation:
5386 case eCSSProperty_color_interpolation_filters:
5387 return ParseVariant(aValue, VARIANT_AHK,
5388 nsCSSProps::kColorInterpolationKTable);
5389 case eCSSProperty_dominant_baseline:
5390 return ParseVariant(aValue, VARIANT_AHK,
5391 nsCSSProps::kDominantBaselineKTable);
5392 case eCSSProperty_fill_opacity:
5393 return ParseVariant(aValue, VARIANT_HN,
5394 nsnull);
5395 case eCSSProperty_fill_rule:
5396 return ParseVariant(aValue, VARIANT_HK,
5397 nsCSSProps::kFillRuleKTable);
5398 case eCSSProperty_filter:
5399 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5400 case eCSSProperty_flood_color:
5401 return ParseVariant(aValue, VARIANT_HC, nsnull);
5402 case eCSSProperty_flood_opacity:
5403 return ParseVariant(aValue, VARIANT_HN, nsnull);
5404 case eCSSProperty_lighting_color:
5405 return ParseVariant(aValue, VARIANT_HC, nsnull);
5406 case eCSSProperty_marker_end:
5407 case eCSSProperty_marker_mid:
5408 case eCSSProperty_marker_start:
5409 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5410 case eCSSProperty_mask:
5411 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5412 case eCSSProperty_pointer_events:
5413 return ParseVariant(aValue, VARIANT_HOK,
5414 nsCSSProps::kPointerEventsKTable);
5415 case eCSSProperty_shape_rendering:
5416 return ParseVariant(aValue, VARIANT_AHK,
5417 nsCSSProps::kShapeRenderingKTable);
5418 case eCSSProperty_stop_color:
5419 return ParseVariant(aValue, VARIANT_HC,
5420 nsnull);
5421 case eCSSProperty_stop_opacity:
5422 return ParseVariant(aValue, VARIANT_HN,
5423 nsnull);
5424 case eCSSProperty_stroke_dashoffset:
5425 return ParseVariant(aValue, VARIANT_HLPN,
5426 nsnull);
5427 case eCSSProperty_stroke_linecap:
5428 return ParseVariant(aValue, VARIANT_HK,
5429 nsCSSProps::kStrokeLinecapKTable);
5430 case eCSSProperty_stroke_linejoin:
5431 return ParseVariant(aValue, VARIANT_HK,
5432 nsCSSProps::kStrokeLinejoinKTable);
5433 case eCSSProperty_stroke_miterlimit:
5434 return ParsePositiveVariant(aValue, VARIANT_HN,
5435 nsnull);
5436 case eCSSProperty_stroke_opacity:
5437 return ParseVariant(aValue, VARIANT_HN,
5438 nsnull);
5439 case eCSSProperty_stroke_width:
5440 return ParsePositiveVariant(aValue, VARIANT_HLPN,
5441 nsnull);
5442 case eCSSProperty_text_anchor:
5443 return ParseVariant(aValue, VARIANT_HK,
5444 nsCSSProps::kTextAnchorKTable);
5445 case eCSSProperty_text_rendering:
5446 return ParseVariant(aValue, VARIANT_AHK,
5447 nsCSSProps::kTextRenderingKTable);
5448 #endif
5449 case eCSSProperty_box_sizing:
5450 return ParseVariant(aValue, VARIANT_HK,
5451 nsCSSProps::kBoxSizingKTable);
5452 case eCSSProperty_height:
5453 return ParsePositiveVariant(aValue, VARIANT_AHLP, nsnull);
5454 case eCSSProperty_width:
5455 return ParsePositiveVariant(aValue, VARIANT_AHKLP,
5456 nsCSSProps::kWidthKTable);
5457 case eCSSProperty_force_broken_image_icon:
5458 return ParsePositiveVariant(aValue, VARIANT_HI, nsnull);
5459 case eCSSProperty_caption_side:
5460 return ParseVariant(aValue, VARIANT_HK,
5461 nsCSSProps::kCaptionSideKTable);
5462 case eCSSProperty_clear:
5463 return ParseVariant(aValue, VARIANT_HOK,
5464 nsCSSProps::kClearKTable);
5465 case eCSSProperty_color:
5466 return ParseVariant(aValue, VARIANT_HC, nsnull);
5467 case eCSSProperty_cue_after:
5468 case eCSSProperty_cue_before:
5469 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5470 case eCSSProperty_direction:
5471 return ParseVariant(aValue, VARIANT_HK,
5472 nsCSSProps::kDirectionKTable);
5473 case eCSSProperty_display:
5474 if (ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kDisplayKTable)) {
5475 if (aValue.GetUnit() == eCSSUnit_Enumerated) {
5476 switch (aValue.GetIntValue()) {
5477 case NS_STYLE_DISPLAY_MARKER: // bug 2055
5478 case NS_STYLE_DISPLAY_RUN_IN: // bug 2056
5479 case NS_STYLE_DISPLAY_COMPACT: // bug 14983
5480 return PR_FALSE;
5483 return PR_TRUE;
5485 return PR_FALSE;
5486 case eCSSProperty_elevation:
5487 return ParseVariant(aValue, VARIANT_HK | VARIANT_ANGLE,
5488 nsCSSProps::kElevationKTable);
5489 case eCSSProperty_empty_cells:
5490 return ParseVariant(aValue, VARIANT_HK,
5491 nsCSSProps::kEmptyCellsKTable);
5492 case eCSSProperty_float:
5493 return ParseVariant(aValue, VARIANT_HOK,
5494 nsCSSProps::kFloatKTable);
5495 case eCSSProperty_float_edge:
5496 return ParseVariant(aValue, VARIANT_HK,
5497 nsCSSProps::kFloatEdgeKTable);
5498 case eCSSProperty_font_family:
5499 return ParseFamily(aValue);
5500 case eCSSProperty_font_size:
5501 return ParsePositiveVariant(aValue,
5502 VARIANT_HKLP | VARIANT_SYSFONT,
5503 nsCSSProps::kFontSizeKTable);
5504 case eCSSProperty_font_size_adjust:
5505 return ParseVariant(aValue, VARIANT_HON | VARIANT_SYSFONT,
5506 nsnull);
5507 case eCSSProperty_font_stretch:
5508 return ParseVariant(aValue, VARIANT_HMK | VARIANT_SYSFONT,
5509 nsCSSProps::kFontStretchKTable);
5510 case eCSSProperty_font_style:
5511 return ParseVariant(aValue, VARIANT_HMK | VARIANT_SYSFONT,
5512 nsCSSProps::kFontStyleKTable);
5513 case eCSSProperty_font_variant:
5514 return ParseVariant(aValue, VARIANT_HMK | VARIANT_SYSFONT,
5515 nsCSSProps::kFontVariantKTable);
5516 case eCSSProperty_font_weight:
5517 return ParseFontWeight(aValue);
5518 case eCSSProperty_ime_mode:
5519 return ParseVariant(aValue, VARIANT_AHK | VARIANT_NORMAL,
5520 nsCSSProps::kIMEModeKTable);
5521 case eCSSProperty_letter_spacing:
5522 case eCSSProperty_word_spacing:
5523 return ParseVariant(aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
5524 case eCSSProperty_line_height:
5525 return ParsePositiveVariant(aValue, VARIANT_HLPN | VARIANT_NORMAL | VARIANT_SYSFONT, nsnull);
5526 case eCSSProperty_list_style_image:
5527 return ParseVariant(aValue, VARIANT_HUO, nsnull);
5528 case eCSSProperty_list_style_position:
5529 return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kListStylePositionKTable);
5530 case eCSSProperty_list_style_type:
5531 return ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kListStyleKTable);
5532 case eCSSProperty_margin_bottom:
5533 case eCSSProperty_margin_end_value: // for internal use
5534 case eCSSProperty_margin_left_value: // for internal use
5535 case eCSSProperty_margin_right_value: // for internal use
5536 case eCSSProperty_margin_start_value: // for internal use
5537 case eCSSProperty_margin_top:
5538 return ParseVariant(aValue, VARIANT_AHLP, nsnull);
5539 case eCSSProperty_marker_offset:
5540 return ParseVariant(aValue, VARIANT_AHL, nsnull);
5541 case eCSSProperty_marks:
5542 return ParseMarks(aValue);
5543 case eCSSProperty_max_height:
5544 return ParsePositiveVariant(aValue, VARIANT_HLPO, nsnull);
5545 case eCSSProperty_max_width:
5546 return ParsePositiveVariant(aValue, VARIANT_HKLPO,
5547 nsCSSProps::kWidthKTable);
5548 case eCSSProperty_min_height:
5549 return ParsePositiveVariant(aValue, VARIANT_HLP, nsnull);
5550 case eCSSProperty_min_width:
5551 return ParsePositiveVariant(aValue, VARIANT_HKLP,
5552 nsCSSProps::kWidthKTable);
5553 case eCSSProperty_opacity:
5554 return ParseVariant(aValue, VARIANT_HN, nsnull);
5555 case eCSSProperty_orphans:
5556 case eCSSProperty_widows:
5557 return ParseVariant(aValue, VARIANT_HI, nsnull);
5558 case eCSSProperty_outline_color:
5559 return ParseVariant(aValue, VARIANT_HCK,
5560 nsCSSProps::kOutlineColorKTable);
5561 case eCSSProperty_outline_style:
5562 return ParseVariant(aValue, VARIANT_HOK | VARIANT_AUTO,
5563 nsCSSProps::kOutlineStyleKTable);
5564 case eCSSProperty_outline_width:
5565 return ParsePositiveVariant(aValue, VARIANT_HKL,
5566 nsCSSProps::kBorderWidthKTable);
5567 case eCSSProperty_outline_offset:
5568 return ParseVariant(aValue, VARIANT_HL, nsnull);
5569 case eCSSProperty_overflow_x:
5570 case eCSSProperty_overflow_y:
5571 return ParseVariant(aValue, VARIANT_AHK,
5572 nsCSSProps::kOverflowSubKTable);
5573 case eCSSProperty_padding_bottom:
5574 case eCSSProperty_padding_end_value: // for internal use
5575 case eCSSProperty_padding_left_value: // for internal use
5576 case eCSSProperty_padding_right_value: // for internal use
5577 case eCSSProperty_padding_start_value: // for internal use
5578 case eCSSProperty_padding_top:
5579 return ParsePositiveVariant(aValue, VARIANT_HLP, nsnull);
5580 case eCSSProperty_page:
5581 return ParseVariant(aValue, VARIANT_AUTO | VARIANT_IDENTIFIER, nsnull);
5582 case eCSSProperty_page_break_after:
5583 case eCSSProperty_page_break_before:
5584 return ParseVariant(aValue, VARIANT_AHK,
5585 nsCSSProps::kPageBreakKTable);
5586 case eCSSProperty_page_break_inside:
5587 return ParseVariant(aValue, VARIANT_AHK,
5588 nsCSSProps::kPageBreakInsideKTable);
5589 case eCSSProperty_pause_after:
5590 case eCSSProperty_pause_before:
5591 return ParseVariant(aValue, VARIANT_HTP, nsnull);
5592 case eCSSProperty_pitch:
5593 return ParseVariant(aValue, VARIANT_HKF, nsCSSProps::kPitchKTable);
5594 case eCSSProperty_pitch_range:
5595 return ParseVariant(aValue, VARIANT_HN, nsnull);
5596 case eCSSProperty_position:
5597 return ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPositionKTable);
5598 case eCSSProperty_richness:
5599 return ParseVariant(aValue, VARIANT_HN, nsnull);
5600 #ifdef MOZ_MATHML
5601 // script-level can take Integer or Number values, but only Integer ("relative")
5602 // values can be specified in a style sheet. Also we only allow this property
5603 // when unsafe rules are enabled, because otherwise it could interfere
5604 // with rulenode optimizations if used in a non-MathML-enabled document.
5605 case eCSSProperty_script_level:
5606 if (!mUnsafeRulesEnabled)
5607 return PR_FALSE;
5608 return ParseVariant(aValue, VARIANT_HI, nsnull);
5609 #endif
5610 case eCSSProperty_speak:
5611 return ParseVariant(aValue, VARIANT_HMK | VARIANT_NONE,
5612 nsCSSProps::kSpeakKTable);
5613 case eCSSProperty_speak_header:
5614 return ParseVariant(aValue, VARIANT_HK,
5615 nsCSSProps::kSpeakHeaderKTable);
5616 case eCSSProperty_speak_numeral:
5617 return ParseVariant(aValue, VARIANT_HK,
5618 nsCSSProps::kSpeakNumeralKTable);
5619 case eCSSProperty_speak_punctuation:
5620 return ParseVariant(aValue, VARIANT_HOK,
5621 nsCSSProps::kSpeakPunctuationKTable);
5622 case eCSSProperty_speech_rate:
5623 return ParseVariant(aValue, VARIANT_HN | VARIANT_KEYWORD,
5624 nsCSSProps::kSpeechRateKTable);
5625 case eCSSProperty_stack_sizing:
5626 return ParseVariant(aValue, VARIANT_HK,
5627 nsCSSProps::kStackSizingKTable);
5628 case eCSSProperty_stress:
5629 return ParseVariant(aValue, VARIANT_HN, nsnull);
5630 case eCSSProperty_table_layout:
5631 return ParseVariant(aValue, VARIANT_AHK,
5632 nsCSSProps::kTableLayoutKTable);
5633 case eCSSProperty_text_align:
5634 // When we support aligning on a string, we can parse text-align
5635 // as a string....
5636 return ParseVariant(aValue, VARIANT_HK /* | VARIANT_STRING */,
5637 nsCSSProps::kTextAlignKTable);
5638 case eCSSProperty_text_decoration:
5639 return ParseTextDecoration(aValue);
5640 case eCSSProperty_text_indent:
5641 return ParseVariant(aValue, VARIANT_HLP, nsnull);
5642 case eCSSProperty_text_transform:
5643 return ParseVariant(aValue, VARIANT_HOK,
5644 nsCSSProps::kTextTransformKTable);
5645 case eCSSProperty_unicode_bidi:
5646 return ParseVariant(aValue, VARIANT_HMK,
5647 nsCSSProps::kUnicodeBidiKTable);
5648 case eCSSProperty_user_focus:
5649 return ParseVariant(aValue, VARIANT_HMK | VARIANT_NONE,
5650 nsCSSProps::kUserFocusKTable);
5651 case eCSSProperty_user_input:
5652 return ParseVariant(aValue, VARIANT_AHK | VARIANT_NONE,
5653 nsCSSProps::kUserInputKTable);
5654 case eCSSProperty_user_modify:
5655 return ParseVariant(aValue, VARIANT_HK,
5656 nsCSSProps::kUserModifyKTable);
5657 case eCSSProperty_user_select:
5658 return ParseVariant(aValue, VARIANT_AHK | VARIANT_NONE,
5659 nsCSSProps::kUserSelectKTable);
5660 case eCSSProperty_vertical_align:
5661 return ParseVariant(aValue, VARIANT_HKLP,
5662 nsCSSProps::kVerticalAlignKTable);
5663 case eCSSProperty_visibility:
5664 return ParseVariant(aValue, VARIANT_HK,
5665 nsCSSProps::kVisibilityKTable);
5666 case eCSSProperty_voice_family:
5667 return ParseFamily(aValue);
5668 case eCSSProperty_volume:
5669 return ParseVariant(aValue, VARIANT_HPN | VARIANT_KEYWORD,
5670 nsCSSProps::kVolumeKTable);
5671 case eCSSProperty_white_space:
5672 return ParseVariant(aValue, VARIANT_HMK,
5673 nsCSSProps::kWhitespaceKTable);
5674 case eCSSProperty__moz_window_shadow:
5675 return ParseVariant(aValue, VARIANT_HOK,
5676 nsCSSProps::kWindowShadowKTable);
5677 case eCSSProperty_word_wrap:
5678 return ParseVariant(aValue, VARIANT_HMK,
5679 nsCSSProps::kWordwrapKTable);
5680 case eCSSProperty_z_index:
5681 return ParseVariant(aValue, VARIANT_AHI, nsnull);
5683 // explicitly do NOT have a default case to let the compiler
5684 // help find missing properties
5685 return PR_FALSE;
5688 // nsFont::EnumerateFamilies callback for ParseFontDescriptorValue
5689 struct NS_STACK_CLASS ExtractFirstFamilyData {
5690 nsAutoString mFamilyName;
5691 PRBool mGood;
5692 ExtractFirstFamilyData() : mFamilyName(), mGood(PR_FALSE) {}
5695 static PRBool
5696 ExtractFirstFamily(const nsString& aFamily,
5697 PRBool aGeneric,
5698 void* aData)
5700 ExtractFirstFamilyData* realData = (ExtractFirstFamilyData*) aData;
5701 if (aGeneric || realData->mFamilyName.Length() > 0) {
5702 realData->mGood = PR_FALSE;
5703 return PR_FALSE;
5705 realData->mFamilyName.Assign(aFamily);
5706 realData->mGood = PR_TRUE;
5707 return PR_TRUE;
5710 // font-descriptor: descriptor ':' value ';'
5711 // caller has advanced mToken to point at the descriptor
5712 PRBool
5713 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
5714 nsCSSValue& aValue)
5716 switch (aDescID) {
5717 // These four are similar to the properties of the same name,
5718 // possibly with more restrictions on the values they can take.
5719 case eCSSFontDesc_Family: {
5720 if (!ParseFamily(aValue) ||
5721 aValue.GetUnit() != eCSSUnit_String)
5722 return PR_FALSE;
5724 // the style parameters to the nsFont constructor are ignored,
5725 // because it's only being used to call EnumerateFamilies
5726 nsAutoString valueStr;
5727 aValue.GetStringValue(valueStr);
5728 nsFont font(valueStr, 0, 0, 0, 0, 0);
5729 ExtractFirstFamilyData dat;
5731 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
5732 if (!dat.mGood)
5733 return PR_FALSE;
5735 aValue.SetStringValue(dat.mFamilyName, eCSSUnit_String);
5736 return PR_TRUE;
5739 case eCSSFontDesc_Style:
5740 // property is VARIANT_HMK|VARIANT_SYSFONT
5741 return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
5742 nsCSSProps::kFontStyleKTable);
5744 case eCSSFontDesc_Weight:
5745 return (ParseFontWeight(aValue) &&
5746 aValue.GetUnit() != eCSSUnit_Inherit &&
5747 aValue.GetUnit() != eCSSUnit_Initial &&
5748 (aValue.GetUnit() != eCSSUnit_Enumerated ||
5749 (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
5750 aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
5752 case eCSSFontDesc_Stretch:
5753 // property is VARIANT_HMK|VARIANT_SYSFONT
5754 return (ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
5755 nsCSSProps::kFontStretchKTable) &&
5756 (aValue.GetUnit() != eCSSUnit_Enumerated ||
5757 (aValue.GetIntValue() != NS_STYLE_FONT_STRETCH_WIDER &&
5758 aValue.GetIntValue() != NS_STYLE_FONT_STRETCH_NARROWER)));
5760 // These two are unique to @font-face and have their own special grammar.
5761 case eCSSFontDesc_Src:
5762 return ParseFontSrc(aValue);
5764 case eCSSFontDesc_UnicodeRange:
5765 return ParseFontRanges(aValue);
5767 case eCSSFontDesc_UNKNOWN:
5768 case eCSSFontDesc_COUNT:
5769 NS_NOTREACHED("bad nsCSSFontDesc code");
5771 // explicitly do NOT have a default case to let the compiler
5772 // help find missing descriptors
5773 return PR_FALSE;
5776 void
5777 CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties)
5779 nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
5780 for (const nsCSSProperty *prop = aSourceProperties;
5781 *prop != eCSSProperty_UNKNOWN; ++prop) {
5782 AppendValue(*prop, physical);
5786 PRBool
5787 CSSParserImpl::ParseAzimuth(nsCSSValue& aValue)
5789 if (ParseVariant(aValue, VARIANT_HK | VARIANT_ANGLE,
5790 nsCSSProps::kAzimuthKTable)) {
5791 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
5792 PRInt32 intValue = aValue.GetIntValue();
5793 if ((NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) &&
5794 (intValue <= NS_STYLE_AZIMUTH_BEHIND)) { // look for optional modifier
5795 nsCSSValue modifier;
5796 if (ParseEnum(modifier, nsCSSProps::kAzimuthKTable)) {
5797 PRInt32 enumValue = modifier.GetIntValue();
5798 if (((intValue == NS_STYLE_AZIMUTH_BEHIND) &&
5799 (NS_STYLE_AZIMUTH_LEFT_SIDE <= enumValue) && (enumValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE)) ||
5800 ((enumValue == NS_STYLE_AZIMUTH_BEHIND) &&
5801 (NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) && (intValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE))) {
5802 aValue.SetIntValue(intValue | enumValue, eCSSUnit_Enumerated);
5803 return PR_TRUE;
5805 // Put the unknown identifier back and return
5806 UngetToken();
5807 return PR_FALSE;
5811 return PR_TRUE;
5813 return PR_FALSE;
5816 static nsCSSValue
5817 BoxPositionMaskToCSSValue(PRInt32 aMask, PRBool isX)
5819 PRInt32 val = NS_STYLE_BG_POSITION_CENTER;
5820 if (isX) {
5821 if (aMask & BG_LEFT) {
5822 val = NS_STYLE_BG_POSITION_LEFT;
5824 else if (aMask & BG_RIGHT) {
5825 val = NS_STYLE_BG_POSITION_RIGHT;
5828 else {
5829 if (aMask & BG_TOP) {
5830 val = NS_STYLE_BG_POSITION_TOP;
5832 else if (aMask & BG_BOTTOM) {
5833 val = NS_STYLE_BG_POSITION_BOTTOM;
5837 return nsCSSValue(val, eCSSUnit_Enumerated);
5840 PRBool
5841 CSSParserImpl::ParseBackground()
5843 nsAutoParseCompoundProperty compound(this);
5845 // Fill in the values that the shorthand will set if we don't find
5846 // other values.
5847 mTempData.mColor.mBackColor.SetColorValue(NS_RGBA(0, 0, 0, 0));
5848 mTempData.SetPropertyBit(eCSSProperty_background_color);
5849 mTempData.mColor.mBackImage.SetNoneValue();
5850 mTempData.SetPropertyBit(eCSSProperty_background_image);
5851 mTempData.mColor.mBackRepeat.SetIntValue(NS_STYLE_BG_REPEAT_XY,
5852 eCSSUnit_Enumerated);
5853 mTempData.SetPropertyBit(eCSSProperty_background_repeat);
5854 mTempData.mColor.mBackAttachment.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL,
5855 eCSSUnit_Enumerated);
5856 mTempData.SetPropertyBit(eCSSProperty_background_attachment);
5857 mTempData.mColor.mBackPosition.mXValue.SetPercentValue(0.0f);
5858 mTempData.mColor.mBackPosition.mYValue.SetPercentValue(0.0f);
5859 mTempData.SetPropertyBit(eCSSProperty_background_position);
5860 // including the ones that we can't set from the shorthand.
5861 mTempData.mColor.mBackClip.SetIntValue(NS_STYLE_BG_CLIP_BORDER,
5862 eCSSUnit_Enumerated);
5863 mTempData.SetPropertyBit(eCSSProperty__moz_background_clip);
5864 mTempData.mColor.mBackOrigin.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING,
5865 eCSSUnit_Enumerated);
5866 mTempData.SetPropertyBit(eCSSProperty__moz_background_origin);
5867 mTempData.mColor.mBackInlinePolicy.SetIntValue(
5868 NS_STYLE_BG_INLINE_POLICY_CONTINUOUS, eCSSUnit_Enumerated);
5869 mTempData.SetPropertyBit(eCSSProperty__moz_background_inline_policy);
5871 // XXX If ParseSingleValueProperty were table-driven (bug 376079) and
5872 // automatically filled in the right field of mTempData, we could move
5873 // ParseBackgroundPosition to it (as a special case) and switch back
5874 // to using ParseChoice here.
5876 PRBool haveColor = PR_FALSE,
5877 haveImage = PR_FALSE,
5878 haveRepeat = PR_FALSE,
5879 haveAttach = PR_FALSE,
5880 havePosition = PR_FALSE;
5881 while (GetToken(PR_TRUE)) {
5882 nsCSSTokenType tt = mToken.mType;
5883 UngetToken(); // ...but we'll still cheat and use mToken
5884 if (tt == eCSSToken_Symbol) {
5885 // ExpectEndProperty only looks for symbols, and nothing else will
5886 // show up as one.
5887 break;
5890 if (tt == eCSSToken_Ident) {
5891 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
5892 PRInt32 dummy;
5893 if (keyword == eCSSKeyword_inherit ||
5894 keyword == eCSSKeyword__moz_initial) {
5895 if (haveColor || haveImage || haveRepeat || haveAttach || havePosition)
5896 return PR_FALSE;
5897 haveColor = haveImage = haveRepeat = haveAttach = havePosition =
5898 PR_TRUE;
5899 GetToken(PR_TRUE); // undo the UngetToken above
5900 nsCSSValue val;
5901 if (keyword == eCSSKeyword_inherit) {
5902 val.SetInheritValue();
5903 } else {
5904 val.SetInitialValue();
5906 mTempData.mColor.mBackColor = val;
5907 mTempData.mColor.mBackImage = val;
5908 mTempData.mColor.mBackRepeat = val;
5909 mTempData.mColor.mBackAttachment = val;
5910 mTempData.mColor.mBackPosition.mXValue = val;
5911 mTempData.mColor.mBackPosition.mYValue = val;
5912 // Reset (for 'inherit') the 3 properties that can't be
5913 // specified, although it's not entirely clear in the spec:
5914 // http://lists.w3.org/Archives/Public/www-style/2007Mar/0110
5915 mTempData.mColor.mBackClip = val;
5916 mTempData.mColor.mBackOrigin = val;
5917 mTempData.mColor.mBackInlinePolicy = val;
5918 break;
5919 } else if (keyword == eCSSKeyword_none) {
5920 if (haveImage)
5921 return PR_FALSE;
5922 haveImage = PR_TRUE;
5923 if (!ParseSingleValueProperty(mTempData.mColor.mBackImage,
5924 eCSSProperty_background_image)) {
5925 NS_NOTREACHED("should be able to parse");
5926 return PR_FALSE;
5928 } else if (nsCSSProps::FindKeyword(keyword,
5929 nsCSSProps::kBackgroundAttachmentKTable, dummy)) {
5930 if (haveAttach)
5931 return PR_FALSE;
5932 haveAttach = PR_TRUE;
5933 if (!ParseSingleValueProperty(mTempData.mColor.mBackAttachment,
5934 eCSSProperty_background_attachment)) {
5935 NS_NOTREACHED("should be able to parse");
5936 return PR_FALSE;
5938 } else if (nsCSSProps::FindKeyword(keyword,
5939 nsCSSProps::kBackgroundRepeatKTable, dummy)) {
5940 if (haveRepeat)
5941 return PR_FALSE;
5942 haveRepeat = PR_TRUE;
5943 if (!ParseSingleValueProperty(mTempData.mColor.mBackRepeat,
5944 eCSSProperty_background_repeat)) {
5945 NS_NOTREACHED("should be able to parse");
5946 return PR_FALSE;
5948 } else if (nsCSSProps::FindKeyword(keyword,
5949 nsCSSProps::kBackgroundPositionKTable, dummy)) {
5950 if (havePosition)
5951 return PR_FALSE;
5952 havePosition = PR_TRUE;
5953 if (!ParseBackgroundPositionValues()) {
5954 return PR_FALSE;
5956 } else {
5957 if (haveColor)
5958 return PR_FALSE;
5959 haveColor = PR_TRUE;
5960 if (!ParseSingleValueProperty(mTempData.mColor.mBackColor,
5961 eCSSProperty_background_color)) {
5962 return PR_FALSE;
5965 } else if (eCSSToken_Function == tt &&
5966 mToken.mIdent.LowerCaseEqualsLiteral("url")) {
5967 if (haveImage)
5968 return PR_FALSE;
5969 haveImage = PR_TRUE;
5970 if (!ParseSingleValueProperty(mTempData.mColor.mBackImage,
5971 eCSSProperty_background_image)) {
5972 return PR_FALSE;
5974 } else if (mToken.IsDimension() || tt == eCSSToken_Percentage) {
5975 if (havePosition)
5976 return PR_FALSE;
5977 havePosition = PR_TRUE;
5978 if (!ParseBackgroundPositionValues()) {
5979 return PR_FALSE;
5981 } else {
5982 if (haveColor)
5983 return PR_FALSE;
5984 haveColor = PR_TRUE;
5985 if (!ParseSingleValueProperty(mTempData.mColor.mBackColor,
5986 eCSSProperty_background_color)) {
5987 return PR_FALSE;
5992 return ExpectEndProperty() &&
5993 (haveColor || haveImage || haveRepeat || haveAttach || havePosition);
5996 PRBool
5997 CSSParserImpl::ParseBackgroundPosition()
5999 if (!ParseBoxPosition(mTempData.mColor.mBackPosition))
6000 return PR_FALSE;
6001 mTempData.SetPropertyBit(eCSSProperty_background_position);
6002 return PR_TRUE;
6005 PRBool
6006 CSSParserImpl::ParseBackgroundPositionValues()
6008 return ParseBoxPositionValues(mTempData.mColor.mBackPosition);
6012 * Parses two values that correspond to positions in a box. These can be
6013 * values corresponding to percentages of the box, raw offsets, or keywords
6014 * like "top," "left center," etc.
6016 * @param aOut The nsCSSValuePair where to place the result.
6017 * @return Whether or not the operation succeeded.
6019 PRBool CSSParserImpl::ParseBoxPosition(nsCSSValuePair &aOut)
6021 // Need to read the box positions and the end of the property.
6022 return ParseBoxPositionValues(aOut) && ExpectEndProperty();
6025 PRBool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut)
6027 // First try a percentage or a length value
6028 nsCSSValue &xValue = aOut.mXValue,
6029 &yValue = aOut.mYValue;
6030 if (ParseVariant(xValue, VARIANT_HLP, nsnull)) {
6031 if (eCSSUnit_Inherit == xValue.GetUnit() ||
6032 eCSSUnit_Initial == xValue.GetUnit()) { // both are inherited or both are set to initial
6033 yValue = xValue;
6034 return PR_TRUE;
6036 // We have one percentage/length. Get the optional second
6037 // percentage/length/keyword.
6038 if (ParseVariant(yValue, VARIANT_LP, nsnull)) {
6039 // We have two numbers
6040 return PR_TRUE;
6043 if (ParseEnum(yValue, nsCSSProps::kBackgroundPositionKTable)) {
6044 PRInt32 yVal = yValue.GetIntValue();
6045 if (!(yVal & BG_CTB)) {
6046 // The second keyword can only be 'center', 'top', or 'bottom'
6047 return PR_FALSE;
6049 yValue = BoxPositionMaskToCSSValue(yVal, PR_FALSE);
6050 return PR_TRUE;
6053 // If only one percentage or length value is given, it sets the
6054 // horizontal position only, and the vertical position will be 50%.
6055 yValue.SetPercentValue(0.5f);
6056 return PR_TRUE;
6059 // Now try keywords. We do this manually to allow for the first
6060 // appearance of "center" to apply to the either the x or y
6061 // position (it's ambiguous so we have to disambiguate). Each
6062 // allowed keyword value is assigned it's own bit. We don't allow
6063 // any duplicate keywords other than center. We try to get two
6064 // keywords but it's okay if there is only one.
6065 PRInt32 mask = 0;
6066 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
6067 PRInt32 bit = xValue.GetIntValue();
6068 mask |= bit;
6069 if (ParseEnum(xValue, nsCSSProps::kBackgroundPositionKTable)) {
6070 bit = xValue.GetIntValue();
6071 if (mask & (bit & ~BG_CENTER)) {
6072 // Only the 'center' keyword can be duplicated.
6073 return PR_FALSE;
6075 mask |= bit;
6077 else {
6078 // Only one keyword. See if we have a length or percentage.
6079 if (ParseVariant(yValue, VARIANT_LP, nsnull)) {
6080 if (!(mask & BG_CLR)) {
6081 // The first keyword can only be 'center', 'left', or 'right'
6082 return PR_FALSE;
6085 xValue = BoxPositionMaskToCSSValue(mask, PR_TRUE);
6086 return PR_TRUE;
6091 // Check for bad input. Bad input consists of no matching keywords,
6092 // or pairs of x keywords or pairs of y keywords.
6093 if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
6094 (mask == (BG_LEFT | BG_RIGHT))) {
6095 return PR_FALSE;
6098 // Create style values
6099 xValue = BoxPositionMaskToCSSValue(mask, PR_TRUE);
6100 yValue = BoxPositionMaskToCSSValue(mask, PR_FALSE);
6101 return PR_TRUE;
6104 PRBool
6105 CSSParserImpl::ParseBorderColor()
6107 static const nsCSSProperty kBorderColorSources[] = {
6108 eCSSProperty_border_left_color_ltr_source,
6109 eCSSProperty_border_left_color_rtl_source,
6110 eCSSProperty_border_right_color_ltr_source,
6111 eCSSProperty_border_right_color_rtl_source,
6112 eCSSProperty_UNKNOWN
6115 // do this now, in case 4 values weren't specified
6116 InitBoxPropsAsPhysical(kBorderColorSources);
6117 return ParseBoxProperties(mTempData.mMargin.mBorderColor,
6118 kBorderColorIDs);
6121 PRBool
6122 CSSParserImpl::ParseBorderImage()
6124 if (ParseVariant(mTempData.mMargin.mBorderImage,
6125 VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
6126 mTempData.SetPropertyBit(eCSSProperty_border_image);
6127 return PR_TRUE;
6130 // <uri> [<number> | <percentage>]{1,4} [ / <border-width>{1,4} ]? [stretch | repeat | round]{0,2}
6131 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(11);
6132 if (!arr) {
6133 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6134 return PR_FALSE;
6137 nsCSSValue& url = arr->Item(0);
6138 nsCSSValue& splitTop = arr->Item(1);
6139 nsCSSValue& splitRight = arr->Item(2);
6140 nsCSSValue& splitBottom = arr->Item(3);
6141 nsCSSValue& splitLeft = arr->Item(4);
6142 nsCSSValue& borderWidthTop = arr->Item(5);
6143 nsCSSValue& borderWidthRight = arr->Item(6);
6144 nsCSSValue& borderWidthBottom = arr->Item(7);
6145 nsCSSValue& borderWidthLeft = arr->Item(8);
6146 nsCSSValue& horizontalKeyword = arr->Item(9);
6147 nsCSSValue& verticalKeyword = arr->Item(10);
6149 // <uri>
6150 if (!ParseVariant(url, VARIANT_URL, nsnull)) {
6151 return PR_FALSE;
6154 // [<number> | <percentage>]{1,4}
6155 if (!ParsePositiveVariant(splitTop,
6156 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
6157 return PR_FALSE;
6159 if (!ParsePositiveVariant(splitRight,
6160 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
6161 splitRight = splitTop;
6163 if (!ParsePositiveVariant(splitBottom,
6164 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
6165 splitBottom = splitTop;
6167 if (!ParsePositiveVariant(splitLeft,
6168 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
6169 splitLeft = splitRight;
6172 // [ / <border-width>{1,4} ]?
6173 if (ExpectSymbol('/', PR_TRUE)) {
6174 // if have '/', at least one value is required
6175 if (!ParsePositiveVariant(borderWidthTop, VARIANT_LENGTH, nsnull)) {
6176 return PR_FALSE;
6178 if (!ParsePositiveVariant(borderWidthRight, VARIANT_LENGTH, nsnull)) {
6179 borderWidthRight = borderWidthTop;
6181 if (!ParsePositiveVariant(borderWidthBottom, VARIANT_LENGTH, nsnull)) {
6182 borderWidthBottom = borderWidthTop;
6184 if (!ParsePositiveVariant(borderWidthLeft, VARIANT_LENGTH, nsnull)) {
6185 borderWidthLeft = borderWidthRight;
6189 // [stretch | repeat | round]{0,2}
6190 // missing keywords are handled in nsRuleNode::ComputeBorderData()
6191 if (ParseEnum(horizontalKeyword, nsCSSProps::kBorderImageKTable)) {
6192 ParseEnum(verticalKeyword, nsCSSProps::kBorderImageKTable);
6195 if (!ExpectEndProperty()) {
6196 return PR_FALSE;
6199 mTempData.mMargin.mBorderImage.SetArrayValue(arr, eCSSUnit_Array);
6200 mTempData.SetPropertyBit(eCSSProperty_border_image);
6202 return PR_TRUE;
6205 PRBool
6206 CSSParserImpl::ParseBorderSpacing()
6208 nsCSSValue xValue;
6209 if (ParsePositiveVariant(xValue, VARIANT_HL, nsnull)) {
6210 if (xValue.IsLengthUnit()) {
6211 // We have one length. Get the optional second length.
6212 nsCSSValue yValue;
6213 if (ParsePositiveVariant(yValue, VARIANT_LENGTH, nsnull)) {
6214 // We have two numbers
6215 if (ExpectEndProperty()) {
6216 mTempData.mTable.mBorderSpacing.mXValue = xValue;
6217 mTempData.mTable.mBorderSpacing.mYValue = yValue;
6218 mTempData.SetPropertyBit(eCSSProperty_border_spacing);
6219 return PR_TRUE;
6221 return PR_FALSE;
6225 // We have one length which is the horizontal spacing. Create a value for
6226 // the vertical spacing which is equal
6227 if (ExpectEndProperty()) {
6228 mTempData.mTable.mBorderSpacing.SetBothValuesTo(xValue);
6229 mTempData.SetPropertyBit(eCSSProperty_border_spacing);
6230 return PR_TRUE;
6233 return PR_FALSE;
6236 PRBool
6237 CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[],
6238 PRBool aSetAllSides)
6240 const PRInt32 numProps = 3;
6241 nsCSSValue values[numProps];
6243 PRInt32 found = ParseChoice(values, aPropIDs, numProps);
6244 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
6245 return PR_FALSE;
6248 if ((found & 1) == 0) { // Provide default border-width
6249 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6251 if ((found & 2) == 0) { // Provide default border-style
6252 values[1].SetNoneValue();
6254 if ((found & 4) == 0) { // text color will be used
6255 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6258 if (aSetAllSides) {
6259 static const nsCSSProperty kBorderSources[] = {
6260 eCSSProperty_border_left_color_ltr_source,
6261 eCSSProperty_border_left_color_rtl_source,
6262 eCSSProperty_border_right_color_ltr_source,
6263 eCSSProperty_border_right_color_rtl_source,
6264 eCSSProperty_border_left_style_ltr_source,
6265 eCSSProperty_border_left_style_rtl_source,
6266 eCSSProperty_border_right_style_ltr_source,
6267 eCSSProperty_border_right_style_rtl_source,
6268 eCSSProperty_border_left_width_ltr_source,
6269 eCSSProperty_border_left_width_rtl_source,
6270 eCSSProperty_border_right_width_ltr_source,
6271 eCSSProperty_border_right_width_rtl_source,
6272 eCSSProperty_UNKNOWN
6275 InitBoxPropsAsPhysical(kBorderSources);
6277 // Parsing "border" shorthand; set all four sides to the same thing
6278 for (PRInt32 index = 0; index < 4; index++) {
6279 NS_ASSERTION(numProps == 3, "This code needs updating");
6280 AppendValue(kBorderWidthIDs[index], values[0]);
6281 AppendValue(kBorderStyleIDs[index], values[1]);
6282 AppendValue(kBorderColorIDs[index], values[2]);
6285 else {
6286 // Just set our one side
6287 for (PRInt32 index = 0; index < numProps; index++) {
6288 AppendValue(aPropIDs[index], values[index]);
6291 return PR_TRUE;
6294 PRBool
6295 CSSParserImpl::ParseDirectionalBorderSide(const nsCSSProperty aPropIDs[],
6296 PRInt32 aSourceType)
6298 const PRInt32 numProps = 3;
6299 nsCSSValue values[numProps];
6301 PRInt32 found = ParseChoice(values, aPropIDs, numProps);
6302 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
6303 return PR_FALSE;
6306 if ((found & 1) == 0) { // Provide default border-width
6307 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6309 if ((found & 2) == 0) { // Provide default border-style
6310 values[1].SetNoneValue();
6312 if ((found & 4) == 0) { // text color will be used
6313 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6315 for (PRInt32 index = 0; index < numProps; index++) {
6316 const nsCSSProperty* subprops =
6317 nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]);
6318 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
6319 "not box property with physical vs. logical cascading");
6320 AppendValue(subprops[0], values[index]);
6321 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
6322 AppendValue(subprops[1], typeVal);
6323 AppendValue(subprops[2], typeVal);
6325 return PR_TRUE;
6328 PRBool
6329 CSSParserImpl::ParseBorderStyle()
6331 static const nsCSSProperty kBorderStyleSources[] = {
6332 eCSSProperty_border_left_style_ltr_source,
6333 eCSSProperty_border_left_style_rtl_source,
6334 eCSSProperty_border_right_style_ltr_source,
6335 eCSSProperty_border_right_style_rtl_source,
6336 eCSSProperty_UNKNOWN
6339 // do this now, in case 4 values weren't specified
6340 InitBoxPropsAsPhysical(kBorderStyleSources);
6341 return ParseBoxProperties(mTempData.mMargin.mBorderStyle,
6342 kBorderStyleIDs);
6345 PRBool
6346 CSSParserImpl::ParseBorderWidth()
6348 static const nsCSSProperty kBorderWidthSources[] = {
6349 eCSSProperty_border_left_width_ltr_source,
6350 eCSSProperty_border_left_width_rtl_source,
6351 eCSSProperty_border_right_width_ltr_source,
6352 eCSSProperty_border_right_width_rtl_source,
6353 eCSSProperty_UNKNOWN
6356 // do this now, in case 4 values weren't specified
6357 InitBoxPropsAsPhysical(kBorderWidthSources);
6358 return ParseBoxProperties(mTempData.mMargin.mBorderWidth,
6359 kBorderWidthIDs);
6362 PRBool
6363 CSSParserImpl::ParseBorderColors(nsCSSValueList** aResult,
6364 nsCSSProperty aProperty)
6366 nsCSSValue value;
6367 if (ParseVariant(value, VARIANT_HCK|VARIANT_NONE, nsCSSProps::kBorderColorKTable)) {
6368 nsCSSValueList* listHead = new nsCSSValueList();
6369 nsCSSValueList* list = listHead;
6370 if (!list) {
6371 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6372 return PR_FALSE;
6374 list->mValue = value;
6376 while (list) {
6377 if (ExpectEndProperty()) {
6378 mTempData.SetPropertyBit(aProperty);
6379 *aResult = listHead;
6380 return PR_TRUE;
6382 // FIXME Bug 389404: We should not accept inherit, -moz-initial,
6383 // or none as anything other than the first value.
6384 if (ParseVariant(value, VARIANT_HCK|VARIANT_NONE, nsCSSProps::kBorderColorKTable)) {
6385 list->mNext = new nsCSSValueList();
6386 list = list->mNext;
6387 if (list)
6388 list->mValue = value;
6389 else
6390 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6392 else
6393 break;
6395 delete listHead;
6397 return PR_FALSE;
6400 PRBool
6401 CSSParserImpl::ParseRect(nsCSSRect& aRect, nsCSSProperty aPropID)
6403 nsCSSRect rect;
6404 PRBool result;
6405 if ((result = DoParseRect(rect)) &&
6406 rect != aRect) {
6407 aRect = rect;
6408 mTempData.SetPropertyBit(aPropID);
6410 return result;
6413 PRBool
6414 CSSParserImpl::DoParseRect(nsCSSRect& aRect)
6416 if (! GetToken(PR_TRUE)) {
6417 return PR_FALSE;
6419 if (eCSSToken_Ident == mToken.mType) {
6420 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
6421 switch (keyword) {
6422 case eCSSKeyword_auto:
6423 if (ExpectEndProperty()) {
6424 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Auto));
6425 return PR_TRUE;
6427 break;
6428 case eCSSKeyword_inherit:
6429 if (ExpectEndProperty()) {
6430 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Inherit));
6431 return PR_TRUE;
6433 break;
6434 case eCSSKeyword__moz_initial:
6435 if (ExpectEndProperty()) {
6436 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Initial));
6437 return PR_TRUE;
6439 break;
6440 default:
6441 UngetToken();
6442 break;
6444 } else if ((eCSSToken_Function == mToken.mType) &&
6445 mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
6446 if (!ExpectSymbol('(', PR_TRUE)) {
6447 return PR_FALSE;
6449 NS_FOR_CSS_SIDES(side) {
6450 if (! ParseVariant(aRect.*(nsCSSRect::sides[side]),
6451 VARIANT_AL, nsnull)) {
6452 return PR_FALSE;
6454 if (3 != side) {
6455 // skip optional commas between elements
6456 ExpectSymbol(',', PR_TRUE);
6459 if (!ExpectSymbol(')', PR_TRUE)) {
6460 return PR_FALSE;
6462 if (ExpectEndProperty()) {
6463 return PR_TRUE;
6465 } else {
6466 UngetToken();
6468 return PR_FALSE;
6471 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
6472 VARIANT_KEYWORD)
6473 PRBool
6474 CSSParserImpl::ParseContent()
6476 // XXX Rewrite to make it look more like ParseCursor or ParseCounterData?
6477 nsCSSValue value;
6478 if (ParseVariant(value,
6479 VARIANT_CONTENT | VARIANT_INHERIT | VARIANT_NORMAL |
6480 VARIANT_NONE,
6481 nsCSSProps::kContentKTable)) {
6482 nsCSSValueList* listHead = new nsCSSValueList();
6483 nsCSSValueList* list = listHead;
6484 if (nsnull == list) {
6485 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6486 return PR_FALSE;
6488 list->mValue = value;
6490 while (nsnull != list) {
6491 if (ExpectEndProperty()) {
6492 mTempData.SetPropertyBit(eCSSProperty_content);
6493 mTempData.mContent.mContent = listHead;
6494 return PR_TRUE;
6496 if (eCSSUnit_Inherit == value.GetUnit() ||
6497 eCSSUnit_Initial == value.GetUnit() ||
6498 eCSSUnit_Normal == value.GetUnit() ||
6499 eCSSUnit_None == value.GetUnit() ||
6500 (eCSSUnit_Enumerated == value.GetUnit() &&
6501 NS_STYLE_CONTENT_ALT_CONTENT == value.GetIntValue())) {
6502 // This only matters the first time through the loop.
6503 delete listHead;
6504 return PR_FALSE;
6506 if (ParseVariant(value, VARIANT_CONTENT, nsCSSProps::kContentKTable) &&
6507 // Make sure we didn't end up with NS_STYLE_CONTENT_ALT_CONTENT here
6508 (value.GetUnit() != eCSSUnit_Enumerated ||
6509 value.GetIntValue() != NS_STYLE_CONTENT_ALT_CONTENT)) {
6510 list->mNext = new nsCSSValueList();
6511 list = list->mNext;
6512 if (nsnull != list) {
6513 list->mValue = value;
6515 else {
6516 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6519 else {
6520 break;
6523 delete listHead;
6525 return PR_FALSE;
6528 struct SingleCounterPropValue {
6529 char str[13];
6530 nsCSSUnit unit;
6533 PRBool
6534 CSSParserImpl::ParseCounterData(nsCSSValuePairList** aResult,
6535 nsCSSProperty aPropID)
6537 nsSubstring* ident = NextIdent();
6538 if (nsnull == ident) {
6539 return PR_FALSE;
6541 static const SingleCounterPropValue singleValues[] = {
6542 { "none", eCSSUnit_None },
6543 { "inherit", eCSSUnit_Inherit },
6544 { "-moz-initial", eCSSUnit_Initial }
6546 for (const SingleCounterPropValue *sv = singleValues,
6547 *sv_end = singleValues + NS_ARRAY_LENGTH(singleValues);
6548 sv != sv_end; ++sv) {
6549 if (ident->LowerCaseEqualsASCII(sv->str)) {
6550 if (CheckEndProperty()) {
6551 nsCSSValuePairList* dataHead = new nsCSSValuePairList();
6552 if (!dataHead) {
6553 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6554 return PR_FALSE;
6556 dataHead->mXValue = nsCSSValue(sv->unit);
6557 *aResult = dataHead;
6558 mTempData.SetPropertyBit(aPropID);
6559 return PR_TRUE;
6561 return PR_FALSE;
6564 UngetToken(); // undo NextIdent
6566 nsCSSValuePairList* dataHead = nsnull;
6567 nsCSSValuePairList **next = &dataHead;
6568 for (;;) {
6569 if (!GetToken(PR_TRUE) || mToken.mType != eCSSToken_Ident) {
6570 break;
6572 nsCSSValuePairList *data = *next = new nsCSSValuePairList();
6573 if (!data) {
6574 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6575 break;
6577 next = &data->mNext;
6578 data->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
6579 if (GetToken(PR_TRUE)) {
6580 if (eCSSToken_Number == mToken.mType && mToken.mIntegerValid) {
6581 data->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
6582 } else {
6583 UngetToken();
6586 if (ExpectEndProperty()) {
6587 mTempData.SetPropertyBit(aPropID);
6588 *aResult = dataHead;
6589 return PR_TRUE;
6592 delete dataHead;
6593 return PR_FALSE;
6596 PRBool
6597 CSSParserImpl::ParseCue()
6599 nsCSSValue before;
6600 if (ParseSingleValueProperty(before, eCSSProperty_cue_before)) {
6601 if (eCSSUnit_Inherit != before.GetUnit() &&
6602 eCSSUnit_Initial != before.GetUnit()) {
6603 nsCSSValue after;
6604 if (ParseSingleValueProperty(after, eCSSProperty_cue_after)) {
6605 if (ExpectEndProperty()) {
6606 AppendValue(eCSSProperty_cue_before, before);
6607 AppendValue(eCSSProperty_cue_after, after);
6608 return PR_TRUE;
6610 return PR_FALSE;
6613 if (ExpectEndProperty()) {
6614 AppendValue(eCSSProperty_cue_before, before);
6615 AppendValue(eCSSProperty_cue_after, before);
6616 return PR_TRUE;
6619 return PR_FALSE;
6622 PRBool
6623 CSSParserImpl::ParseCursor()
6625 nsCSSValueList *list = nsnull;
6626 for (nsCSSValueList **curp = &list, *cur; ; curp = &cur->mNext) {
6627 cur = *curp = new nsCSSValueList();
6628 if (!cur) {
6629 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6630 break;
6632 if (!ParseVariant(cur->mValue,
6633 (cur == list) ? VARIANT_AHUK : VARIANT_AUK,
6634 nsCSSProps::kCursorKTable)) {
6635 break;
6637 if (cur->mValue.GetUnit() != eCSSUnit_URL) {
6638 if (!ExpectEndProperty()) {
6639 break;
6641 // Only success case here, since having the failure case at the
6642 // end allows more sharing of code.
6643 mTempData.SetPropertyBit(eCSSProperty_cursor);
6644 mTempData.mUserInterface.mCursor = list;
6645 return PR_TRUE;
6647 // We have a URL, so make a value array with three values.
6648 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
6649 if (!val) {
6650 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6651 break;
6653 val->Item(0) = cur->mValue;
6654 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
6656 // Parse optional x and y position of cursor hotspot (css3-ui).
6657 if (ParseVariant(val->Item(1), VARIANT_NUMBER, nsnull)) {
6658 // If we have one number, we must have two.
6659 if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nsnull)) {
6660 break;
6664 if (!ExpectSymbol(',', PR_TRUE)) {
6665 break;
6668 // Have failure case at the end so we can |break| to get to it.
6669 delete list;
6670 return PR_FALSE;
6674 PRBool
6675 CSSParserImpl::ParseFont()
6677 static const nsCSSProperty fontIDs[] = {
6678 eCSSProperty_font_style,
6679 eCSSProperty_font_variant,
6680 eCSSProperty_font_weight
6683 nsCSSValue family;
6684 if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
6685 if (ExpectEndProperty()) {
6686 if (eCSSUnit_Inherit == family.GetUnit() ||
6687 eCSSUnit_Initial == family.GetUnit()) {
6688 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
6689 AppendValue(eCSSProperty_font_family, family);
6690 AppendValue(eCSSProperty_font_style, family);
6691 AppendValue(eCSSProperty_font_variant, family);
6692 AppendValue(eCSSProperty_font_weight, family);
6693 AppendValue(eCSSProperty_font_size, family);
6694 AppendValue(eCSSProperty_line_height, family);
6695 AppendValue(eCSSProperty_font_stretch, family);
6696 AppendValue(eCSSProperty_font_size_adjust, family);
6698 else {
6699 AppendValue(eCSSProperty__x_system_font, family);
6700 nsCSSValue systemFont(eCSSUnit_System_Font);
6701 AppendValue(eCSSProperty_font_family, systemFont);
6702 AppendValue(eCSSProperty_font_style, systemFont);
6703 AppendValue(eCSSProperty_font_variant, systemFont);
6704 AppendValue(eCSSProperty_font_weight, systemFont);
6705 AppendValue(eCSSProperty_font_size, systemFont);
6706 AppendValue(eCSSProperty_line_height, systemFont);
6707 AppendValue(eCSSProperty_font_stretch, systemFont);
6708 AppendValue(eCSSProperty_font_size_adjust, systemFont);
6710 return PR_TRUE;
6712 return PR_FALSE;
6715 // Get optional font-style, font-variant and font-weight (in any order)
6716 const PRInt32 numProps = 3;
6717 nsCSSValue values[numProps];
6718 PRInt32 found = ParseChoice(values, fontIDs, numProps);
6719 if ((found < 0) || (eCSSUnit_Inherit == values[0].GetUnit()) ||
6720 (eCSSUnit_Initial == values[0].GetUnit())) { // illegal data
6721 return PR_FALSE;
6723 if ((found & 1) == 0) {
6724 // Provide default font-style
6725 values[0].SetNormalValue();
6727 if ((found & 2) == 0) {
6728 // Provide default font-variant
6729 values[1].SetNormalValue();
6731 if ((found & 4) == 0) {
6732 // Provide default font-weight
6733 values[2].SetNormalValue();
6736 // Get mandatory font-size
6737 nsCSSValue size;
6738 if (! ParseVariant(size, VARIANT_KEYWORD | VARIANT_LP, nsCSSProps::kFontSizeKTable)) {
6739 return PR_FALSE;
6742 // Get optional "/" line-height
6743 nsCSSValue lineHeight;
6744 if (ExpectSymbol('/', PR_TRUE)) {
6745 if (! ParsePositiveVariant(lineHeight,
6746 VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
6747 nsnull)) {
6748 return PR_FALSE;
6751 else {
6752 lineHeight.SetNormalValue();
6755 // Get final mandatory font-family
6756 nsAutoParseCompoundProperty compound(this);
6757 if (ParseFamily(family)) {
6758 if ((eCSSUnit_Inherit != family.GetUnit()) && (eCSSUnit_Initial != family.GetUnit()) &&
6759 ExpectEndProperty()) {
6760 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
6761 AppendValue(eCSSProperty_font_family, family);
6762 AppendValue(eCSSProperty_font_style, values[0]);
6763 AppendValue(eCSSProperty_font_variant, values[1]);
6764 AppendValue(eCSSProperty_font_weight, values[2]);
6765 AppendValue(eCSSProperty_font_size, size);
6766 AppendValue(eCSSProperty_line_height, lineHeight);
6767 AppendValue(eCSSProperty_font_stretch, nsCSSValue(eCSSUnit_Normal));
6768 AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
6769 return PR_TRUE;
6772 return PR_FALSE;
6775 PRBool
6776 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
6778 if (ParseVariant(aValue, VARIANT_HMKI | VARIANT_SYSFONT, nsCSSProps::kFontWeightKTable)) {
6779 if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
6780 PRInt32 intValue = aValue.GetIntValue();
6781 if ((100 <= intValue) &&
6782 (intValue <= 900) &&
6783 (0 == (intValue % 100))) {
6784 return PR_TRUE;
6785 } else {
6786 UngetToken();
6787 return PR_FALSE;
6790 return PR_TRUE;
6792 return PR_FALSE;
6795 PRBool
6796 CSSParserImpl::ParseOneFamily(nsAString& aFamily)
6798 if (!GetToken(PR_TRUE))
6799 return PR_FALSE;
6801 nsCSSToken* tk = &mToken;
6803 if (eCSSToken_Ident == tk->mType) {
6804 aFamily.Append(tk->mIdent);
6805 for (;;) {
6806 if (!GetToken(PR_FALSE))
6807 break;
6809 if (eCSSToken_Ident == tk->mType) {
6810 aFamily.Append(tk->mIdent);
6811 } else if (eCSSToken_WhiteSpace == tk->mType) {
6812 // Lookahead one token and drop whitespace if we are ending the
6813 // font name.
6814 if (!GetToken(PR_TRUE))
6815 break;
6817 UngetToken();
6818 if (eCSSToken_Ident == tk->mType)
6819 aFamily.Append(PRUnichar(' '));
6820 else
6821 break;
6822 } else {
6823 UngetToken();
6824 break;
6827 return PR_TRUE;
6829 } else if (eCSSToken_String == tk->mType) {
6830 aFamily.Append(tk->mSymbol); // replace the quotes
6831 aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
6832 aFamily.Append(tk->mSymbol);
6833 return PR_TRUE;
6835 } else {
6836 UngetToken();
6837 return PR_FALSE;
6841 ///////////////////////////////////////////////////////
6842 // -moz-transform Parsing Implementation
6844 /* Reads a function list of arguments. Do not call this function
6845 * directly; it's mean to be caled from ParseFunction.
6847 PRBool
6848 CSSParserImpl::ParseFunctionInternals(const PRInt32 aVariantMask[],
6849 PRUint16 aMinElems,
6850 PRUint16 aMaxElems,
6851 nsTArray<nsCSSValue> &aOutput)
6853 for (PRUint16 index = 0; index < aMaxElems; ++index) {
6854 nsCSSValue newValue;
6855 if (!ParseVariant(newValue, aVariantMask[index], nsnull))
6856 return PR_FALSE;
6858 if (!aOutput.AppendElement(newValue)) {
6859 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6860 return PR_FALSE;
6863 // See whether to continue or whether to look for end of function.
6864 if (!ExpectSymbol(',', PR_TRUE)) {
6865 // We need to read the closing parenthesis, and also must take care
6866 // that we haven't read too few symbols.
6867 return ExpectSymbol(')', PR_TRUE) && (index + 1) >= aMinElems;
6871 // If we're here, we finished looping without hitting the end, so we read too
6872 // many elements.
6873 return PR_FALSE;
6876 /* Parses a function [ input of the form (a [, b]*) ] and stores it
6877 * as an nsCSSValue that holds a function of the form
6878 * function-name arg1 arg2 ... argN
6880 * On error, the return value is PR_FALSE.
6882 * @param aFunction The name of the function that we're reading.
6883 * @param aAllowedTypes An array of values corresponding to the legal
6884 * types for each element in the function. The zeroth element in the
6885 * array corresponds to the first function parameter, etc. The length
6886 * of this array _must_ be greater than or equal to aMaxElems or the
6887 * behavior is undefined.
6888 * @param aMinElems Minimum number of elements to read. Reading fewer than
6889 * this many elements will result in the function failing.
6890 * @param aMaxElems Maximum number of elements to read. Reading more than
6891 * this many elements will result in the function failing.
6892 * @param aValue (out) The value that was parsed.
6894 PRBool
6895 CSSParserImpl::ParseFunction(const nsString &aFunction,
6896 const PRInt32 aAllowedTypes[],
6897 PRUint16 aMinElems, PRUint16 aMaxElems,
6898 nsCSSValue &aValue)
6900 typedef nsTArray<nsCSSValue>::size_type arrlen_t;
6902 /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1
6903 * elements stored in the the nsCSSValue::Array.
6905 static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE;
6907 /* Make a copy of the function name, since the reference is _probably_ to
6908 * mToken.mIdent, which is going to get overwritten during the course of this
6909 * function.
6911 nsString functionName(aFunction);
6913 /* First things first... read the parenthesis. If it fails, abort. */
6914 if (!ExpectSymbol('(', PR_TRUE))
6915 return PR_FALSE;
6917 /* Read in a list of values as an nsTArray, failing if we can't or if
6918 * it's out of bounds.
6920 nsTArray<nsCSSValue> foundValues;
6921 if (!ParseFunctionInternals(aAllowedTypes, aMinElems, aMaxElems,
6922 foundValues))
6923 return PR_FALSE;
6925 /* Now, convert this nsTArray into an nsCSSValue::Array object.
6926 * We'll need N + 1 spots, one for the function name and the rest for the
6927 * arguments. In case the user has given us more than 2^16 - 2 arguments,
6928 * we'll truncate them at 2^16 - 2 arguments.
6930 PRUint16 numElements = (foundValues.Length() <= MAX_ALLOWED_ELEMS ?
6931 foundValues.Length() + 1 : MAX_ALLOWED_ELEMS);
6932 nsRefPtr<nsCSSValue::Array> convertedArray =
6933 nsCSSValue::Array::Create(numElements);
6934 if (!convertedArray) {
6935 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
6936 return PR_FALSE;
6939 /* Copy things over. */
6940 convertedArray->Item(0).SetStringValue(functionName, eCSSUnit_String);
6941 for (PRUint16 index = 0; index + 1 < numElements; ++index)
6942 convertedArray->Item(index + 1) = foundValues[static_cast<arrlen_t>(index)];
6944 /* Fill in the outparam value with the array. */
6945 aValue.SetArrayValue(convertedArray, eCSSUnit_Function);
6947 /* Return it! */
6948 return PR_TRUE;
6952 * Given a token, determines the minimum and maximum number of function
6953 * parameters to read, along with the mask that should be used to read
6954 * those function parameters. If the token isn't a transform function,
6955 * returns an error.
6957 * @param aToken The token identifying the function.
6958 * @param aMinElems [out] The minimum number of elements to read.
6959 * @param aMaxElems [out] The maximum number of elements to read
6960 * @param aVariantMask [out] The variant mask to use during parsing
6961 * @return Whether the information was loaded successfully.
6963 static PRBool GetFunctionParseInformation(nsCSSKeyword aToken,
6964 PRUint16 &aMinElems,
6965 PRUint16 &aMaxElems,
6966 const PRInt32 *& aVariantMask)
6968 /* These types represent the common variant masks that will be used to
6969 * parse out the individual functions. The order in the enumeration
6970 * must match the order in which the masks are declared.
6972 enum { eLengthPercent,
6973 eTwoLengthPercents,
6974 eAngle,
6975 eTwoAngles,
6976 eNumber,
6977 eTwoNumbers,
6978 eMatrix,
6979 eNumVariantMasks };
6980 static const PRInt32 kMaxElemsPerFunction = 6;
6981 static const PRInt32 kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = {
6982 {VARIANT_LENGTH | VARIANT_PERCENT},
6983 {VARIANT_LENGTH | VARIANT_PERCENT, VARIANT_LENGTH | VARIANT_PERCENT},
6984 {VARIANT_ANGLE},
6985 {VARIANT_ANGLE, VARIANT_ANGLE},
6986 {VARIANT_NUMBER},
6987 {VARIANT_NUMBER, VARIANT_NUMBER},
6988 {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER,
6989 VARIANT_LENGTH | VARIANT_PERCENT, VARIANT_LENGTH | VARIANT_PERCENT}};
6991 #ifdef DEBUG
6992 static const PRUint8 kVariantMaskLengths[eNumVariantMasks] =
6993 {1, 2, 1, 2, 1, 2, 6};
6994 #endif
6996 PRInt32 variantIndex = eNumVariantMasks;
6998 switch (aToken) {
6999 case eCSSKeyword_translatex:
7000 /* Exactly one length or percent. */
7001 variantIndex = eLengthPercent;
7002 aMinElems = 1U;
7003 aMaxElems = 1U;
7004 break;
7005 case eCSSKeyword_translatey:
7006 /* Exactly one length or percent. */
7007 variantIndex = eLengthPercent;
7008 aMinElems = 1U;
7009 aMaxElems = 1U;
7010 break;
7011 case eCSSKeyword_scalex:
7012 /* Exactly one scale factor. */
7013 variantIndex = eNumber;
7014 aMinElems = 1U;
7015 aMaxElems = 1U;
7016 break;
7017 case eCSSKeyword_scaley:
7018 /* Exactly one scale factor. */
7019 variantIndex = eNumber;
7020 aMinElems = 1U;
7021 aMaxElems = 1U;
7022 break;
7023 case eCSSKeyword_rotate:
7024 /* Exactly one angle. */
7025 variantIndex = eAngle;
7026 aMinElems = 1U;
7027 aMaxElems = 1U;
7028 break;
7029 case eCSSKeyword_translate:
7030 /* One or two lengths or percents. */
7031 variantIndex = eTwoLengthPercents;
7032 aMinElems = 1U;
7033 aMaxElems = 2U;
7034 break;
7035 case eCSSKeyword_skew:
7036 /* Exactly one or two angles. */
7037 variantIndex = eTwoAngles;
7038 aMinElems = 1U;
7039 aMaxElems = 2U;
7040 break;
7041 case eCSSKeyword_scale:
7042 /* One or two scale factors. */
7043 variantIndex = eTwoNumbers;
7044 aMinElems = 1U;
7045 aMaxElems = 2U;
7046 break;
7047 case eCSSKeyword_skewx:
7048 /* Exactly one angle. */
7049 variantIndex = eAngle;
7050 aMinElems = 1U;
7051 aMaxElems = 1U;
7052 break;
7053 case eCSSKeyword_skewy:
7054 /* Exactly one angle. */
7055 variantIndex = eAngle;
7056 aMinElems = 1U;
7057 aMaxElems = 1U;
7058 break;
7059 case eCSSKeyword_matrix:
7060 /* Six values, which can be numbers, lengths, or percents. */
7061 variantIndex = eMatrix;
7062 aMinElems = 6U;
7063 aMaxElems = 6U;
7064 break;
7065 default:
7066 /* Oh dear, we didn't match. Report an error. */
7067 return PR_FALSE;
7070 NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!");
7071 NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!");
7072 NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!");
7073 NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!");
7074 NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!");
7075 #ifdef DEBUG
7076 NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex],
7077 "Invalid aMaxElems for this variant mask.");
7078 #endif
7080 // Convert the index into a mask.
7081 aVariantMask = kVariantMasks[variantIndex];
7083 return PR_TRUE;
7087 /* Reads a single transform function from the tokenizer stream, reporting an
7088 * error if something goes wrong.
7090 PRBool CSSParserImpl::ReadSingleTransform(nsCSSValueList **& aTail)
7092 typedef nsTArray<nsCSSValue>::size_type arrlen_t;
7094 if (!GetToken(PR_TRUE))
7095 return PR_FALSE;
7097 /* Check to make sure that we've read a function. */
7098 if (mToken.mType != eCSSToken_Function) {
7099 UngetToken();
7100 return PR_FALSE;
7103 /* Load up the variant mask information for ParseFunction. If we can't,
7104 * abort.
7106 const PRInt32* variantMask;
7107 PRUint16 minElems, maxElems;
7108 if (!GetFunctionParseInformation(nsCSSKeywords::LookupKeyword(mToken.mIdent),
7109 minElems, maxElems, variantMask))
7110 return PR_FALSE;
7112 /* Create a cell to populate, fail if we're out of memory. */
7113 nsAutoPtr<nsCSSValue> newCell(new nsCSSValue);
7114 if (!newCell) {
7115 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7116 return PR_FALSE;
7119 /* Try reading things in, failing if we can't */
7120 if (!ParseFunction(mToken.mIdent, variantMask, minElems, maxElems, *newCell))
7121 return PR_FALSE;
7123 /* Wrap up our result in an nsCSSValueList cell. */
7124 nsAutoPtr<nsCSSValueList> toAppend(new nsCSSValueList);
7125 if (!toAppend)
7126 return PR_FALSE;
7128 toAppend->mValue = *newCell;
7130 /* Chain the element to the end of the transform list, then update the
7131 * list.
7133 *aTail = toAppend.forget();
7134 aTail = &(*aTail)->mNext;
7136 /* It worked! Return true. */
7137 return PR_TRUE;
7140 /* Parses a -moz-transform property list by continuously reading in properties
7141 * and constructing a matrix from it.
7143 PRBool CSSParserImpl::ParseMozTransform()
7145 mTempData.mDisplay.mTransform = nsnull;
7147 /* First, check to see if this is some sort of keyword - none, inherit,
7148 * or initial.
7150 nsCSSValue keywordValue;
7151 if (ParseVariant(keywordValue, VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
7152 /* Looks like it is. Make a new value list and fill it in, failing if
7153 * we can't.
7155 mTempData.mDisplay.mTransform = new nsCSSValueList;
7156 if (!mTempData.mDisplay.mTransform) {
7157 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7158 return PR_FALSE;
7161 /* Inform the parser that everything worked. */
7162 mTempData.mDisplay.mTransform->mValue = keywordValue;
7163 mTempData.SetPropertyBit(eCSSProperty__moz_transform);
7164 return PR_TRUE;
7167 /* We will read a nonempty list of transforms. Thus we'll read in a
7168 * transform, then continuously read transforms until we're no longer
7169 * able to do so.
7171 nsCSSValueList *transformList = nsnull;
7172 nsCSSValueList **tail = &transformList;
7173 do {
7174 /* Try reading a transform. If we fail to do so, abort. */
7175 if (!ReadSingleTransform(tail)) {
7176 delete transformList;
7177 return PR_FALSE;
7180 while (!CheckEndProperty());
7182 /* Confirm that this is the end of the property and set error code
7183 * appropriately otherwise.
7185 if (!ExpectEndProperty()) {
7186 delete transformList;
7187 return PR_FALSE;
7190 /* Validate our data. */
7191 NS_ASSERTION(transformList, "Didn't read any transforms!");
7193 mTempData.SetPropertyBit(eCSSProperty__moz_transform);
7194 mTempData.mDisplay.mTransform = transformList;
7196 return PR_TRUE;
7199 PRBool CSSParserImpl::ParseMozTransformOrigin()
7201 /* Read in a box position, fail if we can't. */
7202 if (!ParseBoxPosition(mTempData.mDisplay.mTransformOrigin))
7203 return PR_FALSE;
7205 /* Set the property bit and return. */
7206 mTempData.SetPropertyBit(eCSSProperty__moz_transform_origin);
7207 return PR_TRUE;
7210 PRBool
7211 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
7213 if (!GetToken(PR_TRUE))
7214 return PR_FALSE;
7216 if (eCSSToken_Ident == mToken.mType) {
7217 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
7218 if (keyword == eCSSKeyword_inherit) {
7219 aValue.SetInheritValue();
7220 return PR_TRUE;
7222 if (keyword == eCSSKeyword__moz_initial) {
7223 aValue.SetInitialValue();
7224 return PR_TRUE;
7226 if (keyword == eCSSKeyword__moz_use_system_font &&
7227 !IsParsingCompoundProperty()) {
7228 aValue.SetSystemFontValue();
7229 return PR_TRUE;
7233 UngetToken();
7235 nsAutoString family;
7236 for (;;) {
7237 if (!ParseOneFamily(family))
7238 return PR_FALSE;
7240 if (!ExpectSymbol(',', PR_TRUE))
7241 break;
7243 family.Append(PRUnichar(','));
7246 if (family.IsEmpty()) {
7247 return PR_FALSE;
7249 aValue.SetStringValue(family, eCSSUnit_String);
7250 return PR_TRUE;
7253 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
7254 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
7255 // local-src: 'local(' ( string | ident ) ')'
7257 PRBool
7258 CSSParserImpl::ParseFontSrc(nsCSSValue& aValue)
7260 // could we maybe turn nsCSSValue::Array into nsTArray<nsCSSValue>?
7261 nsTArray<nsCSSValue> values;
7262 nsCSSValue cur;
7263 for (;;) {
7264 if (!GetToken(PR_TRUE))
7265 break;
7267 if (mToken.mType == eCSSToken_Function &&
7268 mToken.mIdent.LowerCaseEqualsLiteral("url")) {
7269 if (!ParseURL(cur))
7270 return PR_FALSE;
7271 values.AppendElement(cur);
7272 if (!ParseFontSrcFormat(values))
7273 return PR_FALSE;
7275 } else if (mToken.mType == eCSSToken_Function &&
7276 mToken.mIdent.LowerCaseEqualsLiteral("local")) {
7277 // css3-fonts does not specify a formal grammar for local().
7278 // The text permits both unquoted identifiers and quoted
7279 // strings. We resolve this ambiguity in the spec by
7280 // assuming that the appropriate production is a single
7281 // <family-name>, possibly surrounded by whitespace.
7283 nsAutoString family;
7284 if (!ExpectSymbol('(', PR_FALSE))
7285 return PR_FALSE;
7286 if (!ParseOneFamily(family))
7287 return PR_FALSE;
7288 if (!ExpectSymbol(')', PR_TRUE))
7289 return PR_FALSE;
7291 // the style parameters to the nsFont constructor are ignored,
7292 // because it's only being used to call EnumerateFamilies
7293 nsFont font(family, 0, 0, 0, 0, 0);
7294 ExtractFirstFamilyData dat;
7296 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
7297 if (!dat.mGood)
7298 return PR_FALSE;
7300 cur.SetStringValue(dat.mFamilyName, eCSSUnit_Local_Font);
7301 values.AppendElement(cur);
7302 } else {
7303 return PR_FALSE;
7306 if (!ExpectSymbol(',', PR_TRUE))
7307 break;
7310 nsRefPtr<nsCSSValue::Array> srcVals
7311 = nsCSSValue::Array::Create(values.Length());
7312 if (!srcVals)
7313 return PR_FALSE;
7315 PRUint32 i;
7316 for (i = 0; i < values.Length(); i++)
7317 srcVals->Item(i) = values[i];
7318 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
7319 return PR_TRUE;
7322 PRBool
7323 CSSParserImpl::ParseFontSrcFormat(nsTArray<nsCSSValue> & values)
7325 if (!GetToken(PR_TRUE))
7326 return PR_TRUE; // EOF harmless here
7327 if (mToken.mType != eCSSToken_Function ||
7328 !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
7329 UngetToken();
7330 return PR_TRUE;
7332 if (!ExpectSymbol('(', PR_FALSE))
7333 return PR_FALSE;
7335 do {
7336 if (!GetToken(PR_TRUE))
7337 return PR_FALSE;
7339 if (mToken.mType != eCSSToken_String)
7340 return PR_FALSE;
7342 nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
7343 values.AppendElement(cur);
7344 } while (ExpectSymbol(',', PR_TRUE));
7346 return ExpectSymbol(')', PR_TRUE);
7349 // font-ranges: urange ( ',' urange )*
7350 PRBool
7351 CSSParserImpl::ParseFontRanges(nsCSSValue& aValue)
7353 // not currently implemented (bug 443976)
7354 return PR_FALSE;
7357 PRBool
7358 CSSParserImpl::ParseListStyle()
7360 const PRInt32 numProps = 3;
7361 static const nsCSSProperty listStyleIDs[] = {
7362 eCSSProperty_list_style_type,
7363 eCSSProperty_list_style_position,
7364 eCSSProperty_list_style_image
7367 nsCSSValue values[numProps];
7368 PRInt32 index;
7369 PRInt32 found = ParseChoice(values, listStyleIDs, numProps);
7370 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
7371 return PR_FALSE;
7374 // Provide default values
7375 if ((found & 1) == 0) {
7376 values[0].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
7378 if ((found & 2) == 0) {
7379 values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, eCSSUnit_Enumerated);
7381 if ((found & 4) == 0) {
7382 values[2].SetNoneValue();
7385 for (index = 0; index < numProps; index++) {
7386 AppendValue(listStyleIDs[index], values[index]);
7388 return PR_TRUE;
7391 PRBool
7392 CSSParserImpl::ParseMargin()
7394 static const nsCSSProperty kMarginSideIDs[] = {
7395 eCSSProperty_margin_top,
7396 eCSSProperty_margin_right_value,
7397 eCSSProperty_margin_bottom,
7398 eCSSProperty_margin_left_value
7400 static const nsCSSProperty kMarginSources[] = {
7401 eCSSProperty_margin_left_ltr_source,
7402 eCSSProperty_margin_left_rtl_source,
7403 eCSSProperty_margin_right_ltr_source,
7404 eCSSProperty_margin_right_rtl_source,
7405 eCSSProperty_UNKNOWN
7408 // do this now, in case 4 values weren't specified
7409 InitBoxPropsAsPhysical(kMarginSources);
7410 return ParseBoxProperties(mTempData.mMargin.mMargin,
7411 kMarginSideIDs);
7414 PRBool
7415 CSSParserImpl::ParseMarks(nsCSSValue& aValue)
7417 if (ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kPageMarksKTable)) {
7418 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
7419 if (PR_FALSE == CheckEndProperty()) {
7420 nsCSSValue second;
7421 if (ParseEnum(second, nsCSSProps::kPageMarksKTable)) {
7422 aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(), eCSSUnit_Enumerated);
7423 return PR_TRUE;
7425 return PR_FALSE;
7428 return PR_TRUE;
7430 return PR_FALSE;
7433 PRBool
7434 CSSParserImpl::ParseOutline()
7436 const PRInt32 numProps = 3;
7437 static const nsCSSProperty kOutlineIDs[] = {
7438 eCSSProperty_outline_color,
7439 eCSSProperty_outline_style,
7440 eCSSProperty_outline_width
7443 nsCSSValue values[numProps];
7444 PRInt32 found = ParseChoice(values, kOutlineIDs, numProps);
7445 if ((found < 1) || (PR_FALSE == ExpectEndProperty())) {
7446 return PR_FALSE;
7449 // Provide default values
7450 if ((found & 1) == 0) {
7451 #ifdef GFX_HAS_INVERT
7452 values[0].SetIntValue(NS_STYLE_COLOR_INVERT, eCSSUnit_Enumerated);
7453 #else
7454 values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
7455 #endif
7457 if ((found & 2) == 0) {
7458 values[1].SetNoneValue();
7460 if ((found & 4) == 0) {
7461 values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
7464 PRInt32 index;
7465 for (index = 0; index < numProps; index++) {
7466 AppendValue(kOutlineIDs[index], values[index]);
7468 return PR_TRUE;
7471 PRBool
7472 CSSParserImpl::ParseOverflow()
7474 nsCSSValue overflow;
7475 if (!ParseVariant(overflow, VARIANT_AHK,
7476 nsCSSProps::kOverflowKTable) ||
7477 !ExpectEndProperty())
7478 return PR_FALSE;
7480 nsCSSValue overflowX(overflow);
7481 nsCSSValue overflowY(overflow);
7482 if (eCSSUnit_Enumerated == overflow.GetUnit())
7483 switch(overflow.GetIntValue()) {
7484 case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
7485 overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
7486 overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
7487 break;
7488 case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
7489 overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
7490 overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
7491 break;
7493 AppendValue(eCSSProperty_overflow_x, overflowX);
7494 AppendValue(eCSSProperty_overflow_y, overflowY);
7495 return PR_TRUE;
7498 PRBool
7499 CSSParserImpl::ParsePadding()
7501 static const nsCSSProperty kPaddingSideIDs[] = {
7502 eCSSProperty_padding_top,
7503 eCSSProperty_padding_right_value,
7504 eCSSProperty_padding_bottom,
7505 eCSSProperty_padding_left_value
7507 static const nsCSSProperty kPaddingSources[] = {
7508 eCSSProperty_padding_left_ltr_source,
7509 eCSSProperty_padding_left_rtl_source,
7510 eCSSProperty_padding_right_ltr_source,
7511 eCSSProperty_padding_right_rtl_source,
7512 eCSSProperty_UNKNOWN
7515 // do this now, in case 4 values weren't specified
7516 InitBoxPropsAsPhysical(kPaddingSources);
7517 return ParseBoxProperties(mTempData.mMargin.mPadding,
7518 kPaddingSideIDs);
7521 PRBool
7522 CSSParserImpl::ParsePause()
7524 nsCSSValue before;
7525 if (ParseSingleValueProperty(before, eCSSProperty_pause_before)) {
7526 if (eCSSUnit_Inherit != before.GetUnit() && eCSSUnit_Initial != before.GetUnit()) {
7527 nsCSSValue after;
7528 if (ParseSingleValueProperty(after, eCSSProperty_pause_after)) {
7529 if (ExpectEndProperty()) {
7530 AppendValue(eCSSProperty_pause_before, before);
7531 AppendValue(eCSSProperty_pause_after, after);
7532 return PR_TRUE;
7534 return PR_FALSE;
7537 if (ExpectEndProperty()) {
7538 AppendValue(eCSSProperty_pause_before, before);
7539 AppendValue(eCSSProperty_pause_after, before);
7540 return PR_TRUE;
7543 return PR_FALSE;
7546 PRBool
7547 CSSParserImpl::ParseQuotes()
7549 nsCSSValue open;
7550 if (ParseVariant(open, VARIANT_HOS, nsnull)) {
7551 if (eCSSUnit_String == open.GetUnit()) {
7552 nsCSSValuePairList* quotesHead = new nsCSSValuePairList();
7553 nsCSSValuePairList* quotes = quotesHead;
7554 if (nsnull == quotes) {
7555 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7556 return PR_FALSE;
7558 quotes->mXValue = open;
7559 while (nsnull != quotes) {
7560 // get mandatory close
7561 if (ParseVariant(quotes->mYValue, VARIANT_STRING,
7562 nsnull)) {
7563 if (CheckEndProperty()) {
7564 mTempData.SetPropertyBit(eCSSProperty_quotes);
7565 mTempData.mContent.mQuotes = quotesHead;
7566 return PR_TRUE;
7568 // look for another open
7569 if (ParseVariant(open, VARIANT_STRING, nsnull)) {
7570 quotes->mNext = new nsCSSValuePairList();
7571 quotes = quotes->mNext;
7572 if (nsnull != quotes) {
7573 quotes->mXValue = open;
7574 continue;
7576 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7579 break;
7581 delete quotesHead;
7582 return PR_FALSE;
7584 if (ExpectEndProperty()) {
7585 nsCSSValuePairList* quotesHead = new nsCSSValuePairList();
7586 quotesHead->mXValue = open;
7587 mTempData.mContent.mQuotes = quotesHead;
7588 mTempData.SetPropertyBit(eCSSProperty_quotes);
7589 return PR_TRUE;
7592 return PR_FALSE;
7595 PRBool
7596 CSSParserImpl::ParseSize()
7598 nsCSSValue width;
7599 if (ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
7600 if (width.IsLengthUnit()) {
7601 nsCSSValue height;
7602 if (ParseVariant(height, VARIANT_LENGTH, nsnull)) {
7603 if (ExpectEndProperty()) {
7604 mTempData.mPage.mSize.mXValue = width;
7605 mTempData.mPage.mSize.mYValue = height;
7606 mTempData.SetPropertyBit(eCSSProperty_size);
7607 return PR_TRUE;
7609 return PR_FALSE;
7612 if (ExpectEndProperty()) {
7613 mTempData.mPage.mSize.SetBothValuesTo(width);
7614 mTempData.SetPropertyBit(eCSSProperty_size);
7615 return PR_TRUE;
7618 return PR_FALSE;
7621 PRBool
7622 CSSParserImpl::ParseTextDecoration(nsCSSValue& aValue)
7624 if (ParseVariant(aValue, VARIANT_HOK, nsCSSProps::kTextDecorationKTable)) {
7625 if (eCSSUnit_Enumerated == aValue.GetUnit()) { // look for more keywords
7626 PRInt32 intValue = aValue.GetIntValue();
7627 nsCSSValue keyword;
7628 PRInt32 index;
7629 for (index = 0; index < 3; index++) {
7630 if (ParseEnum(keyword, nsCSSProps::kTextDecorationKTable)) {
7631 intValue |= keyword.GetIntValue();
7633 else {
7634 break;
7637 aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
7639 return PR_TRUE;
7641 return PR_FALSE;
7644 nsCSSValueList*
7645 CSSParserImpl::ParseCSSShadowList(PRBool aUsesSpread)
7647 nsAutoParseCompoundProperty compound(this);
7649 // Parses x, y, radius, color (in two possible orders)
7650 // This parses the input into a list. Either it contains just a "none" or
7651 // "inherit" value, or a list of arrays.
7652 // The resulting arrays will always contain the above order, with color and
7653 // radius as null values as needed
7654 enum {
7655 IndexX,
7656 IndexY,
7657 IndexRadius,
7658 IndexSpread,
7659 IndexColor
7662 nsCSSValueList *list = nsnull;
7663 for (nsCSSValueList **curp = &list, *cur; ; curp = &cur->mNext) {
7664 cur = *curp = new nsCSSValueList();
7665 if (!cur) {
7666 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7667 break;
7669 if (!ParseVariant(cur->mValue,
7670 (cur == list) ? VARIANT_HC | VARIANT_LENGTH | VARIANT_NONE
7671 : VARIANT_COLOR | VARIANT_LENGTH,
7672 nsnull)) {
7673 break;
7676 nsCSSUnit unit = cur->mValue.GetUnit();
7677 if (unit != eCSSUnit_None && unit != eCSSUnit_Inherit &&
7678 unit != eCSSUnit_Initial) {
7679 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(5);
7680 if (!val) {
7681 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7682 break;
7684 PRBool haveColor = PR_FALSE;
7685 if (cur->mValue.IsLengthUnit()) {
7686 val->Item(IndexX) = cur->mValue;
7687 } else {
7688 // Must be a color (as string or color value)
7689 NS_ASSERTION(unit == eCSSUnit_String || unit == eCSSUnit_Color ||
7690 unit == eCSSUnit_EnumColor,
7691 "Must be a color value (named color, numeric color, "
7692 "or system color)");
7693 haveColor = PR_TRUE;
7694 val->Item(IndexColor) = cur->mValue;
7696 // Parse the X coordinate
7697 if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH,
7698 nsnull)) {
7699 break;
7702 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
7704 // Y coordinate; this one is not optional
7705 if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH, nsnull)) {
7706 break;
7709 // Optional radius. Ignore errors except if they pass a negative
7710 // value which we must reject. If we use ParsePositiveVariant we can't
7711 // tell the difference between an unspecified radius and a negative
7712 // radius, so that's why we don't use it.
7713 if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH, nsnull) &&
7714 val->Item(IndexRadius).GetFloatValue() < 0) {
7715 break;
7718 if (aUsesSpread) {
7719 // Optional spread (ignore errors)
7720 ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH,
7721 nsnull);
7724 if (!haveColor) {
7725 // Optional color (ignore errors)
7726 ParseVariant(val->Item(IndexColor), VARIANT_COLOR,
7727 nsnull);
7730 // Might be at a comma now
7731 if (ExpectSymbol(',', PR_TRUE)) {
7732 // Go to next value
7733 continue;
7737 if (!ExpectEndProperty()) {
7738 // If we don't have a comma to delimit the next value, we
7739 // must be at the end of the property. Otherwise we've hit
7740 // something else, which is an error.
7741 break;
7744 // Only success case here, since having the failure case at the
7745 // end allows more sharing of code.
7746 return list;
7748 // Have failure case at the end so we can |break| to get to it.
7749 delete list;
7750 return nsnull;
7753 PRBool
7754 CSSParserImpl::ParseTextShadow()
7756 nsCSSValueList* list = ParseCSSShadowList(PR_FALSE);
7757 if (!list)
7758 return PR_FALSE;
7760 mTempData.SetPropertyBit(eCSSProperty_text_shadow);
7761 mTempData.mText.mTextShadow = list;
7762 return PR_TRUE;
7765 PRBool
7766 CSSParserImpl::ParseBoxShadow()
7768 nsCSSValueList* list = ParseCSSShadowList(PR_TRUE);
7769 if (!list)
7770 return PR_FALSE;
7772 mTempData.SetPropertyBit(eCSSProperty_box_shadow);
7773 mTempData.mMargin.mBoxShadow = list;
7774 return PR_TRUE;
7777 PRBool
7778 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix,
7779 PRInt32* aNameSpaceID)
7781 NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
7783 PRInt32 nameSpaceID = kNameSpaceID_Unknown;
7784 if (mNameSpaceMap) {
7785 // user-specified identifiers are case-sensitive (bug 416106)
7786 nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
7787 nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
7789 // else no declared namespaces
7791 if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it
7792 const PRUnichar *params[] = {
7793 aPrefix.get()
7795 REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, params);
7796 if (mUnresolvablePrefixException)
7797 mScanner.SetLowLevelError(NS_ERROR_DOM_NAMESPACE_ERR);
7798 return PR_FALSE;
7801 *aNameSpaceID = nameSpaceID;
7802 return PR_TRUE;
7805 void
7806 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
7808 if (mNameSpaceMap) {
7809 aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nsnull));
7810 } else {
7811 aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
7815 #ifdef MOZ_SVG
7816 PRBool
7817 CSSParserImpl::ParsePaint(nsCSSValuePair* aResult,
7818 nsCSSProperty aPropID)
7820 if (!ParseVariant(aResult->mXValue,
7821 VARIANT_HC | VARIANT_NONE | VARIANT_URL,
7822 nsnull))
7823 return PR_FALSE;
7825 if (aResult->mXValue.GetUnit() == eCSSUnit_URL) {
7826 if (!ParseVariant(aResult->mYValue, VARIANT_COLOR | VARIANT_NONE,
7827 nsnull))
7828 aResult->mYValue.SetColorValue(NS_RGB(0, 0, 0));
7829 } else {
7830 aResult->mYValue = aResult->mXValue;
7833 if (!ExpectEndProperty())
7834 return PR_FALSE;
7836 mTempData.SetPropertyBit(aPropID);
7837 return PR_TRUE;
7840 PRBool
7841 CSSParserImpl::ParseDasharray()
7843 nsCSSValue value;
7844 if (ParseVariant(value, VARIANT_HLPN | VARIANT_NONE, nsnull)) {
7845 nsCSSValueList *listHead = new nsCSSValueList;
7846 nsCSSValueList *list = listHead;
7847 if (!list) {
7848 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7849 return PR_FALSE;
7852 list->mValue = value;
7854 for (;;) {
7855 if (CheckEndProperty()) {
7856 mTempData.SetPropertyBit(eCSSProperty_stroke_dasharray);
7857 mTempData.mSVG.mStrokeDasharray = listHead;
7858 return PR_TRUE;
7861 if (eCSSUnit_Inherit == value.GetUnit() ||
7862 eCSSUnit_Initial == value.GetUnit() ||
7863 eCSSUnit_None == value.GetUnit())
7864 break;
7866 if (!ExpectSymbol(',', PR_TRUE))
7867 break;
7869 if (!ParseVariant(value,
7870 VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_NUMBER,
7871 nsnull))
7872 break;
7874 list->mNext = new nsCSSValueList;
7875 list = list->mNext;
7876 if (list)
7877 list->mValue = value;
7878 else {
7879 mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
7880 break;
7883 delete listHead;
7885 return PR_FALSE;
7888 PRBool
7889 CSSParserImpl::ParseMarker()
7891 nsCSSValue marker;
7892 if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) {
7893 if (ExpectEndProperty()) {
7894 AppendValue(eCSSProperty_marker_end, marker);
7895 AppendValue(eCSSProperty_marker_mid, marker);
7896 AppendValue(eCSSProperty_marker_start, marker);
7897 return PR_TRUE;
7900 return PR_FALSE;
7902 #endif