Bug 451040 ? Passwords Manager Empty after convert to MozStorage. r=gavin
[wine-gecko.git] / layout / style / nsCSSParser.cpp
blobb15aa9ade99721aa4f218db968fa32b2b2183323
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"
87 // Flags for ParseVariant method
88 #define VARIANT_KEYWORD 0x000001 // K
89 #define VARIANT_LENGTH 0x000002 // L
90 #define VARIANT_PERCENT 0x000004 // P
91 #define VARIANT_COLOR 0x000008 // C eCSSUnit_Color, eCSSUnit_String (e.g. "red")
92 #define VARIANT_URL 0x000010 // U
93 #define VARIANT_NUMBER 0x000020 // N
94 #define VARIANT_INTEGER 0x000040 // I
95 #define VARIANT_ANGLE 0x000080 // G
96 #define VARIANT_FREQUENCY 0x000100 // F
97 #define VARIANT_TIME 0x000200 // T
98 #define VARIANT_STRING 0x000400 // S
99 #define VARIANT_COUNTER 0x000800 //
100 #define VARIANT_ATTR 0x001000 //
101 #define VARIANT_IDENTIFIER 0x002000 // D
102 #define VARIANT_AUTO 0x010000 // A
103 #define VARIANT_INHERIT 0x020000 // H eCSSUnit_Initial, eCSSUnit_Inherit
104 #define VARIANT_NONE 0x040000 // O
105 #define VARIANT_NORMAL 0x080000 // M
106 #define VARIANT_SYSFONT 0x100000 // eCSSUnit_System_Font
108 // Common combinations of variants
109 #define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH)
110 #define VARIANT_LP (VARIANT_LENGTH | VARIANT_PERCENT)
111 #define VARIANT_AH (VARIANT_AUTO | VARIANT_INHERIT)
112 #define VARIANT_AHLP (VARIANT_AH | VARIANT_LP)
113 #define VARIANT_AHI (VARIANT_AH | VARIANT_INTEGER)
114 #define VARIANT_AHK (VARIANT_AH | VARIANT_KEYWORD)
115 #define VARIANT_AHKLP (VARIANT_AHLP | VARIANT_KEYWORD)
116 #define VARIANT_AUK (VARIANT_AUTO | VARIANT_URL | VARIANT_KEYWORD)
117 #define VARIANT_AHUK (VARIANT_AH | VARIANT_URL | VARIANT_KEYWORD)
118 #define VARIANT_AHL (VARIANT_AH | VARIANT_LENGTH)
119 #define VARIANT_AHKL (VARIANT_AHK | VARIANT_LENGTH)
120 #define VARIANT_HK (VARIANT_INHERIT | VARIANT_KEYWORD)
121 #define VARIANT_HKF (VARIANT_HK | VARIANT_FREQUENCY)
122 #define VARIANT_HKL (VARIANT_HK | VARIANT_LENGTH)
123 #define VARIANT_HKLP (VARIANT_HK | VARIANT_LP)
124 #define VARIANT_HKLPO (VARIANT_HKLP | VARIANT_NONE)
125 #define VARIANT_HL (VARIANT_INHERIT | VARIANT_LENGTH)
126 #define VARIANT_HI (VARIANT_INHERIT | VARIANT_INTEGER)
127 #define VARIANT_HLP (VARIANT_HL | VARIANT_PERCENT)
128 #define VARIANT_HLPN (VARIANT_HLP | VARIANT_NUMBER)
129 #define VARIANT_HLPO (VARIANT_HLP | VARIANT_NONE)
130 #define VARIANT_HTP (VARIANT_INHERIT | VARIANT_TIME | VARIANT_PERCENT)
131 #define VARIANT_HMK (VARIANT_HK | VARIANT_NORMAL)
132 #define VARIANT_HMKI (VARIANT_HMK | VARIANT_INTEGER)
133 #define VARIANT_HC (VARIANT_INHERIT | VARIANT_COLOR)
134 #define VARIANT_HCK (VARIANT_HK | VARIANT_COLOR)
135 #define VARIANT_HUO (VARIANT_INHERIT | VARIANT_URL | VARIANT_NONE)
136 #define VARIANT_AHUO (VARIANT_AUTO | VARIANT_HUO)
137 #define VARIANT_HPN (VARIANT_INHERIT | VARIANT_PERCENT | VARIANT_NUMBER)
138 #define VARIANT_HOK (VARIANT_HK | VARIANT_NONE)
139 #define VARIANT_HN (VARIANT_INHERIT | VARIANT_NUMBER)
140 #define VARIANT_HON (VARIANT_HN | VARIANT_NONE)
141 #define VARIANT_HOS (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING)
143 //----------------------------------------------------------------------
145 // Your basic top-down recursive descent style parser
146 class CSSParserImpl : public nsICSSParser {
147 public:
148 CSSParserImpl();
149 virtual ~CSSParserImpl();
151 NS_DECL_ISUPPORTS
153 NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet);
155 NS_IMETHOD SetCaseSensitive(PRBool aCaseSensitive);
157 NS_IMETHOD SetQuirkMode(PRBool aQuirkMode);
159 #ifdef MOZ_SVG
160 NS_IMETHOD SetSVGMode(PRBool aSVGMode);
161 #endif
163 NS_IMETHOD SetChildLoader(nsICSSLoader* aChildLoader);
165 NS_IMETHOD Parse(nsIUnicharInputStream* aInput,
166 nsIURI* aSheetURI,
167 nsIURI* aBaseURI,
168 nsIPrincipal* aSheetPrincipal,
169 PRUint32 aLineNumber,
170 PRBool aAllowUnsafeRules);
172 NS_IMETHOD ParseStyleAttribute(const nsAString& aAttributeValue,
173 nsIURI* aDocURL,
174 nsIURI* aBaseURL,
175 nsIPrincipal* aNodePrincipal,
176 nsICSSStyleRule** aResult);
178 NS_IMETHOD ParseAndAppendDeclaration(const nsAString& aBuffer,
179 nsIURI* aSheetURL,
180 nsIURI* aBaseURL,
181 nsIPrincipal* aSheetPrincipal,
182 nsCSSDeclaration* aDeclaration,
183 PRBool aParseOnlyOneDecl,
184 PRBool* aChanged,
185 PRBool aClearOldDecl);
187 NS_IMETHOD ParseRule(const nsAString& aRule,
188 nsIURI* aSheetURL,
189 nsIURI* aBaseURL,
190 nsIPrincipal* aSheetPrincipal,
191 nsCOMArray<nsICSSRule>& aResult);
193 NS_IMETHOD ParseProperty(const nsCSSProperty aPropID,
194 const nsAString& aPropValue,
195 nsIURI* aSheetURL,
196 nsIURI* aBaseURL,
197 nsIPrincipal* aSheetPrincipal,
198 nsCSSDeclaration* aDeclaration,
199 PRBool* aChanged);
201 NS_IMETHOD ParseMediaList(const nsSubstring& aBuffer,
202 nsIURI* aURL, // for error reporting
203 PRUint32 aLineNumber, // for error reporting
204 nsMediaList* aMediaList,
205 PRBool aHTMLMode);
207 NS_IMETHOD ParseColorString(const nsSubstring& aBuffer,
208 nsIURI* aURL, // for error reporting
209 PRUint32 aLineNumber, // for error reporting
210 nscolor* aColor);
212 NS_IMETHOD ParseSelectorString(const nsSubstring& aSelectorString,
213 nsIURI* aURL, // for error reporting
214 PRUint32 aLineNumber, // for error reporting
215 nsCSSSelectorList **aSelectorList);
217 void AppendRule(nsICSSRule* aRule);
219 protected:
220 class nsAutoParseCompoundProperty;
221 friend class nsAutoParseCompoundProperty;
224 * This helper class automatically calls SetParsingCompoundProperty in its
225 * constructor and takes care of resetting it to false in its destructor.
227 class nsAutoParseCompoundProperty {
228 public:
229 nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
231 NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
232 "already parsing compound property");
233 NS_ASSERTION(aParser, "Null parser?");
234 aParser->SetParsingCompoundProperty(PR_TRUE);
237 ~nsAutoParseCompoundProperty()
239 mParser->SetParsingCompoundProperty(PR_FALSE);
241 private:
242 CSSParserImpl* mParser;
245 nsresult InitScanner(nsIUnicharInputStream* aInput, nsIURI* aSheetURI,
246 PRUint32 aLineNumber, nsIURI* aBaseURI,
247 nsIPrincipal* aSheetPrincipal);
248 // the caller must hold on to aBuffer until parsing is done
249 nsresult InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
250 PRUint32 aLineNumber, nsIURI* aBaseURI,
251 nsIPrincipal* aSheetPrincipal);
252 nsresult ReleaseScanner(void);
253 #ifdef MOZ_SVG
254 PRBool IsSVGMode() const {
255 return mScanner.IsSVGMode();
257 #endif
259 PRBool GetToken(nsresult& aErrorCode, PRBool aSkipWS);
260 PRBool GetURLToken(nsresult& aErrorCode);
261 void UngetToken();
263 void AssertInitialState() {
264 NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state");
265 NS_PRECONDITION(!mUnresolvablePrefixException, "Bad initial state");
266 NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state");
269 PRBool ExpectSymbol(nsresult& aErrorCode, PRUnichar aSymbol, PRBool aSkipWS);
270 PRBool ExpectEndProperty(nsresult& aErrorCode);
271 nsSubstring* NextIdent(nsresult& aErrorCode);
272 void SkipUntil(nsresult& aErrorCode, PRUnichar aStopSymbol);
273 void SkipRuleSet(nsresult& aErrorCode);
274 PRBool SkipAtRule(nsresult& aErrorCode);
275 PRBool SkipDeclaration(nsresult& aErrorCode, PRBool aCheckForBraces);
276 PRBool GetNonCloseParenToken(nsresult& aErrorCode, PRBool aSkipWS);
278 PRBool PushGroup(nsICSSGroupRule* aRule);
279 void PopGroup(void);
281 PRBool ParseRuleSet(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
282 PRBool ParseAtRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
283 PRBool ParseCharsetRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
284 PRBool ParseImportRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
285 PRBool GatherURL(nsresult& aErrorCode, nsString& aURL);
286 // Callers must clear or throw out aMedia if GatherMedia returns false.
287 PRBool GatherMedia(nsresult& aErrorCode, nsMediaList* aMedia,
288 PRUnichar aStopSymbol);
289 PRBool ParseMediaQueryExpression(nsresult& aErrorCode, nsMediaQuery* aQuery);
290 PRBool ProcessImport(nsresult& aErrorCode,
291 const nsString& aURLSpec,
292 nsMediaList* aMedia,
293 RuleAppendFunc aAppendFunc,
294 void* aProcessData);
295 PRBool ParseGroupRule(nsresult& aErrorCode, nsICSSGroupRule* aRule, RuleAppendFunc aAppendFunc, void* aProcessData);
296 PRBool ParseMediaRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
297 PRBool ParseMozDocumentRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
298 PRBool ParseNameSpaceRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
299 PRBool ProcessNameSpace(nsresult& aErrorCode, const nsString& aPrefix,
300 const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
301 void* aProcessData);
303 PRBool ParseFontFaceRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
304 PRBool ParseFontDescriptor(nsresult& aErrorCode, nsCSSFontFaceRule* aRule);
305 PRBool ParseFontDescriptorValue(nsresult& aErrorCode, nsCSSFontDesc aDescID,
306 nsCSSValue& aValue);
308 PRBool ParsePageRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aProcessData);
310 enum nsSelectorParsingStatus {
311 // we have parsed a selector and we saw a token that cannot be part of a selector:
312 eSelectorParsingStatus_Done,
313 // we should continue parsing the selector:
314 eSelectorParsingStatus_Continue,
315 // same as "Done" but we did not find a selector:
316 eSelectorParsingStatus_Empty,
317 // we saw an unexpected token or token value,
318 // or we saw end-of-file with an unfinished selector:
319 eSelectorParsingStatus_Error
321 nsSelectorParsingStatus ParseIDSelector(PRInt32& aDataMask,
322 nsCSSSelector& aSelector,
323 nsresult& aErrorCode);
325 nsSelectorParsingStatus ParseClassSelector(PRInt32& aDataMask,
326 nsCSSSelector& aSelector,
327 nsresult& aErrorCode);
329 nsSelectorParsingStatus ParsePseudoSelector(PRInt32& aDataMask,
330 nsCSSSelector& aSelector,
331 nsresult& aErrorCode,
332 PRBool aIsNegated);
334 nsSelectorParsingStatus ParseAttributeSelector(PRInt32& aDataMask,
335 nsCSSSelector& aSelector,
336 nsresult& aErrorCode);
338 nsSelectorParsingStatus ParseTypeOrUniversalSelector(PRInt32& aDataMask,
339 nsCSSSelector& aSelector,
340 nsresult& aErrorCode,
341 PRBool aIsNegated);
343 nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
344 nsIAtom* aPseudo,
345 nsresult& aErrorCode);
347 nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
348 nsIAtom* aPseudo,
349 nsresult& aErrorCode);
351 nsSelectorParsingStatus ParseNegatedSimpleSelector(PRInt32& aDataMask,
352 nsCSSSelector& aSelector,
353 nsresult& aErrorCode);
355 nsSelectorParsingStatus ParseSelector(nsresult& aErrorCode,
356 nsCSSSelector& aSelectorResult);
358 // If aTerminateAtBrace is true, the selector list is done when we
359 // hit a '{'. Otherwise, it's done when we hit EOF.
360 PRBool ParseSelectorList(nsresult& aErrorCode, nsCSSSelectorList*& aListHead,
361 PRBool aTerminateAtBrace);
362 PRBool ParseSelectorGroup(nsresult& aErrorCode, nsCSSSelectorList*& aListHead);
363 nsCSSDeclaration* ParseDeclarationBlock(nsresult& aErrorCode,
364 PRBool aCheckForBraces);
365 PRBool ParseDeclaration(nsresult& aErrorCode,
366 nsCSSDeclaration* aDeclaration,
367 PRBool aCheckForBraces,
368 PRBool aMustCallValueAppended,
369 PRBool* aChanged);
370 // After a parse error parsing |aPropID|, clear the data in
371 // |mTempData|.
372 void ClearTempData(nsCSSProperty aPropID);
373 // After a successful parse of |aPropID|, transfer data from
374 // |mTempData| to |mData|. Set |*aChanged| to true if something
375 // changed, but leave it unmodified otherwise. If aMustCallValueAppended
376 // is false, will not call ValueAppended on aDeclaration if the property
377 // is already set in it.
378 void TransferTempData(nsCSSDeclaration* aDeclaration,
379 nsCSSProperty aPropID, PRBool aIsImportant,
380 PRBool aMustCallValueAppended,
381 PRBool* aChanged);
382 void DoTransferTempData(nsCSSDeclaration* aDeclaration,
383 nsCSSProperty aPropID, PRBool aIsImportant,
384 PRBool aMustCallValueAppended,
385 PRBool* aChanged);
386 PRBool ParseProperty(nsresult& aErrorCode, nsCSSProperty aPropID);
387 PRBool ParseSingleValueProperty(nsresult& aErrorCode, nsCSSValue& aValue,
388 nsCSSProperty aPropID);
390 #ifdef MOZ_XUL
391 PRBool ParseTreePseudoElement(nsresult& aErrorCode, nsCSSSelector& aSelector);
392 #endif
394 void InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties);
396 // Property specific parsing routines
397 PRBool ParseAzimuth(nsresult& aErrorCode, nsCSSValue& aValue);
398 PRBool ParseBackground(nsresult& aErrorCode);
399 PRBool ParseBackgroundPosition(nsresult& aErrorCode);
400 PRBool ParseBackgroundPositionValues(nsresult& aErrorCode);
401 PRBool ParseBorderColor(nsresult& aErrorCode);
402 PRBool ParseBorderColors(nsresult& aErrorCode,
403 nsCSSValueList** aResult,
404 nsCSSProperty aProperty);
405 PRBool ParseBorderImage(nsresult& aErrorCode);
406 PRBool ParseBorderSpacing(nsresult& aErrorCode);
407 PRBool ParseBorderSide(nsresult& aErrorCode,
408 const nsCSSProperty aPropIDs[],
409 PRBool aSetAllSides);
410 PRBool ParseDirectionalBorderSide(nsresult& aErrorCode,
411 const nsCSSProperty aPropIDs[],
412 PRInt32 aSourceType);
413 PRBool ParseBorderStyle(nsresult& aErrorCode);
414 PRBool ParseBorderWidth(nsresult& aErrorCode);
415 PRBool ParseBorderRadius(nsresult& aErrorCode);
416 PRBool ParseOutlineRadius(nsresult& aErrorCode);
417 // for 'clip' and '-moz-image-region'
418 PRBool ParseRect(nsCSSRect& aRect, nsresult& aErrorCode,
419 nsCSSProperty aPropID);
420 PRBool DoParseRect(nsCSSRect& aRect, nsresult& aErrorCode);
421 PRBool ParseContent(nsresult& aErrorCode);
422 PRBool ParseCounterData(nsresult& aErrorCode,
423 nsCSSValuePairList** aResult,
424 nsCSSProperty aPropID);
425 PRBool ParseCue(nsresult& aErrorCode);
426 PRBool ParseCursor(nsresult& aErrorCode);
427 PRBool ParseFont(nsresult& aErrorCode);
428 PRBool ParseFontWeight(nsresult& aErrorCode, nsCSSValue& aValue);
429 PRBool ParseOneFamily(nsresult& aErrorCode, nsAString& aValue);
430 PRBool ParseFamily(nsresult& aErrorCode, nsCSSValue& aValue);
431 PRBool ParseFontSrc(nsresult& aErrorCode, nsCSSValue& aValue);
432 PRBool ParseFontSrcFormat(nsresult& aErrorCode, nsTArray<nsCSSValue>& values);
433 PRBool ParseFontRanges(nsresult& aErrorCode, nsCSSValue& aValue);
434 PRBool ParseListStyle(nsresult& aErrorCode);
435 PRBool ParseMargin(nsresult& aErrorCode);
436 PRBool ParseMarks(nsresult& aErrorCode, nsCSSValue& aValue);
437 PRBool ParseOutline(nsresult& aErrorCode);
438 PRBool ParseOverflow(nsresult& aErrorCode);
439 PRBool ParsePadding(nsresult& aErrorCode);
440 PRBool ParsePause(nsresult& aErrorCode);
441 PRBool ParseQuotes(nsresult& aErrorCode);
442 PRBool ParseSize(nsresult& aErrorCode);
443 PRBool ParseTextDecoration(nsresult& aErrorCode, nsCSSValue& aValue);
445 nsCSSValueList* ParseCSSShadowList(nsresult& aErrorCode,
446 PRBool aUsesSpread);
447 PRBool ParseTextShadow(nsresult& aErrorCode);
448 PRBool ParseBoxShadow(nsresult& aErrorCode);
450 #ifdef MOZ_SVG
451 PRBool ParsePaint(nsresult& aErrorCode,
452 nsCSSValuePair* aResult,
453 nsCSSProperty aPropID);
454 PRBool ParseDasharray(nsresult& aErrorCode);
455 PRBool ParseMarker(nsresult& aErrorCode);
456 #endif
458 // Reused utility parsing routines
459 void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
460 PRBool ParseBoxProperties(nsresult& aErrorCode, nsCSSRect& aResult,
461 const nsCSSProperty aPropIDs[]);
462 PRBool ParseDirectionalBoxProperty(nsresult& aErrorCode,
463 nsCSSProperty aProperty,
464 PRInt32 aSourceType);
465 PRInt32 ParseChoice(nsresult& aErrorCode, nsCSSValue aValues[],
466 const nsCSSProperty aPropIDs[], PRInt32 aNumIDs);
467 PRBool ParseColor(nsresult& aErrorCode, nsCSSValue& aValue);
468 PRBool ParseColorComponent(nsresult& aErrorCode, PRUint8& aComponent,
469 PRInt32& aType, char aStop);
470 // ParseHSLColor parses everything starting with the opening '(' up through
471 // and including the aStop char.
472 PRBool ParseHSLColor(nsresult& aErrorCode, nscolor& aColor, char aStop);
473 // ParseColorOpacity will enforce that the color ends with a ')' after the opacity
474 PRBool ParseColorOpacity(nsresult& aErrorCode, PRUint8& aOpacity);
475 PRBool ParseEnum(nsresult& aErrorCode, nsCSSValue& aValue, const PRInt32 aKeywordTable[]);
476 PRBool ParseVariant(nsresult& aErrorCode, nsCSSValue& aValue,
477 PRInt32 aVariantMask,
478 const PRInt32 aKeywordTable[]);
479 PRBool ParsePositiveVariant(nsresult& aErrorCode, nsCSSValue& aValue,
480 PRInt32 aVariantMask,
481 const PRInt32 aKeywordTable[]);
482 PRBool ParseCounter(nsresult& aErrorCode, nsCSSValue& aValue);
483 PRBool ParseAttr(nsresult& aErrorCode, nsCSSValue& aValue);
484 PRBool ParseURL(nsresult& aErrorCode, nsCSSValue& aValue);
485 PRBool TranslateDimension(nsresult& aErrorCode, nsCSSValue& aValue, PRInt32 aVariantMask,
486 float aNumber, const nsString& aUnit);
488 void SetParsingCompoundProperty(PRBool aBool) {
489 NS_ASSERTION(aBool == PR_TRUE || aBool == PR_FALSE, "bad PRBool value");
490 mParsingCompoundProperty = aBool;
492 PRBool IsParsingCompoundProperty(void) const {
493 return mParsingCompoundProperty;
496 /* Find and return the correct namespace ID for the prefix aPrefix. If the
497 prefix cannot be resolved to a namespace, this method will return false.
498 Otherwise it will return true. When returning false, it may set
499 aErrorCode, depending on the value of mUnresolvablePrefixException.
501 This method never returns kNameSpaceID_Unknown or
502 kNameSpaceID_None for aNameSpaceID while returning true.
504 PRBool GetNamespaceIdForPrefix(const nsString& aPrefix, PRInt32* aNameSpaceID,
505 nsresult& aErrorCode);
507 /* Find the correct default namespace, and set it on aSelector. */
508 void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
510 // Current token. The value is valid after calling GetToken and invalidated
511 // by UngetToken.
512 nsCSSToken mToken;
514 // Our scanner.
515 nsCSSScanner mScanner;
517 // The URI to be used as a base for relative URIs.
518 nsCOMPtr<nsIURI> mBaseURL;
520 // The URI to be used as an HTTP "Referer" and for error reporting.
521 nsCOMPtr<nsIURI> mSheetURL;
523 // The principal of the sheet involved
524 nsCOMPtr<nsIPrincipal> mSheetPrincipal;
526 // The sheet we're parsing into
527 nsCOMPtr<nsICSSStyleSheet> mSheet;
529 // Used for @import rules
530 nsICSSLoader* mChildLoader; // not ref counted, it owns us
532 // Sheet section we're in. This is used to enforce correct ordering of the
533 // various rule types (eg the fact that a @charset rule must come before
534 // anything else).
535 enum nsCSSSection {
536 eCSSSection_Charset,
537 eCSSSection_Import,
538 eCSSSection_NameSpace,
539 eCSSSection_General
541 nsCSSSection mSection;
543 nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it
545 // After an UngetToken is done this flag is true. The next call to
546 // GetToken clears the flag.
547 PRPackedBool mHavePushBack : 1;
549 // True if we are in quirks mode; false in standards or almost standards mode
550 PRPackedBool mNavQuirkMode : 1;
552 // True if unsafe rules should be allowed
553 PRPackedBool mUnsafeRulesEnabled : 1;
555 // True for parsing media lists for HTML attributes, where we have to
556 // ignore CSS comments.
557 PRPackedBool mHTMLMediaMode : 1;
559 // True if tagnames and attributes are case-sensitive
560 PRPackedBool mCaseSensitive : 1;
562 // This flag is set when parsing a non-box shorthand; it's used to not apply
563 // some quirks during shorthand parsing
564 PRPackedBool mParsingCompoundProperty : 1;
566 // If this flag is true, failure to resolve a namespace prefix
567 // should set aErrorCode to NS_ERROR_DOM_NAMESPACE_ERR
568 PRPackedBool mUnresolvablePrefixException : 1;
570 // Stack of rule groups; used for @media and such.
571 nsCOMArray<nsICSSGroupRule> mGroupStack;
573 // During the parsing of a property (which may be a shorthand), the data
574 // are stored in |mTempData|. (It is needed to ensure that parser
575 // errors cause the data to be ignored, and to ensure that a
576 // non-'!important' declaration does not override an '!important'
577 // one.)
578 nsCSSExpandedDataBlock mTempData;
580 // All data from successfully parsed properties are placed into |mData|.
581 nsCSSExpandedDataBlock mData;
583 #ifdef DEBUG
584 PRPackedBool mScannerInited;
585 #endif
588 PR_STATIC_CALLBACK(void) AppendRuleToArray(nsICSSRule* aRule, void* aArray)
590 static_cast<nsCOMArray<nsICSSRule>*>(aArray)->AppendObject(aRule);
593 PR_STATIC_CALLBACK(void) AppendRuleToSheet(nsICSSRule* aRule, void* aParser)
595 CSSParserImpl* parser = (CSSParserImpl*) aParser;
596 parser->AppendRule(aRule);
599 nsresult
600 NS_NewCSSParser(nsICSSParser** aInstancePtrResult)
602 CSSParserImpl *it = new CSSParserImpl();
604 if (it == nsnull) {
605 return NS_ERROR_OUT_OF_MEMORY;
608 return it->QueryInterface(NS_GET_IID(nsICSSParser), (void **) aInstancePtrResult);
611 #ifdef CSS_REPORT_PARSE_ERRORS
613 #define REPORT_UNEXPECTED(msg_) \
614 mScanner.ReportUnexpected(#msg_)
616 #define REPORT_UNEXPECTED_P(msg_, params_) \
617 mScanner.ReportUnexpectedParams(#msg_, params_, NS_ARRAY_LENGTH(params_))
619 #define REPORT_UNEXPECTED_EOF(lf_) \
620 mScanner.ReportUnexpectedEOF(#lf_)
622 #define REPORT_UNEXPECTED_EOF_CHAR(ch_) \
623 mScanner.ReportUnexpectedEOF(ch_)
625 #define REPORT_UNEXPECTED_TOKEN(msg_) \
626 mScanner.ReportUnexpectedToken(mToken, #msg_)
628 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_) \
629 mScanner.ReportUnexpectedTokenParams(mToken, #msg_, \
630 params_, NS_ARRAY_LENGTH(params_))
633 #define OUTPUT_ERROR() \
634 mScanner.OutputError()
636 #define CLEAR_ERROR() \
637 mScanner.ClearError()
639 #else
641 #define REPORT_UNEXPECTED(msg_)
642 #define REPORT_UNEXPECTED_P(msg_, params_)
643 #define REPORT_UNEXPECTED_EOF(lf_)
644 #define REPORT_UNEXPECTED_EOF_CHAR(ch_)
645 #define REPORT_UNEXPECTED_TOKEN(msg_)
646 #define REPORT_UNEXPECTED_TOKEN_P(msg_, params_)
647 #define OUTPUT_ERROR()
648 #define CLEAR_ERROR()
650 #endif
652 CSSParserImpl::CSSParserImpl()
653 : mToken(),
654 mScanner(),
655 mChildLoader(nsnull),
656 mSection(eCSSSection_Charset),
657 mNameSpaceMap(nsnull),
658 mHavePushBack(PR_FALSE),
659 mNavQuirkMode(PR_FALSE),
660 mUnsafeRulesEnabled(PR_FALSE),
661 mHTMLMediaMode(PR_FALSE),
662 mCaseSensitive(PR_FALSE),
663 mParsingCompoundProperty(PR_FALSE),
664 mUnresolvablePrefixException(PR_FALSE)
665 #ifdef DEBUG
666 , mScannerInited(PR_FALSE)
667 #endif
671 NS_IMPL_ISUPPORTS1(CSSParserImpl, nsICSSParser)
673 CSSParserImpl::~CSSParserImpl()
675 mData.AssertInitialState();
676 mTempData.AssertInitialState();
679 NS_IMETHODIMP
680 CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
682 if (aSheet != mSheet) {
683 // Switch to using the new sheet, if any
684 mGroupStack.Clear();
685 mSheet = aSheet;
686 if (mSheet) {
687 mNameSpaceMap = mSheet->GetNameSpaceMap();
688 } else {
689 mNameSpaceMap = nsnull;
693 return NS_OK;
696 NS_IMETHODIMP
697 CSSParserImpl::SetCaseSensitive(PRBool aCaseSensitive)
699 NS_ASSERTION(aCaseSensitive == PR_TRUE || aCaseSensitive == PR_FALSE, "bad PRBool value");
700 mCaseSensitive = aCaseSensitive;
701 return NS_OK;
704 NS_IMETHODIMP
705 CSSParserImpl::SetQuirkMode(PRBool aQuirkMode)
707 NS_ASSERTION(aQuirkMode == PR_TRUE || aQuirkMode == PR_FALSE, "bad PRBool value");
708 mNavQuirkMode = aQuirkMode;
709 return NS_OK;
712 #ifdef MOZ_SVG
713 NS_IMETHODIMP
714 CSSParserImpl::SetSVGMode(PRBool aSVGMode)
716 NS_ASSERTION(aSVGMode == PR_TRUE || aSVGMode == PR_FALSE,
717 "bad PRBool value");
718 mScanner.SetSVGMode(aSVGMode);
719 return NS_OK;
721 #endif
723 NS_IMETHODIMP
724 CSSParserImpl::SetChildLoader(nsICSSLoader* aChildLoader)
726 mChildLoader = aChildLoader; // not ref counted, it owns us
727 return NS_OK;
730 nsresult
731 CSSParserImpl::InitScanner(nsIUnicharInputStream* aInput, nsIURI* aSheetURI,
732 PRUint32 aLineNumber, nsIURI* aBaseURI,
733 nsIPrincipal* aSheetPrincipal)
735 NS_ASSERTION(! mScannerInited, "already have scanner");
737 mScanner.Init(aInput, nsnull, 0, aSheetURI, aLineNumber);
738 #ifdef DEBUG
739 mScannerInited = PR_TRUE;
740 #endif
741 mBaseURL = aBaseURI;
742 mSheetURL = aSheetURI;
743 mSheetPrincipal = aSheetPrincipal;
745 mHavePushBack = PR_FALSE;
747 return NS_OK;
750 nsresult
751 CSSParserImpl::InitScanner(const nsSubstring& aString, nsIURI* aSheetURI,
752 PRUint32 aLineNumber, nsIURI* aBaseURI,
753 nsIPrincipal* aSheetPrincipal)
755 // Having it not own the string is OK since the caller will hold on to
756 // the stream until we're done parsing.
757 NS_ASSERTION(! mScannerInited, "already have scanner");
759 mScanner.Init(nsnull, aString.BeginReading(), aString.Length(), aSheetURI, aLineNumber);
761 #ifdef DEBUG
762 mScannerInited = PR_TRUE;
763 #endif
764 mBaseURL = aBaseURI;
765 mSheetURL = aSheetURI;
766 mSheetPrincipal = aSheetPrincipal;
768 mHavePushBack = PR_FALSE;
770 return NS_OK;
773 nsresult
774 CSSParserImpl::ReleaseScanner(void)
776 mScanner.Close();
777 #ifdef DEBUG
778 mScannerInited = PR_FALSE;
779 #endif
780 mBaseURL = nsnull;
781 mSheetURL = nsnull;
782 mSheetPrincipal = nsnull;
783 return NS_OK;
787 NS_IMETHODIMP
788 CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
789 nsIURI* aSheetURI,
790 nsIURI* aBaseURI,
791 nsIPrincipal* aSheetPrincipal,
792 PRUint32 aLineNumber,
793 PRBool aAllowUnsafeRules)
795 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
797 NS_ASSERTION(nsnull != aBaseURI, "need base URL");
798 NS_ASSERTION(nsnull != aSheetURI, "need sheet URL");
799 AssertInitialState();
801 NS_PRECONDITION(mSheet, "Must have sheet to parse into");
802 NS_ENSURE_STATE(mSheet);
804 #ifdef DEBUG
805 nsCOMPtr<nsIURI> uri;
806 mSheet->GetSheetURI(getter_AddRefs(uri));
807 PRBool equal;
808 NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal,
809 "Sheet URI does not match passed URI");
810 NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal,
811 &equal)) &&
812 equal,
813 "Sheet principal does not match passed principal");
814 #endif
816 nsresult errorCode = NS_OK;
818 nsresult result = InitScanner(aInput, aSheetURI, aLineNumber, aBaseURI,
819 aSheetPrincipal);
820 if (! NS_SUCCEEDED(result)) {
821 return result;
824 PRInt32 ruleCount = 0;
825 mSheet->StyleRuleCount(ruleCount);
826 if (0 < ruleCount) {
827 nsICSSRule* lastRule = nsnull;
828 mSheet->GetStyleRuleAt(ruleCount - 1, lastRule);
829 if (lastRule) {
830 PRInt32 type;
831 lastRule->GetType(type);
832 switch (type) {
833 case nsICSSRule::CHARSET_RULE:
834 case nsICSSRule::IMPORT_RULE:
835 mSection = eCSSSection_Import;
836 break;
837 case nsICSSRule::NAMESPACE_RULE:
838 mSection = eCSSSection_NameSpace;
839 break;
840 default:
841 mSection = eCSSSection_General;
842 break;
844 NS_RELEASE(lastRule);
847 else {
848 mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
851 mUnsafeRulesEnabled = aAllowUnsafeRules;
853 nsCSSToken* tk = &mToken;
854 for (;;) {
855 // Get next non-whitespace token
856 if (!GetToken(errorCode, PR_TRUE)) {
857 OUTPUT_ERROR();
858 break;
860 if (eCSSToken_HTMLComment == tk->mType) {
861 continue; // legal here only
863 if (eCSSToken_AtKeyword == tk->mType) {
864 ParseAtRule(errorCode, AppendRuleToSheet, this);
865 continue;
867 UngetToken();
868 if (ParseRuleSet(errorCode, AppendRuleToSheet, this)) {
869 mSection = eCSSSection_General;
872 ReleaseScanner();
874 mUnsafeRulesEnabled = PR_FALSE;
876 return NS_OK;
880 * Determines whether the identifier contained in the given string is a
881 * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1.
883 static PRBool
884 NonMozillaVendorIdentifier(const nsAString& ident)
886 return (ident.First() == PRUnichar('-') &&
887 !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) ||
888 ident.First() == PRUnichar('_');
892 NS_IMETHODIMP
893 CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue,
894 nsIURI* aDocURL,
895 nsIURI* aBaseURL,
896 nsIPrincipal* aNodePrincipal,
897 nsICSSStyleRule** aResult)
899 NS_PRECONDITION(aNodePrincipal, "Must have principal here!");
900 AssertInitialState();
902 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
904 // XXX line number?
905 nsresult rv = InitScanner(aAttributeValue, aDocURL, 0, aBaseURL, aNodePrincipal);
906 if (! NS_SUCCEEDED(rv)) {
907 return rv;
910 mSection = eCSSSection_General;
911 nsresult errorCode = NS_OK;
913 // In quirks mode, allow style declarations to have braces or not
914 // (bug 99554).
915 PRBool haveBraces;
916 if (mNavQuirkMode && GetToken(errorCode, PR_TRUE)) {
917 haveBraces = eCSSToken_Symbol == mToken.mType &&
918 '{' == mToken.mSymbol;
919 UngetToken();
921 else {
922 haveBraces = PR_FALSE;
925 nsCSSDeclaration* declaration = ParseDeclarationBlock(errorCode, haveBraces);
926 if (declaration) {
927 // Create a style rule for the declaration
928 nsICSSStyleRule* rule = nsnull;
929 rv = NS_NewCSSStyleRule(&rule, nsnull, declaration);
930 if (NS_FAILED(rv)) {
931 declaration->RuleAbort();
932 ReleaseScanner();
933 return rv;
935 *aResult = rule;
937 else {
938 *aResult = nsnull;
941 ReleaseScanner();
943 return NS_OK;
946 NS_IMETHODIMP
947 CSSParserImpl::ParseAndAppendDeclaration(const nsAString& aBuffer,
948 nsIURI* aSheetURL,
949 nsIURI* aBaseURL,
950 nsIPrincipal* aSheetPrincipal,
951 nsCSSDeclaration* aDeclaration,
952 PRBool aParseOnlyOneDecl,
953 PRBool* aChanged,
954 PRBool aClearOldDecl)
956 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
957 AssertInitialState();
959 // NS_ASSERTION(nsnull != aBaseURL, "need base URL");
960 *aChanged = PR_FALSE;
962 nsresult rv = InitScanner(aBuffer, aSheetURL, 0, aBaseURL, aSheetPrincipal);
963 if (! NS_SUCCEEDED(rv)) {
964 return rv;
967 mSection = eCSSSection_General;
968 nsresult errorCode = NS_OK;
970 if (aClearOldDecl) {
971 mData.AssertInitialState();
972 aDeclaration->ClearData();
973 // We could check if it was already empty, but...
974 *aChanged = PR_TRUE;
975 } else {
976 aDeclaration->ExpandTo(&mData);
979 do {
980 // If we cleared the old decl, then we want to be calling
981 // ValueAppended as we parse.
982 if (!ParseDeclaration(errorCode, aDeclaration, PR_FALSE,
983 aClearOldDecl, aChanged)) {
984 NS_ASSERTION(errorCode != nsresult(-1), "-1 is no longer used for EOF");
985 rv = errorCode;
987 if (NS_FAILED(errorCode))
988 break;
990 if (!SkipDeclaration(errorCode, PR_FALSE)) {
991 NS_ASSERTION(errorCode != nsresult(-1), "-1 is no longer used for EOF");
992 rv = errorCode;
993 break;
996 } while (!aParseOnlyOneDecl);
997 aDeclaration->CompressFrom(&mData);
999 ReleaseScanner();
1000 return rv;
1003 NS_IMETHODIMP
1004 CSSParserImpl::ParseRule(const nsAString& aRule,
1005 nsIURI* aSheetURL,
1006 nsIURI* aBaseURL,
1007 nsIPrincipal* aSheetPrincipal,
1008 nsCOMArray<nsICSSRule>& aResult)
1010 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1011 AssertInitialState();
1013 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
1015 nsresult rv = InitScanner(aRule, aSheetURL, 0, aBaseURL, aSheetPrincipal);
1016 if (NS_FAILED(rv)) {
1017 return rv;
1020 mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules.
1021 nsresult errorCode = NS_OK;
1023 nsCSSToken* tk = &mToken;
1024 // Get first non-whitespace token
1025 if (!GetToken(errorCode, PR_TRUE)) {
1026 REPORT_UNEXPECTED(PEParseRuleWSOnly);
1027 OUTPUT_ERROR();
1028 } else if (eCSSToken_AtKeyword == tk->mType) {
1029 ParseAtRule(errorCode, AppendRuleToArray, &aResult);
1031 else {
1032 UngetToken();
1033 ParseRuleSet(errorCode, AppendRuleToArray, &aResult);
1035 OUTPUT_ERROR();
1036 ReleaseScanner();
1037 return NS_OK;
1040 NS_IMETHODIMP
1041 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
1042 const nsAString& aPropValue,
1043 nsIURI* aSheetURL,
1044 nsIURI* aBaseURL,
1045 nsIPrincipal* aSheetPrincipal,
1046 nsCSSDeclaration* aDeclaration,
1047 PRBool* aChanged)
1049 NS_PRECONDITION(aSheetPrincipal, "Must have principal here!");
1050 AssertInitialState();
1052 NS_ASSERTION(nsnull != aBaseURL, "need base URL");
1053 NS_ASSERTION(nsnull != aDeclaration, "Need declaration to parse into!");
1054 *aChanged = PR_FALSE;
1056 nsresult rv = InitScanner(aPropValue, aSheetURL, 0, aBaseURL, aSheetPrincipal);
1057 if (NS_FAILED(rv)) {
1058 return rv;
1061 mSection = eCSSSection_General;
1062 nsresult errorCode = NS_OK;
1064 if (eCSSProperty_UNKNOWN == aPropID) { // unknown property
1065 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1066 const PRUnichar *params[] = {
1067 propName.get()
1069 REPORT_UNEXPECTED_P(PEUnknownProperty, params);
1070 REPORT_UNEXPECTED(PEDeclDropped);
1071 OUTPUT_ERROR();
1072 ReleaseScanner();
1073 return NS_OK;
1076 mData.AssertInitialState();
1077 mTempData.AssertInitialState();
1078 aDeclaration->ExpandTo(&mData);
1079 nsresult result = NS_OK;
1080 PRBool parsedOK = ParseProperty(errorCode, aPropID);
1081 if (parsedOK && !GetToken(errorCode, PR_TRUE)) {
1082 TransferTempData(aDeclaration, aPropID, PR_FALSE, PR_FALSE, aChanged);
1083 } else {
1084 if (parsedOK) {
1085 // Junk at end of property value.
1086 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1088 NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID));
1089 const PRUnichar *params[] = {
1090 propName.get()
1092 REPORT_UNEXPECTED_P(PEValueParsingError, params);
1093 REPORT_UNEXPECTED(PEDeclDropped);
1094 OUTPUT_ERROR();
1095 ClearTempData(aPropID);
1096 NS_ASSERTION(errorCode != nsresult(-1), "-1 is no longer used for EOF");
1097 result = errorCode;
1099 CLEAR_ERROR();
1101 aDeclaration->CompressFrom(&mData);
1103 ReleaseScanner();
1104 return result;
1107 NS_IMETHODIMP
1108 CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer,
1109 nsIURI* aURL, // for error reporting
1110 PRUint32 aLineNumber, // for error reporting
1111 nsMediaList* aMediaList,
1112 PRBool aHTMLMode)
1114 // XXX Are there cases where the caller wants to keep what it already
1115 // has in case of parser error?
1116 aMediaList->Clear();
1118 // fake base URL since media lists don't have URLs in them
1119 nsresult rv = InitScanner(aBuffer, aURL, aLineNumber, aURL, nsnull);
1120 if (NS_FAILED(rv)) {
1121 return rv;
1124 AssertInitialState();
1125 NS_ASSERTION(aHTMLMode == PR_TRUE || aHTMLMode == PR_FALSE,
1126 "invalid PRBool");
1127 mHTMLMediaMode = aHTMLMode;
1129 // XXXldb We need to make the scanner not skip CSS comments! (Or
1130 // should we?)
1132 // For aHTMLMode, we used to follow the parsing rules in
1133 // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors
1134 // which wouldn't work for media queries since they remove all but the
1135 // first word. However, they're changed in
1136 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2
1137 // (as of 2008-05-29) which says that the media attribute just points
1138 // to a media query. (The main substative difference is the relative
1139 // precedence of commas and paretheses.)
1141 if (!GatherMedia(rv, aMediaList, PRUnichar(0))) {
1142 aMediaList->Clear();
1143 aMediaList->SetNonEmpty(); // don't match anything
1144 if (!mHTMLMediaMode) {
1145 OUTPUT_ERROR();
1148 CLEAR_ERROR();
1149 ReleaseScanner();
1150 mHTMLMediaMode = PR_FALSE;
1152 return rv;
1155 NS_IMETHODIMP
1156 CSSParserImpl::ParseColorString(const nsSubstring& aBuffer,
1157 nsIURI* aURL, // for error reporting
1158 PRUint32 aLineNumber, // for error reporting
1159 nscolor* aColor)
1161 AssertInitialState();
1162 nsresult rv = InitScanner(aBuffer, aURL, aLineNumber, aURL, nsnull);
1163 if (NS_FAILED(rv))
1164 return rv;
1166 nsCSSValue value;
1167 PRBool colorParsed = ParseColor(rv, value);
1169 OUTPUT_ERROR();
1170 ReleaseScanner();
1172 if (!colorParsed) {
1173 return NS_ERROR_FAILURE;
1176 if (value.GetUnit() == eCSSUnit_String) {
1177 nscolor rgba;
1178 if (NS_ColorNameToRGB(nsDependentString(value.GetStringBufferValue()), &rgba)) {
1179 (*aColor) = rgba;
1180 rv = NS_OK;
1182 } else if (value.GetUnit() == eCSSUnit_Color) {
1183 (*aColor) = value.GetColorValue();
1184 rv = NS_OK;
1185 } else if (value.GetUnit() == eCSSUnit_EnumColor) {
1186 PRInt32 intValue = value.GetIntValue();
1187 if (intValue >= 0) {
1188 nsCOMPtr<nsILookAndFeel> lfSvc = do_GetService("@mozilla.org/widget/lookandfeel;1");
1189 if (lfSvc) {
1190 nscolor rgba;
1191 rv = lfSvc->GetColor((nsILookAndFeel::nsColorID) value.GetIntValue(), rgba);
1192 if (NS_SUCCEEDED(rv))
1193 (*aColor) = rgba;
1195 } else {
1196 // XXX - this is NS_COLOR_CURRENTCOLOR, NS_COLOR_MOZ_HYPERLINKTEXT, etc.
1197 // which we don't handle as per the ParseColorString definition. Should
1198 // remove this limitation at some point.
1199 rv = NS_ERROR_FAILURE;
1203 return rv;
1206 NS_IMETHODIMP
1207 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
1208 nsIURI* aURL, // for error reporting
1209 PRUint32 aLineNumber, // for error reporting
1210 nsCSSSelectorList **aSelectorList)
1212 nsresult rv = InitScanner(aSelectorString, aURL, aLineNumber, aURL, nsnull);
1213 if (NS_FAILED(rv))
1214 return rv;
1216 AssertInitialState();
1218 mUnresolvablePrefixException = PR_TRUE;
1220 PRBool success = ParseSelectorList(rv, *aSelectorList, PR_FALSE);
1221 OUTPUT_ERROR();
1222 ReleaseScanner();
1224 mUnresolvablePrefixException = PR_FALSE;
1226 if (success) {
1227 NS_ASSERTION(*aSelectorList, "Should have list!");
1228 return NS_OK;
1231 NS_ASSERTION(!*aSelectorList, "Shouldn't have list!");
1232 if (NS_SUCCEEDED(rv)) {
1233 rv = NS_ERROR_DOM_SYNTAX_ERR;
1235 return rv;
1238 //----------------------------------------------------------------------
1240 PRBool CSSParserImpl::GetToken(nsresult& aErrorCode, PRBool aSkipWS)
1242 for (;;) {
1243 if (!mHavePushBack) {
1244 if (!mScanner.Next(aErrorCode, mToken)) {
1245 break;
1248 mHavePushBack = PR_FALSE;
1249 if (aSkipWS && (eCSSToken_WhiteSpace == mToken.mType)) {
1250 continue;
1252 return PR_TRUE;
1254 return PR_FALSE;
1257 PRBool CSSParserImpl::GetURLToken(nsresult& aErrorCode)
1259 for (;;) {
1260 // XXXldb This pushback code doesn't make sense.
1261 if (! mHavePushBack) {
1262 if (! mScanner.NextURL(aErrorCode, mToken)) {
1263 break;
1266 mHavePushBack = PR_FALSE;
1267 if (eCSSToken_WhiteSpace != mToken.mType) {
1268 return PR_TRUE;
1271 return PR_FALSE;
1274 void CSSParserImpl::UngetToken()
1276 NS_PRECONDITION(mHavePushBack == PR_FALSE, "double pushback");
1277 mHavePushBack = PR_TRUE;
1280 PRBool CSSParserImpl::ExpectSymbol(nsresult& aErrorCode,
1281 PRUnichar aSymbol,
1282 PRBool aSkipWS)
1284 if (!GetToken(aErrorCode, aSkipWS)) {
1285 // CSS2.1 specifies that all "open constructs" are to be closed at
1286 // EOF. It simplifies higher layers if we claim to have found an
1287 // ), ], }, or ; if we encounter EOF while looking for one of them.
1288 // Do still issue a diagnostic, to aid debugging.
1289 if (aSymbol == ')' || aSymbol == ']' ||
1290 aSymbol == '}' || aSymbol == ';') {
1291 REPORT_UNEXPECTED_EOF_CHAR(aSymbol);
1292 return PR_TRUE;
1294 else
1295 return PR_FALSE;
1297 if (mToken.IsSymbol(aSymbol)) {
1298 return PR_TRUE;
1300 UngetToken();
1301 return PR_FALSE;
1304 PRBool CSSParserImpl::ExpectEndProperty(nsresult& aErrorCode)
1306 if (!GetToken(aErrorCode, PR_TRUE)) {
1307 return PR_TRUE; // properties may end with eof
1309 if ((eCSSToken_Symbol == mToken.mType) &&
1310 ((';' == mToken.mSymbol) || ('!' == mToken.mSymbol) || ('}' == mToken.mSymbol))) {
1311 // XXX need to verify that ! is only followed by "important [;|}]
1312 // XXX this requires a multi-token pushback buffer
1313 UngetToken();
1314 return PR_TRUE;
1316 REPORT_UNEXPECTED_TOKEN(PEExpectEndValue);
1317 UngetToken();
1318 return PR_FALSE;
1322 nsSubstring* CSSParserImpl::NextIdent(nsresult& aErrorCode)
1324 // XXX Error reporting?
1325 if (!GetToken(aErrorCode, PR_TRUE)) {
1326 return nsnull;
1328 if (eCSSToken_Ident != mToken.mType) {
1329 UngetToken();
1330 return nsnull;
1332 return &mToken.mIdent;
1335 PRBool CSSParserImpl::SkipAtRule(nsresult& aErrorCode)
1337 for (;;) {
1338 if (!GetToken(aErrorCode, PR_TRUE)) {
1339 REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF);
1340 return PR_FALSE;
1342 if (eCSSToken_Symbol == mToken.mType) {
1343 PRUnichar symbol = mToken.mSymbol;
1344 if (symbol == ';') {
1345 break;
1347 if (symbol == '{') {
1348 SkipUntil(aErrorCode, '}');
1349 break;
1350 } else if (symbol == '(') {
1351 SkipUntil(aErrorCode, ')');
1352 } else if (symbol == '[') {
1353 SkipUntil(aErrorCode, ']');
1357 return PR_TRUE;
1360 PRBool CSSParserImpl::ParseAtRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc,
1361 void* aData)
1363 if ((mSection <= eCSSSection_Charset) &&
1364 (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) {
1365 if (ParseCharsetRule(aErrorCode, aAppendFunc, aData)) {
1366 mSection = eCSSSection_Import; // only one charset allowed
1367 return PR_TRUE;
1370 if ((mSection <= eCSSSection_Import) &&
1371 mToken.mIdent.LowerCaseEqualsLiteral("import")) {
1372 if (ParseImportRule(aErrorCode, aAppendFunc, aData)) {
1373 mSection = eCSSSection_Import;
1374 return PR_TRUE;
1377 if ((mSection <= eCSSSection_NameSpace) &&
1378 mToken.mIdent.LowerCaseEqualsLiteral("namespace")) {
1379 if (ParseNameSpaceRule(aErrorCode, aAppendFunc, aData)) {
1380 mSection = eCSSSection_NameSpace;
1381 return PR_TRUE;
1384 if (mToken.mIdent.LowerCaseEqualsLiteral("media")) {
1385 if (ParseMediaRule(aErrorCode, aAppendFunc, aData)) {
1386 mSection = eCSSSection_General;
1387 return PR_TRUE;
1390 if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) {
1391 if (ParseMozDocumentRule(aErrorCode, aAppendFunc, aData)) {
1392 mSection = eCSSSection_General;
1393 return PR_TRUE;
1396 if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) {
1397 if (ParseFontFaceRule(aErrorCode, aAppendFunc, aData)) {
1398 mSection = eCSSSection_General;
1399 return PR_TRUE;
1402 if (mToken.mIdent.LowerCaseEqualsLiteral("page")) {
1403 if (ParsePageRule(aErrorCode, aAppendFunc, aData)) {
1404 mSection = eCSSSection_General;
1405 return PR_TRUE;
1409 if (!NonMozillaVendorIdentifier(mToken.mIdent)) {
1410 REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule);
1411 OUTPUT_ERROR();
1414 // Skip over unsupported at rule, don't advance section
1415 return SkipAtRule(aErrorCode);
1418 PRBool CSSParserImpl::ParseCharsetRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc,
1419 void* aData)
1421 if (!GetToken(aErrorCode, PR_TRUE)) {
1422 REPORT_UNEXPECTED_EOF(PECharsetRuleEOF);
1423 return PR_FALSE;
1426 if (eCSSToken_String != mToken.mType) {
1427 REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString);
1428 return PR_FALSE;
1431 nsAutoString charset = mToken.mIdent;
1433 if (!ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
1434 return PR_FALSE;
1437 nsCOMPtr<nsICSSRule> rule;
1438 NS_NewCSSCharsetRule(getter_AddRefs(rule), charset);
1440 if (rule) {
1441 (*aAppendFunc)(rule, aData);
1444 return PR_TRUE;
1447 PRBool CSSParserImpl::GatherURL(nsresult& aErrorCode, nsString& aURL)
1449 if (!GetToken(aErrorCode, PR_TRUE)) {
1450 return PR_FALSE;
1452 if (eCSSToken_String == mToken.mType) {
1453 aURL = mToken.mIdent;
1454 return PR_TRUE;
1456 else if (eCSSToken_Function == mToken.mType &&
1457 mToken.mIdent.LowerCaseEqualsLiteral("url") &&
1458 ExpectSymbol(aErrorCode, '(', PR_FALSE) &&
1459 GetURLToken(aErrorCode) &&
1460 (eCSSToken_String == mToken.mType ||
1461 eCSSToken_URL == mToken.mType)) {
1462 aURL = mToken.mIdent;
1463 if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
1464 return PR_TRUE;
1467 return PR_FALSE;
1470 // Callers must clear or throw out aMedia if GatherMedia returns false.
1471 PRBool CSSParserImpl::GatherMedia(nsresult& aErrorCode,
1472 nsMediaList* aMedia,
1473 PRUnichar aStopSymbol)
1475 // "If the comma-separated list is the empty list it is assumed to
1476 // specify the media query 'all'." (css3-mediaqueries, section
1477 // "Media Queries")
1478 if (!GetToken(aErrorCode, PR_TRUE)) {
1479 // expected termination by EOF
1480 if (aStopSymbol == PRUnichar(0))
1481 return PR_TRUE;
1483 // unexpected termination by EOF; if we were looking for a
1484 // semicolon, return true anyway, for the same reason this is
1485 // done by ExpectSymbol().
1486 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1487 return aStopSymbol == PRUnichar(';');
1490 if (eCSSToken_Symbol == mToken.mType &&
1491 mToken.mSymbol == aStopSymbol) {
1492 UngetToken();
1493 return PR_TRUE;
1495 UngetToken();
1496 aMedia->SetNonEmpty();
1498 for (;;) {
1499 // We want to still have |query| after we transfer ownership from
1500 // |queryHolder| to |aMedia|.
1501 nsMediaQuery *query;
1503 nsAutoPtr<nsMediaQuery> queryHolder(new nsMediaQuery);
1504 if (!queryHolder) {
1505 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
1506 return PR_FALSE;
1508 query = queryHolder;
1510 // In terms of error handling, it doesn't really matter when we
1511 // append this, since aMedia's contents get dropped entirely
1512 // whenever there is an error.
1513 nsresult rv = aMedia->AppendQuery(queryHolder);
1514 if (NS_FAILED(rv)) {
1515 aErrorCode = rv;
1516 return PR_FALSE;
1518 NS_ASSERTION(!queryHolder, "ownership should have been transferred");
1521 if (ExpectSymbol(aErrorCode, '(', PR_TRUE)) {
1522 // we got an expression without a media type
1523 UngetToken(); // so ParseMediaQueryExpression can handle it
1524 query->SetType(nsGkAtoms::all);
1525 query->SetTypeOmitted();
1526 // Just parse the first expression here.
1527 if (!ParseMediaQueryExpression(aErrorCode, query)) {
1528 OUTPUT_ERROR();
1529 query->SetHadUnknownExpression();
1531 } else {
1532 nsCOMPtr<nsIAtom> mediaType;
1533 PRBool gotNotOrOnly = PR_FALSE;
1534 for (;;) {
1535 if (!GetToken(aErrorCode, PR_TRUE)) {
1536 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1537 return PR_FALSE;
1539 if (eCSSToken_Ident != mToken.mType) {
1540 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent);
1541 UngetToken();
1542 return PR_FALSE;
1544 // case insensitive from CSS - must be lower cased
1545 ToLowerCase(mToken.mIdent);
1546 mediaType = do_GetAtom(mToken.mIdent);
1547 if (gotNotOrOnly ||
1548 (mediaType != nsGkAtoms::_not && mediaType != nsGkAtoms::only))
1549 break;
1550 gotNotOrOnly = PR_TRUE;
1551 if (mediaType == nsGkAtoms::_not)
1552 query->SetNegated();
1553 else
1554 query->SetHasOnly();
1556 query->SetType(mediaType);
1559 for (;;) {
1560 if (!GetToken(aErrorCode, PR_TRUE)) {
1561 // expected termination by EOF
1562 if (aStopSymbol == PRUnichar(0))
1563 return PR_TRUE;
1565 // unexpected termination by EOF; if we were looking for a
1566 // semicolon, return true anyway, for the same reason this is
1567 // done by ExpectSymbol().
1568 REPORT_UNEXPECTED_EOF(PEGatherMediaEOF);
1569 return aStopSymbol == PRUnichar(';');
1572 if (eCSSToken_Symbol == mToken.mType &&
1573 mToken.mSymbol == aStopSymbol) {
1574 UngetToken();
1575 return PR_TRUE;
1577 if (eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') {
1578 // Done with the expressions for this query
1579 break;
1581 if (eCSSToken_Ident != mToken.mType ||
1582 !mToken.mIdent.LowerCaseEqualsLiteral("and")) {
1583 REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma);
1584 UngetToken();
1585 return PR_FALSE;
1587 if (!ParseMediaQueryExpression(aErrorCode, query)) {
1588 OUTPUT_ERROR();
1589 query->SetHadUnknownExpression();
1593 NS_NOTREACHED("unreachable code");
1594 return PR_FALSE; // keep the compiler happy
1597 PRBool CSSParserImpl::ParseMediaQueryExpression(nsresult& aErrorCode, nsMediaQuery* aQuery)
1599 if (!ExpectSymbol(aErrorCode, '(', PR_TRUE)) {
1600 REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart);
1601 return PR_FALSE;
1603 if (! GetToken(aErrorCode, PR_TRUE)) {
1604 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
1605 return PR_FALSE;
1607 if (eCSSToken_Ident != mToken.mType) {
1608 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
1609 SkipUntil(aErrorCode, ')');
1610 return PR_FALSE;
1613 nsMediaExpression *expr = aQuery->NewExpression();
1614 if (!expr) {
1615 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
1616 SkipUntil(aErrorCode, ')');
1617 return PR_FALSE;
1620 // case insensitive from CSS - must be lower cased
1621 ToLowerCase(mToken.mIdent);
1622 const PRUnichar *featureString;
1623 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("min-"))) {
1624 expr->mRange = nsMediaExpression::eMin;
1625 featureString = mToken.mIdent.get() + 4;
1626 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("max-"))) {
1627 expr->mRange = nsMediaExpression::eMax;
1628 featureString = mToken.mIdent.get() + 4;
1629 } else {
1630 expr->mRange = nsMediaExpression::eEqual;
1631 featureString = mToken.mIdent.get();
1634 nsCOMPtr<nsIAtom> mediaFeatureAtom = do_GetAtom(featureString);
1635 const nsMediaFeature *feature = nsMediaFeatures::features;
1636 for (; feature->mName; ++feature) {
1637 if (*(feature->mName) == mediaFeatureAtom) {
1638 break;
1641 if (!feature->mName ||
1642 (expr->mRange != nsMediaExpression::eEqual &&
1643 feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) {
1644 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName);
1645 SkipUntil(aErrorCode, ')');
1646 return PR_FALSE;
1648 expr->mFeature = feature;
1650 if (! GetToken(aErrorCode, PR_TRUE)) {
1651 REPORT_UNEXPECTED_EOF(PEMQExpressionEOF);
1652 return PR_FALSE;
1654 if (eCSSToken_Symbol != mToken.mType ||
1655 (mToken.mSymbol != PRUnichar(':') && mToken.mSymbol != PRUnichar(')'))) {
1656 REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd);
1657 SkipUntil(aErrorCode, ')');
1658 return PR_FALSE;
1661 if (mToken.mSymbol == PRUnichar(')')) {
1662 // All query expressions can be given without a value.
1663 expr->mValue.Reset();
1664 return PR_TRUE;
1667 PRBool rv;
1668 switch (feature->mValueType) {
1669 case nsMediaFeature::eLength:
1670 rv = ParsePositiveVariant(aErrorCode, expr->mValue,
1671 VARIANT_LENGTH, nsnull);
1672 break;
1673 case nsMediaFeature::eInteger:
1674 rv = ParsePositiveVariant(aErrorCode, expr->mValue,
1675 VARIANT_INTEGER, nsnull);
1676 break;
1677 case nsMediaFeature::eIntRatio:
1679 // Two integers separated by '/', with optional whitespace on
1680 // either side of the '/'.
1681 nsRefPtr<nsCSSValue::Array> a = nsCSSValue::Array::Create(2);
1682 if (!a) {
1683 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
1684 SkipUntil(aErrorCode, ')');
1685 return PR_FALSE;
1687 expr->mValue.SetArrayValue(a, eCSSUnit_Array);
1688 // We don't bother with ParsePositiveVariant since we have to
1689 // check for != 0 as well; no need to worry about the UngetToken
1690 // since we're throwing out up to the next ')' anyway.
1691 rv = ParseVariant(aErrorCode, a->Item(0), VARIANT_INTEGER, nsnull) &&
1692 a->Item(0).GetIntValue() > 0 &&
1693 ExpectSymbol(aErrorCode, '/', PR_TRUE) &&
1694 ParseVariant(aErrorCode, a->Item(1), VARIANT_INTEGER, nsnull) &&
1695 a->Item(1).GetIntValue() > 0;
1697 break;
1698 case nsMediaFeature::eResolution:
1699 rv = GetToken(aErrorCode, PR_TRUE) && mToken.IsDimension() &&
1700 mToken.mIntegerValid && mToken.mNumber > 0.0f;
1701 if (rv) {
1702 // No worries about whether unitless zero is allowed, since the
1703 // value must be positive (and we checked that above).
1704 NS_ASSERTION(!mToken.mIdent.IsEmpty(), "IsDimension lied");
1705 if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) {
1706 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch);
1707 } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) {
1708 expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter);
1709 } else {
1710 rv = PR_FALSE;
1713 break;
1714 case nsMediaFeature::eEnumerated:
1715 rv = ParseVariant(aErrorCode, expr->mValue, VARIANT_KEYWORD,
1716 feature->mKeywordTable);
1717 break;
1719 if (!rv) {
1720 REPORT_UNEXPECTED(PEMQExpectedFeatureValue);
1721 return PR_FALSE;
1724 return ExpectSymbol(aErrorCode, ')', PR_TRUE);
1727 // Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]"
1728 PRBool CSSParserImpl::ParseImportRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aData)
1730 nsCOMPtr<nsMediaList> media = new nsMediaList();
1731 if (!media) {
1732 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
1733 return PR_FALSE;
1736 nsAutoString url;
1737 if (!GatherURL(aErrorCode, url)) {
1738 REPORT_UNEXPECTED_TOKEN(PEImportNotURI);
1739 return PR_FALSE;
1742 if (!ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
1743 if (!GatherMedia(aErrorCode, media, ';') ||
1744 !ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
1745 REPORT_UNEXPECTED_TOKEN(PEImportUnexpected);
1746 // don't advance section, simply ignore invalid @import
1747 return PR_FALSE;
1749 NS_ASSERTION(media->Count() != 0, "media list must be nonempty");
1752 ProcessImport(aErrorCode, url, media, aAppendFunc, aData);
1753 return PR_TRUE;
1757 PRBool CSSParserImpl::ProcessImport(nsresult& aErrorCode,
1758 const nsString& aURLSpec,
1759 nsMediaList* aMedia,
1760 RuleAppendFunc aAppendFunc,
1761 void* aData)
1763 nsCOMPtr<nsICSSImportRule> rule;
1764 aErrorCode = NS_NewCSSImportRule(getter_AddRefs(rule), aURLSpec, aMedia);
1765 if (NS_FAILED(aErrorCode)) {
1766 return PR_FALSE;
1768 (*aAppendFunc)(rule, aData);
1770 if (mChildLoader) {
1771 nsCOMPtr<nsIURI> url;
1772 // XXX should pass a charset!
1773 aErrorCode = NS_NewURI(getter_AddRefs(url), aURLSpec, nsnull, mBaseURL);
1775 if (NS_FAILED(aErrorCode)) {
1776 // import url is bad
1777 // XXX log this somewhere for easier web page debugging
1778 return PR_FALSE;
1781 mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule);
1784 return PR_TRUE;
1787 // Parse the {} part of an @media or @-moz-document rule.
1788 PRBool CSSParserImpl::ParseGroupRule(nsresult& aErrorCode,
1789 nsICSSGroupRule* aRule,
1790 RuleAppendFunc aAppendFunc,
1791 void* aData)
1793 // XXXbz this could use better error reporting throughout the method
1794 if (!ExpectSymbol(aErrorCode, '{', PR_TRUE)) {
1795 return PR_FALSE;
1798 // push rule on stack, loop over children
1799 if (!PushGroup(aRule)) {
1800 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
1801 return PR_FALSE;
1803 nsCSSSection holdSection = mSection;
1804 mSection = eCSSSection_General;
1806 for (;;) {
1807 // Get next non-whitespace token
1808 if (! GetToken(aErrorCode, PR_TRUE)) {
1809 REPORT_UNEXPECTED_EOF(PEGroupRuleEOF);
1810 break;
1812 if (mToken.IsSymbol('}')) { // done!
1813 UngetToken();
1814 break;
1816 if (eCSSToken_AtKeyword == mToken.mType) {
1817 SkipAtRule(aErrorCode); // group rules cannot contain @rules
1818 continue;
1820 UngetToken();
1821 ParseRuleSet(aErrorCode, AppendRuleToSheet, this);
1823 PopGroup();
1825 if (!ExpectSymbol(aErrorCode, '}', PR_TRUE)) {
1826 mSection = holdSection;
1827 return PR_FALSE;
1829 (*aAppendFunc)(aRule, aData);
1830 return PR_TRUE;
1833 // Parse a CSS2 media rule: "@media medium [, medium] { ... }"
1834 PRBool CSSParserImpl::ParseMediaRule(nsresult& aErrorCode,
1835 RuleAppendFunc aAppendFunc,
1836 void* aData)
1838 nsCOMPtr<nsMediaList> media = new nsMediaList();
1839 if (!media) {
1840 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
1841 return PR_FALSE;
1844 if (GatherMedia(aErrorCode, media, '{')) {
1845 NS_ASSERTION(media->Count() != 0, "media list must be nonempty");
1846 // XXXbz this could use better error reporting throughout the method
1847 nsRefPtr<nsCSSMediaRule> rule(new nsCSSMediaRule());
1848 // Append first, so when we do SetMedia() the rule
1849 // knows what its stylesheet is.
1850 if (rule && ParseGroupRule(aErrorCode, rule, aAppendFunc, aData)) {
1851 rule->SetMedia(media);
1852 return PR_TRUE;
1856 return PR_FALSE;
1859 // Parse a @-moz-document rule. This is like an @media rule, but instead
1860 // of a medium it has a nonempty list of items where each item is either
1861 // url(), url-prefix(), or domain().
1862 PRBool CSSParserImpl::ParseMozDocumentRule(nsresult& aErrorCode,
1863 RuleAppendFunc aAppendFunc,
1864 void* aData)
1866 nsCSSDocumentRule::URL *urls = nsnull;
1867 nsCSSDocumentRule::URL **next = &urls;
1868 do {
1869 if (!GetToken(aErrorCode, PR_TRUE) ||
1870 eCSSToken_Function != mToken.mType ||
1871 !(mToken.mIdent.LowerCaseEqualsLiteral("url") ||
1872 mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") ||
1873 mToken.mIdent.LowerCaseEqualsLiteral("domain"))) {
1874 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc);
1875 delete urls;
1876 return PR_FALSE;
1878 nsCSSDocumentRule::URL *cur = *next = new nsCSSDocumentRule::URL;
1879 if (!cur) {
1880 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
1881 delete urls;
1882 return PR_FALSE;
1884 next = &cur->next;
1885 if (mToken.mIdent.LowerCaseEqualsLiteral("url")) {
1886 cur->func = nsCSSDocumentRule::eURL;
1887 } else if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) {
1888 cur->func = nsCSSDocumentRule::eURLPrefix;
1889 } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) {
1890 cur->func = nsCSSDocumentRule::eDomain;
1893 if (!ExpectSymbol(aErrorCode, '(', PR_FALSE) ||
1894 !GetURLToken(aErrorCode) ||
1895 (eCSSToken_String != mToken.mType &&
1896 eCSSToken_URL != mToken.mType)) {
1897 REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI);
1898 delete urls;
1899 return PR_FALSE;
1901 if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
1902 delete urls;
1903 return PR_FALSE;
1906 // We could try to make the URL (as long as it's not domain())
1907 // canonical and absolute with NS_NewURI and GetSpec, but I'm
1908 // inclined to think we shouldn't.
1909 CopyUTF16toUTF8(mToken.mIdent, cur->url);
1910 } while (ExpectSymbol(aErrorCode, ',', PR_TRUE));
1912 nsRefPtr<nsCSSDocumentRule> rule(new nsCSSDocumentRule());
1913 if (!rule) {
1914 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
1915 delete urls;
1916 return PR_FALSE;
1918 rule->SetURLs(urls);
1920 return ParseGroupRule(aErrorCode, rule, aAppendFunc, aData);
1923 // Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
1924 PRBool CSSParserImpl::ParseNameSpaceRule(nsresult& aErrorCode,
1925 RuleAppendFunc aAppendFunc,
1926 void* aData)
1928 if (!GetToken(aErrorCode, PR_TRUE)) {
1929 REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF);
1930 return PR_FALSE;
1933 nsAutoString prefix;
1934 nsAutoString url;
1936 if (eCSSToken_Ident == mToken.mType) {
1937 prefix = mToken.mIdent;
1938 // user-specified identifiers are case-sensitive (bug 416106)
1939 if (! GetToken(aErrorCode, PR_TRUE)) {
1940 REPORT_UNEXPECTED_EOF(PEAtNSURIEOF);
1941 return PR_FALSE;
1945 if (eCSSToken_String == mToken.mType) {
1946 url = mToken.mIdent;
1947 if (ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
1948 ProcessNameSpace(aErrorCode, prefix, url, aAppendFunc, aData);
1949 return PR_TRUE;
1952 else if ((eCSSToken_Function == mToken.mType) &&
1953 (mToken.mIdent.LowerCaseEqualsLiteral("url"))) {
1954 if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
1955 if (GetURLToken(aErrorCode)) {
1956 if ((eCSSToken_String == mToken.mType) || (eCSSToken_URL == mToken.mType)) {
1957 url = mToken.mIdent;
1958 if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
1959 if (ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
1960 ProcessNameSpace(aErrorCode, prefix, url, aAppendFunc, aData);
1961 return PR_TRUE;
1968 REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected);
1970 return PR_FALSE;
1973 PRBool CSSParserImpl::ProcessNameSpace(nsresult& aErrorCode, const nsString& aPrefix,
1974 const nsString& aURLSpec, RuleAppendFunc aAppendFunc,
1975 void* aData)
1977 PRBool result = PR_FALSE;
1979 nsCOMPtr<nsICSSNameSpaceRule> rule;
1980 nsCOMPtr<nsIAtom> prefix;
1982 if (!aPrefix.IsEmpty()) {
1983 prefix = do_GetAtom(aPrefix);
1986 NS_NewCSSNameSpaceRule(getter_AddRefs(rule), prefix, aURLSpec);
1987 if (rule) {
1988 (*aAppendFunc)(rule, aData);
1990 // If this was the first namespace rule encountered, it will trigger
1991 // creation of a namespace map.
1992 if (!mNameSpaceMap) {
1993 mNameSpaceMap = mSheet->GetNameSpaceMap();
1997 return result;
2000 // font-face-rule: '@font-face' '{' font-description '}'
2001 // font-description: font-descriptor+
2002 PRBool
2003 CSSParserImpl::ParseFontFaceRule(nsresult& aErrorCode,
2004 RuleAppendFunc aAppendFunc, void* aData)
2006 if (!ExpectSymbol(aErrorCode, '{', PR_TRUE))
2007 return PR_FALSE;
2009 nsRefPtr<nsCSSFontFaceRule> rule(new nsCSSFontFaceRule());
2010 if (!rule) {
2011 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
2012 return PR_FALSE;
2015 for (;;) {
2016 if (!GetToken(aErrorCode, PR_TRUE)) {
2017 REPORT_UNEXPECTED_EOF(PEFontFaceEOF);
2018 break;
2020 if (mToken.IsSymbol('}')) { // done!
2021 UngetToken();
2022 break;
2025 // ignore extra semicolons
2026 if (mToken.IsSymbol(';'))
2027 continue;
2029 if (!ParseFontDescriptor(aErrorCode, rule)) {
2030 REPORT_UNEXPECTED(PEDeclSkipped);
2031 OUTPUT_ERROR();
2032 if (!SkipDeclaration(aErrorCode, PR_TRUE))
2033 break;
2036 if (!ExpectSymbol(aErrorCode, '}', PR_TRUE))
2037 return PR_FALSE;
2038 (*aAppendFunc)(rule, aData);
2039 return PR_TRUE;
2042 // font-descriptor: font-family-desc
2043 // | font-style-desc
2044 // | font-weight-desc
2045 // | font-stretch-desc
2046 // | font-src-desc
2047 // | unicode-range-desc
2049 // All font-*-desc productions follow the pattern
2050 // IDENT ':' value ';'
2052 // On entry to this function, mToken is the IDENT.
2054 PRBool
2055 CSSParserImpl::ParseFontDescriptor(nsresult& aErrorCode,
2056 nsCSSFontFaceRule* aRule)
2058 if (eCSSToken_Ident != mToken.mType) {
2059 REPORT_UNEXPECTED_TOKEN(PEFontDescExpected);
2060 return PR_FALSE;
2063 nsString descName = mToken.mIdent;
2064 if (!ExpectSymbol(aErrorCode, ':', PR_TRUE)) {
2065 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
2066 OUTPUT_ERROR();
2067 return PR_FALSE;
2070 nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName);
2071 nsCSSValue value;
2073 if (descID == eCSSFontDesc_UNKNOWN) {
2074 if (NonMozillaVendorIdentifier(descName)) {
2075 // silently skip other vendors' extensions
2076 SkipDeclaration(aErrorCode, PR_TRUE);
2077 return PR_TRUE;
2078 } else {
2079 const PRUnichar *params[] = {
2080 descName.get()
2082 REPORT_UNEXPECTED_P(PEUnknownFontDesc, params);
2083 return PR_FALSE;
2087 if (!ParseFontDescriptorValue(aErrorCode, descID, value)) {
2088 const PRUnichar *params[] = {
2089 descName.get()
2091 REPORT_UNEXPECTED_P(PEValueParsingError, params);
2092 return PR_FALSE;
2095 if (!ExpectEndProperty(aErrorCode))
2096 return PR_FALSE;
2098 aRule->SetDesc(descID, value);
2099 return PR_TRUE;
2103 PRBool CSSParserImpl::ParsePageRule(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aData)
2105 // XXX not yet implemented
2106 return PR_FALSE;
2109 void CSSParserImpl::SkipUntil(nsresult& aErrorCode, PRUnichar aStopSymbol)
2111 nsCSSToken* tk = &mToken;
2112 for (;;) {
2113 if (!GetToken(aErrorCode, PR_TRUE)) {
2114 break;
2116 if (eCSSToken_Symbol == tk->mType) {
2117 PRUnichar symbol = tk->mSymbol;
2118 if (symbol == aStopSymbol) {
2119 break;
2120 } else if ('{' == symbol) {
2121 SkipUntil(aErrorCode, '}');
2122 } else if ('[' == symbol) {
2123 SkipUntil(aErrorCode, ']');
2124 } else if ('(' == symbol) {
2125 SkipUntil(aErrorCode, ')');
2131 PRBool CSSParserImpl::GetNonCloseParenToken(nsresult& aErrorCode, PRBool aSkipWS)
2133 if (!GetToken(aErrorCode, aSkipWS))
2134 return PR_FALSE;
2135 if (mToken.mType == eCSSToken_Symbol && mToken.mSymbol == ')') {
2136 UngetToken();
2137 return PR_FALSE;
2139 return PR_TRUE;
2142 PRBool
2143 CSSParserImpl::SkipDeclaration(nsresult& aErrorCode, PRBool aCheckForBraces)
2145 nsCSSToken* tk = &mToken;
2146 for (;;) {
2147 if (!GetToken(aErrorCode, PR_TRUE)) {
2148 if (aCheckForBraces) {
2149 REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF);
2151 return PR_FALSE;
2153 if (eCSSToken_Symbol == tk->mType) {
2154 PRUnichar symbol = tk->mSymbol;
2155 if (';' == symbol) {
2156 break;
2158 if (aCheckForBraces) {
2159 if ('}' == symbol) {
2160 UngetToken();
2161 break;
2164 if ('{' == symbol) {
2165 SkipUntil(aErrorCode, '}');
2166 } else if ('(' == symbol) {
2167 SkipUntil(aErrorCode, ')');
2168 } else if ('[' == symbol) {
2169 SkipUntil(aErrorCode, ']');
2173 return PR_TRUE;
2176 void CSSParserImpl::SkipRuleSet(nsresult& aErrorCode)
2178 nsCSSToken* tk = &mToken;
2179 for (;;) {
2180 if (!GetToken(aErrorCode, PR_TRUE)) {
2181 REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF);
2182 break;
2184 if (eCSSToken_Symbol == tk->mType) {
2185 PRUnichar symbol = tk->mSymbol;
2186 if ('{' == symbol) {
2187 SkipUntil(aErrorCode, '}');
2188 break;
2190 if ('(' == symbol) {
2191 SkipUntil(aErrorCode, ')');
2192 } else if ('[' == symbol) {
2193 SkipUntil(aErrorCode, ']');
2199 PRBool CSSParserImpl::PushGroup(nsICSSGroupRule* aRule)
2201 if (mGroupStack.AppendObject(aRule))
2202 return PR_TRUE;
2204 return PR_FALSE;
2207 void CSSParserImpl::PopGroup(void)
2209 PRInt32 count = mGroupStack.Count();
2210 if (0 < count) {
2211 mGroupStack.RemoveObjectAt(count - 1);
2215 void CSSParserImpl::AppendRule(nsICSSRule* aRule)
2217 PRInt32 count = mGroupStack.Count();
2218 if (0 < count) {
2219 mGroupStack[count - 1]->AppendStyleRule(aRule);
2221 else {
2222 mSheet->AppendStyleRule(aRule);
2226 PRBool CSSParserImpl::ParseRuleSet(nsresult& aErrorCode, RuleAppendFunc aAppendFunc, void* aData)
2228 // First get the list of selectors for the rule
2229 nsCSSSelectorList* slist = nsnull;
2230 PRUint32 linenum = mScanner.GetLineNumber();
2231 if (! ParseSelectorList(aErrorCode, slist, PR_TRUE)) {
2232 REPORT_UNEXPECTED(PEBadSelectorRSIgnored);
2233 OUTPUT_ERROR();
2234 SkipRuleSet(aErrorCode);
2235 return PR_FALSE;
2237 NS_ASSERTION(nsnull != slist, "null selector list");
2238 CLEAR_ERROR();
2240 // Next parse the declaration block
2241 nsCSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE);
2242 if (nsnull == declaration) {
2243 // XXX skip something here
2244 delete slist;
2245 return PR_FALSE;
2248 #if 0
2249 slist->Dump();
2250 fputs("{\n", stdout);
2251 declaration->List();
2252 fputs("}\n", stdout);
2253 #endif
2255 // Translate the selector list and declaration block into style data
2257 nsCOMPtr<nsICSSStyleRule> rule;
2258 NS_NewCSSStyleRule(getter_AddRefs(rule), slist, declaration);
2259 if (!rule) {
2260 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
2261 delete slist;
2262 return PR_FALSE;
2264 rule->SetLineNumber(linenum);
2265 (*aAppendFunc)(rule, aData);
2267 return PR_TRUE;
2270 PRBool CSSParserImpl::ParseSelectorList(nsresult& aErrorCode,
2271 nsCSSSelectorList*& aListHead,
2272 PRBool aTerminateAtBrace)
2274 nsCSSSelectorList* list = nsnull;
2275 if (! ParseSelectorGroup(aErrorCode, list)) {
2276 // must have at least one selector group
2277 aListHead = nsnull;
2278 return PR_FALSE;
2280 NS_ASSERTION(nsnull != list, "no selector list");
2281 aListHead = list;
2283 // After that there must either be a "," or a "{" (the latter if
2284 // aTerminateAtBrace is true)
2285 nsCSSToken* tk = &mToken;
2286 for (;;) {
2287 if (! GetToken(aErrorCode, PR_TRUE)) {
2288 if (!aTerminateAtBrace) {
2289 return PR_TRUE;
2292 REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF);
2293 break;
2296 if (eCSSToken_Symbol == tk->mType) {
2297 if (',' == tk->mSymbol) {
2298 nsCSSSelectorList* newList = nsnull;
2299 // Another selector group must follow
2300 if (! ParseSelectorGroup(aErrorCode, newList)) {
2301 break;
2303 // add new list to the end of the selector list
2304 list->mNext = newList;
2305 list = newList;
2306 continue;
2307 } else if ('{' == tk->mSymbol && aTerminateAtBrace) {
2308 UngetToken();
2309 return PR_TRUE;
2312 REPORT_UNEXPECTED_TOKEN(PESelectorListExtra);
2313 UngetToken();
2314 break;
2317 delete aListHead;
2318 aListHead = nsnull;
2319 return PR_FALSE;
2322 static PRBool IsSinglePseudoClass(const nsCSSSelector& aSelector)
2324 return PRBool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
2325 (aSelector.mTag == nsnull) &&
2326 (aSelector.mIDList == nsnull) &&
2327 (aSelector.mClassList == nsnull) &&
2328 (aSelector.mAttrList == nsnull) &&
2329 (aSelector.mNegations == nsnull) &&
2330 (aSelector.mPseudoClassList != nsnull) &&
2331 (aSelector.mPseudoClassList->mNext == nsnull));
2334 #ifdef MOZ_XUL
2335 static PRBool IsTreePseudoElement(nsIAtom* aPseudo)
2337 const char* str;
2338 aPseudo->GetUTF8String(&str);
2339 static const char moz_tree[] = ":-moz-tree-";
2340 return nsCRT::strncmp(str, moz_tree, PRInt32(sizeof(moz_tree)-1)) == 0;
2342 #endif
2344 PRBool CSSParserImpl::ParseSelectorGroup(nsresult& aErrorCode,
2345 nsCSSSelectorList*& aList)
2347 nsAutoPtr<nsCSSSelectorList> list;
2348 PRUnichar combinator = PRUnichar(0);
2349 PRInt32 weight = 0;
2350 PRBool havePseudoElement = PR_FALSE;
2351 PRBool done = PR_FALSE;
2352 while (!done) {
2353 nsAutoPtr<nsCSSSelector> newSelector(new nsCSSSelector());
2354 if (!newSelector) {
2355 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
2356 return PR_FALSE;
2358 nsSelectorParsingStatus parsingStatus =
2359 ParseSelector(aErrorCode, *newSelector);
2360 if (parsingStatus == eSelectorParsingStatus_Empty) {
2361 if (!list) {
2362 REPORT_UNEXPECTED(PESelectorGroupNoSelector);
2364 break;
2366 if (parsingStatus == eSelectorParsingStatus_Error) {
2367 list = nsnull;
2368 break;
2370 if (nsnull == list) {
2371 list = new nsCSSSelectorList();
2372 if (nsnull == list) {
2373 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
2374 return PR_FALSE;
2377 list->AddSelector(newSelector);
2378 nsCSSSelector* listSel = list->mSelectors;
2380 // pull out pseudo elements here
2381 nsPseudoClassList* prevList = nsnull;
2382 nsPseudoClassList* pseudoClassList = listSel->mPseudoClassList;
2383 while (nsnull != pseudoClassList) {
2384 if (! nsCSSPseudoClasses::IsPseudoClass(pseudoClassList->mAtom)) {
2385 havePseudoElement = PR_TRUE;
2386 if (IsSinglePseudoClass(*listSel)) { // convert to pseudo element selector
2387 nsIAtom* pseudoElement = pseudoClassList->mAtom; // steal ref count
2388 pseudoClassList->mAtom = nsnull;
2389 listSel->Reset();
2390 if (listSel->mNext) {// more to the selector
2391 listSel->mOperator = PRUnichar('>');
2392 nsAutoPtr<nsCSSSelector> empty(new nsCSSSelector());
2393 if (!empty) {
2394 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
2395 return PR_FALSE;
2397 list->AddSelector(empty); // leave a blank (universal) selector in the middle
2398 listSel = list->mSelectors; // use the new one for the pseudo
2400 listSel->mTag = pseudoElement;
2402 else { // append new pseudo element selector
2403 nsAutoPtr<nsCSSSelector> pseudoTagSelector(new nsCSSSelector());
2404 if (!pseudoTagSelector) {
2405 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
2406 return PR_FALSE;
2408 pseudoTagSelector->mTag = pseudoClassList->mAtom; // steal ref count
2409 #ifdef MOZ_XUL
2410 if (IsTreePseudoElement(pseudoTagSelector->mTag)) {
2411 // Take the remaining "pseudoclasses" that we parsed
2412 // inside the tree pseudoelement's ()-list, and
2413 // make our new selector have these pseudoclasses
2414 // in its pseudoclass list.
2415 pseudoTagSelector->mPseudoClassList = pseudoClassList->mNext;
2416 pseudoClassList->mNext = nsnull;
2418 #endif
2419 list->AddSelector(pseudoTagSelector);
2420 pseudoClassList->mAtom = nsnull;
2421 listSel->mOperator = PRUnichar('>');
2422 if (nsnull == prevList) { // delete list entry
2423 listSel->mPseudoClassList = pseudoClassList->mNext;
2425 else {
2426 prevList->mNext = pseudoClassList->mNext;
2428 pseudoClassList->mNext = nsnull;
2429 delete pseudoClassList;
2430 weight += listSel->CalcWeight(); // capture weight from remainder
2432 break; // only one pseudo element per selector
2434 prevList = pseudoClassList;
2435 pseudoClassList = pseudoClassList->mNext;
2438 combinator = PRUnichar(0);
2439 if (!GetToken(aErrorCode, PR_FALSE)) {
2440 break;
2443 // Assume we are done unless we find a combinator here.
2444 done = PR_TRUE;
2445 if (eCSSToken_WhiteSpace == mToken.mType) {
2446 if (!GetToken(aErrorCode, PR_TRUE)) {
2447 break;
2449 done = PR_FALSE;
2452 if (eCSSToken_Symbol == mToken.mType &&
2453 ('+' == mToken.mSymbol ||
2454 '>' == mToken.mSymbol ||
2455 '~' == mToken.mSymbol)) {
2456 done = PR_FALSE;
2457 combinator = mToken.mSymbol;
2458 list->mSelectors->SetOperator(combinator);
2460 else {
2461 if (eCSSToken_Symbol == mToken.mType &&
2462 ('{' == mToken.mSymbol ||
2463 ',' == mToken.mSymbol)) {
2464 // End of this selector group
2465 done = PR_TRUE;
2467 UngetToken(); // give it back to selector if we're not done, or make sure
2468 // we see it as the end of the selector if we are.
2471 if (havePseudoElement) {
2472 break;
2474 else {
2475 weight += listSel->CalcWeight();
2479 if (PRUnichar(0) != combinator) { // no dangling combinators
2480 list = nsnull;
2481 // This should report the problematic combinator
2482 REPORT_UNEXPECTED(PESelectorGroupExtraCombinator);
2484 aList = list.forget();
2485 if (aList) {
2486 aList->mWeight = weight;
2488 return PRBool(nsnull != aList);
2491 #define SEL_MASK_NSPACE 0x01
2492 #define SEL_MASK_ELEM 0x02
2493 #define SEL_MASK_ID 0x04
2494 #define SEL_MASK_CLASS 0x08
2495 #define SEL_MASK_ATTRIB 0x10
2496 #define SEL_MASK_PCLASS 0x20
2497 #define SEL_MASK_PELEM 0x40
2500 // Parses an ID selector #name
2502 CSSParserImpl::nsSelectorParsingStatus
2503 CSSParserImpl::ParseIDSelector(PRInt32& aDataMask,
2504 nsCSSSelector& aSelector,
2505 nsresult& aErrorCode)
2507 NS_ASSERTION(!mToken.mIdent.IsEmpty(),
2508 "Empty mIdent in eCSSToken_ID token?");
2509 aDataMask |= SEL_MASK_ID;
2510 aSelector.AddID(mToken.mIdent);
2511 return eSelectorParsingStatus_Continue;
2515 // Parses a class selector .name
2517 CSSParserImpl::nsSelectorParsingStatus
2518 CSSParserImpl::ParseClassSelector(PRInt32& aDataMask,
2519 nsCSSSelector& aSelector,
2520 nsresult& aErrorCode)
2522 if (! GetToken(aErrorCode, PR_FALSE)) { // get ident
2523 REPORT_UNEXPECTED_EOF(PEClassSelEOF);
2524 return eSelectorParsingStatus_Error;
2526 if (eCSSToken_Ident != mToken.mType) { // malformed selector
2527 REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent);
2528 UngetToken();
2529 return eSelectorParsingStatus_Error;
2531 aDataMask |= SEL_MASK_CLASS;
2533 aSelector.AddClass(mToken.mIdent);
2535 return eSelectorParsingStatus_Continue;
2539 // Parse a type element selector or a universal selector
2540 // namespace|type or namespace|* or *|* or *
2542 CSSParserImpl::nsSelectorParsingStatus
2543 CSSParserImpl::ParseTypeOrUniversalSelector(PRInt32& aDataMask,
2544 nsCSSSelector& aSelector,
2545 nsresult& aErrorCode,
2546 PRBool aIsNegated)
2548 nsAutoString buffer;
2549 if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
2550 if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was namespace
2551 aDataMask |= SEL_MASK_NSPACE;
2552 aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
2554 if (! GetToken(aErrorCode, PR_FALSE)) {
2555 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2556 return eSelectorParsingStatus_Error;
2558 if (eCSSToken_Ident == mToken.mType) { // element name
2559 aDataMask |= SEL_MASK_ELEM;
2560 if (mCaseSensitive) {
2561 aSelector.SetTag(mToken.mIdent);
2563 else {
2564 ToLowerCase(mToken.mIdent, buffer);
2565 aSelector.SetTag(buffer);
2568 else if (mToken.IsSymbol('*')) { // universal selector
2569 aDataMask |= SEL_MASK_ELEM;
2570 // don't set tag
2572 else {
2573 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2574 UngetToken();
2575 return eSelectorParsingStatus_Error;
2578 else { // was universal element selector
2579 SetDefaultNamespaceOnSelector(aSelector);
2580 aDataMask |= SEL_MASK_ELEM;
2581 // don't set any tag in the selector
2583 if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
2584 return eSelectorParsingStatus_Done;
2587 else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
2588 buffer = mToken.mIdent; // hang on to ident
2590 if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was namespace
2591 aDataMask |= SEL_MASK_NSPACE;
2592 PRInt32 nameSpaceID;
2593 if (!GetNamespaceIdForPrefix(buffer, &nameSpaceID, aErrorCode)) {
2594 return eSelectorParsingStatus_Error;
2596 aSelector.SetNameSpace(nameSpaceID);
2598 if (! GetToken(aErrorCode, PR_FALSE)) {
2599 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2600 return eSelectorParsingStatus_Error;
2602 if (eCSSToken_Ident == mToken.mType) { // element name
2603 aDataMask |= SEL_MASK_ELEM;
2604 if (mCaseSensitive) {
2605 aSelector.SetTag(mToken.mIdent);
2607 else {
2608 ToLowerCase(mToken.mIdent, buffer);
2609 aSelector.SetTag(buffer);
2612 else if (mToken.IsSymbol('*')) { // universal selector
2613 aDataMask |= SEL_MASK_ELEM;
2614 // don't set tag
2616 else {
2617 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2618 UngetToken();
2619 return eSelectorParsingStatus_Error;
2622 else { // was element name
2623 SetDefaultNamespaceOnSelector(aSelector);
2624 if (mCaseSensitive) {
2625 aSelector.SetTag(buffer);
2627 else {
2628 ToLowerCase(buffer);
2629 aSelector.SetTag(buffer);
2631 aDataMask |= SEL_MASK_ELEM;
2633 if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
2634 return eSelectorParsingStatus_Done;
2637 else if (mToken.IsSymbol('|')) { // No namespace
2638 aDataMask |= SEL_MASK_NSPACE;
2639 aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
2641 // get mandatory tag
2642 if (! GetToken(aErrorCode, PR_FALSE)) {
2643 REPORT_UNEXPECTED_EOF(PETypeSelEOF);
2644 return eSelectorParsingStatus_Error;
2646 if (eCSSToken_Ident == mToken.mType) { // element name
2647 aDataMask |= SEL_MASK_ELEM;
2648 if (mCaseSensitive) {
2649 aSelector.SetTag(mToken.mIdent);
2651 else {
2652 ToLowerCase(mToken.mIdent, buffer);
2653 aSelector.SetTag(buffer);
2656 else if (mToken.IsSymbol('*')) { // universal selector
2657 aDataMask |= SEL_MASK_ELEM;
2658 // don't set tag
2660 else {
2661 REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
2662 UngetToken();
2663 return eSelectorParsingStatus_Error;
2665 if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
2666 return eSelectorParsingStatus_Done;
2669 else {
2670 SetDefaultNamespaceOnSelector(aSelector);
2673 if (aIsNegated) {
2674 // restore last token read in case of a negated type selector
2675 UngetToken();
2677 return eSelectorParsingStatus_Continue;
2681 // Parse attribute selectors [attr], [attr=value], [attr|=value],
2682 // [attr~=value], [attr^=value], [attr$=value] and [attr*=value]
2684 CSSParserImpl::nsSelectorParsingStatus
2685 CSSParserImpl::ParseAttributeSelector(PRInt32& aDataMask,
2686 nsCSSSelector& aSelector,
2687 nsresult& aErrorCode)
2689 if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
2690 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2691 return eSelectorParsingStatus_Error;
2694 PRInt32 nameSpaceID = kNameSpaceID_None;
2695 nsAutoString attr;
2696 if (mToken.IsSymbol('*')) { // wildcard namespace
2697 nameSpaceID = kNameSpaceID_Unknown;
2698 if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) {
2699 if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
2700 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2701 return eSelectorParsingStatus_Error;
2703 if (eCSSToken_Ident == mToken.mType) { // attr name
2704 attr = mToken.mIdent;
2706 else {
2707 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2708 UngetToken();
2709 return eSelectorParsingStatus_Error;
2712 else {
2713 REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar);
2714 return eSelectorParsingStatus_Error;
2717 else if (mToken.IsSymbol('|')) { // NO namespace
2718 if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
2719 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2720 return eSelectorParsingStatus_Error;
2722 if (eCSSToken_Ident == mToken.mType) { // attr name
2723 attr = mToken.mIdent;
2725 else {
2726 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2727 UngetToken();
2728 return eSelectorParsingStatus_Error;
2731 else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
2732 attr = mToken.mIdent; // hang on to it
2733 if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was a namespace
2734 if (!GetNamespaceIdForPrefix(attr, &nameSpaceID, aErrorCode)) {
2735 return eSelectorParsingStatus_Error;
2737 if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
2738 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
2739 return eSelectorParsingStatus_Error;
2741 if (eCSSToken_Ident == mToken.mType) { // attr name
2742 attr = mToken.mIdent;
2744 else {
2745 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
2746 UngetToken();
2747 return eSelectorParsingStatus_Error;
2751 else { // malformed
2752 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
2753 UngetToken();
2754 return eSelectorParsingStatus_Error;
2757 if (! mCaseSensitive) {
2758 ToLowerCase(attr);
2760 if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
2761 REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
2762 return eSelectorParsingStatus_Error;
2764 if ((eCSSToken_Symbol == mToken.mType) ||
2765 (eCSSToken_Includes == mToken.mType) ||
2766 (eCSSToken_Dashmatch == mToken.mType) ||
2767 (eCSSToken_Beginsmatch == mToken.mType) ||
2768 (eCSSToken_Endsmatch == mToken.mType) ||
2769 (eCSSToken_Containsmatch == mToken.mType)) {
2770 PRUint8 func;
2771 if (eCSSToken_Includes == mToken.mType) {
2772 func = NS_ATTR_FUNC_INCLUDES;
2774 else if (eCSSToken_Dashmatch == mToken.mType) {
2775 func = NS_ATTR_FUNC_DASHMATCH;
2777 else if (eCSSToken_Beginsmatch == mToken.mType) {
2778 func = NS_ATTR_FUNC_BEGINSMATCH;
2780 else if (eCSSToken_Endsmatch == mToken.mType) {
2781 func = NS_ATTR_FUNC_ENDSMATCH;
2783 else if (eCSSToken_Containsmatch == mToken.mType) {
2784 func = NS_ATTR_FUNC_CONTAINSMATCH;
2786 else if (']' == mToken.mSymbol) {
2787 aDataMask |= SEL_MASK_ATTRIB;
2788 aSelector.AddAttribute(nameSpaceID, attr);
2789 func = NS_ATTR_FUNC_SET;
2791 else if ('=' == mToken.mSymbol) {
2792 func = NS_ATTR_FUNC_EQUALS;
2794 else {
2795 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
2796 UngetToken(); // bad function
2797 return eSelectorParsingStatus_Error;
2799 if (NS_ATTR_FUNC_SET != func) { // get value
2800 if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
2801 REPORT_UNEXPECTED_EOF(PEAttSelValueEOF);
2802 return eSelectorParsingStatus_Error;
2804 if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
2805 nsAutoString value(mToken.mIdent);
2806 if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
2807 REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
2808 return eSelectorParsingStatus_Error;
2810 if (mToken.IsSymbol(']')) {
2811 PRBool isCaseSensitive = PR_TRUE;
2813 // If we're parsing a style sheet for an HTML document, and
2814 // the attribute selector is for a non-namespaced attribute,
2815 // then check to see if it's one of the known attributes whose
2816 // VALUE is case-insensitive.
2817 if (!mCaseSensitive && nameSpaceID == kNameSpaceID_None) {
2818 static const char* caseInsensitiveHTMLAttribute[] = {
2819 // list based on http://www.w3.org/TR/html4/
2820 "lang",
2821 "dir",
2822 "http-equiv",
2823 "text",
2824 "link",
2825 "vlink",
2826 "alink",
2827 "compact",
2828 "align",
2829 "frame",
2830 "rules",
2831 "valign",
2832 "scope",
2833 "axis",
2834 "nowrap",
2835 "hreflang",
2836 "rel",
2837 "rev",
2838 "charset",
2839 "codetype",
2840 "declare",
2841 "valuetype",
2842 "shape",
2843 "nohref",
2844 "media",
2845 "bgcolor",
2846 "clear",
2847 "color",
2848 "face",
2849 "noshade",
2850 "noresize",
2851 "scrolling",
2852 "target",
2853 "method",
2854 "enctype",
2855 "accept-charset",
2856 "accept",
2857 "checked",
2858 "multiple",
2859 "selected",
2860 "disabled",
2861 "readonly",
2862 "language",
2863 "defer",
2864 "type",
2865 // additional attributes not in HTML4
2866 "direction", // marquee
2867 nsnull
2869 short i = 0;
2870 const char* htmlAttr;
2871 while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) {
2872 if (attr.EqualsIgnoreCase(htmlAttr)) {
2873 isCaseSensitive = PR_FALSE;
2874 break;
2878 aDataMask |= SEL_MASK_ATTRIB;
2879 aSelector.AddAttribute(nameSpaceID, attr, func, value, isCaseSensitive);
2881 else {
2882 REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose);
2883 UngetToken();
2884 return eSelectorParsingStatus_Error;
2887 else {
2888 REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue);
2889 UngetToken();
2890 return eSelectorParsingStatus_Error;
2894 else {
2895 REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected);
2896 UngetToken(); // bad dog, no biscut!
2897 return eSelectorParsingStatus_Error;
2899 return eSelectorParsingStatus_Continue;
2903 // Parse pseudo-classes and pseudo-elements
2905 CSSParserImpl::nsSelectorParsingStatus
2906 CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask,
2907 nsCSSSelector& aSelector,
2908 nsresult& aErrorCode,
2909 PRBool aIsNegated)
2911 if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof
2912 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
2913 return eSelectorParsingStatus_Error;
2916 // First, find out whether we are parsing a CSS3 pseudo-element
2917 PRBool parsingPseudoElement = PR_FALSE;
2918 if (mToken.IsSymbol(':')) {
2919 parsingPseudoElement = PR_TRUE;
2920 if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof
2921 REPORT_UNEXPECTED_EOF(PEPseudoSelEOF);
2922 return eSelectorParsingStatus_Error;
2926 // Do some sanity-checking on the token
2927 if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) {
2928 // malformed selector
2929 REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName);
2930 UngetToken();
2931 return eSelectorParsingStatus_Error;
2934 // OK, now we know we have an mIdent. Atomize it. All the atoms, for
2935 // pseudo-classes as well as pseudo-elements, start with a single ':'.
2936 nsAutoString buffer;
2937 buffer.Append(PRUnichar(':'));
2938 buffer.Append(mToken.mIdent);
2939 ToLowerCase(buffer);
2940 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(buffer);
2942 // stash away some info about this pseudo so we only have to get it once.
2943 PRBool isTreePseudo = PR_FALSE;
2944 #ifdef MOZ_XUL
2945 isTreePseudo = IsTreePseudoElement(pseudo);
2946 // If a tree pseudo-element is using the function syntax, it will
2947 // get isTree set here and will pass the check below that only
2948 // allows functions if they are in our list of things allowed to be
2949 // functions. If it is _not_ using the function syntax, isTree will
2950 // be false, and it will still pass that check. So the tree
2951 // pseudo-elements are allowed to be either functions or not, as
2952 // desired.
2953 PRBool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo;
2954 #endif
2955 PRBool isPseudoElement = nsCSSPseudoElements::IsPseudoElement(pseudo);
2956 // anonymous boxes are only allowed if they're the tree boxes or we have
2957 // enabled unsafe rules
2958 PRBool isAnonBox = nsCSSAnonBoxes::IsAnonBox(pseudo) &&
2959 (mUnsafeRulesEnabled || isTreePseudo);
2960 PRBool isPseudoClass = nsCSSPseudoClasses::IsPseudoClass(pseudo);
2962 if (!isPseudoClass && !isPseudoElement && !isAnonBox) {
2963 // Not a pseudo-class, not a pseudo-element.... forget it
2964 REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
2965 UngetToken();
2966 return eSelectorParsingStatus_Error;
2969 // If it's a function token, it better be on our "ok" list, and if the name
2970 // is that of a function pseudo it better be a function token
2971 if ((eCSSToken_Function == mToken.mType) !=
2973 #ifdef MOZ_XUL
2974 isTree ||
2975 #endif
2976 nsCSSPseudoClasses::notPseudo == pseudo ||
2977 nsCSSPseudoClasses::HasStringArg(pseudo) ||
2978 nsCSSPseudoClasses::HasNthPairArg(pseudo))) {
2979 // There are no other function pseudos
2980 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
2981 UngetToken();
2982 return eSelectorParsingStatus_Error;
2985 // If it starts with "::", it better be a pseudo-element
2986 if (parsingPseudoElement &&
2987 !isPseudoElement &&
2988 !isAnonBox) {
2989 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE);
2990 UngetToken();
2991 return eSelectorParsingStatus_Error;
2994 if (!parsingPseudoElement && nsCSSPseudoClasses::notPseudo == pseudo) {
2995 if (aIsNegated) { // :not() can't be itself negated
2996 REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot);
2997 UngetToken();
2998 return eSelectorParsingStatus_Error;
3000 // CSS 3 Negation pseudo-class takes one simple selector as argument
3001 nsSelectorParsingStatus parsingStatus =
3002 ParseNegatedSimpleSelector(aDataMask, aSelector, aErrorCode);
3003 if (eSelectorParsingStatus_Continue != parsingStatus) {
3004 return parsingStatus;
3007 else if (!parsingPseudoElement && isPseudoClass) {
3008 aDataMask |= SEL_MASK_PCLASS;
3009 if (nsCSSPseudoClasses::HasStringArg(pseudo)) {
3010 nsSelectorParsingStatus parsingStatus =
3011 ParsePseudoClassWithIdentArg(aSelector, pseudo, aErrorCode);
3012 if (eSelectorParsingStatus_Continue != parsingStatus) {
3013 return parsingStatus;
3016 else if (nsCSSPseudoClasses::HasNthPairArg(pseudo)) {
3017 nsSelectorParsingStatus parsingStatus =
3018 ParsePseudoClassWithNthPairArg(aSelector, pseudo, aErrorCode);
3019 if (eSelectorParsingStatus_Continue != parsingStatus) {
3020 return parsingStatus;
3023 else {
3024 aSelector.AddPseudoClass(pseudo);
3027 else if (isPseudoElement || isAnonBox) {
3028 // Pseudo-element. Make some more sanity checks.
3030 if (aIsNegated) { // pseudo-elements can't be negated
3031 REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot);
3032 UngetToken();
3033 return eSelectorParsingStatus_Error;
3035 // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed
3036 // to have a single ':' on them. Others (CSS3+ pseudo-elements and
3037 // various -moz-* pseudo-elements) must have |parsingPseudoElement|
3038 // set.
3039 if (!parsingPseudoElement &&
3040 !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo)
3041 #ifdef MOZ_XUL
3042 && !isTreePseudo
3043 #endif
3045 REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
3046 UngetToken();
3047 return eSelectorParsingStatus_Error;
3050 if (0 == (aDataMask & SEL_MASK_PELEM)) {
3051 aDataMask |= SEL_MASK_PELEM;
3052 aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later
3054 #ifdef MOZ_XUL
3055 if (isTree) {
3056 // We have encountered a pseudoelement of the form
3057 // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each
3058 // item in the list to the pseudoclass list. They will be pulled
3059 // from the list later along with the pseudo-element.
3060 if (!ParseTreePseudoElement(aErrorCode, aSelector)) {
3061 return eSelectorParsingStatus_Error;
3064 #endif
3066 // ensure selector ends here, must be followed by EOF, space, '{' or ','
3067 if (GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
3068 if ((eCSSToken_WhiteSpace == mToken.mType) ||
3069 (mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
3070 UngetToken();
3071 return eSelectorParsingStatus_Done;
3073 REPORT_UNEXPECTED_TOKEN(PEPseudoSelTrailing);
3074 UngetToken();
3075 return eSelectorParsingStatus_Error;
3078 else { // multiple pseudo elements, not legal
3079 REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE);
3080 UngetToken();
3081 return eSelectorParsingStatus_Error;
3084 #ifdef DEBUG
3085 else {
3086 // We should never end up here. Indeed, if we ended up here, we know (from
3087 // the current if/else cascade) that !isPseudoElement and !isAnonBox. But
3088 // then due to our earlier check we know that isPseudoClass. Since we
3089 // didn't fall into the isPseudoClass case in this cascade, we must have
3090 // parsingPseudoElement. But we've already checked the
3091 // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if
3092 // it's happened.
3093 NS_NOTREACHED("How did this happen?");
3095 #endif
3096 return eSelectorParsingStatus_Continue;
3100 // Parse the argument of a negation pseudo-class :not()
3102 CSSParserImpl::nsSelectorParsingStatus
3103 CSSParserImpl::ParseNegatedSimpleSelector(PRInt32& aDataMask,
3104 nsCSSSelector& aSelector,
3105 nsresult& aErrorCode)
3107 // Check if we have the first parenthesis
3108 if (!ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
3109 REPORT_UNEXPECTED_TOKEN(PENegationBadArg);
3110 return eSelectorParsingStatus_Error;
3113 if (! GetToken(aErrorCode, PR_TRUE)) { // premature eof
3114 REPORT_UNEXPECTED_EOF(PENegationEOF);
3115 return eSelectorParsingStatus_Error;
3118 // Create a new nsCSSSelector and add it to the end of
3119 // aSelector.mNegations.
3120 // Given the current parsing rules, every selector in mNegations
3121 // contains only one simple selector (css3 definition) within it.
3122 // This could easily change in future versions of CSS, and the only
3123 // thing we need to change to support that is this parsing code.
3124 nsCSSSelector *newSel = new nsCSSSelector();
3125 if (!newSel) {
3126 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
3127 return eSelectorParsingStatus_Error;
3129 nsCSSSelector* negations = &aSelector;
3130 while (negations->mNegations) {
3131 negations = negations->mNegations;
3133 negations->mNegations = newSel;
3135 nsSelectorParsingStatus parsingStatus;
3136 if (eCSSToken_ID == mToken.mType) { // #id
3137 parsingStatus = ParseIDSelector(aDataMask, *newSel, aErrorCode);
3139 else if (mToken.IsSymbol('.')) { // .class
3140 parsingStatus = ParseClassSelector(aDataMask, *newSel, aErrorCode);
3142 else if (mToken.IsSymbol(':')) { // :pseudo
3143 parsingStatus = ParsePseudoSelector(aDataMask, *newSel, aErrorCode, PR_TRUE);
3145 else if (mToken.IsSymbol('[')) { // [attribute
3146 parsingStatus = ParseAttributeSelector(aDataMask, *newSel, aErrorCode);
3148 else {
3149 // then it should be a type element or universal selector
3150 parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, aErrorCode, PR_TRUE);
3152 if (eSelectorParsingStatus_Error == parsingStatus) {
3153 REPORT_UNEXPECTED_TOKEN(PENegationBadInner);
3154 return parsingStatus;
3156 // close the parenthesis
3157 if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
3158 REPORT_UNEXPECTED_TOKEN(PENegationNoClose);
3159 return eSelectorParsingStatus_Error;
3162 return eSelectorParsingStatus_Continue;
3166 // Parse the argument of a pseudo-class that has an ident arg
3168 CSSParserImpl::nsSelectorParsingStatus
3169 CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector,
3170 nsIAtom* aPseudo,
3171 nsresult& aErrorCode)
3173 // Check if we have the first parenthesis
3174 if (!ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
3175 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoArg);
3176 return eSelectorParsingStatus_Error;
3179 if (! GetToken(aErrorCode, PR_TRUE)) { // premature eof
3180 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3181 return eSelectorParsingStatus_Error;
3183 // We expect an identifier with a language abbreviation
3184 if (eCSSToken_Ident != mToken.mType) {
3185 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent);
3186 UngetToken();
3187 // XXX Call SkipUntil to the next ")"?
3188 return eSelectorParsingStatus_Error;
3191 // Add the pseudo with the language parameter
3192 aSelector.AddPseudoClass(aPseudo, mToken.mIdent.get());
3194 // close the parenthesis
3195 if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
3196 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3197 // XXX Call SkipUntil to the next ")"?
3198 return eSelectorParsingStatus_Error;
3201 return eSelectorParsingStatus_Continue;
3204 CSSParserImpl::nsSelectorParsingStatus
3205 CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector,
3206 nsIAtom* aPseudo,
3207 nsresult& aErrorCode)
3209 PRInt32 numbers[2] = { 0, 0 };
3210 PRBool lookForB = PR_TRUE;
3212 // Check whether we have the first parenthesis
3213 if (!ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
3214 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoArg);
3215 return eSelectorParsingStatus_Error;
3218 // Follow the whitespace rules as proposed in
3219 // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
3221 if (! GetToken(aErrorCode, PR_TRUE)) {
3222 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3223 return eSelectorParsingStatus_Error;
3226 if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) {
3227 // The CSS tokenization doesn't handle :nth-child() containing - well:
3228 // 2n-1 is a dimension
3229 // n-1 is an identifier
3230 // The easiest way to deal with that is to push everything from the
3231 // minus on back onto the scanner's pushback buffer.
3232 PRUint32 truncAt = 0;
3233 if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("n-"))) {
3234 truncAt = 1;
3235 } else if (StringBeginsWith(mToken.mIdent, NS_LITERAL_STRING("-n-"))) {
3236 truncAt = 2;
3238 if (truncAt != 0) {
3239 for (PRUint32 i = mToken.mIdent.Length() - 1; i >= truncAt; --i) {
3240 mScanner.Pushback(mToken.mIdent[i]);
3242 mToken.mIdent.Truncate(truncAt);
3246 if (eCSSToken_Ident == mToken.mType) {
3247 if (mToken.mIdent.EqualsIgnoreCase("odd")) {
3248 numbers[0] = 2;
3249 numbers[1] = 1;
3250 lookForB = PR_FALSE;
3252 else if (mToken.mIdent.EqualsIgnoreCase("even")) {
3253 numbers[0] = 2;
3254 numbers[1] = 0;
3255 lookForB = PR_FALSE;
3257 else if (mToken.mIdent.EqualsIgnoreCase("n")) {
3258 numbers[0] = 1;
3260 else if (mToken.mIdent.EqualsIgnoreCase("-n")) {
3261 numbers[0] = -1;
3263 else {
3264 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3265 // XXX Call SkipUntil to the next ")"?
3266 return eSelectorParsingStatus_Error;
3269 else if (eCSSToken_Number == mToken.mType) {
3270 if (!mToken.mIntegerValid) {
3271 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3272 // XXX Call SkipUntil to the next ")"?
3273 return eSelectorParsingStatus_Error;
3275 numbers[1] = mToken.mInteger;
3276 lookForB = PR_FALSE;
3278 else if (eCSSToken_Dimension == mToken.mType) {
3279 if (!mToken.mIntegerValid || !mToken.mIdent.EqualsIgnoreCase("n")) {
3280 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3281 // XXX Call SkipUntil to the next ")"?
3282 return eSelectorParsingStatus_Error;
3284 numbers[0] = mToken.mInteger;
3286 // XXX If it's a ')', is that valid? (as 0n+0)
3287 else {
3288 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3289 // XXX Call SkipUntil to the next ")" (unless this is one already)?
3290 return eSelectorParsingStatus_Error;
3293 if (! GetToken(aErrorCode, PR_TRUE)) {
3294 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3295 return eSelectorParsingStatus_Error;
3297 if (lookForB && !mToken.IsSymbol(')')) {
3298 // The '+' or '-' sign can optionally be separated by whitespace.
3299 // If it is separated by whitespace from what follows it, it appears
3300 // as a separate token rather than part of the number token.
3301 PRBool haveSign = PR_FALSE;
3302 PRInt32 sign = 1;
3303 if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) {
3304 haveSign = PR_TRUE;
3305 if (mToken.IsSymbol('-')) {
3306 sign = -1;
3308 if (! GetToken(aErrorCode, PR_TRUE)) {
3309 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3310 return eSelectorParsingStatus_Error;
3313 if (eCSSToken_Number != mToken.mType ||
3314 !mToken.mIntegerValid || mToken.mHasSign == haveSign) {
3315 REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth);
3316 // XXX Call SkipUntil to the next ")"?
3317 return eSelectorParsingStatus_Error;
3319 numbers[1] = mToken.mInteger * sign;
3320 if (! GetToken(aErrorCode, PR_TRUE)) {
3321 REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF);
3322 return eSelectorParsingStatus_Error;
3325 if (!mToken.IsSymbol(')')) {
3326 REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose);
3327 // XXX Call SkipUntil to the next ")"?
3328 return eSelectorParsingStatus_Error;
3330 aSelector.AddPseudoClass(aPseudo, numbers);
3331 return eSelectorParsingStatus_Continue;
3336 * This is the format for selectors:
3337 * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
3339 CSSParserImpl::nsSelectorParsingStatus
3340 CSSParserImpl::ParseSelector(nsresult& aErrorCode, nsCSSSelector& aSelector)
3342 if (! GetToken(aErrorCode, PR_TRUE)) {
3343 REPORT_UNEXPECTED_EOF(PESelectorEOF);
3344 return eSelectorParsingStatus_Error;
3347 PRInt32 dataMask = 0;
3348 nsSelectorParsingStatus parsingStatus =
3349 ParseTypeOrUniversalSelector(dataMask, aSelector, aErrorCode, PR_FALSE);
3350 if (parsingStatus != eSelectorParsingStatus_Continue) {
3351 return parsingStatus;
3354 for (;;) {
3355 if (eCSSToken_ID == mToken.mType) { // #id
3356 parsingStatus = ParseIDSelector(dataMask, aSelector, aErrorCode);
3358 else if (mToken.IsSymbol('.')) { // .class
3359 parsingStatus = ParseClassSelector(dataMask, aSelector, aErrorCode);
3361 else if (mToken.IsSymbol(':')) { // :pseudo
3362 parsingStatus = ParsePseudoSelector(dataMask, aSelector, aErrorCode, PR_FALSE);
3364 else if (mToken.IsSymbol('[')) { // [attribute
3365 parsingStatus = ParseAttributeSelector(dataMask, aSelector, aErrorCode);
3367 else { // not a selector token, we're done
3368 parsingStatus = eSelectorParsingStatus_Done;
3369 break;
3372 if (parsingStatus != eSelectorParsingStatus_Continue) {
3373 return parsingStatus;
3376 if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
3377 return eSelectorParsingStatus_Done;
3380 UngetToken();
3381 return dataMask ? parsingStatus : eSelectorParsingStatus_Empty;
3384 nsCSSDeclaration*
3385 CSSParserImpl::ParseDeclarationBlock(nsresult& aErrorCode,
3386 PRBool aCheckForBraces)
3388 if (aCheckForBraces) {
3389 if (!ExpectSymbol(aErrorCode, '{', PR_TRUE)) {
3390 REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
3391 OUTPUT_ERROR();
3392 return nsnull;
3395 nsCSSDeclaration* declaration = new nsCSSDeclaration();
3396 mData.AssertInitialState();
3397 if (declaration) {
3398 for (;;) {
3399 PRBool changed;
3400 if (!ParseDeclaration(aErrorCode, declaration, aCheckForBraces,
3401 PR_TRUE, &changed)) {
3402 if (!SkipDeclaration(aErrorCode, aCheckForBraces)) {
3403 break;
3405 if (aCheckForBraces) {
3406 if (ExpectSymbol(aErrorCode, '}', PR_TRUE)) {
3407 break;
3410 // Since the skipped declaration didn't end the block we parse
3411 // the next declaration.
3414 declaration->CompressFrom(&mData);
3416 return declaration;
3419 // The types to pass to ParseColorComponent. These correspond to the
3420 // various datatypes that can go within rgb().
3421 #define COLOR_TYPE_UNKNOWN 0
3422 #define COLOR_TYPE_INTEGERS 1
3423 #define COLOR_TYPE_PERCENTAGES 2
3425 PRBool CSSParserImpl::ParseColor(nsresult& aErrorCode, nsCSSValue& aValue)
3427 if (!GetToken(aErrorCode, PR_TRUE)) {
3428 REPORT_UNEXPECTED_EOF(PEColorEOF);
3429 return PR_FALSE;
3432 nsCSSToken* tk = &mToken;
3433 nscolor rgba;
3434 switch (tk->mType) {
3435 case eCSSToken_ID:
3436 case eCSSToken_Ref:
3437 // #xxyyzz
3438 if (NS_HexToRGB(tk->mIdent, &rgba)) {
3439 aValue.SetColorValue(rgba);
3440 return PR_TRUE;
3442 break;
3444 case eCSSToken_Ident:
3445 if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
3446 aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
3447 return PR_TRUE;
3449 else {
3450 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
3451 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
3452 PRInt32 value;
3453 // XXX Now that non-cairo is no longer supported, we should remove
3454 // the special parsing of transparent for background-color and
3455 // border-color. (It currently overrides this, since keywords
3456 // are checked earlier in ParseVariant.)
3457 if (keyword == eCSSKeyword_transparent) {
3458 aValue.SetColorValue(NS_RGBA(0, 0, 0, 0));
3459 return PR_TRUE;
3461 if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) {
3462 aValue.SetIntValue(value, eCSSUnit_EnumColor);
3463 return PR_TRUE;
3467 break;
3468 case eCSSToken_Function:
3469 if (mToken.mIdent.LowerCaseEqualsLiteral("rgb")) {
3470 // rgb ( component , component , component )
3471 PRUint8 r, g, b;
3472 PRInt32 type = COLOR_TYPE_UNKNOWN;
3473 if (ExpectSymbol(aErrorCode, '(', PR_FALSE) && // this won't fail
3474 ParseColorComponent(aErrorCode, r, type, ',') &&
3475 ParseColorComponent(aErrorCode, g, type, ',') &&
3476 ParseColorComponent(aErrorCode, b, type, ')')) {
3477 aValue.SetColorValue(NS_RGB(r,g,b));
3478 return PR_TRUE;
3480 return PR_FALSE; // already pushed back
3482 else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
3483 mToken.mIdent.LowerCaseEqualsLiteral("rgba")) {
3484 // rgba ( component , component , component , opacity )
3485 PRUint8 r, g, b, a;
3486 PRInt32 type = COLOR_TYPE_UNKNOWN;
3487 if (ExpectSymbol(aErrorCode, '(', PR_FALSE) && // this won't fail
3488 ParseColorComponent(aErrorCode, r, type, ',') &&
3489 ParseColorComponent(aErrorCode, g, type, ',') &&
3490 ParseColorComponent(aErrorCode, b, type, ',') &&
3491 ParseColorOpacity(aErrorCode, a)) {
3492 aValue.SetColorValue(NS_RGBA(r, g, b, a));
3493 return PR_TRUE;
3495 return PR_FALSE; // already pushed back
3497 else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
3498 // hsl ( hue , saturation , lightness )
3499 // "hue" is a number, "saturation" and "lightness" are percentages.
3500 if (ParseHSLColor(aErrorCode, rgba, ')')) {
3501 aValue.SetColorValue(rgba);
3502 return PR_TRUE;
3504 return PR_FALSE;
3506 else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
3507 mToken.mIdent.LowerCaseEqualsLiteral("hsla")) {
3508 // hsla ( hue , saturation , lightness , opacity )
3509 // "hue" is a number, "saturation" and "lightness" are percentages,
3510 // "opacity" is a number.
3511 PRUint8 a;
3512 if (ParseHSLColor(aErrorCode, rgba, ',') &&
3513 ParseColorOpacity(aErrorCode, a)) {
3514 aValue.SetColorValue(NS_RGBA(NS_GET_R(rgba), NS_GET_G(rgba),
3515 NS_GET_B(rgba), a));
3516 return PR_TRUE;
3518 return PR_FALSE;
3520 break;
3521 default:
3522 break;
3525 // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
3526 if (mNavQuirkMode && !IsParsingCompoundProperty()) {
3527 // - If the string starts with 'a-f', the nsCSSScanner builds the
3528 // token as a eCSSToken_Ident and we can parse the string as a
3529 // 'xxyyzz' RGB color.
3530 // - If it only contains '0-9' digits, the token is a
3531 // eCSSToken_Number and it must be converted back to a 6
3532 // characters string to be parsed as a RGB color.
3533 // - If it starts with '0-9' and contains any 'a-f', the token is a
3534 // eCSSToken_Dimension, the mNumber part must be converted back to
3535 // a string and the mIdent part must be appended to that string so
3536 // that the resulting string has 6 characters.
3537 // Note: This is a hack for Nav compatibility. Do not attempt to
3538 // simplify it by hacking into the ncCSSScanner. This would be very
3539 // bad.
3540 nsAutoString str;
3541 char buffer[20];
3542 switch (tk->mType) {
3543 case eCSSToken_Ident:
3544 str.Assign(tk->mIdent);
3545 break;
3547 case eCSSToken_Number:
3548 if (tk->mIntegerValid) {
3549 PR_snprintf(buffer, sizeof(buffer), "%06d", tk->mInteger);
3550 str.AssignWithConversion(buffer);
3552 break;
3554 case eCSSToken_Dimension:
3555 if (tk->mIdent.Length() <= 6) {
3556 PR_snprintf(buffer, sizeof(buffer), "%06.0f", tk->mNumber);
3557 nsAutoString temp;
3558 temp.AssignWithConversion(buffer);
3559 temp.Right(str, 6 - tk->mIdent.Length());
3560 str.Append(tk->mIdent);
3562 break;
3563 default:
3564 // There is a whole bunch of cases that are
3565 // not handled by this switch. Ignore them.
3566 break;
3568 if (NS_HexToRGB(str, &rgba)) {
3569 aValue.SetColorValue(rgba);
3570 return PR_TRUE;
3574 // It's not a color
3575 REPORT_UNEXPECTED_TOKEN(PEColorNotColor);
3576 UngetToken();
3577 return PR_FALSE;
3580 // aType will be set if we have already parsed other color components
3581 // in this color spec
3582 PRBool CSSParserImpl::ParseColorComponent(nsresult& aErrorCode,
3583 PRUint8& aComponent,
3584 PRInt32& aType,
3585 char aStop)
3587 if (!GetToken(aErrorCode, PR_TRUE)) {
3588 REPORT_UNEXPECTED_EOF(PEColorComponentEOF);
3589 return PR_FALSE;
3591 float value;
3592 nsCSSToken* tk = &mToken;
3593 switch (tk->mType) {
3594 case eCSSToken_Number:
3595 switch (aType) {
3596 case COLOR_TYPE_UNKNOWN:
3597 aType = COLOR_TYPE_INTEGERS;
3598 break;
3599 case COLOR_TYPE_INTEGERS:
3600 break;
3601 case COLOR_TYPE_PERCENTAGES:
3602 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3603 UngetToken();
3604 return PR_FALSE;
3605 default:
3606 NS_NOTREACHED("Someone forgot to add the new color component type in here");
3609 if (!mToken.mIntegerValid) {
3610 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
3611 UngetToken();
3612 return PR_FALSE;
3614 value = tk->mNumber;
3615 break;
3616 case eCSSToken_Percentage:
3617 switch (aType) {
3618 case COLOR_TYPE_UNKNOWN:
3619 aType = COLOR_TYPE_PERCENTAGES;
3620 break;
3621 case COLOR_TYPE_INTEGERS:
3622 REPORT_UNEXPECTED_TOKEN(PEExpectedInt);
3623 UngetToken();
3624 return PR_FALSE;
3625 case COLOR_TYPE_PERCENTAGES:
3626 break;
3627 default:
3628 NS_NOTREACHED("Someone forgot to add the new color component type in here");
3630 value = tk->mNumber * 255.0f;
3631 break;
3632 default:
3633 REPORT_UNEXPECTED_TOKEN(PEColorBadRGBContents);
3634 UngetToken();
3635 return PR_FALSE;
3637 if (ExpectSymbol(aErrorCode, aStop, PR_TRUE)) {
3638 if (value < 0.0f) value = 0.0f;
3639 if (value > 255.0f) value = 255.0f;
3640 aComponent = NSToIntRound(value);
3641 return PR_TRUE;
3643 const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
3644 const PRUnichar *params[] = {
3645 nsnull,
3646 stopString
3648 REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
3649 return PR_FALSE;
3653 PRBool CSSParserImpl::ParseHSLColor(nsresult& aErrorCode, nscolor& aColor,
3654 char aStop)
3656 float h, s, l;
3657 if (!ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
3658 NS_ERROR("How did this get to be a function token?");
3659 return PR_FALSE;
3662 // Get the hue
3663 if (!GetToken(aErrorCode, PR_TRUE)) {
3664 REPORT_UNEXPECTED_EOF(PEColorHueEOF);
3665 return PR_FALSE;
3667 if (mToken.mType != eCSSToken_Number) {
3668 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
3669 UngetToken();
3670 return PR_FALSE;
3672 h = mToken.mNumber;
3673 h /= 360.0f;
3674 // hue values are wraparound
3675 h = h - floor(h);
3677 if (!ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
3678 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
3679 return PR_FALSE;
3682 // Get the saturation
3683 if (!GetToken(aErrorCode, PR_TRUE)) {
3684 REPORT_UNEXPECTED_EOF(PEColorSaturationEOF);
3685 return PR_FALSE;
3687 if (mToken.mType != eCSSToken_Percentage) {
3688 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3689 UngetToken();
3690 return PR_FALSE;
3692 s = mToken.mNumber;
3693 if (s < 0.0f) s = 0.0f;
3694 if (s > 1.0f) s = 1.0f;
3696 if (!ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
3697 REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
3698 return PR_FALSE;
3701 // Get the lightness
3702 if (!GetToken(aErrorCode, PR_TRUE)) {
3703 REPORT_UNEXPECTED_EOF(PEColorLightnessEOF);
3704 return PR_FALSE;
3706 if (mToken.mType != eCSSToken_Percentage) {
3707 REPORT_UNEXPECTED_TOKEN(PEExpectedPercent);
3708 UngetToken();
3709 return PR_FALSE;
3711 l = mToken.mNumber;
3712 if (l < 0.0f) l = 0.0f;
3713 if (l > 1.0f) l = 1.0f;
3715 if (ExpectSymbol(aErrorCode, aStop, PR_TRUE)) {
3716 aColor = NS_HSL2RGB(h, s, l);
3717 return PR_TRUE;
3720 const PRUnichar stopString[] = { PRUnichar(aStop), PRUnichar(0) };
3721 const PRUnichar *params[] = {
3722 nsnull,
3723 stopString
3725 REPORT_UNEXPECTED_TOKEN_P(PEColorComponentBadTerm, params);
3726 return PR_FALSE;
3730 PRBool CSSParserImpl::ParseColorOpacity(nsresult& aErrorCode, PRUint8& aOpacity)
3732 if (!GetToken(aErrorCode, PR_TRUE)) {
3733 REPORT_UNEXPECTED_EOF(PEColorOpacityEOF);
3734 return PR_FALSE;
3737 if (mToken.mType != eCSSToken_Number) {
3738 REPORT_UNEXPECTED_TOKEN(PEExpectedNumber);
3739 UngetToken();
3740 return PR_FALSE;
3743 if (mToken.mNumber < 0.0f) {
3744 mToken.mNumber = 0.0f;
3745 } else if (mToken.mNumber > 1.0f) {
3746 mToken.mNumber = 1.0f;
3749 PRUint8 value = nsStyleUtil::FloatToColorComponent(mToken.mNumber);
3750 NS_ASSERTION(fabs(mToken.mNumber - value/255.0f) <= 0.5f,
3751 "FloatToColorComponent did something weird");
3753 if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
3754 REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
3755 return PR_FALSE;
3758 aOpacity = value;
3760 return PR_TRUE;
3763 #ifdef MOZ_XUL
3764 PRBool CSSParserImpl::ParseTreePseudoElement(nsresult& aErrorCode,
3765 nsCSSSelector& aSelector)
3767 if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
3768 while (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
3769 if (!GetToken(aErrorCode, PR_TRUE)) {
3770 return PR_FALSE;
3772 else if (eCSSToken_Ident == mToken.mType) {
3773 nsCOMPtr<nsIAtom> pseudo = do_GetAtom(mToken.mIdent);
3774 aSelector.AddPseudoClass(pseudo);
3776 else if (eCSSToken_Symbol == mToken.mType) {
3777 if (!mToken.IsSymbol(','))
3778 return PR_FALSE;
3780 else return PR_FALSE;
3782 return PR_TRUE;
3784 return PR_FALSE;
3786 #endif
3788 //----------------------------------------------------------------------
3790 PRBool
3791 CSSParserImpl::ParseDeclaration(nsresult& aErrorCode,
3792 nsCSSDeclaration* aDeclaration,
3793 PRBool aCheckForBraces,
3794 PRBool aMustCallValueAppended,
3795 PRBool* aChanged)
3797 mTempData.AssertInitialState();
3799 // Get property name
3800 nsCSSToken* tk = &mToken;
3801 nsAutoString propertyName;
3802 for (;;) {
3803 if (!GetToken(aErrorCode, PR_TRUE)) {
3804 if (aCheckForBraces) {
3805 REPORT_UNEXPECTED_EOF(PEDeclEndEOF);
3807 return PR_FALSE;
3809 if (eCSSToken_Ident == tk->mType) {
3810 propertyName = tk->mIdent;
3811 // grab the ident before the ExpectSymbol trashes the token
3812 if (!ExpectSymbol(aErrorCode, ':', PR_TRUE)) {
3813 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon);
3814 REPORT_UNEXPECTED(PEDeclDropped);
3815 OUTPUT_ERROR();
3816 return PR_FALSE;
3818 break;
3820 if (tk->IsSymbol(';')) {
3821 // dangling semicolons are skipped
3822 continue;
3825 if (!tk->IsSymbol('}')) {
3826 REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
3827 REPORT_UNEXPECTED(PEDeclSkipped);
3828 OUTPUT_ERROR();
3830 // Not a declaration...
3831 UngetToken();
3832 return PR_FALSE;
3835 // Map property name to its ID and then parse the property
3836 nsCSSProperty propID = nsCSSProps::LookupProperty(propertyName);
3837 if (eCSSProperty_UNKNOWN == propID) { // unknown property
3838 if (!NonMozillaVendorIdentifier(propertyName)) {
3839 const PRUnichar *params[] = {
3840 propertyName.get()
3842 REPORT_UNEXPECTED_P(PEUnknownProperty, params);
3843 REPORT_UNEXPECTED(PEDeclDropped);
3844 OUTPUT_ERROR();
3847 return PR_FALSE;
3849 if (! ParseProperty(aErrorCode, propID)) {
3850 // XXX Much better to put stuff in the value parsers instead...
3851 const PRUnichar *params[] = {
3852 propertyName.get()
3854 REPORT_UNEXPECTED_P(PEValueParsingError, params);
3855 REPORT_UNEXPECTED(PEDeclDropped);
3856 OUTPUT_ERROR();
3857 ClearTempData(propID);
3858 return PR_FALSE;
3860 CLEAR_ERROR();
3862 // See if the declaration is followed by a "!important" declaration
3863 PRBool isImportant = PR_FALSE;
3864 if (!GetToken(aErrorCode, PR_TRUE)) {
3865 // EOF is a perfectly good way to end a declaration and declaration block
3866 TransferTempData(aDeclaration, propID, isImportant,
3867 aMustCallValueAppended, aChanged);
3868 return PR_TRUE;
3871 if (eCSSToken_Symbol == tk->mType && '!' == tk->mSymbol) {
3872 // Look for important ident
3873 if (!GetToken(aErrorCode, PR_TRUE)) {
3874 // Premature eof is not ok
3875 REPORT_UNEXPECTED_EOF(PEImportantEOF);
3876 ClearTempData(propID);
3877 return PR_FALSE;
3879 if ((eCSSToken_Ident != tk->mType) ||
3880 !tk->mIdent.LowerCaseEqualsLiteral("important")) {
3881 REPORT_UNEXPECTED_TOKEN(PEExpectedImportant);
3882 OUTPUT_ERROR();
3883 UngetToken();
3884 ClearTempData(propID);
3885 return PR_FALSE;
3887 isImportant = PR_TRUE;
3889 else {
3890 // Not a !important declaration
3891 UngetToken();
3894 // Make sure valid property declaration is terminated with either a
3895 // semicolon, EOF or a right-curly-brace (this last only when
3896 // aCheckForBraces is true).
3897 if (!GetToken(aErrorCode, PR_TRUE)) {
3898 // EOF is a perfectly good way to end a declaration and declaration block
3899 TransferTempData(aDeclaration, propID, isImportant,
3900 aMustCallValueAppended, aChanged);
3901 return PR_TRUE;
3903 if (eCSSToken_Symbol == tk->mType) {
3904 if (';' == tk->mSymbol) {
3905 TransferTempData(aDeclaration, propID, isImportant,
3906 aMustCallValueAppended, aChanged);
3907 return PR_TRUE;
3909 if (aCheckForBraces && '}' == tk->mSymbol) {
3910 // Unget the '}' so we'll be able to tell that this is the end
3911 // of the declaration block when we unwind from here.
3912 UngetToken();
3913 TransferTempData(aDeclaration, propID, isImportant,
3914 aMustCallValueAppended, aChanged);
3915 return PR_TRUE;
3918 if (aCheckForBraces)
3919 REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2);
3920 else
3921 REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd);
3922 REPORT_UNEXPECTED(PEDeclDropped);
3923 OUTPUT_ERROR();
3924 ClearTempData(propID);
3925 return PR_FALSE;
3928 void
3929 CSSParserImpl::ClearTempData(nsCSSProperty aPropID)
3931 if (nsCSSProps::IsShorthand(aPropID)) {
3932 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
3933 mTempData.ClearProperty(*p);
3935 } else {
3936 mTempData.ClearProperty(aPropID);
3938 mTempData.AssertInitialState();
3941 void
3942 CSSParserImpl::TransferTempData(nsCSSDeclaration* aDeclaration,
3943 nsCSSProperty aPropID, PRBool aIsImportant,
3944 PRBool aMustCallValueAppended,
3945 PRBool* aChanged)
3947 if (nsCSSProps::IsShorthand(aPropID)) {
3948 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
3949 DoTransferTempData(aDeclaration, *p, aIsImportant,
3950 aMustCallValueAppended, aChanged);
3952 } else {
3953 DoTransferTempData(aDeclaration, aPropID, aIsImportant,
3954 aMustCallValueAppended, aChanged);
3956 mTempData.AssertInitialState();
3959 // Perhaps the transferring code should be in nsCSSExpandedDataBlock, in
3960 // case some other caller wants to use it in the future (although I
3961 // can't think of why).
3962 void
3963 CSSParserImpl::DoTransferTempData(nsCSSDeclaration* aDeclaration,
3964 nsCSSProperty aPropID, PRBool aIsImportant,
3965 PRBool aMustCallValueAppended,
3966 PRBool* aChanged)
3968 NS_ASSERTION(mTempData.HasPropertyBit(aPropID), "oops");
3969 if (aIsImportant) {
3970 if (!mData.HasImportantBit(aPropID))
3971 *aChanged = PR_TRUE;
3972 mData.SetImportantBit(aPropID);
3973 } else {
3974 if (mData.HasImportantBit(aPropID)) {
3975 mTempData.ClearProperty(aPropID);
3976 return;
3980 if (aMustCallValueAppended || !mData.HasPropertyBit(aPropID)) {
3981 aDeclaration->ValueAppended(aPropID);
3984 mData.SetPropertyBit(aPropID);
3985 mTempData.ClearPropertyBit(aPropID);
3988 * Save needless copying and allocation by calling the destructor in
3989 * the destination, copying memory directly, and then using placement
3990 * new.
3992 void *v_source = mTempData.PropertyAt(aPropID);
3993 void *v_dest = mData.PropertyAt(aPropID);
3994 switch (nsCSSProps::kTypeTable[aPropID]) {
3995 case eCSSType_Value: {
3996 nsCSSValue *source = static_cast<nsCSSValue*>(v_source);
3997 nsCSSValue *dest = static_cast<nsCSSValue*>(v_dest);
3998 if (*source != *dest)
3999 *aChanged = PR_TRUE;
4000 dest->~nsCSSValue();
4001 memcpy(dest, source, sizeof(nsCSSValue));
4002 new (source) nsCSSValue();
4003 } break;
4005 case eCSSType_Rect: {
4006 nsCSSRect *source = static_cast<nsCSSRect*>(v_source);
4007 nsCSSRect *dest = static_cast<nsCSSRect*>(v_dest);
4008 if (*source != *dest)
4009 *aChanged = PR_TRUE;
4010 dest->~nsCSSRect();
4011 memcpy(dest, source, sizeof(nsCSSRect));
4012 new (source) nsCSSRect();
4013 } break;
4015 case eCSSType_ValuePair: {
4016 nsCSSValuePair *source = static_cast<nsCSSValuePair*>(v_source);
4017 nsCSSValuePair *dest = static_cast<nsCSSValuePair*>(v_dest);
4018 if (*source != *dest)
4019 *aChanged = PR_TRUE;
4020 dest->~nsCSSValuePair();
4021 memcpy(dest, source, sizeof(nsCSSValuePair));
4022 new (source) nsCSSValuePair();
4023 } break;
4025 case eCSSType_ValueList: {
4026 nsCSSValueList **source = static_cast<nsCSSValueList**>(v_source);
4027 nsCSSValueList **dest = static_cast<nsCSSValueList**>(v_dest);
4028 if (!nsCSSValueList::Equal(*source, *dest))
4029 *aChanged = PR_TRUE;
4030 delete *dest;
4031 *dest = *source;
4032 *source = nsnull;
4033 } break;
4035 case eCSSType_ValuePairList: {
4036 nsCSSValuePairList **source =
4037 static_cast<nsCSSValuePairList**>(v_source);
4038 nsCSSValuePairList **dest =
4039 static_cast<nsCSSValuePairList**>(v_dest);
4040 if (!nsCSSValuePairList::Equal(*source, *dest))
4041 *aChanged = PR_TRUE;
4042 delete *dest;
4043 *dest = *source;
4044 *source = nsnull;
4045 } break;
4049 static const nsCSSProperty kBorderTopIDs[] = {
4050 eCSSProperty_border_top_width,
4051 eCSSProperty_border_top_style,
4052 eCSSProperty_border_top_color
4054 static const nsCSSProperty kBorderRightIDs[] = {
4055 eCSSProperty_border_right_width_value,
4056 eCSSProperty_border_right_style_value,
4057 eCSSProperty_border_right_color_value,
4058 eCSSProperty_border_right_width,
4059 eCSSProperty_border_right_style,
4060 eCSSProperty_border_right_color
4062 static const nsCSSProperty kBorderBottomIDs[] = {
4063 eCSSProperty_border_bottom_width,
4064 eCSSProperty_border_bottom_style,
4065 eCSSProperty_border_bottom_color
4067 static const nsCSSProperty kBorderLeftIDs[] = {
4068 eCSSProperty_border_left_width_value,
4069 eCSSProperty_border_left_style_value,
4070 eCSSProperty_border_left_color_value,
4071 eCSSProperty_border_left_width,
4072 eCSSProperty_border_left_style,
4073 eCSSProperty_border_left_color
4075 static const nsCSSProperty kBorderStartIDs[] = {
4076 eCSSProperty_border_start_width_value,
4077 eCSSProperty_border_start_style_value,
4078 eCSSProperty_border_start_color_value,
4079 eCSSProperty_border_start_width,
4080 eCSSProperty_border_start_style,
4081 eCSSProperty_border_start_color
4083 static const nsCSSProperty kBorderEndIDs[] = {
4084 eCSSProperty_border_end_width_value,
4085 eCSSProperty_border_end_style_value,
4086 eCSSProperty_border_end_color_value,
4087 eCSSProperty_border_end_width,
4088 eCSSProperty_border_end_style,
4089 eCSSProperty_border_end_color
4091 static const nsCSSProperty kColumnRuleIDs[] = {
4092 eCSSProperty__moz_column_rule_width,
4093 eCSSProperty__moz_column_rule_style,
4094 eCSSProperty__moz_column_rule_color
4097 PRBool CSSParserImpl::ParseEnum(nsresult& aErrorCode, nsCSSValue& aValue,
4098 const PRInt32 aKeywordTable[])
4100 nsSubstring* ident = NextIdent(aErrorCode);
4101 if (nsnull == ident) {
4102 return PR_FALSE;
4104 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
4105 if (eCSSKeyword_UNKNOWN < keyword) {
4106 PRInt32 value;
4107 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
4108 aValue.SetIntValue(value, eCSSUnit_Enumerated);
4109 return PR_TRUE;
4113 // Put the unknown identifier back and return
4114 UngetToken();
4115 return PR_FALSE;
4119 struct UnitInfo {
4120 char name[5]; // needs to be long enough for the longest unit, with
4121 // terminating null.
4122 PRUint32 length;
4123 nsCSSUnit unit;
4124 PRInt32 type;
4127 #define STR_WITH_LEN(_str) \
4128 _str, sizeof(_str) - 1
4130 const UnitInfo UnitData[] = {
4131 { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH },
4132 { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH },
4133 { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH },
4134 { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH },
4135 { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH },
4136 { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH },
4137 { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH },
4138 { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH },
4139 { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH },
4140 { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE },
4141 { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE },
4142 { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE },
4143 { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY },
4144 { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY },
4145 { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME },
4146 { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME }
4149 #undef STR_WITH_LEN
4151 PRBool CSSParserImpl::TranslateDimension(nsresult& aErrorCode,
4152 nsCSSValue& aValue,
4153 PRInt32 aVariantMask,
4154 float aNumber,
4155 const nsString& aUnit)
4157 nsCSSUnit units;
4158 PRInt32 type = 0;
4159 if (!aUnit.IsEmpty()) {
4160 PRUint32 i;
4161 for (i = 0; i < NS_ARRAY_LENGTH(UnitData); ++i) {
4162 if (aUnit.LowerCaseEqualsASCII(UnitData[i].name,
4163 UnitData[i].length)) {
4164 units = UnitData[i].unit;
4165 type = UnitData[i].type;
4166 break;
4170 if (i == NS_ARRAY_LENGTH(UnitData)) {
4171 // Unknown unit
4172 return PR_FALSE;
4174 } else {
4175 // Must be a zero number...
4176 NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
4177 if ((VARIANT_LENGTH & aVariantMask) != 0) {
4178 units = eCSSUnit_Point;
4179 type = VARIANT_LENGTH;
4181 else if ((VARIANT_ANGLE & aVariantMask) != 0) {
4182 units = eCSSUnit_Degree;
4183 type = VARIANT_ANGLE;
4185 else if ((VARIANT_FREQUENCY & aVariantMask) != 0) {
4186 units = eCSSUnit_Hertz;
4187 type = VARIANT_FREQUENCY;
4189 else if ((VARIANT_TIME & aVariantMask) != 0) {
4190 units = eCSSUnit_Seconds;
4191 type = VARIANT_TIME;
4193 else {
4194 NS_ERROR("Variant mask does not include dimension; why were we called?");
4195 return PR_FALSE;
4198 if ((type & aVariantMask) != 0) {
4199 aValue.SetFloatValue(aNumber, units);
4200 return PR_TRUE;
4202 return PR_FALSE;
4205 PRBool CSSParserImpl::ParsePositiveVariant(nsresult& aErrorCode,
4206 nsCSSValue& aValue,
4207 PRInt32 aVariantMask,
4208 const PRInt32 aKeywordTable[])
4210 if (ParseVariant(aErrorCode, aValue, aVariantMask, aKeywordTable)) {
4211 if (eCSSUnit_Number == aValue.GetUnit() ||
4212 aValue.IsLengthUnit()){
4213 if (aValue.GetFloatValue() < 0) {
4214 UngetToken();
4215 return PR_FALSE;
4218 else if(aValue.GetUnit() == eCSSUnit_Percent) {
4219 if (aValue.GetPercentValue() < 0) {
4220 UngetToken();
4221 return PR_FALSE;
4223 } else if (aValue.GetUnit() == eCSSUnit_Integer) {
4224 if (aValue.GetIntValue() < 0) {
4225 UngetToken();
4226 return PR_FALSE;
4229 return PR_TRUE;
4231 return PR_FALSE;
4234 // Assigns to aValue iff it returns PR_TRUE.
4235 PRBool CSSParserImpl::ParseVariant(nsresult& aErrorCode, nsCSSValue& aValue,
4236 PRInt32 aVariantMask,
4237 const PRInt32 aKeywordTable[])
4239 NS_ASSERTION(IsParsingCompoundProperty() ||
4240 ((~aVariantMask) & (VARIANT_LENGTH|VARIANT_COLOR)),
4241 "cannot distinguish lengths and colors in quirks mode");
4243 if (!GetToken(aErrorCode, PR_TRUE)) {
4244 return PR_FALSE;
4246 nsCSSToken* tk = &mToken;
4247 if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE)) != 0) &&
4248 (eCSSToken_Ident == tk->mType)) {
4249 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
4250 if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
4251 if ((aVariantMask & VARIANT_AUTO) != 0) {
4252 if (eCSSKeyword_auto == keyword) {
4253 aValue.SetAutoValue();
4254 return PR_TRUE;
4257 if ((aVariantMask & VARIANT_INHERIT) != 0) {
4258 // XXX Should we check IsParsingCompoundProperty, or do all
4259 // callers handle it? (Not all callers set it, though, since
4260 // they want the quirks that are disabled by setting it.)
4261 if (eCSSKeyword_inherit == keyword) {
4262 aValue.SetInheritValue();
4263 return PR_TRUE;
4265 else if (eCSSKeyword__moz_initial == keyword) { // anything that can inherit can also take an initial val.
4266 aValue.SetInitialValue();
4267 return PR_TRUE;
4270 if ((aVariantMask & VARIANT_NONE) != 0) {
4271 if (eCSSKeyword_none == keyword) {
4272 aValue.SetNoneValue();
4273 return PR_TRUE;
4276 if ((aVariantMask & VARIANT_NORMAL) != 0) {
4277 if (eCSSKeyword_normal == keyword) {
4278 aValue.SetNormalValue();
4279 return PR_TRUE;
4282 if ((aVariantMask & VARIANT_SYSFONT) != 0) {
4283 if (eCSSKeyword__moz_use_system_font == keyword &&
4284 !IsParsingCompoundProperty()) {
4285 aValue.SetSystemFontValue();
4286 return PR_TRUE;
4289 if ((aVariantMask & VARIANT_KEYWORD) != 0) {
4290 PRInt32 value;
4291 if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
4292 aValue.SetIntValue(value, eCSSUnit_Enumerated);
4293 return PR_TRUE;
4298 if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | VARIANT_FREQUENCY | VARIANT_TIME)) != 0) &&
4299 tk->IsDimension()) {
4300 if (TranslateDimension(aErrorCode, aValue, aVariantMask, tk->mNumber, tk->mIdent)) {
4301 return PR_TRUE;
4303 // Put the token back; we didn't parse it, so we shouldn't consume it
4304 UngetToken();
4305 return PR_FALSE;
4307 if (((aVariantMask & VARIANT_PERCENT) != 0) &&
4308 (eCSSToken_Percentage == tk->mType)) {
4309 aValue.SetPercentValue(tk->mNumber);
4310 return PR_TRUE;
4312 if (((aVariantMask & VARIANT_NUMBER) != 0) &&
4313 (eCSSToken_Number == tk->mType)) {
4314 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
4315 return PR_TRUE;
4317 if (((aVariantMask & VARIANT_INTEGER) != 0) &&
4318 (eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
4319 aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
4320 return PR_TRUE;
4322 if (mNavQuirkMode && !IsParsingCompoundProperty()) { // NONSTANDARD: Nav interprets unitless numbers as px
4323 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
4324 (eCSSToken_Number == tk->mType)) {
4325 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
4326 return PR_TRUE;
4330 #ifdef MOZ_SVG
4331 if (IsSVGMode() && !IsParsingCompoundProperty()) {
4332 // STANDARD: SVG Spec states that lengths and coordinates can be unitless
4333 // in which case they default to user-units (1 px = 1 user unit)
4334 if (((aVariantMask & VARIANT_LENGTH) != 0) &&
4335 (eCSSToken_Number == tk->mType)) {
4336 aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
4337 return PR_TRUE;
4340 #endif
4342 if (((aVariantMask & VARIANT_URL) != 0) &&
4343 (eCSSToken_Function == tk->mType) &&
4344 tk->mIdent.LowerCaseEqualsLiteral("url")) {
4345 if (ParseURL(aErrorCode, aValue)) {
4346 return PR_TRUE;
4348 return PR_FALSE;
4350 if ((aVariantMask & VARIANT_COLOR) != 0) {
4351 if ((mNavQuirkMode && !IsParsingCompoundProperty()) || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
4352 (eCSSToken_ID == tk->mType) ||
4353 (eCSSToken_Ref == tk->mType) ||
4354 (eCSSToken_Ident == tk->mType) ||
4355 ((eCSSToken_Function == tk->mType) &&
4356 (tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
4357 tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
4358 tk->mIdent.LowerCaseEqualsLiteral("-moz-rgba") ||
4359 tk->mIdent.LowerCaseEqualsLiteral("-moz-hsla") ||
4360 tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
4361 tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
4363 // Put token back so that parse color can get it
4364 UngetToken();
4365 if (ParseColor(aErrorCode, aValue)) {
4366 return PR_TRUE;
4368 return PR_FALSE;
4371 if (((aVariantMask & VARIANT_STRING) != 0) &&
4372 (eCSSToken_String == tk->mType)) {
4373 nsAutoString buffer;
4374 buffer.Append(tk->mSymbol);
4375 buffer.Append(tk->mIdent);
4376 buffer.Append(tk->mSymbol);
4377 aValue.SetStringValue(buffer, eCSSUnit_String);
4378 return PR_TRUE;
4380 if (((aVariantMask & VARIANT_IDENTIFIER) != 0) &&
4381 (eCSSToken_Ident == tk->mType)) {
4382 aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
4383 return PR_TRUE;
4385 if (((aVariantMask & VARIANT_COUNTER) != 0) &&
4386 (eCSSToken_Function == tk->mType) &&
4387 (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
4388 tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
4389 return ParseCounter(aErrorCode, aValue);
4391 if (((aVariantMask & VARIANT_ATTR) != 0) &&
4392 (eCSSToken_Function == tk->mType) &&
4393 tk->mIdent.LowerCaseEqualsLiteral("attr")) {
4394 return ParseAttr(aErrorCode, aValue);
4397 UngetToken();
4398 return PR_FALSE;
4402 PRBool CSSParserImpl::ParseCounter(nsresult& aErrorCode, nsCSSValue& aValue)
4404 nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ?
4405 eCSSUnit_Counter : eCSSUnit_Counters);
4407 if (!ExpectSymbol(aErrorCode, '(', PR_FALSE))
4408 return PR_FALSE;
4410 if (!GetNonCloseParenToken(aErrorCode, PR_TRUE) ||
4411 eCSSToken_Ident != mToken.mType) {
4412 SkipUntil(aErrorCode, ')');
4413 return PR_FALSE;
4416 nsRefPtr<nsCSSValue::Array> val =
4417 nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3);
4418 if (!val) {
4419 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
4420 return PR_FALSE;
4423 val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_String);
4425 if (eCSSUnit_Counters == unit) {
4426 // get mandatory separator string
4427 if (!ExpectSymbol(aErrorCode, ',', PR_TRUE) ||
4428 !(GetNonCloseParenToken(aErrorCode, PR_TRUE) &&
4429 eCSSToken_String == mToken.mType)) {
4430 SkipUntil(aErrorCode, ')');
4431 return PR_FALSE;
4433 val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String);
4436 // get optional type
4437 PRInt32 type = NS_STYLE_LIST_STYLE_DECIMAL;
4438 if (ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
4439 nsCSSKeyword keyword;
4440 PRBool success = GetNonCloseParenToken(aErrorCode, PR_TRUE) &&
4441 eCSSToken_Ident == mToken.mType &&
4442 (keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent)) !=
4443 eCSSKeyword_UNKNOWN;
4444 if (success) {
4445 if (keyword == eCSSKeyword_none) {
4446 type = NS_STYLE_LIST_STYLE_NONE;
4447 } else {
4448 success = nsCSSProps::FindKeyword(keyword,
4449 nsCSSProps::kListStyleKTable, type);
4452 if (!success) {
4453 SkipUntil(aErrorCode, ')');
4454 return PR_FALSE;
4457 PRInt32 typeItem = eCSSUnit_Counters == unit ? 2 : 1;
4458 val->Item(typeItem).SetIntValue(type, eCSSUnit_Enumerated);
4460 if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
4461 SkipUntil(aErrorCode, ')');
4462 return PR_FALSE;
4465 aValue.SetArrayValue(val, unit);
4466 return PR_TRUE;
4469 PRBool CSSParserImpl::ParseAttr(nsresult& aErrorCode, nsCSSValue& aValue)
4471 if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
4472 if (GetToken(aErrorCode, PR_TRUE)) {
4473 nsAutoString attr;
4474 if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
4475 nsAutoString holdIdent(mToken.mIdent);
4476 if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // namespace
4477 PRInt32 nameSpaceID;
4478 if (!GetNamespaceIdForPrefix(holdIdent, &nameSpaceID, aErrorCode)) {
4479 return PR_FALSE;
4481 attr.AppendInt(nameSpaceID, 10);
4482 attr.Append(PRUnichar('|'));
4483 if (! GetToken(aErrorCode, PR_FALSE)) {
4484 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4485 return PR_FALSE;
4487 if (eCSSToken_Ident == mToken.mType) {
4488 if (mCaseSensitive) {
4489 attr.Append(mToken.mIdent);
4490 } else {
4491 nsAutoString buffer;
4492 ToLowerCase(mToken.mIdent, buffer);
4493 attr.Append(buffer);
4496 else {
4497 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4498 UngetToken();
4499 return PR_FALSE;
4502 else { // no namespace
4503 if (mCaseSensitive) {
4504 attr = holdIdent;
4506 else {
4507 ToLowerCase(holdIdent, attr);
4511 else if (mToken.IsSymbol('*')) { // namespace wildcard
4512 // Wildcard namespace makes no sense here and is not allowed
4513 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4514 UngetToken();
4515 return PR_FALSE;
4517 else if (mToken.IsSymbol('|')) { // explicit NO namespace
4518 if (! GetToken(aErrorCode, PR_FALSE)) {
4519 REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
4520 return PR_FALSE;
4522 if (eCSSToken_Ident == mToken.mType) {
4523 if (mCaseSensitive) {
4524 attr.Append(mToken.mIdent);
4525 } else {
4526 nsAutoString buffer;
4527 ToLowerCase(mToken.mIdent, buffer);
4528 attr.Append(buffer);
4531 else {
4532 REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
4533 UngetToken();
4534 return PR_FALSE;
4537 else {
4538 REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
4539 UngetToken();
4540 return PR_FALSE;
4542 if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
4543 aValue.SetStringValue(attr, eCSSUnit_Attr);
4544 return PR_TRUE;
4548 return PR_FALSE;
4551 PRBool
4552 CSSParserImpl::ParseURL(nsresult& aErrorCode, nsCSSValue& aValue)
4554 if (!mSheetPrincipal) {
4555 NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
4556 "origin principal");
4557 return PR_FALSE;
4560 if (!ExpectSymbol(aErrorCode, '(', PR_FALSE))
4561 return PR_FALSE;
4562 if (!GetURLToken(aErrorCode))
4563 return PR_FALSE;
4565 nsCSSToken* tk = &mToken;
4566 if (eCSSToken_String != tk->mType && eCSSToken_URL != tk->mType)
4567 return PR_FALSE;
4569 nsString url = tk->mIdent;
4570 if (!ExpectSymbol(aErrorCode, ')', PR_TRUE))
4571 return PR_FALSE;
4573 // Translate url into an absolute url if the url is relative to the
4574 // style sheet.
4575 nsCOMPtr<nsIURI> uri;
4576 NS_NewURI(getter_AddRefs(uri), url, nsnull, mBaseURL);
4578 nsStringBuffer* buffer = nsCSSValue::BufferFromString(url);
4579 if (NS_UNLIKELY(!buffer)) {
4580 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
4581 return PR_FALSE;
4583 nsCSSValue::URL *urlVal =
4584 new nsCSSValue::URL(uri, buffer, mSheetURL, mSheetPrincipal);
4586 buffer->Release();
4587 if (NS_UNLIKELY(!urlVal)) {
4588 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
4589 return PR_FALSE;
4591 aValue.SetURLValue(urlVal);
4592 return PR_TRUE;
4595 PRInt32 CSSParserImpl::ParseChoice(nsresult& aErrorCode, nsCSSValue aValues[],
4596 const nsCSSProperty aPropIDs[], PRInt32 aNumIDs)
4598 PRInt32 found = 0;
4599 nsAutoParseCompoundProperty compound(this);
4601 PRInt32 loop;
4602 for (loop = 0; loop < aNumIDs; loop++) {
4603 // Try each property parser in order
4604 PRInt32 hadFound = found;
4605 PRInt32 index;
4606 for (index = 0; index < aNumIDs; index++) {
4607 PRInt32 bit = 1 << index;
4608 if ((found & bit) == 0) {
4609 if (ParseSingleValueProperty(aErrorCode, aValues[index], aPropIDs[index])) {
4610 found |= bit;
4614 if (found == hadFound) { // found nothing new
4615 break;
4618 if (0 < found) {
4619 if (1 == found) { // only first property
4620 if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
4621 for (loop = 1; loop < aNumIDs; loop++) {
4622 aValues[loop].SetInheritValue();
4624 found = ((1 << aNumIDs) - 1);
4626 else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
4627 for (loop = 1; loop < aNumIDs; loop++) {
4628 aValues[loop].SetInitialValue();
4630 found = ((1 << aNumIDs) - 1);
4633 else { // more than one value, verify no inherits or initials
4634 for (loop = 0; loop < aNumIDs; loop++) {
4635 if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
4636 found = -1;
4637 break;
4639 else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
4640 found = -1;
4641 break;
4646 return found;
4649 void
4650 CSSParserImpl::AppendValue(nsCSSProperty aPropID,
4651 const nsCSSValue& aValue)
4653 NS_ASSERTION(0 <= aPropID && aPropID < eCSSProperty_COUNT_no_shorthands,
4654 "property out of range");
4655 NS_ASSERTION(nsCSSProps::kTypeTable[aPropID] == eCSSType_Value,
4656 nsPrintfCString(64, "type error (property=\'%s\')",
4657 nsCSSProps::GetStringValue(aPropID).get()).get());
4658 nsCSSValue& storage =
4659 *static_cast<nsCSSValue*>(mTempData.PropertyAt(aPropID));
4660 storage = aValue;
4661 mTempData.SetPropertyBit(aPropID);
4665 * Parse a "box" property. Box properties have 1 to 4 values. When less
4666 * than 4 values are provided a standard mapping is used to replicate
4667 * existing values.
4669 PRBool CSSParserImpl::ParseBoxProperties(nsresult& aErrorCode,
4670 nsCSSRect& aResult,
4671 const nsCSSProperty aPropIDs[])
4673 // Get up to four values for the property
4674 PRInt32 count = 0;
4675 PRInt32 index;
4676 nsCSSRect result;
4677 for (index = 0; index < 4; index++) {
4678 if (! ParseSingleValueProperty(aErrorCode,
4679 result.*(nsCSSRect::sides[index]),
4680 aPropIDs[index])) {
4681 break;
4683 count++;
4685 if ((count == 0) || (PR_FALSE == ExpectEndProperty(aErrorCode))) {
4686 return PR_FALSE;
4689 if (1 < count) { // verify no more than single inherit or initial
4690 for (index = 0; index < 4; index++) {
4691 nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
4692 if (eCSSUnit_Inherit == unit || eCSSUnit_Initial == unit) {
4693 return PR_FALSE;
4698 // Provide missing values by replicating some of the values found
4699 switch (count) {
4700 case 1: // Make right == top
4701 result.mRight = result.mTop;
4702 case 2: // Make bottom == top
4703 result.mBottom = result.mTop;
4704 case 3: // Make left == right
4705 result.mLeft = result.mRight;
4708 for (index = 0; index < 4; index++) {
4709 mTempData.SetPropertyBit(aPropIDs[index]);
4711 aResult = result;
4712 return PR_TRUE;
4715 PRBool CSSParserImpl::ParseDirectionalBoxProperty(nsresult& aErrorCode,
4716 nsCSSProperty aProperty,
4717 PRInt32 aSourceType)
4719 const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty);
4720 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
4721 "not box property with physical vs. logical cascading");
4722 nsCSSValue value;
4723 if (!ParseSingleValueProperty(aErrorCode, value, subprops[0]) ||
4724 !ExpectEndProperty(aErrorCode))
4725 return PR_FALSE;
4727 AppendValue(subprops[0], value);
4728 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
4729 AppendValue(subprops[1], typeVal);
4730 AppendValue(subprops[2], typeVal);
4731 aErrorCode = NS_OK;
4732 return PR_TRUE;
4735 PRBool CSSParserImpl::ParseProperty(nsresult& aErrorCode,
4736 nsCSSProperty aPropID)
4738 NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range");
4740 switch (aPropID) { // handle shorthand or multiple properties
4741 case eCSSProperty_background:
4742 return ParseBackground(aErrorCode);
4743 case eCSSProperty_background_position:
4744 return ParseBackgroundPosition(aErrorCode);
4745 case eCSSProperty_border:
4746 return ParseBorderSide(aErrorCode, kBorderTopIDs, PR_TRUE);
4747 case eCSSProperty_border_color:
4748 return ParseBorderColor(aErrorCode);
4749 case eCSSProperty_border_spacing:
4750 return ParseBorderSpacing(aErrorCode);
4751 case eCSSProperty_border_style:
4752 return ParseBorderStyle(aErrorCode);
4753 case eCSSProperty_border_bottom:
4754 return ParseBorderSide(aErrorCode, kBorderBottomIDs, PR_FALSE);
4755 case eCSSProperty_border_end:
4756 return ParseDirectionalBorderSide(aErrorCode, kBorderEndIDs,
4757 NS_BOXPROP_SOURCE_LOGICAL);
4758 case eCSSProperty_border_left:
4759 return ParseDirectionalBorderSide(aErrorCode, kBorderLeftIDs,
4760 NS_BOXPROP_SOURCE_PHYSICAL);
4761 case eCSSProperty_border_right:
4762 return ParseDirectionalBorderSide(aErrorCode, kBorderRightIDs,
4763 NS_BOXPROP_SOURCE_PHYSICAL);
4764 case eCSSProperty_border_start:
4765 return ParseDirectionalBorderSide(aErrorCode, kBorderStartIDs,
4766 NS_BOXPROP_SOURCE_LOGICAL);
4767 case eCSSProperty_border_top:
4768 return ParseBorderSide(aErrorCode, kBorderTopIDs, PR_FALSE);
4769 case eCSSProperty_border_bottom_colors:
4770 return ParseBorderColors(aErrorCode,
4771 &mTempData.mMargin.mBorderColors.mBottom,
4772 aPropID);
4773 case eCSSProperty_border_left_colors:
4774 return ParseBorderColors(aErrorCode,
4775 &mTempData.mMargin.mBorderColors.mLeft,
4776 aPropID);
4777 case eCSSProperty_border_right_colors:
4778 return ParseBorderColors(aErrorCode,
4779 &mTempData.mMargin.mBorderColors.mRight,
4780 aPropID);
4781 case eCSSProperty_border_top_colors:
4782 return ParseBorderColors(aErrorCode,
4783 &mTempData.mMargin.mBorderColors.mTop,
4784 aPropID);
4785 case eCSSProperty_border_image:
4786 return ParseBorderImage(aErrorCode);
4787 case eCSSProperty_border_width:
4788 return ParseBorderWidth(aErrorCode);
4789 case eCSSProperty_border_end_color:
4790 return ParseDirectionalBoxProperty(aErrorCode,
4791 eCSSProperty_border_end_color,
4792 NS_BOXPROP_SOURCE_LOGICAL);
4793 case eCSSProperty_border_left_color:
4794 return ParseDirectionalBoxProperty(aErrorCode,
4795 eCSSProperty_border_left_color,
4796 NS_BOXPROP_SOURCE_PHYSICAL);
4797 case eCSSProperty_border_right_color:
4798 return ParseDirectionalBoxProperty(aErrorCode,
4799 eCSSProperty_border_right_color,
4800 NS_BOXPROP_SOURCE_PHYSICAL);
4801 case eCSSProperty_border_start_color:
4802 return ParseDirectionalBoxProperty(aErrorCode,
4803 eCSSProperty_border_start_color,
4804 NS_BOXPROP_SOURCE_LOGICAL);
4805 case eCSSProperty_border_end_width:
4806 return ParseDirectionalBoxProperty(aErrorCode,
4807 eCSSProperty_border_end_width,
4808 NS_BOXPROP_SOURCE_LOGICAL);
4809 case eCSSProperty_border_left_width:
4810 return ParseDirectionalBoxProperty(aErrorCode,
4811 eCSSProperty_border_left_width,
4812 NS_BOXPROP_SOURCE_PHYSICAL);
4813 case eCSSProperty_border_right_width:
4814 return ParseDirectionalBoxProperty(aErrorCode,
4815 eCSSProperty_border_right_width,
4816 NS_BOXPROP_SOURCE_PHYSICAL);
4817 case eCSSProperty_border_start_width:
4818 return ParseDirectionalBoxProperty(aErrorCode,
4819 eCSSProperty_border_start_width,
4820 NS_BOXPROP_SOURCE_LOGICAL);
4821 case eCSSProperty_border_end_style:
4822 return ParseDirectionalBoxProperty(aErrorCode,
4823 eCSSProperty_border_end_style,
4824 NS_BOXPROP_SOURCE_LOGICAL);
4825 case eCSSProperty_border_left_style:
4826 return ParseDirectionalBoxProperty(aErrorCode,
4827 eCSSProperty_border_left_style,
4828 NS_BOXPROP_SOURCE_PHYSICAL);
4829 case eCSSProperty_border_right_style:
4830 return ParseDirectionalBoxProperty(aErrorCode,
4831 eCSSProperty_border_right_style,
4832 NS_BOXPROP_SOURCE_PHYSICAL);
4833 case eCSSProperty_border_start_style:
4834 return ParseDirectionalBoxProperty(aErrorCode,
4835 eCSSProperty_border_start_style,
4836 NS_BOXPROP_SOURCE_LOGICAL);
4837 case eCSSProperty__moz_border_radius:
4838 return ParseBorderRadius(aErrorCode);
4839 case eCSSProperty__moz_outline_radius:
4840 return ParseOutlineRadius(aErrorCode);
4841 case eCSSProperty_clip:
4842 return ParseRect(mTempData.mDisplay.mClip, aErrorCode,
4843 eCSSProperty_clip);
4844 case eCSSProperty__moz_column_rule:
4845 return ParseBorderSide(aErrorCode, kColumnRuleIDs, PR_FALSE);
4846 case eCSSProperty_content:
4847 return ParseContent(aErrorCode);
4848 case eCSSProperty_counter_increment:
4849 return ParseCounterData(aErrorCode, &mTempData.mContent.mCounterIncrement,
4850 aPropID);
4851 case eCSSProperty_counter_reset:
4852 return ParseCounterData(aErrorCode, &mTempData.mContent.mCounterReset,
4853 aPropID);
4854 case eCSSProperty_cue:
4855 return ParseCue(aErrorCode);
4856 case eCSSProperty_cursor:
4857 return ParseCursor(aErrorCode);
4858 case eCSSProperty_font:
4859 return ParseFont(aErrorCode);
4860 case eCSSProperty_image_region:
4861 return ParseRect(mTempData.mList.mImageRegion, aErrorCode,
4862 eCSSProperty_image_region);
4863 case eCSSProperty_list_style:
4864 return ParseListStyle(aErrorCode);
4865 case eCSSProperty_margin:
4866 return ParseMargin(aErrorCode);
4867 case eCSSProperty_margin_end:
4868 return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_margin_end,
4869 NS_BOXPROP_SOURCE_LOGICAL);
4870 case eCSSProperty_margin_left:
4871 return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_margin_left,
4872 NS_BOXPROP_SOURCE_PHYSICAL);
4873 case eCSSProperty_margin_right:
4874 return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_margin_right,
4875 NS_BOXPROP_SOURCE_PHYSICAL);
4876 case eCSSProperty_margin_start:
4877 return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_margin_start,
4878 NS_BOXPROP_SOURCE_LOGICAL);
4879 case eCSSProperty_outline:
4880 return ParseOutline(aErrorCode);
4881 case eCSSProperty_overflow:
4882 return ParseOverflow(aErrorCode);
4883 case eCSSProperty_padding:
4884 return ParsePadding(aErrorCode);
4885 case eCSSProperty_padding_end:
4886 return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_padding_end,
4887 NS_BOXPROP_SOURCE_LOGICAL);
4888 case eCSSProperty_padding_left:
4889 return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_padding_left,
4890 NS_BOXPROP_SOURCE_PHYSICAL);
4891 case eCSSProperty_padding_right:
4892 return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_padding_right,
4893 NS_BOXPROP_SOURCE_PHYSICAL);
4894 case eCSSProperty_padding_start:
4895 return ParseDirectionalBoxProperty(aErrorCode, eCSSProperty_padding_start,
4896 NS_BOXPROP_SOURCE_LOGICAL);
4897 case eCSSProperty_pause:
4898 return ParsePause(aErrorCode);
4899 case eCSSProperty_quotes:
4900 return ParseQuotes(aErrorCode);
4901 case eCSSProperty_size:
4902 return ParseSize(aErrorCode);
4903 case eCSSProperty_text_shadow:
4904 return ParseTextShadow(aErrorCode);
4905 case eCSSProperty_box_shadow:
4906 return ParseBoxShadow(aErrorCode);
4908 #ifdef MOZ_SVG
4909 case eCSSProperty_fill:
4910 return ParsePaint(aErrorCode, &mTempData.mSVG.mFill, eCSSProperty_fill);
4911 case eCSSProperty_stroke:
4912 return ParsePaint(aErrorCode, &mTempData.mSVG.mStroke, eCSSProperty_stroke);
4913 case eCSSProperty_stroke_dasharray:
4914 return ParseDasharray(aErrorCode);
4915 case eCSSProperty_marker:
4916 return ParseMarker(aErrorCode);
4917 #endif
4919 // Strip out properties we use internally.
4920 case eCSSProperty__x_system_font:
4921 case eCSSProperty_margin_end_value:
4922 case eCSSProperty_margin_left_value:
4923 case eCSSProperty_margin_right_value:
4924 case eCSSProperty_margin_start_value:
4925 case eCSSProperty_margin_left_ltr_source:
4926 case eCSSProperty_margin_left_rtl_source:
4927 case eCSSProperty_margin_right_ltr_source:
4928 case eCSSProperty_margin_right_rtl_source:
4929 case eCSSProperty_padding_end_value:
4930 case eCSSProperty_padding_left_value:
4931 case eCSSProperty_padding_right_value:
4932 case eCSSProperty_padding_start_value:
4933 case eCSSProperty_padding_left_ltr_source:
4934 case eCSSProperty_padding_left_rtl_source:
4935 case eCSSProperty_padding_right_ltr_source:
4936 case eCSSProperty_padding_right_rtl_source:
4937 case eCSSProperty_border_end_color_value:
4938 case eCSSProperty_border_left_color_value:
4939 case eCSSProperty_border_right_color_value:
4940 case eCSSProperty_border_start_color_value:
4941 case eCSSProperty_border_left_color_ltr_source:
4942 case eCSSProperty_border_left_color_rtl_source:
4943 case eCSSProperty_border_right_color_ltr_source:
4944 case eCSSProperty_border_right_color_rtl_source:
4945 case eCSSProperty_border_end_style_value:
4946 case eCSSProperty_border_left_style_value:
4947 case eCSSProperty_border_right_style_value:
4948 case eCSSProperty_border_start_style_value:
4949 case eCSSProperty_border_left_style_ltr_source:
4950 case eCSSProperty_border_left_style_rtl_source:
4951 case eCSSProperty_border_right_style_ltr_source:
4952 case eCSSProperty_border_right_style_rtl_source:
4953 case eCSSProperty_border_end_width_value:
4954 case eCSSProperty_border_left_width_value:
4955 case eCSSProperty_border_right_width_value:
4956 case eCSSProperty_border_start_width_value:
4957 case eCSSProperty_border_left_width_ltr_source:
4958 case eCSSProperty_border_left_width_rtl_source:
4959 case eCSSProperty_border_right_width_ltr_source:
4960 case eCSSProperty_border_right_width_rtl_source:
4961 // The user can't use these
4962 REPORT_UNEXPECTED(PEInaccessibleProperty2);
4963 return PR_FALSE;
4965 default: // must be single property
4967 nsCSSValue value;
4968 if (ParseSingleValueProperty(aErrorCode, value, aPropID)) {
4969 if (ExpectEndProperty(aErrorCode)) {
4970 AppendValue(aPropID, value);
4971 aErrorCode = NS_OK;
4972 return PR_TRUE;
4974 // XXX Report errors?
4976 // XXX Report errors?
4979 return PR_FALSE;
4982 // Bits used in determining which background position info we have
4983 #define BG_CENTER NS_STYLE_BG_POSITION_CENTER
4984 #define BG_TOP NS_STYLE_BG_POSITION_TOP
4985 #define BG_BOTTOM NS_STYLE_BG_POSITION_BOTTOM
4986 #define BG_LEFT NS_STYLE_BG_POSITION_LEFT
4987 #define BG_RIGHT NS_STYLE_BG_POSITION_RIGHT
4988 #define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM)
4989 #define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT)
4991 PRBool CSSParserImpl::ParseSingleValueProperty(nsresult& aErrorCode,
4992 nsCSSValue& aValue,
4993 nsCSSProperty aPropID)
4995 switch (aPropID) {
4996 case eCSSProperty_UNKNOWN:
4997 case eCSSProperty_background:
4998 case eCSSProperty_background_position:
4999 case eCSSProperty_border:
5000 case eCSSProperty_border_color:
5001 case eCSSProperty_border_bottom_colors:
5002 case eCSSProperty_border_image:
5003 case eCSSProperty_border_left_colors:
5004 case eCSSProperty_border_right_colors:
5005 case eCSSProperty_border_end_color:
5006 case eCSSProperty_border_left_color:
5007 case eCSSProperty_border_right_color:
5008 case eCSSProperty_border_start_color:
5009 case eCSSProperty_border_end_style:
5010 case eCSSProperty_border_left_style:
5011 case eCSSProperty_border_right_style:
5012 case eCSSProperty_border_start_style:
5013 case eCSSProperty_border_end_width:
5014 case eCSSProperty_border_left_width:
5015 case eCSSProperty_border_right_width:
5016 case eCSSProperty_border_start_width:
5017 case eCSSProperty_border_top_colors:
5018 case eCSSProperty_border_spacing:
5019 case eCSSProperty_border_style:
5020 case eCSSProperty_border_bottom:
5021 case eCSSProperty_border_end:
5022 case eCSSProperty_border_left:
5023 case eCSSProperty_border_right:
5024 case eCSSProperty_border_start:
5025 case eCSSProperty_border_top:
5026 case eCSSProperty_border_width:
5027 case eCSSProperty__moz_border_radius:
5028 case eCSSProperty_box_shadow:
5029 case eCSSProperty_clip:
5030 case eCSSProperty__moz_column_rule:
5031 case eCSSProperty_content:
5032 case eCSSProperty_counter_increment:
5033 case eCSSProperty_counter_reset:
5034 case eCSSProperty_cue:
5035 case eCSSProperty_cursor:
5036 case eCSSProperty_font:
5037 case eCSSProperty_image_region:
5038 case eCSSProperty_list_style:
5039 case eCSSProperty_margin:
5040 case eCSSProperty_margin_end:
5041 case eCSSProperty_margin_left:
5042 case eCSSProperty_margin_right:
5043 case eCSSProperty_margin_start:
5044 case eCSSProperty_outline:
5045 case eCSSProperty__moz_outline_radius:
5046 case eCSSProperty_overflow:
5047 case eCSSProperty_padding:
5048 case eCSSProperty_padding_end:
5049 case eCSSProperty_padding_left:
5050 case eCSSProperty_padding_right:
5051 case eCSSProperty_padding_start:
5052 case eCSSProperty_pause:
5053 case eCSSProperty_quotes:
5054 case eCSSProperty_size:
5055 case eCSSProperty_text_shadow:
5056 case eCSSProperty_COUNT:
5057 #ifdef MOZ_SVG
5058 case eCSSProperty_fill:
5059 case eCSSProperty_stroke:
5060 case eCSSProperty_stroke_dasharray:
5061 case eCSSProperty_marker:
5062 #endif
5063 NS_ERROR("not a single value property");
5064 return PR_FALSE;
5066 case eCSSProperty__x_system_font:
5067 case eCSSProperty_margin_left_ltr_source:
5068 case eCSSProperty_margin_left_rtl_source:
5069 case eCSSProperty_margin_right_ltr_source:
5070 case eCSSProperty_margin_right_rtl_source:
5071 case eCSSProperty_padding_left_ltr_source:
5072 case eCSSProperty_padding_left_rtl_source:
5073 case eCSSProperty_padding_right_ltr_source:
5074 case eCSSProperty_padding_right_rtl_source:
5075 case eCSSProperty_border_left_color_ltr_source:
5076 case eCSSProperty_border_left_color_rtl_source:
5077 case eCSSProperty_border_right_color_ltr_source:
5078 case eCSSProperty_border_right_color_rtl_source:
5079 case eCSSProperty_border_left_style_ltr_source:
5080 case eCSSProperty_border_left_style_rtl_source:
5081 case eCSSProperty_border_right_style_ltr_source:
5082 case eCSSProperty_border_right_style_rtl_source:
5083 case eCSSProperty_border_left_width_ltr_source:
5084 case eCSSProperty_border_left_width_rtl_source:
5085 case eCSSProperty_border_right_width_ltr_source:
5086 case eCSSProperty_border_right_width_rtl_source:
5087 #ifdef MOZ_MATHML
5088 case eCSSProperty_script_size_multiplier:
5089 case eCSSProperty_script_min_size:
5090 #endif
5091 NS_ERROR("not currently parsed here");
5092 return PR_FALSE;
5094 case eCSSProperty_appearance:
5095 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5096 nsCSSProps::kAppearanceKTable);
5097 case eCSSProperty_azimuth:
5098 return ParseAzimuth(aErrorCode, aValue);
5099 case eCSSProperty_background_attachment:
5100 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5101 nsCSSProps::kBackgroundAttachmentKTable);
5102 case eCSSProperty__moz_background_clip:
5103 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5104 nsCSSProps::kBackgroundClipKTable);
5105 case eCSSProperty_background_color:
5106 return ParseVariant(aErrorCode, aValue, VARIANT_HCK,
5107 nsCSSProps::kBackgroundColorKTable);
5108 case eCSSProperty_background_image:
5109 return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
5110 case eCSSProperty__moz_background_inline_policy:
5111 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5112 nsCSSProps::kBackgroundInlinePolicyKTable);
5113 case eCSSProperty__moz_background_origin:
5114 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5115 nsCSSProps::kBackgroundOriginKTable);
5116 case eCSSProperty_background_repeat:
5117 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5118 nsCSSProps::kBackgroundRepeatKTable);
5119 case eCSSProperty_binding:
5120 return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
5121 case eCSSProperty_border_collapse:
5122 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5123 nsCSSProps::kBorderCollapseKTable);
5124 case eCSSProperty_border_bottom_color:
5125 case eCSSProperty_border_end_color_value: // for internal use
5126 case eCSSProperty_border_left_color_value: // for internal use
5127 case eCSSProperty_border_right_color_value: // for internal use
5128 case eCSSProperty_border_start_color_value: // for internal use
5129 case eCSSProperty_border_top_color:
5130 case eCSSProperty__moz_column_rule_color:
5131 return ParseVariant(aErrorCode, aValue, VARIANT_HCK,
5132 nsCSSProps::kBorderColorKTable);
5133 case eCSSProperty_border_bottom_style:
5134 case eCSSProperty_border_end_style_value: // for internal use
5135 case eCSSProperty_border_left_style_value: // for internal use
5136 case eCSSProperty_border_right_style_value: // for internal use
5137 case eCSSProperty_border_start_style_value: // for internal use
5138 case eCSSProperty_border_top_style:
5139 case eCSSProperty__moz_column_rule_style:
5140 return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
5141 nsCSSProps::kBorderStyleKTable);
5142 case eCSSProperty_border_bottom_width:
5143 case eCSSProperty_border_end_width_value: // for internal use
5144 case eCSSProperty_border_left_width_value: // for internal use
5145 case eCSSProperty_border_right_width_value: // for internal use
5146 case eCSSProperty_border_start_width_value: // for internal use
5147 case eCSSProperty_border_top_width:
5148 case eCSSProperty__moz_column_rule_width:
5149 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HKL,
5150 nsCSSProps::kBorderWidthKTable);
5151 case eCSSProperty__moz_border_radius_topLeft:
5152 case eCSSProperty__moz_border_radius_topRight:
5153 case eCSSProperty__moz_border_radius_bottomRight:
5154 case eCSSProperty__moz_border_radius_bottomLeft:
5155 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
5156 case eCSSProperty__moz_column_count:
5157 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_AHI, nsnull);
5158 case eCSSProperty__moz_column_width:
5159 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_AHL, nsnull);
5160 case eCSSProperty__moz_column_gap:
5161 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
5162 case eCSSProperty__moz_outline_radius_topLeft:
5163 case eCSSProperty__moz_outline_radius_topRight:
5164 case eCSSProperty__moz_outline_radius_bottomRight:
5165 case eCSSProperty__moz_outline_radius_bottomLeft:
5166 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
5167 case eCSSProperty_bottom:
5168 case eCSSProperty_top:
5169 case eCSSProperty_left:
5170 case eCSSProperty_right:
5171 return ParseVariant(aErrorCode, aValue, VARIANT_AHLP, nsnull);
5172 case eCSSProperty_box_align:
5173 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5174 nsCSSProps::kBoxAlignKTable);
5175 case eCSSProperty_box_direction:
5176 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5177 nsCSSProps::kBoxDirectionKTable);
5178 case eCSSProperty_box_flex:
5179 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
5180 case eCSSProperty_box_orient:
5181 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5182 nsCSSProps::kBoxOrientKTable);
5183 case eCSSProperty_box_pack:
5184 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5185 nsCSSProps::kBoxPackKTable);
5186 case eCSSProperty_box_ordinal_group:
5187 return ParseVariant(aErrorCode, aValue, VARIANT_HI, nsnull);
5188 #ifdef MOZ_SVG
5189 case eCSSProperty_clip_path:
5190 return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
5191 case eCSSProperty_clip_rule:
5192 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5193 nsCSSProps::kFillRuleKTable);
5194 case eCSSProperty_color_interpolation:
5195 case eCSSProperty_color_interpolation_filters:
5196 return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
5197 nsCSSProps::kColorInterpolationKTable);
5198 case eCSSProperty_dominant_baseline:
5199 return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
5200 nsCSSProps::kDominantBaselineKTable);
5201 case eCSSProperty_fill_opacity:
5202 return ParseVariant(aErrorCode, aValue, VARIANT_HN,
5203 nsnull);
5204 case eCSSProperty_fill_rule:
5205 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5206 nsCSSProps::kFillRuleKTable);
5207 case eCSSProperty_filter:
5208 return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
5209 case eCSSProperty_flood_color:
5210 return ParseVariant(aErrorCode, aValue, VARIANT_HC, nsnull);
5211 case eCSSProperty_flood_opacity:
5212 return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
5213 case eCSSProperty_lighting_color:
5214 return ParseVariant(aErrorCode, aValue, VARIANT_HC, nsnull);
5215 case eCSSProperty_marker_end:
5216 case eCSSProperty_marker_mid:
5217 case eCSSProperty_marker_start:
5218 return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
5219 case eCSSProperty_mask:
5220 return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
5221 case eCSSProperty_pointer_events:
5222 return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
5223 nsCSSProps::kPointerEventsKTable);
5224 case eCSSProperty_shape_rendering:
5225 return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
5226 nsCSSProps::kShapeRenderingKTable);
5227 case eCSSProperty_stop_color:
5228 return ParseVariant(aErrorCode, aValue, VARIANT_HC,
5229 nsnull);
5230 case eCSSProperty_stop_opacity:
5231 return ParseVariant(aErrorCode, aValue, VARIANT_HN,
5232 nsnull);
5233 case eCSSProperty_stroke_dashoffset:
5234 return ParseVariant(aErrorCode, aValue, VARIANT_HLPN,
5235 nsnull);
5236 case eCSSProperty_stroke_linecap:
5237 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5238 nsCSSProps::kStrokeLinecapKTable);
5239 case eCSSProperty_stroke_linejoin:
5240 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5241 nsCSSProps::kStrokeLinejoinKTable);
5242 case eCSSProperty_stroke_miterlimit:
5243 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HN,
5244 nsnull);
5245 case eCSSProperty_stroke_opacity:
5246 return ParseVariant(aErrorCode, aValue, VARIANT_HN,
5247 nsnull);
5248 case eCSSProperty_stroke_width:
5249 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLPN,
5250 nsnull);
5251 case eCSSProperty_text_anchor:
5252 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5253 nsCSSProps::kTextAnchorKTable);
5254 case eCSSProperty_text_rendering:
5255 return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
5256 nsCSSProps::kTextRenderingKTable);
5257 #endif
5258 case eCSSProperty_box_sizing:
5259 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5260 nsCSSProps::kBoxSizingKTable);
5261 case eCSSProperty_height:
5262 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_AHLP, nsnull);
5263 case eCSSProperty_width:
5264 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_AHKLP,
5265 nsCSSProps::kWidthKTable);
5266 case eCSSProperty_force_broken_image_icon:
5267 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HI, nsnull);
5268 case eCSSProperty_caption_side:
5269 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5270 nsCSSProps::kCaptionSideKTable);
5271 case eCSSProperty_clear:
5272 return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
5273 nsCSSProps::kClearKTable);
5274 case eCSSProperty_color:
5275 return ParseVariant(aErrorCode, aValue, VARIANT_HC, nsnull);
5276 case eCSSProperty_cue_after:
5277 case eCSSProperty_cue_before:
5278 return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
5279 case eCSSProperty_direction:
5280 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5281 nsCSSProps::kDirectionKTable);
5282 case eCSSProperty_display:
5283 if (ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kDisplayKTable)) {
5284 if (aValue.GetUnit() == eCSSUnit_Enumerated) {
5285 switch (aValue.GetIntValue()) {
5286 case NS_STYLE_DISPLAY_MARKER: // bug 2055
5287 case NS_STYLE_DISPLAY_RUN_IN: // bug 2056
5288 case NS_STYLE_DISPLAY_COMPACT: // bug 14983
5289 return PR_FALSE;
5292 return PR_TRUE;
5294 return PR_FALSE;
5295 case eCSSProperty_elevation:
5296 return ParseVariant(aErrorCode, aValue, VARIANT_HK | VARIANT_ANGLE,
5297 nsCSSProps::kElevationKTable);
5298 case eCSSProperty_empty_cells:
5299 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5300 nsCSSProps::kEmptyCellsKTable);
5301 case eCSSProperty_float:
5302 return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
5303 nsCSSProps::kFloatKTable);
5304 case eCSSProperty_float_edge:
5305 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5306 nsCSSProps::kFloatEdgeKTable);
5307 case eCSSProperty_font_family:
5308 return ParseFamily(aErrorCode, aValue);
5309 case eCSSProperty_font_size:
5310 return ParsePositiveVariant(aErrorCode, aValue,
5311 VARIANT_HKLP | VARIANT_SYSFONT,
5312 nsCSSProps::kFontSizeKTable);
5313 case eCSSProperty_font_size_adjust:
5314 return ParseVariant(aErrorCode, aValue, VARIANT_HON | VARIANT_SYSFONT,
5315 nsnull);
5316 case eCSSProperty_font_stretch:
5317 return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_SYSFONT,
5318 nsCSSProps::kFontStretchKTable);
5319 case eCSSProperty_font_style:
5320 return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_SYSFONT,
5321 nsCSSProps::kFontStyleKTable);
5322 case eCSSProperty_font_variant:
5323 return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_SYSFONT,
5324 nsCSSProps::kFontVariantKTable);
5325 case eCSSProperty_font_weight:
5326 return ParseFontWeight(aErrorCode, aValue);
5327 case eCSSProperty_ime_mode:
5328 return ParseVariant(aErrorCode, aValue, VARIANT_AHK | VARIANT_NORMAL,
5329 nsCSSProps::kIMEModeKTable);
5330 case eCSSProperty_letter_spacing:
5331 case eCSSProperty_word_spacing:
5332 return ParseVariant(aErrorCode, aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
5333 case eCSSProperty_line_height:
5334 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLPN | VARIANT_NORMAL | VARIANT_SYSFONT, nsnull);
5335 case eCSSProperty_list_style_image:
5336 return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
5337 case eCSSProperty_list_style_position:
5338 return ParseVariant(aErrorCode, aValue, VARIANT_HK, nsCSSProps::kListStylePositionKTable);
5339 case eCSSProperty_list_style_type:
5340 return ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kListStyleKTable);
5341 case eCSSProperty_margin_bottom:
5342 case eCSSProperty_margin_end_value: // for internal use
5343 case eCSSProperty_margin_left_value: // for internal use
5344 case eCSSProperty_margin_right_value: // for internal use
5345 case eCSSProperty_margin_start_value: // for internal use
5346 case eCSSProperty_margin_top:
5347 return ParseVariant(aErrorCode, aValue, VARIANT_AHLP, nsnull);
5348 case eCSSProperty_marker_offset:
5349 return ParseVariant(aErrorCode, aValue, VARIANT_AHL, nsnull);
5350 case eCSSProperty_marks:
5351 return ParseMarks(aErrorCode, aValue);
5352 case eCSSProperty_max_height:
5353 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLPO, nsnull);
5354 case eCSSProperty_max_width:
5355 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HKLPO,
5356 nsCSSProps::kWidthKTable);
5357 case eCSSProperty_min_height:
5358 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
5359 case eCSSProperty_min_width:
5360 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HKLP,
5361 nsCSSProps::kWidthKTable);
5362 case eCSSProperty_opacity:
5363 return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
5364 case eCSSProperty_orphans:
5365 case eCSSProperty_widows:
5366 return ParseVariant(aErrorCode, aValue, VARIANT_HI, nsnull);
5367 case eCSSProperty_outline_color:
5368 return ParseVariant(aErrorCode, aValue, VARIANT_HCK,
5369 nsCSSProps::kOutlineColorKTable);
5370 case eCSSProperty_outline_style:
5371 return ParseVariant(aErrorCode, aValue, VARIANT_HOK | VARIANT_AUTO,
5372 nsCSSProps::kOutlineStyleKTable);
5373 case eCSSProperty_outline_width:
5374 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HKL,
5375 nsCSSProps::kBorderWidthKTable);
5376 case eCSSProperty_outline_offset:
5377 return ParseVariant(aErrorCode, aValue, VARIANT_HL, nsnull);
5378 case eCSSProperty_overflow_x:
5379 case eCSSProperty_overflow_y:
5380 return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
5381 nsCSSProps::kOverflowSubKTable);
5382 case eCSSProperty_padding_bottom:
5383 case eCSSProperty_padding_end_value: // for internal use
5384 case eCSSProperty_padding_left_value: // for internal use
5385 case eCSSProperty_padding_right_value: // for internal use
5386 case eCSSProperty_padding_start_value: // for internal use
5387 case eCSSProperty_padding_top:
5388 return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
5389 case eCSSProperty_page:
5390 return ParseVariant(aErrorCode, aValue, VARIANT_AUTO | VARIANT_IDENTIFIER, nsnull);
5391 case eCSSProperty_page_break_after:
5392 case eCSSProperty_page_break_before:
5393 return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
5394 nsCSSProps::kPageBreakKTable);
5395 case eCSSProperty_page_break_inside:
5396 return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
5397 nsCSSProps::kPageBreakInsideKTable);
5398 case eCSSProperty_pause_after:
5399 case eCSSProperty_pause_before:
5400 return ParseVariant(aErrorCode, aValue, VARIANT_HTP, nsnull);
5401 case eCSSProperty_pitch:
5402 return ParseVariant(aErrorCode, aValue, VARIANT_HKF, nsCSSProps::kPitchKTable);
5403 case eCSSProperty_pitch_range:
5404 return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
5405 case eCSSProperty_position:
5406 return ParseVariant(aErrorCode, aValue, VARIANT_HK, nsCSSProps::kPositionKTable);
5407 case eCSSProperty_richness:
5408 return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
5409 #ifdef MOZ_MATHML
5410 // script-level can take Integer or Number values, but only Integer ("relative")
5411 // values can be specified in a style sheet. Also we only allow this property
5412 // when unsafe rules are enabled, because otherwise it could interfere
5413 // with rulenode optimizations if used in a non-MathML-enabled document.
5414 case eCSSProperty_script_level:
5415 if (!mUnsafeRulesEnabled)
5416 return PR_FALSE;
5417 return ParseVariant(aErrorCode, aValue, VARIANT_HI, nsnull);
5418 #endif
5419 case eCSSProperty_speak:
5420 return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_NONE,
5421 nsCSSProps::kSpeakKTable);
5422 case eCSSProperty_speak_header:
5423 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5424 nsCSSProps::kSpeakHeaderKTable);
5425 case eCSSProperty_speak_numeral:
5426 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5427 nsCSSProps::kSpeakNumeralKTable);
5428 case eCSSProperty_speak_punctuation:
5429 return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
5430 nsCSSProps::kSpeakPunctuationKTable);
5431 case eCSSProperty_speech_rate:
5432 return ParseVariant(aErrorCode, aValue, VARIANT_HN | VARIANT_KEYWORD,
5433 nsCSSProps::kSpeechRateKTable);
5434 case eCSSProperty_stack_sizing:
5435 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5436 nsCSSProps::kStackSizingKTable);
5437 case eCSSProperty_stress:
5438 return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
5439 case eCSSProperty_table_layout:
5440 return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
5441 nsCSSProps::kTableLayoutKTable);
5442 case eCSSProperty_text_align:
5443 // When we support aligning on a string, we can parse text-align
5444 // as a string....
5445 return ParseVariant(aErrorCode, aValue, VARIANT_HK /* | VARIANT_STRING */,
5446 nsCSSProps::kTextAlignKTable);
5447 case eCSSProperty_text_decoration:
5448 return ParseTextDecoration(aErrorCode, aValue);
5449 case eCSSProperty_text_indent:
5450 return ParseVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
5451 case eCSSProperty_text_transform:
5452 return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
5453 nsCSSProps::kTextTransformKTable);
5454 case eCSSProperty_unicode_bidi:
5455 return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
5456 nsCSSProps::kUnicodeBidiKTable);
5457 case eCSSProperty_user_focus:
5458 return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_NONE,
5459 nsCSSProps::kUserFocusKTable);
5460 case eCSSProperty_user_input:
5461 return ParseVariant(aErrorCode, aValue, VARIANT_AHK | VARIANT_NONE,
5462 nsCSSProps::kUserInputKTable);
5463 case eCSSProperty_user_modify:
5464 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5465 nsCSSProps::kUserModifyKTable);
5466 case eCSSProperty_user_select:
5467 return ParseVariant(aErrorCode, aValue, VARIANT_AHK | VARIANT_NONE,
5468 nsCSSProps::kUserSelectKTable);
5469 case eCSSProperty_vertical_align:
5470 return ParseVariant(aErrorCode, aValue, VARIANT_HKLP,
5471 nsCSSProps::kVerticalAlignKTable);
5472 case eCSSProperty_visibility:
5473 return ParseVariant(aErrorCode, aValue, VARIANT_HK,
5474 nsCSSProps::kVisibilityKTable);
5475 case eCSSProperty_voice_family:
5476 return ParseFamily(aErrorCode, aValue);
5477 case eCSSProperty_volume:
5478 return ParseVariant(aErrorCode, aValue, VARIANT_HPN | VARIANT_KEYWORD,
5479 nsCSSProps::kVolumeKTable);
5480 case eCSSProperty_white_space:
5481 return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
5482 nsCSSProps::kWhitespaceKTable);
5483 case eCSSProperty_word_wrap:
5484 return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
5485 nsCSSProps::kWordwrapKTable);
5486 case eCSSProperty_z_index:
5487 return ParseVariant(aErrorCode, aValue, VARIANT_AHI, nsnull);
5489 // explicitly do NOT have a default case to let the compiler
5490 // help find missing properties
5491 return PR_FALSE;
5494 // nsFont::EnumerateFamilies callback for ParseFontDescriptorValue
5495 struct NS_STACK_CLASS ExtractFirstFamilyData {
5496 nsAutoString mFamilyName;
5497 PRBool mGood;
5498 ExtractFirstFamilyData() : mFamilyName(), mGood(PR_FALSE) {}
5501 static PRBool
5502 ExtractFirstFamily(const nsString& aFamily,
5503 PRBool aGeneric,
5504 void* aData)
5506 ExtractFirstFamilyData* realData = (ExtractFirstFamilyData*) aData;
5507 if (aGeneric || realData->mFamilyName.Length() > 0) {
5508 realData->mGood = PR_FALSE;
5509 return PR_FALSE;
5511 realData->mFamilyName.Assign(aFamily);
5512 realData->mGood = PR_TRUE;
5513 return PR_TRUE;
5516 // font-descriptor: descriptor ':' value ';'
5517 // caller has advanced mToken to point at the descriptor
5518 PRBool
5519 CSSParserImpl::ParseFontDescriptorValue(nsresult& aErrorCode,
5520 nsCSSFontDesc aDescID,
5521 nsCSSValue& aValue)
5523 switch (aDescID) {
5524 // These four are similar to the properties of the same name,
5525 // possibly with more restrictions on the values they can take.
5526 case eCSSFontDesc_Family: {
5527 if (!ParseFamily(aErrorCode, aValue) ||
5528 aValue.GetUnit() != eCSSUnit_String)
5529 return PR_FALSE;
5531 // the style parameters to the nsFont constructor are ignored,
5532 // because it's only being used to call EnumerateFamilies
5533 nsAutoString valueStr;
5534 aValue.GetStringValue(valueStr);
5535 nsFont font(valueStr, 0, 0, 0, 0, 0);
5536 ExtractFirstFamilyData dat;
5538 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
5539 if (!dat.mGood)
5540 return PR_FALSE;
5542 aValue.SetStringValue(dat.mFamilyName, eCSSUnit_String);
5543 return PR_TRUE;
5546 case eCSSFontDesc_Style:
5547 // property is VARIANT_HMK|VARIANT_SYSFONT
5548 return ParseVariant(aErrorCode, aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
5549 nsCSSProps::kFontStyleKTable);
5551 case eCSSFontDesc_Weight:
5552 return (ParseFontWeight(aErrorCode, aValue) &&
5553 aValue.GetUnit() != eCSSUnit_Inherit &&
5554 aValue.GetUnit() != eCSSUnit_Initial &&
5555 (aValue.GetUnit() != eCSSUnit_Enumerated ||
5556 (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
5557 aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
5559 case eCSSFontDesc_Stretch:
5560 // property is VARIANT_HMK|VARIANT_SYSFONT
5561 return (ParseVariant(aErrorCode, aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
5562 nsCSSProps::kFontStretchKTable) &&
5563 (aValue.GetUnit() != eCSSUnit_Enumerated ||
5564 (aValue.GetIntValue() != NS_STYLE_FONT_STRETCH_WIDER &&
5565 aValue.GetIntValue() != NS_STYLE_FONT_STRETCH_NARROWER)));
5567 // These two are unique to @font-face and have their own special grammar.
5568 case eCSSFontDesc_Src:
5569 return ParseFontSrc(aErrorCode, aValue);
5571 case eCSSFontDesc_UnicodeRange:
5572 return ParseFontRanges(aErrorCode, aValue);
5574 case eCSSFontDesc_UNKNOWN:
5575 case eCSSFontDesc_COUNT:
5576 NS_NOTREACHED("bad nsCSSFontDesc code");
5578 // explicitly do NOT have a default case to let the compiler
5579 // help find missing descriptors
5580 return PR_FALSE;
5583 void
5584 CSSParserImpl::InitBoxPropsAsPhysical(const nsCSSProperty *aSourceProperties)
5586 nsCSSValue physical(NS_BOXPROP_SOURCE_PHYSICAL, eCSSUnit_Enumerated);
5587 for (const nsCSSProperty *prop = aSourceProperties;
5588 *prop != eCSSProperty_UNKNOWN; ++prop) {
5589 AppendValue(*prop, physical);
5593 PRBool CSSParserImpl::ParseAzimuth(nsresult& aErrorCode, nsCSSValue& aValue)
5595 if (ParseVariant(aErrorCode, aValue, VARIANT_HK | VARIANT_ANGLE,
5596 nsCSSProps::kAzimuthKTable)) {
5597 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
5598 PRInt32 intValue = aValue.GetIntValue();
5599 if ((NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) &&
5600 (intValue <= NS_STYLE_AZIMUTH_BEHIND)) { // look for optional modifier
5601 nsCSSValue modifier;
5602 if (ParseEnum(aErrorCode, modifier, nsCSSProps::kAzimuthKTable)) {
5603 PRInt32 enumValue = modifier.GetIntValue();
5604 if (((intValue == NS_STYLE_AZIMUTH_BEHIND) &&
5605 (NS_STYLE_AZIMUTH_LEFT_SIDE <= enumValue) && (enumValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE)) ||
5606 ((enumValue == NS_STYLE_AZIMUTH_BEHIND) &&
5607 (NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) && (intValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE))) {
5608 aValue.SetIntValue(intValue | enumValue, eCSSUnit_Enumerated);
5609 return PR_TRUE;
5611 // Put the unknown identifier back and return
5612 UngetToken();
5613 return PR_FALSE;
5617 return PR_TRUE;
5619 return PR_FALSE;
5622 static nsCSSValue
5623 BackgroundPositionMaskToCSSValue(PRInt32 aMask, PRBool isX)
5625 PRInt32 val = NS_STYLE_BG_POSITION_CENTER;
5626 if (isX) {
5627 if (aMask & BG_LEFT) {
5628 val = NS_STYLE_BG_POSITION_LEFT;
5630 else if (aMask & BG_RIGHT) {
5631 val = NS_STYLE_BG_POSITION_RIGHT;
5634 else {
5635 if (aMask & BG_TOP) {
5636 val = NS_STYLE_BG_POSITION_TOP;
5638 else if (aMask & BG_BOTTOM) {
5639 val = NS_STYLE_BG_POSITION_BOTTOM;
5643 return nsCSSValue(val, eCSSUnit_Enumerated);
5646 PRBool CSSParserImpl::ParseBackground(nsresult& aErrorCode)
5648 nsAutoParseCompoundProperty compound(this);
5650 // Fill in the values that the shorthand will set if we don't find
5651 // other values.
5652 mTempData.mColor.mBackColor.SetIntValue(NS_STYLE_BG_COLOR_TRANSPARENT,
5653 eCSSUnit_Enumerated);
5654 mTempData.SetPropertyBit(eCSSProperty_background_color);
5655 mTempData.mColor.mBackImage.SetNoneValue();
5656 mTempData.SetPropertyBit(eCSSProperty_background_image);
5657 mTempData.mColor.mBackRepeat.SetIntValue(NS_STYLE_BG_REPEAT_XY,
5658 eCSSUnit_Enumerated);
5659 mTempData.SetPropertyBit(eCSSProperty_background_repeat);
5660 mTempData.mColor.mBackAttachment.SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL,
5661 eCSSUnit_Enumerated);
5662 mTempData.SetPropertyBit(eCSSProperty_background_attachment);
5663 mTempData.mColor.mBackPosition.mXValue.SetPercentValue(0.0f);
5664 mTempData.mColor.mBackPosition.mYValue.SetPercentValue(0.0f);
5665 mTempData.SetPropertyBit(eCSSProperty_background_position);
5666 // including the ones that we can't set from the shorthand.
5667 mTempData.mColor.mBackClip.SetInitialValue();
5668 mTempData.SetPropertyBit(eCSSProperty__moz_background_clip);
5669 mTempData.mColor.mBackOrigin.SetInitialValue();
5670 mTempData.SetPropertyBit(eCSSProperty__moz_background_origin);
5671 mTempData.mColor.mBackInlinePolicy.SetInitialValue();
5672 mTempData.SetPropertyBit(eCSSProperty__moz_background_inline_policy);
5674 // XXX If ParseSingleValueProperty were table-driven (bug 376079) and
5675 // automatically filled in the right field of mTempData, we could move
5676 // ParseBackgroundPosition to it (as a special case) and switch back
5677 // to using ParseChoice here.
5679 PRBool haveColor = PR_FALSE,
5680 haveImage = PR_FALSE,
5681 haveRepeat = PR_FALSE,
5682 haveAttach = PR_FALSE,
5683 havePosition = PR_FALSE;
5684 while (GetToken(aErrorCode, PR_TRUE)) {
5685 nsCSSTokenType tt = mToken.mType;
5686 UngetToken(); // ...but we'll still cheat and use mToken
5687 if (tt == eCSSToken_Symbol) {
5688 // ExpectEndProperty only looks for symbols, and nothing else will
5689 // show up as one.
5690 break;
5693 if (tt == eCSSToken_Ident) {
5694 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
5695 PRInt32 dummy;
5696 if (keyword == eCSSKeyword_inherit ||
5697 keyword == eCSSKeyword__moz_initial) {
5698 if (haveColor || haveImage || haveRepeat || haveAttach || havePosition)
5699 return PR_FALSE;
5700 haveColor = haveImage = haveRepeat = haveAttach = havePosition =
5701 PR_TRUE;
5702 GetToken(aErrorCode, PR_TRUE); // undo the UngetToken above
5703 nsCSSValue val;
5704 if (keyword == eCSSKeyword_inherit) {
5705 val.SetInheritValue();
5706 } else {
5707 val.SetInitialValue();
5709 mTempData.mColor.mBackColor = val;
5710 mTempData.mColor.mBackImage = val;
5711 mTempData.mColor.mBackRepeat = val;
5712 mTempData.mColor.mBackAttachment = val;
5713 mTempData.mColor.mBackPosition.mXValue = val;
5714 mTempData.mColor.mBackPosition.mYValue = val;
5715 // Reset (for 'inherit') the 3 properties that can't be
5716 // specified, although it's not entirely clear in the spec:
5717 // http://lists.w3.org/Archives/Public/www-style/2007Mar/0110
5718 mTempData.mColor.mBackClip = val;
5719 mTempData.mColor.mBackOrigin = val;
5720 mTempData.mColor.mBackInlinePolicy = val;
5721 break;
5722 } else if (keyword == eCSSKeyword_none) {
5723 if (haveImage)
5724 return PR_FALSE;
5725 haveImage = PR_TRUE;
5726 if (!ParseSingleValueProperty(aErrorCode, mTempData.mColor.mBackImage,
5727 eCSSProperty_background_image)) {
5728 NS_NOTREACHED("should be able to parse");
5729 return PR_FALSE;
5731 } else if (nsCSSProps::FindKeyword(keyword,
5732 nsCSSProps::kBackgroundAttachmentKTable, dummy)) {
5733 if (haveAttach)
5734 return PR_FALSE;
5735 haveAttach = PR_TRUE;
5736 if (!ParseSingleValueProperty(aErrorCode,
5737 mTempData.mColor.mBackAttachment,
5738 eCSSProperty_background_attachment)) {
5739 NS_NOTREACHED("should be able to parse");
5740 return PR_FALSE;
5742 } else if (nsCSSProps::FindKeyword(keyword,
5743 nsCSSProps::kBackgroundRepeatKTable, dummy)) {
5744 if (haveRepeat)
5745 return PR_FALSE;
5746 haveRepeat = PR_TRUE;
5747 if (!ParseSingleValueProperty(aErrorCode, mTempData.mColor.mBackRepeat,
5748 eCSSProperty_background_repeat)) {
5749 NS_NOTREACHED("should be able to parse");
5750 return PR_FALSE;
5752 } else if (nsCSSProps::FindKeyword(keyword,
5753 nsCSSProps::kBackgroundPositionKTable, dummy)) {
5754 if (havePosition)
5755 return PR_FALSE;
5756 havePosition = PR_TRUE;
5757 if (!ParseBackgroundPositionValues(aErrorCode)) {
5758 return PR_FALSE;
5760 } else {
5761 if (haveColor)
5762 return PR_FALSE;
5763 haveColor = PR_TRUE;
5764 if (!ParseSingleValueProperty(aErrorCode, mTempData.mColor.mBackColor,
5765 eCSSProperty_background_color)) {
5766 return PR_FALSE;
5769 } else if (eCSSToken_Function == tt &&
5770 mToken.mIdent.LowerCaseEqualsLiteral("url")) {
5771 if (haveImage)
5772 return PR_FALSE;
5773 haveImage = PR_TRUE;
5774 if (!ParseSingleValueProperty(aErrorCode, mTempData.mColor.mBackImage,
5775 eCSSProperty_background_image)) {
5776 return PR_FALSE;
5778 } else if (mToken.IsDimension() || tt == eCSSToken_Percentage) {
5779 if (havePosition)
5780 return PR_FALSE;
5781 havePosition = PR_TRUE;
5782 if (!ParseBackgroundPositionValues(aErrorCode)) {
5783 return PR_FALSE;
5785 } else {
5786 if (haveColor)
5787 return PR_FALSE;
5788 haveColor = PR_TRUE;
5789 if (!ParseSingleValueProperty(aErrorCode, mTempData.mColor.mBackColor,
5790 eCSSProperty_background_color)) {
5791 return PR_FALSE;
5796 return ExpectEndProperty(aErrorCode) &&
5797 (haveColor || haveImage || haveRepeat || haveAttach || havePosition);
5800 PRBool CSSParserImpl::ParseBackgroundPosition(nsresult& aErrorCode)
5802 if (!ParseBackgroundPositionValues(aErrorCode) ||
5803 !ExpectEndProperty(aErrorCode))
5804 return PR_FALSE;
5805 mTempData.SetPropertyBit(eCSSProperty_background_position);
5806 return PR_TRUE;
5809 PRBool CSSParserImpl::ParseBackgroundPositionValues(nsresult& aErrorCode)
5811 // First try a percentage or a length value
5812 nsCSSValue &xValue = mTempData.mColor.mBackPosition.mXValue,
5813 &yValue = mTempData.mColor.mBackPosition.mYValue;
5814 if (ParseVariant(aErrorCode, xValue, VARIANT_HLP, nsnull)) {
5815 if (eCSSUnit_Inherit == xValue.GetUnit() ||
5816 eCSSUnit_Initial == xValue.GetUnit()) { // both are inherited or both are set to initial
5817 yValue = xValue;
5818 return PR_TRUE;
5820 // We have one percentage/length. Get the optional second
5821 // percentage/length/keyword.
5822 if (ParseVariant(aErrorCode, yValue, VARIANT_LP, nsnull)) {
5823 // We have two numbers
5824 return PR_TRUE;
5827 if (ParseEnum(aErrorCode, yValue, nsCSSProps::kBackgroundPositionKTable)) {
5828 PRInt32 yVal = yValue.GetIntValue();
5829 if (!(yVal & BG_CTB)) {
5830 // The second keyword can only be 'center', 'top', or 'bottom'
5831 return PR_FALSE;
5833 yValue = BackgroundPositionMaskToCSSValue(yVal, PR_FALSE);
5834 return PR_TRUE;
5837 // If only one percentage or length value is given, it sets the
5838 // horizontal position only, and the vertical position will be 50%.
5839 yValue.SetPercentValue(0.5f);
5840 return PR_TRUE;
5843 // Now try keywords. We do this manually to allow for the first
5844 // appearance of "center" to apply to the either the x or y
5845 // position (it's ambiguous so we have to disambiguate). Each
5846 // allowed keyword value is assigned it's own bit. We don't allow
5847 // any duplicate keywords other than center. We try to get two
5848 // keywords but it's okay if there is only one.
5849 PRInt32 mask = 0;
5850 if (ParseEnum(aErrorCode, xValue, nsCSSProps::kBackgroundPositionKTable)) {
5851 PRInt32 bit = xValue.GetIntValue();
5852 mask |= bit;
5853 if (ParseEnum(aErrorCode, xValue, nsCSSProps::kBackgroundPositionKTable)) {
5854 bit = xValue.GetIntValue();
5855 if (mask & (bit & ~BG_CENTER)) {
5856 // Only the 'center' keyword can be duplicated.
5857 return PR_FALSE;
5859 mask |= bit;
5861 else {
5862 // Only one keyword. See if we have a length or percentage.
5863 if (ParseVariant(aErrorCode, yValue, VARIANT_LP, nsnull)) {
5864 if (!(mask & BG_CLR)) {
5865 // The first keyword can only be 'center', 'left', or 'right'
5866 return PR_FALSE;
5869 xValue = BackgroundPositionMaskToCSSValue(mask, PR_TRUE);
5870 return PR_TRUE;
5875 // Check for bad input. Bad input consists of no matching keywords,
5876 // or pairs of x keywords or pairs of y keywords.
5877 if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
5878 (mask == (BG_LEFT | BG_RIGHT))) {
5879 return PR_FALSE;
5882 // Create style values
5883 xValue = BackgroundPositionMaskToCSSValue(mask, PR_TRUE);
5884 yValue = BackgroundPositionMaskToCSSValue(mask, PR_FALSE);
5885 return PR_TRUE;
5888 // These must be in CSS order (top,right,bottom,left) for indexing to work
5889 static const nsCSSProperty kBorderStyleIDs[] = {
5890 eCSSProperty_border_top_style,
5891 eCSSProperty_border_right_style_value,
5892 eCSSProperty_border_bottom_style,
5893 eCSSProperty_border_left_style_value
5895 static const nsCSSProperty kBorderWidthIDs[] = {
5896 eCSSProperty_border_top_width,
5897 eCSSProperty_border_right_width_value,
5898 eCSSProperty_border_bottom_width,
5899 eCSSProperty_border_left_width_value
5901 static const nsCSSProperty kBorderColorIDs[] = {
5902 eCSSProperty_border_top_color,
5903 eCSSProperty_border_right_color_value,
5904 eCSSProperty_border_bottom_color,
5905 eCSSProperty_border_left_color_value
5907 static const nsCSSProperty kBorderRadiusIDs[] = {
5908 eCSSProperty__moz_border_radius_topLeft,
5909 eCSSProperty__moz_border_radius_topRight,
5910 eCSSProperty__moz_border_radius_bottomRight,
5911 eCSSProperty__moz_border_radius_bottomLeft
5913 static const nsCSSProperty kOutlineRadiusIDs[] = {
5914 eCSSProperty__moz_outline_radius_topLeft,
5915 eCSSProperty__moz_outline_radius_topRight,
5916 eCSSProperty__moz_outline_radius_bottomRight,
5917 eCSSProperty__moz_outline_radius_bottomLeft
5920 PRBool CSSParserImpl::ParseBorderColor(nsresult& aErrorCode)
5922 static const nsCSSProperty kBorderColorSources[] = {
5923 eCSSProperty_border_left_color_ltr_source,
5924 eCSSProperty_border_left_color_rtl_source,
5925 eCSSProperty_border_right_color_ltr_source,
5926 eCSSProperty_border_right_color_rtl_source,
5927 eCSSProperty_UNKNOWN
5930 // do this now, in case 4 values weren't specified
5931 InitBoxPropsAsPhysical(kBorderColorSources);
5932 return ParseBoxProperties(aErrorCode, mTempData.mMargin.mBorderColor,
5933 kBorderColorIDs);
5936 PRBool CSSParserImpl::ParseBorderImage(nsresult& aErrorCode)
5938 if (ParseVariant(aErrorCode, mTempData.mMargin.mBorderImage,
5939 VARIANT_INHERIT | VARIANT_NONE, nsnull)) {
5940 mTempData.SetPropertyBit(eCSSProperty_border_image);
5941 return PR_TRUE;
5944 // <uri> [<number> | <percentage>]{1,4} [ / <border-width>{1,4} ]? [stretch | repeat | round]{0,2}
5945 nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(11);
5946 if (!arr) {
5947 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
5948 return PR_FALSE;
5951 nsCSSValue& url = arr->Item(0);
5952 nsCSSValue& splitTop = arr->Item(1);
5953 nsCSSValue& splitRight = arr->Item(2);
5954 nsCSSValue& splitBottom = arr->Item(3);
5955 nsCSSValue& splitLeft = arr->Item(4);
5956 nsCSSValue& borderWidthTop = arr->Item(5);
5957 nsCSSValue& borderWidthRight = arr->Item(6);
5958 nsCSSValue& borderWidthBottom = arr->Item(7);
5959 nsCSSValue& borderWidthLeft = arr->Item(8);
5960 nsCSSValue& horizontalKeyword = arr->Item(9);
5961 nsCSSValue& verticalKeyword = arr->Item(10);
5963 // <uri>
5964 if (!ParseVariant(aErrorCode, url, VARIANT_URL, nsnull)) {
5965 return PR_FALSE;
5968 // [<number> | <percentage>]{1,4}
5969 if (!ParsePositiveVariant(aErrorCode, splitTop,
5970 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
5971 return PR_FALSE;
5973 if (!ParsePositiveVariant(aErrorCode, splitRight,
5974 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
5975 splitRight = splitTop;
5977 if (!ParsePositiveVariant(aErrorCode, splitBottom,
5978 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
5979 splitBottom = splitTop;
5981 if (!ParsePositiveVariant(aErrorCode, splitLeft,
5982 VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) {
5983 splitLeft = splitRight;
5986 // [ / <border-width>{1,4} ]?
5987 if (ExpectSymbol(aErrorCode, '/', PR_TRUE)) {
5988 // if have '/', at least one value is required
5989 if (!ParsePositiveVariant(aErrorCode, borderWidthTop,
5990 VARIANT_LENGTH, nsnull)) {
5991 return PR_FALSE;
5993 if (!ParsePositiveVariant(aErrorCode, borderWidthRight,
5994 VARIANT_LENGTH, nsnull)) {
5995 borderWidthRight = borderWidthTop;
5997 if (!ParsePositiveVariant(aErrorCode, borderWidthBottom,
5998 VARIANT_LENGTH, nsnull)) {
5999 borderWidthBottom = borderWidthTop;
6001 if (!ParsePositiveVariant(aErrorCode, borderWidthLeft,
6002 VARIANT_LENGTH, nsnull)) {
6003 borderWidthLeft = borderWidthRight;
6007 // [stretch | repeat | round]{0,2}
6008 // missing keywords are handled in nsRuleNode::ComputeBorderData()
6009 if (ParseEnum(aErrorCode, horizontalKeyword, nsCSSProps::kBorderImageKTable)) {
6010 ParseEnum(aErrorCode, verticalKeyword, nsCSSProps::kBorderImageKTable);
6013 if (!ExpectEndProperty(aErrorCode)) {
6014 return PR_FALSE;
6017 mTempData.mMargin.mBorderImage.SetArrayValue(arr, eCSSUnit_Array);
6018 mTempData.SetPropertyBit(eCSSProperty_border_image);
6020 return PR_TRUE;
6023 PRBool CSSParserImpl::ParseBorderSpacing(nsresult& aErrorCode)
6025 nsCSSValue xValue;
6026 if (ParsePositiveVariant(aErrorCode, xValue, VARIANT_HL, nsnull)) {
6027 if (xValue.IsLengthUnit()) {
6028 // We have one length. Get the optional second length.
6029 nsCSSValue yValue;
6030 if (ParsePositiveVariant(aErrorCode, yValue, VARIANT_LENGTH, nsnull)) {
6031 // We have two numbers
6032 if (ExpectEndProperty(aErrorCode)) {
6033 mTempData.mTable.mBorderSpacing.mXValue = xValue;
6034 mTempData.mTable.mBorderSpacing.mYValue = yValue;
6035 mTempData.SetPropertyBit(eCSSProperty_border_spacing);
6036 return PR_TRUE;
6038 return PR_FALSE;
6042 // We have one length which is the horizontal spacing. Create a value for
6043 // the vertical spacing which is equal
6044 if (ExpectEndProperty(aErrorCode)) {
6045 mTempData.mTable.mBorderSpacing.SetBothValuesTo(xValue);
6046 mTempData.SetPropertyBit(eCSSProperty_border_spacing);
6047 return PR_TRUE;
6050 return PR_FALSE;
6053 PRBool CSSParserImpl::ParseBorderSide(nsresult& aErrorCode,
6054 const nsCSSProperty aPropIDs[],
6055 PRBool aSetAllSides)
6057 const PRInt32 numProps = 3;
6058 nsCSSValue values[numProps];
6060 PRInt32 found = ParseChoice(aErrorCode, values, aPropIDs, numProps);
6061 if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode))) {
6062 return PR_FALSE;
6065 if ((found & 1) == 0) { // Provide default border-width
6066 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6068 if ((found & 2) == 0) { // Provide default border-style
6069 values[1].SetNoneValue();
6071 if ((found & 4) == 0) { // text color will be used
6072 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6075 if (aSetAllSides) {
6076 static const nsCSSProperty kBorderSources[] = {
6077 eCSSProperty_border_left_color_ltr_source,
6078 eCSSProperty_border_left_color_rtl_source,
6079 eCSSProperty_border_right_color_ltr_source,
6080 eCSSProperty_border_right_color_rtl_source,
6081 eCSSProperty_border_left_style_ltr_source,
6082 eCSSProperty_border_left_style_rtl_source,
6083 eCSSProperty_border_right_style_ltr_source,
6084 eCSSProperty_border_right_style_rtl_source,
6085 eCSSProperty_border_left_width_ltr_source,
6086 eCSSProperty_border_left_width_rtl_source,
6087 eCSSProperty_border_right_width_ltr_source,
6088 eCSSProperty_border_right_width_rtl_source,
6089 eCSSProperty_UNKNOWN
6092 InitBoxPropsAsPhysical(kBorderSources);
6094 // Parsing "border" shorthand; set all four sides to the same thing
6095 for (PRInt32 index = 0; index < 4; index++) {
6096 NS_ASSERTION(numProps == 3, "This code needs updating");
6097 AppendValue(kBorderWidthIDs[index], values[0]);
6098 AppendValue(kBorderStyleIDs[index], values[1]);
6099 AppendValue(kBorderColorIDs[index], values[2]);
6102 else {
6103 // Just set our one side
6104 for (PRInt32 index = 0; index < numProps; index++) {
6105 AppendValue(aPropIDs[index], values[index]);
6108 return PR_TRUE;
6111 PRBool CSSParserImpl::ParseDirectionalBorderSide(nsresult& aErrorCode,
6112 const nsCSSProperty aPropIDs[],
6113 PRInt32 aSourceType)
6115 const PRInt32 numProps = 3;
6116 nsCSSValue values[numProps];
6118 PRInt32 found = ParseChoice(aErrorCode, values, aPropIDs, numProps);
6119 if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode))) {
6120 return PR_FALSE;
6123 if ((found & 1) == 0) { // Provide default border-width
6124 values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6126 if ((found & 2) == 0) { // Provide default border-style
6127 values[1].SetNoneValue();
6129 if ((found & 4) == 0) { // text color will be used
6130 values[2].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6132 for (PRInt32 index = 0; index < numProps; index++) {
6133 const nsCSSProperty* subprops =
6134 nsCSSProps::SubpropertyEntryFor(aPropIDs[index + numProps]);
6135 NS_ASSERTION(subprops[3] == eCSSProperty_UNKNOWN,
6136 "not box property with physical vs. logical cascading");
6137 AppendValue(subprops[0], values[index]);
6138 nsCSSValue typeVal(aSourceType, eCSSUnit_Enumerated);
6139 AppendValue(subprops[1], typeVal);
6140 AppendValue(subprops[2], typeVal);
6142 return PR_TRUE;
6145 PRBool CSSParserImpl::ParseBorderStyle(nsresult& aErrorCode)
6147 static const nsCSSProperty kBorderStyleSources[] = {
6148 eCSSProperty_border_left_style_ltr_source,
6149 eCSSProperty_border_left_style_rtl_source,
6150 eCSSProperty_border_right_style_ltr_source,
6151 eCSSProperty_border_right_style_rtl_source,
6152 eCSSProperty_UNKNOWN
6155 // do this now, in case 4 values weren't specified
6156 InitBoxPropsAsPhysical(kBorderStyleSources);
6157 return ParseBoxProperties(aErrorCode, mTempData.mMargin.mBorderStyle,
6158 kBorderStyleIDs);
6161 PRBool CSSParserImpl::ParseBorderWidth(nsresult& aErrorCode)
6163 static const nsCSSProperty kBorderWidthSources[] = {
6164 eCSSProperty_border_left_width_ltr_source,
6165 eCSSProperty_border_left_width_rtl_source,
6166 eCSSProperty_border_right_width_ltr_source,
6167 eCSSProperty_border_right_width_rtl_source,
6168 eCSSProperty_UNKNOWN
6171 // do this now, in case 4 values weren't specified
6172 InitBoxPropsAsPhysical(kBorderWidthSources);
6173 return ParseBoxProperties(aErrorCode, mTempData.mMargin.mBorderWidth,
6174 kBorderWidthIDs);
6177 PRBool CSSParserImpl::ParseBorderRadius(nsresult& aErrorCode)
6179 return ParseBoxProperties(aErrorCode, mTempData.mMargin.mBorderRadius,
6180 kBorderRadiusIDs);
6183 PRBool CSSParserImpl::ParseOutlineRadius(nsresult& aErrorCode)
6185 return ParseBoxProperties(aErrorCode, mTempData.mMargin.mOutlineRadius,
6186 kOutlineRadiusIDs);
6189 PRBool CSSParserImpl::ParseBorderColors(nsresult& aErrorCode,
6190 nsCSSValueList** aResult,
6191 nsCSSProperty aProperty)
6193 nsCSSValue value;
6194 if (ParseVariant(aErrorCode, value, VARIANT_HCK|VARIANT_NONE, nsCSSProps::kBorderColorKTable)) {
6195 nsCSSValueList* listHead = new nsCSSValueList();
6196 nsCSSValueList* list = listHead;
6197 if (!list) {
6198 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
6199 return PR_FALSE;
6201 list->mValue = value;
6203 while (list) {
6204 if (ExpectEndProperty(aErrorCode)) {
6205 mTempData.SetPropertyBit(aProperty);
6206 *aResult = listHead;
6207 aErrorCode = NS_OK;
6208 return PR_TRUE;
6210 // FIXME Bug 389404: We should not accept inherit, -moz-initial,
6211 // or none as anything other than the first value.
6212 if (ParseVariant(aErrorCode, value, VARIANT_HCK|VARIANT_NONE, nsCSSProps::kBorderColorKTable)) {
6213 list->mNext = new nsCSSValueList();
6214 list = list->mNext;
6215 if (list)
6216 list->mValue = value;
6217 else
6218 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
6220 else
6221 break;
6223 delete listHead;
6225 return PR_FALSE;
6228 PRBool
6229 CSSParserImpl::ParseRect(nsCSSRect& aRect, nsresult& aErrorCode,
6230 nsCSSProperty aPropID)
6232 nsCSSRect rect;
6233 PRBool result;
6234 if ((result = DoParseRect(rect, aErrorCode)) &&
6235 rect != aRect) {
6236 aRect = rect;
6237 mTempData.SetPropertyBit(aPropID);
6239 return result;
6242 PRBool
6243 CSSParserImpl::DoParseRect(nsCSSRect& aRect, nsresult& aErrorCode)
6245 if (! GetToken(aErrorCode, PR_TRUE)) {
6246 return PR_FALSE;
6248 if (eCSSToken_Ident == mToken.mType) {
6249 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
6250 switch (keyword) {
6251 case eCSSKeyword_auto:
6252 if (ExpectEndProperty(aErrorCode)) {
6253 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Auto));
6254 return PR_TRUE;
6256 break;
6257 case eCSSKeyword_inherit:
6258 if (ExpectEndProperty(aErrorCode)) {
6259 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Inherit));
6260 return PR_TRUE;
6262 break;
6263 case eCSSKeyword__moz_initial:
6264 if (ExpectEndProperty(aErrorCode)) {
6265 aRect.SetAllSidesTo(nsCSSValue(eCSSUnit_Initial));
6266 return PR_TRUE;
6268 break;
6269 default:
6270 UngetToken();
6271 break;
6273 } else if ((eCSSToken_Function == mToken.mType) &&
6274 mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
6275 if (!ExpectSymbol(aErrorCode, '(', PR_TRUE)) {
6276 return PR_FALSE;
6278 NS_FOR_CSS_SIDES(side) {
6279 if (! ParseVariant(aErrorCode, aRect.*(nsCSSRect::sides[side]),
6280 VARIANT_AL, nsnull)) {
6281 return PR_FALSE;
6283 if (3 != side) {
6284 // skip optional commas between elements
6285 ExpectSymbol(aErrorCode, ',', PR_TRUE);
6288 if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
6289 return PR_FALSE;
6291 if (ExpectEndProperty(aErrorCode)) {
6292 return PR_TRUE;
6294 } else {
6295 UngetToken();
6297 return PR_FALSE;
6300 #define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
6301 VARIANT_KEYWORD)
6302 PRBool CSSParserImpl::ParseContent(nsresult& aErrorCode)
6304 // XXX Rewrite to make it look more like ParseCursor or ParseCounterData?
6305 nsCSSValue value;
6306 if (ParseVariant(aErrorCode, value,
6307 VARIANT_CONTENT | VARIANT_INHERIT | VARIANT_NORMAL |
6308 VARIANT_NONE,
6309 nsCSSProps::kContentKTable)) {
6310 nsCSSValueList* listHead = new nsCSSValueList();
6311 nsCSSValueList* list = listHead;
6312 if (nsnull == list) {
6313 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
6314 return PR_FALSE;
6316 list->mValue = value;
6318 while (nsnull != list) {
6319 if (ExpectEndProperty(aErrorCode)) {
6320 mTempData.SetPropertyBit(eCSSProperty_content);
6321 mTempData.mContent.mContent = listHead;
6322 aErrorCode = NS_OK;
6323 return PR_TRUE;
6325 if (eCSSUnit_Inherit == value.GetUnit() ||
6326 eCSSUnit_Initial == value.GetUnit() ||
6327 eCSSUnit_Normal == value.GetUnit() ||
6328 eCSSUnit_None == value.GetUnit() ||
6329 (eCSSUnit_Enumerated == value.GetUnit() &&
6330 NS_STYLE_CONTENT_ALT_CONTENT == value.GetIntValue())) {
6331 // This only matters the first time through the loop.
6332 delete listHead;
6333 return PR_FALSE;
6335 if (ParseVariant(aErrorCode, value, VARIANT_CONTENT, nsCSSProps::kContentKTable) &&
6336 // Make sure we didn't end up with NS_STYLE_CONTENT_ALT_CONTENT here
6337 (value.GetUnit() != eCSSUnit_Enumerated ||
6338 value.GetIntValue() != NS_STYLE_CONTENT_ALT_CONTENT)) {
6339 list->mNext = new nsCSSValueList();
6340 list = list->mNext;
6341 if (nsnull != list) {
6342 list->mValue = value;
6344 else {
6345 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
6348 else {
6349 break;
6352 delete listHead;
6354 return PR_FALSE;
6357 struct SingleCounterPropValue {
6358 char str[13];
6359 nsCSSUnit unit;
6362 PRBool CSSParserImpl::ParseCounterData(nsresult& aErrorCode,
6363 nsCSSValuePairList** aResult,
6364 nsCSSProperty aPropID)
6366 nsSubstring* ident = NextIdent(aErrorCode);
6367 if (nsnull == ident) {
6368 return PR_FALSE;
6370 static const SingleCounterPropValue singleValues[] = {
6371 { "none", eCSSUnit_None },
6372 { "inherit", eCSSUnit_Inherit },
6373 { "-moz-initial", eCSSUnit_Initial }
6375 for (const SingleCounterPropValue *sv = singleValues,
6376 *sv_end = singleValues + NS_ARRAY_LENGTH(singleValues);
6377 sv != sv_end; ++sv) {
6378 if (ident->LowerCaseEqualsASCII(sv->str)) {
6379 if (ExpectEndProperty(aErrorCode)) {
6380 nsCSSValuePairList* dataHead = new nsCSSValuePairList();
6381 if (!dataHead) {
6382 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
6383 return PR_FALSE;
6385 dataHead->mXValue = nsCSSValue(sv->unit);
6386 *aResult = dataHead;
6387 mTempData.SetPropertyBit(aPropID);
6388 return PR_TRUE;
6390 return PR_FALSE;
6393 UngetToken(); // undo NextIdent
6395 nsCSSValuePairList* dataHead = nsnull;
6396 nsCSSValuePairList **next = &dataHead;
6397 for (;;) {
6398 if (!GetToken(aErrorCode, PR_TRUE) || mToken.mType != eCSSToken_Ident) {
6399 break;
6401 nsCSSValuePairList *data = *next = new nsCSSValuePairList();
6402 if (!data) {
6403 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
6404 break;
6406 next = &data->mNext;
6407 data->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
6408 if (GetToken(aErrorCode, PR_TRUE)) {
6409 if (eCSSToken_Number == mToken.mType && mToken.mIntegerValid) {
6410 data->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
6411 } else {
6412 UngetToken();
6415 if (ExpectEndProperty(aErrorCode)) {
6416 mTempData.SetPropertyBit(aPropID);
6417 *aResult = dataHead;
6418 aErrorCode = NS_OK;
6419 return PR_TRUE;
6422 delete dataHead;
6423 return PR_FALSE;
6426 PRBool CSSParserImpl::ParseCue(nsresult& aErrorCode)
6428 nsCSSValue before;
6429 if (ParseSingleValueProperty(aErrorCode, before, eCSSProperty_cue_before)) {
6430 if (eCSSUnit_Inherit != before.GetUnit() &&
6431 eCSSUnit_Initial != before.GetUnit()) {
6432 nsCSSValue after;
6433 if (ParseSingleValueProperty(aErrorCode, after, eCSSProperty_cue_after)) {
6434 if (ExpectEndProperty(aErrorCode)) {
6435 AppendValue(eCSSProperty_cue_before, before);
6436 AppendValue(eCSSProperty_cue_after, after);
6437 return PR_TRUE;
6439 return PR_FALSE;
6442 if (ExpectEndProperty(aErrorCode)) {
6443 AppendValue(eCSSProperty_cue_before, before);
6444 AppendValue(eCSSProperty_cue_after, before);
6445 return PR_TRUE;
6448 return PR_FALSE;
6451 PRBool CSSParserImpl::ParseCursor(nsresult& aErrorCode)
6453 nsCSSValueList *list = nsnull;
6454 for (nsCSSValueList **curp = &list, *cur; ; curp = &cur->mNext) {
6455 cur = *curp = new nsCSSValueList();
6456 if (!cur) {
6457 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
6458 break;
6460 if (!ParseVariant(aErrorCode, cur->mValue,
6461 (cur == list) ? VARIANT_AHUK : VARIANT_AUK,
6462 nsCSSProps::kCursorKTable)) {
6463 break;
6465 if (cur->mValue.GetUnit() != eCSSUnit_URL) {
6466 if (!ExpectEndProperty(aErrorCode)) {
6467 break;
6469 // Only success case here, since having the failure case at the
6470 // end allows more sharing of code.
6471 mTempData.SetPropertyBit(eCSSProperty_cursor);
6472 mTempData.mUserInterface.mCursor = list;
6473 aErrorCode = NS_OK;
6474 return PR_TRUE;
6476 // We have a URL, so make a value array with three values.
6477 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(3);
6478 if (!val) {
6479 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
6480 break;
6482 val->Item(0) = cur->mValue;
6483 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
6485 // Parse optional x and y position of cursor hotspot (css3-ui).
6486 if (ParseVariant(aErrorCode, val->Item(1), VARIANT_NUMBER, nsnull)) {
6487 // If we have one number, we must have two.
6488 if (!ParseVariant(aErrorCode, val->Item(2), VARIANT_NUMBER, nsnull)) {
6489 break;
6493 if (!ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
6494 break;
6497 // Have failure case at the end so we can |break| to get to it.
6498 delete list;
6499 return PR_FALSE;
6503 PRBool CSSParserImpl::ParseFont(nsresult& aErrorCode)
6505 static const nsCSSProperty fontIDs[] = {
6506 eCSSProperty_font_style,
6507 eCSSProperty_font_variant,
6508 eCSSProperty_font_weight
6511 nsCSSValue family;
6512 if (ParseVariant(aErrorCode, family, VARIANT_HK, nsCSSProps::kFontKTable)) {
6513 if (ExpectEndProperty(aErrorCode)) {
6514 if (eCSSUnit_Inherit == family.GetUnit() ||
6515 eCSSUnit_Initial == family.GetUnit()) {
6516 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
6517 AppendValue(eCSSProperty_font_family, family);
6518 AppendValue(eCSSProperty_font_style, family);
6519 AppendValue(eCSSProperty_font_variant, family);
6520 AppendValue(eCSSProperty_font_weight, family);
6521 AppendValue(eCSSProperty_font_size, family);
6522 AppendValue(eCSSProperty_line_height, family);
6523 AppendValue(eCSSProperty_font_stretch, family);
6524 AppendValue(eCSSProperty_font_size_adjust, family);
6526 else {
6527 AppendValue(eCSSProperty__x_system_font, family);
6528 nsCSSValue systemFont(eCSSUnit_System_Font);
6529 AppendValue(eCSSProperty_font_family, systemFont);
6530 AppendValue(eCSSProperty_font_style, systemFont);
6531 AppendValue(eCSSProperty_font_variant, systemFont);
6532 AppendValue(eCSSProperty_font_weight, systemFont);
6533 AppendValue(eCSSProperty_font_size, systemFont);
6534 AppendValue(eCSSProperty_line_height, systemFont);
6535 AppendValue(eCSSProperty_font_stretch, systemFont);
6536 AppendValue(eCSSProperty_font_size_adjust, systemFont);
6538 return PR_TRUE;
6540 return PR_FALSE;
6543 // Get optional font-style, font-variant and font-weight (in any order)
6544 const PRInt32 numProps = 3;
6545 nsCSSValue values[numProps];
6546 PRInt32 found = ParseChoice(aErrorCode, values, fontIDs, numProps);
6547 if ((found < 0) || (eCSSUnit_Inherit == values[0].GetUnit()) ||
6548 (eCSSUnit_Initial == values[0].GetUnit())) { // illegal data
6549 return PR_FALSE;
6551 if ((found & 1) == 0) {
6552 // Provide default font-style
6553 values[0].SetNormalValue();
6555 if ((found & 2) == 0) {
6556 // Provide default font-variant
6557 values[1].SetNormalValue();
6559 if ((found & 4) == 0) {
6560 // Provide default font-weight
6561 values[2].SetNormalValue();
6564 // Get mandatory font-size
6565 nsCSSValue size;
6566 if (! ParseVariant(aErrorCode, size, VARIANT_KEYWORD | VARIANT_LP, nsCSSProps::kFontSizeKTable)) {
6567 return PR_FALSE;
6570 // Get optional "/" line-height
6571 nsCSSValue lineHeight;
6572 if (ExpectSymbol(aErrorCode, '/', PR_TRUE)) {
6573 if (! ParsePositiveVariant(aErrorCode, lineHeight,
6574 VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL,
6575 nsnull)) {
6576 return PR_FALSE;
6579 else {
6580 lineHeight.SetNormalValue();
6583 // Get final mandatory font-family
6584 nsAutoParseCompoundProperty compound(this);
6585 if (ParseFamily(aErrorCode, family)) {
6586 if ((eCSSUnit_Inherit != family.GetUnit()) && (eCSSUnit_Initial != family.GetUnit()) &&
6587 ExpectEndProperty(aErrorCode)) {
6588 AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
6589 AppendValue(eCSSProperty_font_family, family);
6590 AppendValue(eCSSProperty_font_style, values[0]);
6591 AppendValue(eCSSProperty_font_variant, values[1]);
6592 AppendValue(eCSSProperty_font_weight, values[2]);
6593 AppendValue(eCSSProperty_font_size, size);
6594 AppendValue(eCSSProperty_line_height, lineHeight);
6595 AppendValue(eCSSProperty_font_stretch, nsCSSValue(eCSSUnit_Normal));
6596 AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
6597 return PR_TRUE;
6600 return PR_FALSE;
6603 PRBool CSSParserImpl::ParseFontWeight(nsresult& aErrorCode, nsCSSValue& aValue)
6605 if (ParseVariant(aErrorCode, aValue, VARIANT_HMKI | VARIANT_SYSFONT, nsCSSProps::kFontWeightKTable)) {
6606 if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
6607 PRInt32 intValue = aValue.GetIntValue();
6608 if ((100 <= intValue) &&
6609 (intValue <= 900) &&
6610 (0 == (intValue % 100))) {
6611 return PR_TRUE;
6612 } else {
6613 UngetToken();
6614 return PR_FALSE;
6617 return PR_TRUE;
6619 return PR_FALSE;
6622 PRBool CSSParserImpl::ParseOneFamily(nsresult& aErrorCode, nsAString& aFamily)
6624 if (!GetToken(aErrorCode, PR_TRUE))
6625 return PR_FALSE;
6627 nsCSSToken* tk = &mToken;
6629 if (eCSSToken_Ident == tk->mType) {
6630 aFamily.Append(tk->mIdent);
6631 for (;;) {
6632 if (!GetToken(aErrorCode, PR_FALSE))
6633 break;
6635 if (eCSSToken_Ident == tk->mType) {
6636 aFamily.Append(tk->mIdent);
6637 } else if (eCSSToken_WhiteSpace == tk->mType) {
6638 // Lookahead one token and drop whitespace if we are ending the
6639 // font name.
6640 if (!GetToken(aErrorCode, PR_TRUE))
6641 break;
6643 UngetToken();
6644 if (eCSSToken_Ident == tk->mType)
6645 aFamily.Append(PRUnichar(' '));
6646 else
6647 break;
6648 } else {
6649 UngetToken();
6650 break;
6653 return PR_TRUE;
6655 } else if (eCSSToken_String == tk->mType) {
6656 aFamily.Append(tk->mSymbol); // replace the quotes
6657 aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
6658 aFamily.Append(tk->mSymbol);
6659 return PR_TRUE;
6661 } else {
6662 UngetToken();
6663 return PR_FALSE;
6667 PRBool CSSParserImpl::ParseFamily(nsresult& aErrorCode, nsCSSValue& aValue)
6669 if (!GetToken(aErrorCode, PR_TRUE))
6670 return PR_FALSE;
6672 if (eCSSToken_Ident == mToken.mType) {
6673 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
6674 if (keyword == eCSSKeyword_inherit) {
6675 aValue.SetInheritValue();
6676 return PR_TRUE;
6678 if (keyword == eCSSKeyword__moz_initial) {
6679 aValue.SetInitialValue();
6680 return PR_TRUE;
6682 if (keyword == eCSSKeyword__moz_use_system_font &&
6683 !IsParsingCompoundProperty()) {
6684 aValue.SetSystemFontValue();
6685 return PR_TRUE;
6689 UngetToken();
6691 nsAutoString family;
6692 for (;;) {
6693 if (!ParseOneFamily(aErrorCode, family))
6694 return PR_FALSE;
6696 if (!ExpectSymbol(aErrorCode, ',', PR_TRUE))
6697 break;
6699 family.Append(PRUnichar(','));
6702 if (family.IsEmpty()) {
6703 return PR_FALSE;
6705 aValue.SetStringValue(family, eCSSUnit_String);
6706 return PR_TRUE;
6709 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
6710 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
6711 // local-src: 'local(' ( string | ident ) ')'
6713 PRBool
6714 CSSParserImpl::ParseFontSrc(nsresult& aErrorCode, nsCSSValue& aValue)
6716 // could we maybe turn nsCSSValue::Array into nsTArray<nsCSSValue>?
6717 nsTArray<nsCSSValue> values;
6718 nsCSSValue cur;
6719 for (;;) {
6720 if (!GetToken(aErrorCode, PR_TRUE))
6721 break;
6723 if (mToken.mType == eCSSToken_Function &&
6724 mToken.mIdent.LowerCaseEqualsLiteral("url")) {
6725 if (!ParseURL(aErrorCode, cur))
6726 return PR_FALSE;
6727 values.AppendElement(cur);
6728 if (!ParseFontSrcFormat(aErrorCode, values))
6729 return PR_FALSE;
6731 } else if (mToken.mType == eCSSToken_Function &&
6732 mToken.mIdent.LowerCaseEqualsLiteral("local")) {
6733 // css3-fonts does not specify a formal grammar for local().
6734 // The text permits both unquoted identifiers and quoted
6735 // strings. We resolve this ambiguity in the spec by
6736 // assuming that the appropriate production is a single
6737 // <family-name>, possibly surrounded by whitespace.
6739 nsAutoString family;
6740 if (!ExpectSymbol(aErrorCode, '(', PR_FALSE))
6741 return PR_FALSE;
6742 if (!ParseOneFamily(aErrorCode, family))
6743 return PR_FALSE;
6744 if (!ExpectSymbol(aErrorCode, ')', PR_TRUE))
6745 return PR_FALSE;
6747 // the style parameters to the nsFont constructor are ignored,
6748 // because it's only being used to call EnumerateFamilies
6749 nsFont font(family, 0, 0, 0, 0, 0);
6750 ExtractFirstFamilyData dat;
6752 font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
6753 if (!dat.mGood)
6754 return PR_FALSE;
6756 cur.SetStringValue(dat.mFamilyName, eCSSUnit_Local_Font);
6757 values.AppendElement(cur);
6758 } else {
6759 return PR_FALSE;
6762 if (!ExpectSymbol(aErrorCode, ',', PR_TRUE))
6763 break;
6766 nsRefPtr<nsCSSValue::Array> srcVals
6767 = nsCSSValue::Array::Create(values.Length());
6768 if (!srcVals)
6769 return PR_FALSE;
6771 PRUint32 i;
6772 for (i = 0; i < values.Length(); i++)
6773 srcVals->Item(i) = values[i];
6774 aValue.SetArrayValue(srcVals, eCSSUnit_Array);
6775 return PR_TRUE;
6778 PRBool
6779 CSSParserImpl::ParseFontSrcFormat(nsresult& aErrorCode,
6780 nsTArray<nsCSSValue> & values)
6782 if (!GetToken(aErrorCode, PR_TRUE))
6783 return PR_TRUE; // EOF harmless here
6784 if (mToken.mType != eCSSToken_Function ||
6785 !mToken.mIdent.LowerCaseEqualsLiteral("format")) {
6786 UngetToken();
6787 return PR_TRUE;
6789 if (!ExpectSymbol(aErrorCode, '(', PR_FALSE))
6790 return PR_FALSE;
6792 do {
6793 if (!GetToken(aErrorCode, PR_TRUE))
6794 return PR_FALSE;
6796 if (mToken.mType != eCSSToken_String)
6797 return PR_FALSE;
6799 nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format);
6800 values.AppendElement(cur);
6801 } while (ExpectSymbol(aErrorCode, ',', PR_TRUE));
6803 return ExpectSymbol(aErrorCode, ')', PR_TRUE);
6806 // font-ranges: urange ( ',' urange )*
6807 PRBool
6808 CSSParserImpl::ParseFontRanges(nsresult& aErrorCode, nsCSSValue& aValue)
6810 // not currently implemented (bug 443976)
6811 return PR_FALSE;
6814 PRBool CSSParserImpl::ParseListStyle(nsresult& aErrorCode)
6816 const PRInt32 numProps = 3;
6817 static const nsCSSProperty listStyleIDs[] = {
6818 eCSSProperty_list_style_type,
6819 eCSSProperty_list_style_position,
6820 eCSSProperty_list_style_image
6823 nsCSSValue values[numProps];
6824 PRInt32 index;
6825 PRInt32 found = ParseChoice(aErrorCode, values, listStyleIDs, numProps);
6826 if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode))) {
6827 return PR_FALSE;
6830 // Provide default values
6831 if ((found & 1) == 0) {
6832 values[0].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
6834 if ((found & 2) == 0) {
6835 values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, eCSSUnit_Enumerated);
6837 if ((found & 4) == 0) {
6838 values[2].SetNoneValue();
6841 for (index = 0; index < numProps; index++) {
6842 AppendValue(listStyleIDs[index], values[index]);
6844 return PR_TRUE;
6847 PRBool CSSParserImpl::ParseMargin(nsresult& aErrorCode)
6849 static const nsCSSProperty kMarginSideIDs[] = {
6850 eCSSProperty_margin_top,
6851 eCSSProperty_margin_right_value,
6852 eCSSProperty_margin_bottom,
6853 eCSSProperty_margin_left_value
6855 static const nsCSSProperty kMarginSources[] = {
6856 eCSSProperty_margin_left_ltr_source,
6857 eCSSProperty_margin_left_rtl_source,
6858 eCSSProperty_margin_right_ltr_source,
6859 eCSSProperty_margin_right_rtl_source,
6860 eCSSProperty_UNKNOWN
6863 // do this now, in case 4 values weren't specified
6864 InitBoxPropsAsPhysical(kMarginSources);
6865 return ParseBoxProperties(aErrorCode, mTempData.mMargin.mMargin,
6866 kMarginSideIDs);
6869 PRBool CSSParserImpl::ParseMarks(nsresult& aErrorCode, nsCSSValue& aValue)
6871 if (ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kPageMarksKTable)) {
6872 if (eCSSUnit_Enumerated == aValue.GetUnit()) {
6873 if (PR_FALSE == ExpectEndProperty(aErrorCode)) {
6874 nsCSSValue second;
6875 if (ParseEnum(aErrorCode, second, nsCSSProps::kPageMarksKTable)) {
6876 aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(), eCSSUnit_Enumerated);
6877 return PR_TRUE;
6879 return PR_FALSE;
6882 return PR_TRUE;
6884 return PR_FALSE;
6887 PRBool CSSParserImpl::ParseOutline(nsresult& aErrorCode)
6889 const PRInt32 numProps = 3;
6890 static const nsCSSProperty kOutlineIDs[] = {
6891 eCSSProperty_outline_color,
6892 eCSSProperty_outline_style,
6893 eCSSProperty_outline_width
6896 nsCSSValue values[numProps];
6897 PRInt32 found = ParseChoice(aErrorCode, values, kOutlineIDs, numProps);
6898 if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode))) {
6899 return PR_FALSE;
6902 // Provide default values
6903 if ((found & 1) == 0) {
6904 #ifdef GFX_HAS_INVERT
6905 values[0].SetIntValue(NS_STYLE_COLOR_INVERT, eCSSUnit_Enumerated);
6906 #else
6907 values[0].SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
6908 #endif
6910 if ((found & 2) == 0) {
6911 values[1].SetNoneValue();
6913 if ((found & 4) == 0) {
6914 values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
6917 PRInt32 index;
6918 for (index = 0; index < numProps; index++) {
6919 AppendValue(kOutlineIDs[index], values[index]);
6921 return PR_TRUE;
6924 PRBool CSSParserImpl::ParseOverflow(nsresult& aErrorCode)
6926 nsCSSValue overflow;
6927 if (!ParseVariant(aErrorCode, overflow, VARIANT_AHK,
6928 nsCSSProps::kOverflowKTable) ||
6929 !ExpectEndProperty(aErrorCode))
6930 return PR_FALSE;
6932 nsCSSValue overflowX(overflow);
6933 nsCSSValue overflowY(overflow);
6934 if (eCSSUnit_Enumerated == overflow.GetUnit())
6935 switch(overflow.GetIntValue()) {
6936 case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL:
6937 overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
6938 overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
6939 break;
6940 case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL:
6941 overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated);
6942 overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated);
6943 break;
6945 AppendValue(eCSSProperty_overflow_x, overflowX);
6946 AppendValue(eCSSProperty_overflow_y, overflowY);
6947 aErrorCode = NS_OK;
6948 return PR_TRUE;
6951 PRBool CSSParserImpl::ParsePadding(nsresult& aErrorCode)
6953 static const nsCSSProperty kPaddingSideIDs[] = {
6954 eCSSProperty_padding_top,
6955 eCSSProperty_padding_right_value,
6956 eCSSProperty_padding_bottom,
6957 eCSSProperty_padding_left_value
6959 static const nsCSSProperty kPaddingSources[] = {
6960 eCSSProperty_padding_left_ltr_source,
6961 eCSSProperty_padding_left_rtl_source,
6962 eCSSProperty_padding_right_ltr_source,
6963 eCSSProperty_padding_right_rtl_source,
6964 eCSSProperty_UNKNOWN
6967 // do this now, in case 4 values weren't specified
6968 InitBoxPropsAsPhysical(kPaddingSources);
6969 return ParseBoxProperties(aErrorCode, mTempData.mMargin.mPadding,
6970 kPaddingSideIDs);
6973 PRBool CSSParserImpl::ParsePause(nsresult& aErrorCode)
6975 nsCSSValue before;
6976 if (ParseSingleValueProperty(aErrorCode, before, eCSSProperty_pause_before)) {
6977 if (eCSSUnit_Inherit != before.GetUnit() && eCSSUnit_Initial != before.GetUnit()) {
6978 nsCSSValue after;
6979 if (ParseSingleValueProperty(aErrorCode, after, eCSSProperty_pause_after)) {
6980 if (ExpectEndProperty(aErrorCode)) {
6981 AppendValue(eCSSProperty_pause_before, before);
6982 AppendValue(eCSSProperty_pause_after, after);
6983 return PR_TRUE;
6985 return PR_FALSE;
6988 if (ExpectEndProperty(aErrorCode)) {
6989 AppendValue(eCSSProperty_pause_before, before);
6990 AppendValue(eCSSProperty_pause_after, before);
6991 return PR_TRUE;
6994 return PR_FALSE;
6997 PRBool CSSParserImpl::ParseQuotes(nsresult& aErrorCode)
6999 nsCSSValue open;
7000 if (ParseVariant(aErrorCode, open, VARIANT_HOS, nsnull)) {
7001 if (eCSSUnit_String == open.GetUnit()) {
7002 nsCSSValuePairList* quotesHead = new nsCSSValuePairList();
7003 nsCSSValuePairList* quotes = quotesHead;
7004 if (nsnull == quotes) {
7005 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
7006 return PR_FALSE;
7008 quotes->mXValue = open;
7009 while (nsnull != quotes) {
7010 // get mandatory close
7011 if (ParseVariant(aErrorCode, quotes->mYValue, VARIANT_STRING,
7012 nsnull)) {
7013 if (ExpectEndProperty(aErrorCode)) {
7014 mTempData.SetPropertyBit(eCSSProperty_quotes);
7015 mTempData.mContent.mQuotes = quotesHead;
7016 aErrorCode = NS_OK;
7017 return PR_TRUE;
7019 // look for another open
7020 if (ParseVariant(aErrorCode, open, VARIANT_STRING, nsnull)) {
7021 quotes->mNext = new nsCSSValuePairList();
7022 quotes = quotes->mNext;
7023 if (nsnull != quotes) {
7024 quotes->mXValue = open;
7025 continue;
7027 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
7030 break;
7032 delete quotesHead;
7033 return PR_FALSE;
7035 if (ExpectEndProperty(aErrorCode)) {
7036 nsCSSValuePairList* quotesHead = new nsCSSValuePairList();
7037 quotesHead->mXValue = open;
7038 mTempData.mContent.mQuotes = quotesHead;
7039 mTempData.SetPropertyBit(eCSSProperty_quotes);
7040 return PR_TRUE;
7043 return PR_FALSE;
7046 PRBool CSSParserImpl::ParseSize(nsresult& aErrorCode)
7048 nsCSSValue width;
7049 if (ParseVariant(aErrorCode, width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
7050 if (width.IsLengthUnit()) {
7051 nsCSSValue height;
7052 if (ParseVariant(aErrorCode, height, VARIANT_LENGTH, nsnull)) {
7053 if (ExpectEndProperty(aErrorCode)) {
7054 mTempData.mPage.mSize.mXValue = width;
7055 mTempData.mPage.mSize.mYValue = height;
7056 mTempData.SetPropertyBit(eCSSProperty_size);
7057 return PR_TRUE;
7059 return PR_FALSE;
7062 if (ExpectEndProperty(aErrorCode)) {
7063 mTempData.mPage.mSize.SetBothValuesTo(width);
7064 mTempData.SetPropertyBit(eCSSProperty_size);
7065 return PR_TRUE;
7068 return PR_FALSE;
7071 PRBool CSSParserImpl::ParseTextDecoration(nsresult& aErrorCode, nsCSSValue& aValue)
7073 if (ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kTextDecorationKTable)) {
7074 if (eCSSUnit_Enumerated == aValue.GetUnit()) { // look for more keywords
7075 PRInt32 intValue = aValue.GetIntValue();
7076 nsCSSValue keyword;
7077 PRInt32 index;
7078 for (index = 0; index < 3; index++) {
7079 if (ParseEnum(aErrorCode, keyword, nsCSSProps::kTextDecorationKTable)) {
7080 intValue |= keyword.GetIntValue();
7082 else {
7083 break;
7086 aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
7088 return PR_TRUE;
7090 return PR_FALSE;
7093 nsCSSValueList* CSSParserImpl::ParseCSSShadowList(nsresult& aErrorCode,
7094 PRBool aUsesSpread)
7096 nsAutoParseCompoundProperty compound(this);
7098 // Parses x, y, radius, color (in two possible orders)
7099 // This parses the input into a list. Either it contains just a "none" or
7100 // "inherit" value, or a list of arrays.
7101 // The resulting arrays will always contain the above order, with color and
7102 // radius as null values as needed
7103 enum {
7104 IndexX,
7105 IndexY,
7106 IndexRadius,
7107 IndexSpread,
7108 IndexColor
7111 nsCSSValueList *list = nsnull;
7112 for (nsCSSValueList **curp = &list, *cur; ; curp = &cur->mNext) {
7113 cur = *curp = new nsCSSValueList();
7114 if (!cur) {
7115 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
7116 break;
7118 if (!ParseVariant(aErrorCode, cur->mValue,
7119 (cur == list) ? VARIANT_HC | VARIANT_LENGTH | VARIANT_NONE
7120 : VARIANT_COLOR | VARIANT_LENGTH,
7121 nsnull)) {
7122 break;
7125 nsCSSUnit unit = cur->mValue.GetUnit();
7126 if (unit != eCSSUnit_None && unit != eCSSUnit_Inherit &&
7127 unit != eCSSUnit_Initial) {
7128 nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(5);
7129 if (!val) {
7130 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
7131 break;
7133 PRBool haveColor = PR_FALSE;
7134 if (cur->mValue.IsLengthUnit()) {
7135 val->Item(IndexX) = cur->mValue;
7136 } else {
7137 // Must be a color (as string or color value)
7138 NS_ASSERTION(unit == eCSSUnit_String || unit == eCSSUnit_Color ||
7139 unit == eCSSUnit_EnumColor,
7140 "Must be a color value (named color, numeric color, "
7141 "or system color)");
7142 haveColor = PR_TRUE;
7143 val->Item(IndexColor) = cur->mValue;
7145 // Parse the X coordinate
7146 if (!ParseVariant(aErrorCode, val->Item(IndexX), VARIANT_LENGTH,
7147 nsnull)) {
7148 break;
7151 cur->mValue.SetArrayValue(val, eCSSUnit_Array);
7153 // Y coordinate; this one is not optional
7154 if (!ParseVariant(aErrorCode, val->Item(IndexY), VARIANT_LENGTH, nsnull)) {
7155 break;
7158 // Optional radius. Ignore errors except if they pass a negative
7159 // value which we must reject. If we use ParsePositiveVariant we can't
7160 // tell the difference between an unspecified radius and a negative
7161 // radius, so that's why we don't use it.
7162 if (ParseVariant(aErrorCode, val->Item(IndexRadius), VARIANT_LENGTH, nsnull) &&
7163 val->Item(IndexRadius).GetFloatValue() < 0) {
7164 break;
7167 if (aUsesSpread) {
7168 // Optional spread (ignore errors)
7169 ParseVariant(aErrorCode, val->Item(IndexSpread), VARIANT_LENGTH,
7170 nsnull);
7173 if (!haveColor) {
7174 // Optional color (ignore errors)
7175 ParseVariant(aErrorCode, val->Item(IndexColor), VARIANT_COLOR,
7176 nsnull);
7179 // Might be at a comma now
7180 if (ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
7181 // Go to next value
7182 continue;
7186 if (!ExpectEndProperty(aErrorCode)) {
7187 // If we don't have a comma to delimit the next value, we
7188 // must be at the end of the property. Otherwise we've hit
7189 // something else, which is an error.
7190 break;
7193 // Only success case here, since having the failure case at the
7194 // end allows more sharing of code.
7195 aErrorCode = NS_OK;
7196 return list;
7198 // Have failure case at the end so we can |break| to get to it.
7199 delete list;
7200 return nsnull;
7203 PRBool CSSParserImpl::ParseTextShadow(nsresult& aErrorCode)
7205 nsCSSValueList* list = ParseCSSShadowList(aErrorCode, PR_FALSE);
7206 if (!list)
7207 return PR_FALSE;
7209 mTempData.SetPropertyBit(eCSSProperty_text_shadow);
7210 mTempData.mText.mTextShadow = list;
7211 return PR_TRUE;
7214 PRBool CSSParserImpl::ParseBoxShadow(nsresult& aErrorCode)
7216 nsCSSValueList* list = ParseCSSShadowList(aErrorCode, PR_TRUE);
7217 if (!list)
7218 return PR_FALSE;
7220 mTempData.SetPropertyBit(eCSSProperty_box_shadow);
7221 mTempData.mMargin.mBoxShadow = list;
7222 return PR_TRUE;
7225 PRBool
7226 CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix,
7227 PRInt32* aNameSpaceID,
7228 nsresult& aErrorCode)
7230 NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here");
7231 NS_PRECONDITION(NS_SUCCEEDED(aErrorCode),
7232 "Why did we even get called?");
7234 PRInt32 nameSpaceID = kNameSpaceID_Unknown;
7235 if (mNameSpaceMap) {
7236 // user-specified identifiers are case-sensitive (bug 416106)
7237 nsCOMPtr<nsIAtom> prefix = do_GetAtom(aPrefix);
7238 nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix);
7240 // else no declared namespaces
7242 NS_ASSERTION(nameSpaceID != kNameSpaceID_None, "Shouldn't happen!");
7244 if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it
7245 const PRUnichar *params[] = {
7246 aPrefix.get()
7248 REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, params);
7249 if (mUnresolvablePrefixException) {
7250 aErrorCode = NS_ERROR_DOM_NAMESPACE_ERR;
7252 return PR_FALSE;
7255 *aNameSpaceID = nameSpaceID;
7256 return PR_TRUE;
7259 void
7260 CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector)
7262 aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
7263 if (mNameSpaceMap) { // look for default namespace
7264 PRInt32 defaultID = mNameSpaceMap->FindNameSpaceID(nsnull);
7265 if (defaultID != kNameSpaceID_None) {
7266 aSelector.SetNameSpace(defaultID);
7271 #ifdef MOZ_SVG
7272 PRBool CSSParserImpl::ParsePaint(nsresult& aErrorCode,
7273 nsCSSValuePair* aResult,
7274 nsCSSProperty aPropID)
7276 if (!ParseVariant(aErrorCode, aResult->mXValue,
7277 VARIANT_HC | VARIANT_NONE | VARIANT_URL,
7278 nsnull))
7279 return PR_FALSE;
7281 if (aResult->mXValue.GetUnit() == eCSSUnit_URL) {
7282 if (!ParseVariant(aErrorCode, aResult->mYValue, VARIANT_COLOR | VARIANT_NONE,
7283 nsnull))
7284 aResult->mYValue.SetColorValue(NS_RGB(0, 0, 0));
7285 } else {
7286 aResult->mYValue = aResult->mXValue;
7289 if (!ExpectEndProperty(aErrorCode))
7290 return PR_FALSE;
7292 mTempData.SetPropertyBit(aPropID);
7293 return PR_TRUE;
7296 PRBool CSSParserImpl::ParseDasharray(nsresult& aErrorCode)
7298 nsCSSValue value;
7299 if (ParseVariant(aErrorCode, value, VARIANT_HLPN | VARIANT_NONE, nsnull)) {
7300 nsCSSValueList *listHead = new nsCSSValueList;
7301 nsCSSValueList *list = listHead;
7302 if (!list) {
7303 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
7304 return PR_FALSE;
7307 list->mValue = value;
7309 for (;;) {
7310 if (ExpectEndProperty(aErrorCode)) {
7311 mTempData.SetPropertyBit(eCSSProperty_stroke_dasharray);
7312 mTempData.mSVG.mStrokeDasharray = listHead;
7313 aErrorCode = NS_OK;
7314 return PR_TRUE;
7317 if (eCSSUnit_Inherit == value.GetUnit() ||
7318 eCSSUnit_Initial == value.GetUnit() ||
7319 eCSSUnit_None == value.GetUnit())
7320 break;
7322 if (!ExpectSymbol(aErrorCode, ',', PR_TRUE))
7323 break;
7325 if (!ParseVariant(aErrorCode, value,
7326 VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_NUMBER,
7327 nsnull))
7328 break;
7330 list->mNext = new nsCSSValueList;
7331 list = list->mNext;
7332 if (list)
7333 list->mValue = value;
7334 else {
7335 aErrorCode = NS_ERROR_OUT_OF_MEMORY;
7336 break;
7339 delete listHead;
7341 return PR_FALSE;
7344 PRBool CSSParserImpl::ParseMarker(nsresult& aErrorCode)
7346 nsCSSValue marker;
7347 if (ParseSingleValueProperty(aErrorCode, marker, eCSSProperty_marker_end)) {
7348 if (ExpectEndProperty(aErrorCode)) {
7349 AppendValue(eCSSProperty_marker_end, marker);
7350 AppendValue(eCSSProperty_marker_mid, marker);
7351 AppendValue(eCSSProperty_marker_start, marker);
7352 aErrorCode = NS_OK;
7353 return PR_TRUE;
7356 return PR_FALSE;
7358 #endif